Changeset 1467
- Timestamp:
- 07/07/08 18:01:07 (2 months ago)
- Location:
- trunk/peppy
- Files:
-
- 2 modified
-
actions/minibuffer.py (modified) (1 diff)
-
lib/textctrl_autocomplete.py (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/peppy/actions/minibuffer.py
r1418 r1467 413 413 found = [] 414 414 for match in self.sorted: 415 if match. startswith(text):415 if match.find(text) >= 0: 416 416 found.append(match) 417 417 return found -
trunk/peppy/lib/textctrl_autocomplete.py
r1419 r1467 8 8 """ 9 9 import locale, wx, sys, cStringIO 10 import wx.lib.mixins.listctrl as listmix11 from wx import ImageFromStream, BitmapFromImage12 #----------------------------------------------------------------------13 def getSmallUpArrowData():14 return \15 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\16 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\17 \x00\x00<IDAT8\x8dcddbf\xa0\x040Q\xa4{h\x18\xf0\xff\xdf\xdf\xffd\x1b\x00\xd3\18 \x8c\xcf\x10\x9c\x06\xa0k\xc2e\x08m\xc2\x00\x97m\xd8\xc41\x0c \x14h\xe8\xf2\19 \x8c\xa3)q\x10\x18\x00\x00R\xd8#\xec\xb2\xcd\xc1Y\x00\x00\x00\x00IEND\xaeB`\20 \x82'21 22 def getSmallUpArrowBitmap():23 return BitmapFromImage(getSmallUpArrowImage())24 25 def getSmallUpArrowImage():26 stream = cStringIO.StringIO(getSmallUpArrowData())27 return ImageFromStream(stream)28 29 def getSmallDnArrowData():30 return \31 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\32 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\33 \x00\x00HIDAT8\x8dcddbf\xa0\x040Q\xa4{\xd4\x00\x06\x06\x06\x06\x06\x16t\x81\34 \xff\xff\xfe\xfe'\xa4\x89\x91\x89\x99\x11\xa7\x0b\x90%\ti\xc6j\x00>C\xb0\x89\35 \xd3.\x10\xd1m\xc3\xe5*\xbc.\x80i\xc2\x17.\x8c\xa3y\x81\x01\x00\xa1\x0e\x04e\36 ?\x84B\xef\x00\x00\x00\x00IEND\xaeB`\x82"37 38 def getSmallDnArrowBitmap():39 return BitmapFromImage(getSmallDnArrowImage())40 41 def getSmallDnArrowImage():42 stream = cStringIO.StringIO(getSmallDnArrowData())43 return ImageFromStream(stream)44 45 10 46 11 … … 171 136 172 137 173 #---------------------------------------------------------------------- 174 class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): 175 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, 176 size=wx.DefaultSize, style=0): 177 wx.ListCtrl.__init__(self, parent, ID, pos, size, style) 178 listmix.ListCtrlAutoWidthMixin.__init__(self) 179 180 class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin ): 181 def __init__ ( self, parent, colNames=None, choices = None, 182 multiChoices=None, showHead=True, dropDownClick=True, 183 colFetch=-1, colSearch=0, hideOnNoMatch=True, 184 selectCallback=None, entryCallback=None, matchFunction=None, 138 class HighlightListBox(wx.HtmlListBox): 139 """Virtual List Box used to highlight partial matches in the ListCtrl""" 140 def setChoices(self, choices): 141 self.choices = choices 142 self.clearBold() 143 self.SetItemCount(len(choices)) 144 145 def setBold(self, index, start, count): 146 self.bold[index] = (start, start+count) 147 #print(self.bold[index]) 148 self.RefreshLine(index) 149 150 def clearBold(self): 151 self.bold = [None] * len(self.choices) 152 if len(self.choices) > 0: 153 self.RefreshLines(0, len(self.choices) - 1) 154 155 def OnGetItem(self, n): 156 if hasattr(self, 'bold') and self.bold[n] is not None: 157 text = self.choices[n] 158 start = self.bold[n][0] 159 end = self.bold[n][1] 160 val = "%s<b>%s</b>%s" % (text[0:start], text[start:end], text[end:]) 161 #print "index %d: %s" % (n, val) 162 return val 163 return self.choices[n] 164 165 def selectNextMatch(self): 166 """Set the selection to the next match in the list. 167 168 A match is determined by any item that exists in the bold list. The 169 selection will wrap around to the top of the list. 170 """ 171 start = self.GetSelection() 172 index = start + 1 173 count = self.GetLineCount() 174 while index < count: 175 if self.bold[index] is not None: 176 break 177 index += 1 178 if index >= count: 179 index = 0 180 while index < start: 181 if self.bold[index] is not None: 182 break 183 index += 1 184 if index == start and self.bold[index] is None: 185 index += 1 186 if index >= count: 187 index = 0 188 if index < count: 189 self.SetSelection(index) 190 191 def selectPrevMatch(self): 192 """Set the selection to the previous match in the list. 193 194 A match is determined by any item that exists in the bold list. The 195 selection will wrap around to the bottom of the list. 196 """ 197 start = self.GetSelection() 198 index = start - 1 199 count = self.GetLineCount() 200 while index >= 0: 201 if self.bold[index] is not None: 202 break 203 index -= 1 204 if index < 0: 205 index = count - 1 206 while index > start: 207 if self.bold[index] is not None: 208 break 209 index -= 1 210 if index == start and self.bold[index] is None: 211 index -= 1 212 if index < 0: 213 index = count - 1 214 if index >= 0: 215 self.SetSelection(index) 216 217 def getLargestCommon(self, input): 218 """Get the largest common string from the list of already matched strings. 219 220 Using the list of strings that already have been matched, see if there 221 are any more characters that can be matched that are in common among 222 all of the strings. 223 """ 224 found = None 225 start = len(input) 226 most = None 227 for index, bold in enumerate(self.bold): 228 if bold is not None: 229 text = self.choices[index][bold[0]:] 230 if found is None: 231 # get first match 232 found = text 233 most = len(found) 234 else: 235 for j in range(start, most): 236 if text[j] != found[j]: 237 most = j 238 break 239 #print "after %d %s: found=%s most=%d" % (index, text, found, most) 240 if not found: 241 found = input 242 else: 243 found = found[0:most] 244 return found 245 246 247 class TextCtrlAutoComplete(wx.TextCtrl): 248 def __init__ (self, parent, choices=None, dropDownClick=True, 249 hideOnNoMatch=True, entryCallback=None, matchFunction=None, 185 250 mac=False, 186 251 **therest) : … … 197 262 #Some variables 198 263 self._dropDownClick = dropDownClick 199 self._colNames = colNames200 self._multiChoices = multiChoices201 self._showHead = showHead202 264 self._choices = choices 203 265 self._lastinsertionpoint = 0 204 266 self._hideOnNoMatch = hideOnNoMatch 205 self._selectCallback = selectCallback206 267 self._entryCallback = entryCallback 207 268 self._matchFunction = matchFunction 208 269 self._screenheight = wx.SystemSettings.GetMetric( wx.SYS_SCREEN_Y ) 209 270 self._tabCount = 0 210 #sort variable needed by listmix 211 self.itemDataMap = dict() 212 #Load and sort data 213 ## if not (self._multiChoices or self._choices): 214 ## raise ValueError, "Pass me at least one of multiChoices OR choices" 215 #widgets 271 self._onlyAtStart = False 272 216 273 self._mac = mac | bool(wx.Platform == '__WXMAC__') 217 274 if self._mac: … … 219 276 else: 220 277 self.dropdown = wx.PopupWindow( self ) 221 #Control the style 222 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING 223 if not (showHead and multiChoices) : 224 flags = flags | wx.LC_NO_HEADER 225 #Create the list and bind the events 226 self.dropdownlistbox = myListCtrl( self.dropdown, style=flags, 227 pos=wx.Point( 0, 0) ) 278 279 self.dropdownlistbox = HighlightListBox(self.dropdown) 228 280 if mac: 229 281 self.dropdown.setList(self.dropdownlistbox) … … 233 285 self.Bind( wx.EVT_KILL_FOCUS, self.onControlChanged, self ) 234 286 235 #initialize the parent 236 if multiChoices: ln = len(multiChoices) 237 else: ln = 1 238 #else: ln = len(choices) 239 listmix.ColumnSorterMixin.__init__(self, ln) 240 #load the data 241 if multiChoices: self.SetMultipleChoices (multiChoices, colSearch=colSearch, colFetch=colFetch) 242 else: self.SetChoices ( choices ) 243 ## gp = self 244 ## while ( gp != None ) : 245 ## gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) 246 ## gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) 247 ## gp = gp.GetParent() 287 self.SetChoices(choices) 288 248 289 self.Bind( wx.EVT_TEXT , self.onEnteredText, self ) 249 290 self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self ) … … 255 296 self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.onListClick) 256 297 self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.onListDClick) 257 self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.onListColClick)258 self.il = wx.ImageList(16, 16)259 self.sm_dn = self.il.Add(getSmallDnArrowBitmap())260 self.sm_up = self.il.Add(getSmallUpArrowBitmap())261 self.dropdownlistbox.SetImageList(self.il, wx.IMAGE_LIST_SMALL)262 self._ascending = True263 264 #-- methods called from mixin class265 def GetSortImages(self):266 return (self.sm_dn, self.sm_up)267 268 def GetListCtrl(self):269 return self.dropdownlistbox270 # -- event methods271 298 272 299 def onListClick(self, evt): 273 toSel , flag= self.dropdownlistbox.HitTest( evt.GetPosition() )300 toSel = self.dropdownlistbox.HitTest( evt.GetPosition() ) 274 301 #no values on poition, return 275 302 if toSel == -1: return 276 self.dropdownlistbox.Se lect(toSel)303 self.dropdownlistbox.SetSelection(toSel) 277 304 278 305 def onListDClick(self, evt): 279 306 self._setValueFromSelected() 280 281 def onListColClick(self, evt):282 col = evt.GetColumn()283 #reverse the sort284 if col == self._colSearch:285 self._ascending = not self._ascending286 self.SortListItems( evt.GetColumn(), ascending=self._ascending )287 self._colSearch = evt.GetColumn()288 evt.Skip()289 307 290 308 def onEnteredText(self, event): … … 292 310 if self._entryCallback: 293 311 self._entryCallback() 312 self.dropdownlistbox.clearBold() 294 313 if not text: 295 314 # control is empty; hide dropdown if shown: … … 298 317 event.Skip() 299 318 return 319 best = None 300 320 found = False 301 if self._multiChoices: 302 #load the sorted data into the listbox 303 dd = self.dropdownlistbox 304 choices = [dd.GetItem(x, self._colSearch).GetText() 305 for x in xrange(dd.GetItemCount())] 306 else: 307 choices = self._choices 321 nchar = len(text) 322 choices = self._choices 308 323 for numCh, choice in enumerate(choices): 309 if self._matchFunction and self._matchFunction(text, choice): 310 found = True 311 elif choice.lower().startswith(text.lower()) : 312 found = True 324 if self._matchFunction: 325 index, count = self._matchFunction(text, choice) 326 if count > 0: 327 found = True 328 self.dropdownlistbox.setBold(numCh, index, count) 329 else: 330 if self._onlyAtStart: 331 if choice.lower().startswith(text.lower()): 332 found = True 333 self.dropdownlistbox.setBold(numCh, 0, nchar) 334 else: 335 index = choice.lower().find(text.lower()) 336 if index >= 0: 337 found = True 338 self.dropdownlistbox.setBold(numCh, index, nchar) 313 339 if found: 314 self._showDropDown(True) 315 item = self.dropdownlistbox.GetItem(numCh) 316 toSel = item.GetId() 317 self.dropdownlistbox.Select(toSel) 318 #self.SetFocus() 319 break 320 if not found: 321 self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False) 340 if best is None: 341 best = numCh 342 #print("best = %s" % best) 343 if best is not None: 344 self._showDropDown(True) 345 self.dropdownlistbox.SetSelection(best) 346 #self.SetFocus() 347 else: 348 self.dropdownlistbox.SetSelection(-1) 322 349 if self._hideOnNoMatch: 323 350 self._showDropDown(False) … … 331 358 """ 332 359 skip = True 333 sel = self.dropdownlistbox.GetFirstSelected() 360 dd = self.dropdownlistbox 361 sel = dd.GetSelection() 334 362 visible = self.dropdown.IsShown() 335 363 KC = event.GetKeyCode() 336 364 if KC == wx.WXK_DOWN : 337 if sel < ( self.dropdownlistbox.GetItemCount () - 1) :338 self.dropdownlistbox.Select( sel+1 )365 if sel < (dd.GetItemCount () - 1) : 366 dd.SetSelection ( sel+1 ) 339 367 self._listItemVisible() 340 368 self._showDropDown () … … 342 370 elif KC == wx.WXK_UP : 343 371 if sel > 0 : 344 self.dropdownlistbox.Select( sel - 1 )372 dd.SetSelection ( sel - 1 ) 345 373 self._listItemVisible() 346 374 self._showDropDown () 347 375 skip = False 348 elif KC == wx.WXK_LEFT : 349 if not self._multiChoices: 350 event.Skip() 351 return 352 if self._colSearch > 0: 353 self._colSearch -=1 354 self._showDropDown () 355 elif KC == wx.WXK_RIGHT: 356 if not self._multiChoices: 357 event.Skip() 358 return 359 if self._colSearch < self.dropdownlistbox.GetColumnCount() -1: 360 self._colSearch += 1 361 self._showDropDown() 376 elif KC == wx.WXK_PAGEDOWN: 377 if sel == -1: 378 dd.SetSelection(0) 379 else: 380 first = dd.GetVisibleBegin() 381 last = dd.GetVisibleEnd() 382 if not dd.IsVisible(last): 383 last -= 1 384 dd.ScrollPages(1) 385 dd.SetSelection(last) 386 elif KC == wx.WXK_PAGEUP: 387 if sel == -1: 388 dd.SetSelection(dd.GetLineCount() - 1) 389 else: 390 first = dd.GetVisibleBegin() 391 last = dd.GetVisibleEnd() 392 dd.ScrollPages(-1) 393 dd.SetSelection(first) 362 394 if KC == wx.WXK_TAB: 363 395 #print self._choices … … 367 399 # Find the largest common match in the list of choices 368 400 # and expand to that 369 start = len(self.GetValue()) 370 matched = self._choices[0] 371 most = len(matched) 372 #print "before start=%d most=%d" % (start, most) 373 for match in self._choices[1:]: 374 for j in range(start, most): 375 if match[j] != matched[j]: 376 most = j 377 break 378 #print "after %s: start=%d most=%d" % (match, start, most) 379 if most == start: 380 break 381 if most > start: 382 self.AppendText(matched[start:most]) 401 input = self.GetValue() 402 text = dd.getLargestCommon(input) 403 if text != input: 404 self.SetValue(text) 383 405 self.SetInsertionPointEnd() 406 else: 407 self._tabCount = 1 384 408 self._tabCount += 1 385 409 if self._tabCount > 1: 386 410 # scroll the list on multiple consecutive tabs 387 dd = self.dropdownlistbox 388 count = dd.GetItemCount() 389 page = dd.GetCountPerPage() 390 sel = dd.GetFirstSelected() 391 sel += page 392 if sel >= count: 393 sel = 0 394 dd.Select(sel) 395 dd.EnsureVisible(sel) 396 397 # Lacking some "EnsureVisibleIsAtTop" method, make 398 # the selected item at the top of the list by 399 # forcing some more entries to be visible. 400 bot = sel + page - 1 401 if bot >= count: 402 bot = count - 1 403 dd.EnsureVisible(bot) 411 if event.GetModifiers() & wx.MOD_SHIFT: 412 dd.selectPrevMatch() 413 else: 414 dd.selectNextMatch() 404 415 405 416 skip = False … … 446 457 event.Skip() 447 458 448 # -- Interfaces methods449 def SetMultipleChoices(self, choices, colSearch=0, colFetch=-1):450 """ Set multi-column choice451 """452 self._multiChoices = choices453 self._choices = None454 if not isinstance(self._multiChoices, list):455 self._multiChoices = [ x for x in self._multiChoices]456 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING457 if not self._showHead:458 flags |= wx.LC_NO_HEADER459 self.dropdownlistbox.SetWindowStyleFlag(flags)460 #prevent errors on "old" systems461 if sys.version.startswith("2.3"):462 self._multiChoices.sort(lambda x, y: cmp(x[0].lower(), y[0].lower()))463 else:464 self._multiChoices.sort(key=lambda x: locale.strxfrm(x[0]).lower() )465 self._updateDataList(self._multiChoices)466 lChoices = len(choices)467 if lChoices < 2:468 raise ValueError, "You have to pass me a multi-dimension list"469 for numCol, rowValues in enumerate(choices[0]):470 if self._colNames: colName = self._colNames[numCol]471 else: colName = "Select %i" % numCol472 self.dropdownlistbox.InsertColumn(numCol, colName)473 for numRow, valRow in enumerate(choices):474 for numCol, colVal in enumerate(valRow):475 if numCol == 0:476 index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)477 self.dropdownlistbox.SetStringItem(index, numCol, colVal)478 self.dropdownlistbox.SetItemData(index, numRow)479 self._setListSize()480 self._colSearch = colSearch481 self._colFetch = colFetch482 483 459 def SetChoices(self, choices): 484 460 """ … … 486 462 The items will be sorted case insensitively. 487 463 """ 488 self._choices = choices489 self._multiChoices = None490 flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER491 self.dropdownlistbox.SetWindowStyleFlag(flags)492 464 if not isinstance(choices, list): 493 465 self._choices = [ x for x in choices] 466 else: 467 self._choices = choices 468 self.dropdownlistbox.setChoices(self._choices) 494 469 #prevent errors on "old" systems 495 470 if sys.version.startswith("2.3"): … … 500 475 except UnicodeEncodeError: 501 476 self._choices.sort(key=lambda x: locale.strxfrm(x.encode("UTF-8")).lower()) 502 self._updateDataList(self._choices)503 self.dropdownlistbox.InsertColumn(0, "")504 for num, colVal in enumerate(self._choices):505 index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)506 self.dropdownlistbox.SetStringItem(index, 0, colVal)507 self.dropdownlistbox.SetItemData(index, num)508 477 self._setListSize() 509 # there is only one choice for both search and fetch if setting a single column:510 self._colSearch = 0511 self._colFetch = -1512 478 513 479 def GetChoices(self): 514 if self._choices: 515 return self._choices 516 else: 517 return self._multiChoices 480 return self._choices 518 481 519 482 def SetSelectCallback(self, cb=None): … … 528 491 #-- Internal methods 529 492 def _setValueFromSelected( self ) : 530 """ 531 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. 532 Will do nothing if no item is selected in the wx.ListCtrl. 533 """ 534 sel = self.dropdownlistbox.GetFirstSelected() 535 if sel > -1: 536 if self._colFetch != -1: col = self._colFetch 537 else: col = self._colSearch 538 itemtext = self.dropdownlistbox.GetItem(sel, col).GetText() 539 if self._selectCallback: 540 dd = self.dropdownlistbox 541 values = [dd.GetItem(sel, x).GetText() 542 for x in xrange(dd.GetColumnCount())] 543 self._selectCallback( values ) 493 """ 494 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. 495 Will do nothing if no item is selected in the wx.ListCtrl. 496 """ 497 sel = self.dropdownlistbox.GetSelection() 498 if sel > -1: 499 itemtext = self._choices[sel] 544 500 self.SetValue (itemtext) 545 501 self.SetInsertionPointEnd () 546 self.SetSelection ( -1, -1)502 self.SetSelection(-1, -1) 547 503 self._showDropDown ( False ) 548 504 … … 569 525 Moves the selected item to the top of the list ensuring it is always visible. 570 526 """ 571 toSel = self.dropdownlistbox.Get FirstSelected()527 toSel = self.dropdownlistbox.GetSelection () 572 528 if toSel == -1: return 573 self.dropdownlistbox.EnsureVisible( toSel ) 574 575 def _updateDataList(self, choices): 576 #delete, if need, all the previous data 577 if self.dropdownlistbox.GetColumnCount() != 0: 578 self.dropdownlistbox.DeleteAllColumns() 579 self.dropdownlistbox.DeleteAllItems() 580 #and update the dict 581 if choices: 582 for numVal, data in enumerate(choices): 583 self.itemDataMap[numVal] = data 584 else: 585 numVal = 0 586 self.SetColumnCount(numVal) 529 self.dropdownlistbox.SetSelection( toSel ) 587 530 588 531 def _setListSize(self): 589 if self._multiChoices: 590 choices = self._multiChoices 591 else: 592 choices = self._choices 532 choices = self._choices 593 533 longest = 0 594 534 for choice in choices : … … 694 634 if c.startswith(t): return True 695 635 if c.startswith('www.'): c = c[4:] 696 return c.startswith(t) 636 if c.startswith(t): 637 return 0, len(t) 638 return -1, 0 697 639 698 640 def setDynamicChoices(self): … … 715 657 'aegis', 'ascertain', 'asteroid', 716 658 'beautiful', 'bold', 'classic', 717 'daring', 'da zzling', 'debonair', 'definitive',659 'daring', 'daft', 'debonair', 'definitive', 'defective', 718 660 'effective', 'elegant', 719 661 'http://python.org', 'http://www.google.com', 720 662 'fabulous', 'fantastic', 'friendly', 'forgiving', 'feature', 721 663 'sage', 'scarlet', 'scenic', 'seaside', 'showpiece', 'spiffy', 722 'www.wxPython.org', 'www.osafoundation.org' 664 'www.wxPython.org', 'www.osafoundation.org', 665 'zqqqq1', 'zqqqq2222', 'zqqqq2228', 723 666 ] 724 667 app = wx.PySimpleApp()
