Mercurial > traipse_dev
comparison orpg/tools/predTextCtrl.py @ 0:4385a7d0efd1 grumpy-goblin
Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author | sirebral |
---|---|
date | Tue, 14 Jul 2009 16:41:58 -0500 |
parents | |
children | 5aff3ef1ae46 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4385a7d0efd1 |
---|---|
1 # Copyright (C) 2001 The OpenRPG Project | |
2 # | |
3 # openrpg-dev@lists.sourceforge.net | |
4 # | |
5 # This program is free software; you can redistribute it and/or modify | |
6 # it under the terms of the GNU General Public License as published by | |
7 # the Free Software Foundation; either version 2 of the License, or | |
8 # (at your option) any later version. | |
9 # | |
10 # This program is distributed in the hope that it will be useful, | |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 # GNU General Public License for more details. | |
14 # | |
15 # You should have received a copy of the GNU General Public License | |
16 # along with this program; if not, write to the Free Software | |
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 # -- | |
19 # | |
20 # File: predTextCtrl.py | |
21 # Author: Andrew Bennett | |
22 # Maintainer: Andrew Bennett | |
23 # Version: $id:$ | |
24 # | |
25 # Description: This file contains an extension to the wxPython wx.TextCtrl that provides predictive word completion | |
26 # based on a word file loaded at instantiation. Also, it learns new words as you type, dynamically | |
27 # adjusting a weight for each word based on how often you type it. | |
28 # | |
29 | |
30 ## | |
31 ## Module Loading | |
32 ## | |
33 | |
34 import string | |
35 from orpg.orpg_windows import * | |
36 | |
37 # This line added to test CVS commit | |
38 | |
39 ## | |
40 ## Class Definitions | |
41 ## | |
42 | |
43 # This class implements a tree node that represents a letter | |
44 # | |
45 # Defines: | |
46 # __init__(self,filename) | |
47 # | |
48 class Letter: | |
49 def __init__(self,asciiCharIn,parentNodeIn): | |
50 self.asciiChar = asciiCharIn # should be an ASCII char | |
51 self.parentNode = parentNodeIn # should be ref to a class Letter | |
52 self.priority = 0 # should be an int | |
53 self.mostCommon = self # should be a ref to a class Letter | |
54 self.children = {} # should be a ref to a dictionary of class Letter | |
55 | |
56 def __str__(self): | |
57 return self.asciiChar | |
58 | |
59 | |
60 # This class implements the tree structure to hold the words | |
61 # | |
62 # Defines: | |
63 # __init__(self,filename) | |
64 # updateMostCommon(self,target) | |
65 # setWord(self,wordText,priority,sumFlag) | |
66 # addWord(self,wordText) | |
67 # setWord(self,wordText) | |
68 # setWordPriority(self,wordText,priority) | |
69 # findWordNode(self,wordText) returns class Letter | |
70 # findWordPriority(self,wordText) returns int | |
71 # getPredition(self,k,cur) returns string | |
72 class LetterTreeClass(object): | |
73 | |
74 # Initialization subroutine. | |
75 # | |
76 # self : instance of self | |
77 # filename : name of word file to use | |
78 # | |
79 # returns None | |
80 # | |
81 # Purpose: Constructor for LetterTree. Basically, it initializes itself with a word file, if present. | |
82 def __init__(self, singletonKey): | |
83 if not isinstance(singletonKey, _SingletonKey): | |
84 raise invalid_argument(_("Use LetterTree() to get access to singleton")) | |
85 | |
86 self.rootNode = Letter("",None) # rootNode is a class Letter | |
87 self.rootNode.children = {} # initialize the children list | |
88 | |
89 | |
90 # updateMostCommon subroutine. | |
91 # | |
92 # self : instance of self | |
93 # target : class Letter that was updated | |
94 # | |
95 # Returns None | |
96 # | |
97 # Purpose: Updates all of the parent nodes of the target, such that their mostCommon member | |
98 # points to the proper class Letter, based on the newly updated priorities. | |
99 def updateMostCommon(self, target): | |
100 # cur is a class Letter | |
101 prev = target.parentNode # prev is a class Letter | |
102 while prev: | |
103 if prev.mostCommon is None: | |
104 prev.mostCommon = target | |
105 else: | |
106 if target.priority > prev.mostCommon.priority: | |
107 prev.mostCommon = target | |
108 prev = prev.parentNode | |
109 | |
110 | |
111 | |
112 # setWord subroutine. | |
113 # | |
114 # self : instance of self | |
115 # wordText : string representing word to add | |
116 # priority : integer priority to set the word | |
117 # sumFlag : if True, add the priority to the existing, else assign the priority | |
118 # | |
119 # Returns: None | |
120 # | |
121 # Purpose: Sets or increments the priority of a word, adding the word if necessary | |
122 def setWord(self,wordText,priority = 1,sumFlag = 0): | |
123 | |
124 cur = self.rootNode # start from the root | |
125 | |
126 for ch in wordText: # for each character in the word | |
127 if cur.children.has_key(ch): # check to see if we've found a new word | |
128 | |
129 cur = cur.children[ch] # if we haven't found a new word, move to the next letter and try again | |
130 | |
131 else: # in this clause, we're creating a new branch, as the word is new | |
132 newLetter = Letter(ch,cur) # create a new class Letter using this ascii code and the current letter as a parent | |
133 | |
134 if cur is self.rootNode: # special case: Others expect the top level letters to point to None, not self.rootNode | |
135 newLetter.parentNode = None | |
136 | |
137 cur.children[ch] = newLetter # add the new letter to the list of children of the current letter | |
138 cur = newLetter # make the new letter the current one for the next time through | |
139 | |
140 # at this point, cur is pointing to the last letter of either a new or existing word. | |
141 | |
142 if sumFlag: # if the caller wants to add to the existing (0 if new) | |
143 cur.priority += priority | |
144 else: # else, the set the priority directly | |
145 cur.priority = priority | |
146 | |
147 self.updateMostCommon(cur) # this will run back through the tree to fix up the mostCommon members | |
148 | |
149 | |
150 # addWord subroutine. | |
151 # | |
152 # self : instance of self | |
153 # wordText : string representing word to add | |
154 # | |
155 # Returns: None | |
156 # | |
157 # Purpose: Convenience method that wraps setWord. Used to add words known not to exist. | |
158 def addWord(self,wordText): | |
159 self.setWord(wordText,priority = 1) | |
160 | |
161 | |
162 # incWord subroutine. | |
163 # | |
164 # self : instance of self | |
165 # wordText : string representing word to add | |
166 # | |
167 # Returns: None | |
168 # | |
169 # Purpose: Convenience method that wraps setWord. Used to increment the priority of existing words and add new words. | |
170 # Note: Generally, this method can be used instead of addWord. | |
171 def incWord(self,wordText): | |
172 self.setWord(wordText,priority = 1, sumFlag = 1) | |
173 | |
174 | |
175 # setWordPriority subroutine. | |
176 # | |
177 # self : instance of self | |
178 # wordText : string representing word to add | |
179 # priority: int that is the new priority | |
180 # | |
181 # Returns: None | |
182 # | |
183 # Purpose: Convenience method that wraps setWord. Sets existing words to priority or adds new words with priority = priority | |
184 def setWordPriority(self,wordText,priority): | |
185 self.setWord(wordText,priority = priority) | |
186 | |
187 | |
188 # findWordNode subroutine. | |
189 # | |
190 # self : instance of self | |
191 # wordText : string representing word to add | |
192 # | |
193 # Returns: class Letter or None if word isn't found. | |
194 # | |
195 # Purpose: Given a word, it returns the class Letter node that corresponds to the word. Used mostly in prep for a call to | |
196 # getPrediction() | |
197 def findWordNode(self,wordText): #returns class Letter that represents the last letter in the word | |
198 | |
199 cur = self.rootNode # start at the root | |
200 | |
201 for ch in wordText: # move through each letter in the word | |
202 if cur.children.has_key(ch): # if the next letter exists, make cur equal that letter and loop | |
203 cur = cur.children[ch] | |
204 else: | |
205 return None # return None if letter not found | |
206 | |
207 return cur # return cur, as this points to the last letter if we got this far | |
208 | |
209 | |
210 # findWordPriority subroutine. | |
211 # | |
212 # self : instance of self | |
213 # wordText : string representing word to add | |
214 # | |
215 # Returns: Int representing the word's priority or 0 if not found. | |
216 # | |
217 # Purpose: Returns the priority of the given word | |
218 def findWordPriority(self,wordText): | |
219 | |
220 cur = self.findWordNode(wordText) # find the class Letter node that corresponds to this word | |
221 if cur: | |
222 return cur.priority # if it was found, return it's priority | |
223 else: | |
224 return 0 # else, return 0, meaning word not found | |
225 | |
226 | |
227 def printTree(self, current=None): | |
228 letters = [] | |
229 if current is None: | |
230 current = self.rootNode | |
231 | |
232 for l, letter in current.children.iteritems(): | |
233 letters.append(str(letter)) | |
234 if letter.children != {}: | |
235 m = self.printTree(letter) | |
236 letters.append(m) | |
237 | |
238 return letters | |
239 | |
240 | |
241 | |
242 # getPrediction subroutine. | |
243 # | |
244 # self : instance of self | |
245 # k : ASCII char that was typed | |
246 # cur : class Letter that points to the current node in LetterTree | |
247 # | |
248 # Returns: The predicted text or "" if none found | |
249 # | |
250 # Purpose: This is the meat and potatoes of data structure. It takes the "current" Letter node and the next key typed | |
251 # and returns it's guess of the rest of the word, based on the highest priority letter in the rest of the branch. | |
252 def getPrediction(self,k,cur): | |
253 | |
254 if cur.children.has_key(k) : # check to see if the key typed is a sub branch | |
255 # If so, make a prediction. Otherwise, do the else at the bottom of | |
256 # the method (see below). | |
257 | |
258 cur = cur.children[k] # set the cur to the typed key's class Letter in the sub-branch | |
259 | |
260 backtrace = cur.mostCommon # backtrace is a class Letter. It's used as a placeholder to back trace | |
261 # from the last letter of the mostCommon word in the | |
262 # sub-tree up through the tree until we meet ourself at cur. We'll | |
263 # build the guess text this way | |
264 | |
265 returnText = "" # returnText is a string. This will act as a buffer to hold the string | |
266 # we build. | |
267 | |
268 while cur is not backtrace: # Here's the loop. We loop until we've snaked our way back to cur | |
269 | |
270 returnText = backtrace.asciiChar + returnText # Create a new string that is the character at backtrace + everything | |
271 # so far. So, for "tion" we'll build "n","on","ion","tion" as we .. | |
272 | |
273 backtrace = backtrace.parentNode # ... climb back towards cur | |
274 | |
275 return returnText # And, having reached here, we've met up with cur, and returnText holds | |
276 # the string we built. Return it. | |
277 | |
278 else: # This is the else to the original if. | |
279 # If the letter typed isn't in a sub branch, then | |
280 # the letter being typed isn't in our tree, so | |
281 return "" # return the empty string | |
282 | |
283 | |
284 # End of class LetterTree! | |
285 | |
286 | |
287 | |
288 class _SingletonKey(object): | |
289 def __new__(cls, *args, **kwargs): | |
290 if not hasattr(cls, '_inst'): | |
291 cls._inst = super(_SingletonKey, cls).__new__(cls, *args, **kwargs) | |
292 return cls._inst | |
293 | |
294 __key = _SingletonKey() | |
295 LetterTree = LetterTreeClass(__key) | |
296 | |
297 # This class extends wx.TextCtrl | |
298 # | |
299 # Extends: wx.TextCtrl | |
300 # | |
301 # Overrides: | |
302 # wx.TextCtrl.__init__(self,parent,id,value,size,style,name) | |
303 # wx.TextCtrl.OnChar(self,Event) | |
304 # | |
305 # Defines: | |
306 # findWord(self,insert,st) | |
307 class predTextCtrl(wx.TextCtrl): | |
308 | |
309 # Initialization subroutine. | |
310 # | |
311 # self : instance of self | |
312 # parent: reference to parent window (wxWindow, me-thinks) | |
313 # id: new Window Id, default to -1 to create new (I think: see docs for wxPython) | |
314 # value: String that is the initial value the control holds, defaulting to "" | |
315 # size: defaults to wx.DefaultSize | |
316 # style: defaults to 0 | |
317 # name: defaults to "text" | |
318 # keyHook: must be a function pointer that takes self and a GetKeyCode object | |
319 # validator: defaults to None | |
320 # | |
321 # Note: These parameters are essentially just passed back to the native wx.TextCtrl. | |
322 # I basically just included (stole) enough of them from chatutils.py to make | |
323 # it work. Known missing args are pos and validator, which aren't used by | |
324 # chatutils.py. | |
325 # | |
326 # Returns: None | |
327 # | |
328 # Purpose: Constructor for predTextCtrl. Calls wx.TextCtrl.__init__ to get default init | |
329 # behavior and then inits a LetterTree and captures the parent for later use in | |
330 # passing events up the chain. | |
331 def __init__(self, parent, id = -1, value = "" , size = wx.DefaultSize, style = 0, name = "text",keyHook = None, validator=None): | |
332 | |
333 # Call super() for default behavior | |
334 if validator: | |
335 wx.TextCtrl.__init__(self, parent, id=id, value=value, size=size, style=style, name=name, validator=validator ) | |
336 else: | |
337 wx.TextCtrl.__init__(self, parent, id=id, value=value, size=size, style=style, name=name) | |
338 | |
339 self.tree = LetterTree # Instantiate a new LetterTree. | |
340 # TODO: make name of word file an argument. | |
341 | |
342 | |
343 | |
344 | |
345 self.parent = parent # Save parent for later use in passing KeyEvents | |
346 | |
347 self.cur = self.tree.rootNode # self.cur is a short cut placeholder for typing consecutive chars | |
348 # It may be vestigal | |
349 | |
350 self.keyHook = keyHook # Save the keyHook passed in | |
351 | |
352 | |
353 # findWord subroutine. | |
354 # | |
355 # self : instance of self | |
356 # insert: index of last char in st | |
357 # st : string from insert to the left | |
358 # | |
359 # Note: This implementation is about the third one for this method. Originally, | |
360 # st was an arbitrary string and insert was the point within | |
361 # this string to begin looking left. Since, I finally got it | |
362 # to work right as it is around 2 in the morning, I'm not touching it, for now. | |
363 # | |
364 # Returns: String that is the word or "" if none found. This generally doesn't | |
365 # happen, as st usually ends in a letter, which will be returned. | |
366 # | |
367 # Purpose: This function is generally used to figure out the beginning of the | |
368 # current word being typed, for later use in a LetterTree.getPrediction() | |
369 def findWord(self,insert,st): | |
370 | |
371 # Good luck reading this one. Basically, I started out with an idea, and fiddled with the | |
372 # constants as best I could until it worked. It's not a piece of work of which I'm too | |
373 # proud. Basically, it's intent is to check each character to the left until it finds one | |
374 # that isn't a letter. If it finds such a character, it stops and returns the slice | |
375 # from that point to insert. Otherwise, it returns the whole thing, due to begin being | |
376 # initialized to 0 | |
377 | |
378 begin = 0 | |
379 for offset in range(insert - 1): | |
380 if st[-(offset + 2)] not in string.letters: | |
381 begin = insert - (offset + 1) | |
382 break | |
383 return st[begin:insert] | |
384 | |
385 | |
386 # OnChar subroutine. | |
387 # | |
388 # self : instance of self | |
389 # event: a GetKeyCode object | |
390 # | |
391 # Returns: None | |
392 # | |
393 # Purpose: This function is the key event handler for predTextCtrl. It handles what it | |
394 # needs to and passes the event on to it's parent's OnChar method. | |
395 def OnChar(self,event): | |
396 | |
397 # Before we do anything, call the keyHook handler, if not None | |
398 # This is currently used to implement the typing/not_typing messages in a manner that | |
399 # doesn't place the code here. Maybe I should reconsider that. :) | |
400 if(self.keyHook): | |
401 if self.keyHook(event) == 1: # if the passed in keyHook routine returns a one, it wants no predictive behavior | |
402 self.parent.OnChar(event) | |
403 return | |
404 | |
405 | |
406 | |
407 # This bit converts the GetKeyCode() return (int) to a char if it's in a certain range | |
408 asciiKey = "" | |
409 if (event.GetKeyCode() < 256) and (event.GetKeyCode() > 19): | |
410 asciiKey = chr(event.GetKeyCode()) | |
411 | |
412 | |
413 if asciiKey == "": # If we didn't convert it to a char, then process based on the int GetKeyCodes | |
414 | |
415 if event.GetKeyCode() == wx.WXK_TAB: # We want to hook tabs to allow the user to signify acceptance of a | |
416 # predicted word. | |
417 # Handle Tab key | |
418 | |
419 fromPos = toPos = 0 # get the current selection range | |
420 (fromPos,toPos) = self.GetSelection() | |
421 | |
422 if (toPos - fromPos) == 0: # if there is no selection, pass tab on | |
423 self.parent.OnChar(event) | |
424 return | |
425 | |
426 else: # This means at least one char is selected | |
427 | |
428 self.SetInsertionPoint(toPos) # move the insertion point to the end of the selection | |
429 self.SetSelection(toPos,toPos) # and set the selection to no chars | |
430 # The prediction, if any, had been inserted into the text earlier, so | |
431 # moving the insertion point to the spot directly afterwards is | |
432 # equivalent to acceptance. Without this, the next typed key would | |
433 # clobber the prediction. | |
434 | |
435 return # Don't pass tab on in this case | |
436 | |
437 elif event.GetKeyCode() == wx.WXK_RETURN: # We want to hook returns, so that we can update the word list | |
438 | |
439 st = self.GetValue() # Grab the text from the control | |
440 newSt = "" # Init a buffer | |
441 | |
442 # This block of code, by popular demand, changes the behavior of the control to ignore any prediction that | |
443 # hasn't been "accepted" when the enter key is struck. | |
444 (startSel,endSel) = self.GetSelection() # get the curren selection | |
445 | |
446 # | |
447 # Start update | |
448 # Changed the following to allow for more friendly behavior in | |
449 # a multilined predTextCtrl. | |
450 # | |
451 # front = st[:startSel] # Slice off the text to the front of where we are | |
452 # back = st[endSel:] # Slice off the text to the end from where we are | |
453 # st = front + back # This expression creates a string that get rid of any selected text. | |
454 # self.SetValue(st) | |
455 | |
456 self.Remove( startSel, endSel ) | |
457 st = string.strip( self.GetValue() ) | |
458 # | |
459 # End update | |
460 # | |
461 | |
462 # this loop will walk through every character in st and add it to | |
463 # newSt if it's a letter. If it's not a letter, (e.g. a comma or | |
464 # hyphen) a space is added to newSt in it's place. | |
465 for ch in st: | |
466 if ch not in string.letters: | |
467 newSt += " " | |
468 else: | |
469 newSt += ch | |
470 | |
471 # Now that we've got a string of just letter sequences (words) and spaces | |
472 # split it and to a LetterTree.incWord on the lowercase version of it. | |
473 # Reminder: incWord will increment the priority of existing words and add | |
474 # new ones | |
475 for aWord in string.split(newSt): | |
476 self.tree.incWord(string.lower(aWord)) | |
477 | |
478 self.parent.OnChar(event) # Now that all of the words are added, pass the event and return | |
479 return | |
480 | |
481 # We want to capture the right arrow key to fix a slight UI bug that occurs when one right arrows | |
482 # out of a selection. I set the InsertionPoint to the beginning of the selection. When the default | |
483 # right arrow event occurs, the selection goes away, but the cursor is in an unexpected location. | |
484 # This snippet fixes this behavior and then passes on the event. | |
485 elif event.GetKeyCode() == wx.WXK_RIGHT: | |
486 (startSel,endSel) = self.GetSelection() | |
487 self.SetInsertionPoint(endSel) | |
488 self.parent.OnChar(event) | |
489 return | |
490 | |
491 # Ditto as wx.WXK_RIGHT, but for completeness sake | |
492 elif event.GetKeyCode() == wx.WXK_LEFT: | |
493 (startSel,endSel) = self.GetSelection() | |
494 self.SetInsertionPoint(startSel) | |
495 self.parent.OnChar(event) | |
496 return | |
497 | |
498 | |
499 else: | |
500 # Handle any other non-ascii events by calling parent's OnChar() | |
501 self.parent.OnChar(event) #Call super.OnChar to get default behavior | |
502 return | |
503 | |
504 | |
505 elif asciiKey in string.letters: | |
506 # This is the real meat and potatoes of predTextCtrl. This is where most of the | |
507 # wx.TextCtrl logic is changed. | |
508 | |
509 (startSel,endSel) = self.GetSelection() # get the curren selection | |
510 st = self.GetValue() # and the text in the control | |
511 front = st[:startSel] # Slice off the text to the front of where we are | |
512 back = st[endSel:] # Slice off the text to the end from where we are | |
513 | |
514 st = front + asciiKey + back # This expression creates a string that will insert the | |
515 # typed character (asciiKey is generated at the | |
516 # beginning of OnChar()) into the text. If there | |
517 # was text selected, that text will not be part | |
518 # of the new string, due to the way front and back | |
519 # were sliced. | |
520 | |
521 insert = startSel + 1 # creates an int that denotes where the new InsertionPoint | |
522 # should be. | |
523 | |
524 curWord = "" # Assume there's a problem with finding the curWord | |
525 | |
526 if (len(back) == 0) or (back[0] not in string.letters): # We should only insert a prediction if we are typing | |
527 # at the end of a word, not in the middle. There are | |
528 # three cases: we are typing at the end of the string or | |
529 # we are typing in the middle of the string and the next | |
530 # char is NOT a letter or we are typing in the middle of the | |
531 # string and the next char IS a letter. Only the former two | |
532 # cases denote that we should make a prediction | |
533 # Note: The order of the two logical clauses here is important! | |
534 # If len(back) == 0, then the expression back[0] will | |
535 # blow up with an out of bounds array subscript. Luckily | |
536 # the or operator is a short-circuit operator and in this | |
537 # case will only evaluate back[0] if len(back) != 0, in | |
538 # which we're safely in bounds. | |
539 | |
540 curWord = self.findWord(insert,front + asciiKey) # Now that we know we're supposed to make a prediction, | |
541 # let's find what word root to use in our prediction. | |
542 # Note: This is using the confusing findWord method. I | |
543 # send it insert and the text from the beginning | |
544 # of the text through the key just entered. This is | |
545 # NOT the original usage, but it does work. See | |
546 # findWord() for more details. | |
547 | |
548 else: # Here, we've found we're in the middle of a word, so we're | |
549 # going to call the parent's event handler. | |
550 self.parent.OnChar(event) | |
551 return | |
552 | |
553 if curWord == "": # Here, we do a quick check to make sure we have a good root | |
554 # word. If not, allow the default thing to happen. Of course, | |
555 # now that I'm documenting this, it occurs to me to wonder why | |
556 # I didn't do the same thing I just talked about. Hmmmmmm. | |
557 | |
558 self.parent.OnChar(event) # we're done here | |
559 return | |
560 | |
561 self.cur = self.tree.findWordNode(string.lower(curWord[:-1])) # Still with me? We're almost done. At this point, we | |
562 # need to convert our word string to a Letter node, | |
563 # because that's what getPrediction expects. Notice | |
564 # that we're feeding in the string with the last | |
565 # char sliced off. For developmentally historical | |
566 # reasons, getPrediction wants the node just before | |
567 # the typed character and the typed char separately. | |
568 | |
569 | |
570 if self.cur is None: | |
571 self.parent.OnChar(event) # if there's no word or no match, we're done | |
572 return | |
573 | |
574 | |
575 # get the prediction | |
576 predictText = self.tree.getPrediction(string.lower(asciiKey),self.cur) # This is the big prediction, as noted above | |
577 # Note the use of string.lower() because we | |
578 # keep the word list in all lower case,but we | |
579 # want to be able to match any capitalization | |
580 | |
581 if predictText == "": | |
582 self.parent.OnChar(event) # if there's no prediction, we're done | |
583 return | |
584 | |
585 # And now for the big finale. We're going to take the string st | |
586 # we created earlier and insert the prediction right after the | |
587 # newly typed character. | |
588 front = st[:insert] # Grab a new front from st | |
589 back = st[insert:] # Grab a new back | |
590 | |
591 st = front + predictText + back # Insert the prediction | |
592 | |
593 self.SetValue(st) # Now, overwrite the controls text with the new text | |
594 self.SetInsertionPoint(insert) # Set the proper insertion point, directly behind the | |
595 # newly typed character and directly in front of the | |
596 # predicted text. | |
597 | |
598 self.SetSelection(insert,insert+len(predictText)) # Very important! Set the selection to encompass the predicted | |
599 # text. This way, the user can ignore the prediction by simply | |
600 # continuing to type. Remember, if the user wants the prediction | |
601 # s/he must strike the tab key at this point. Of course, one could | |
602 # just use the right arrow key as well, but that's not as easy to | |
603 # reach. | |
604 | |
605 return # Done! Do NOT pass the event on at this point, because it's all done. | |
606 | |
607 | |
608 else: | |
609 # Handle every other non-letter ascii (e.g. semicolon) by passing the event on. | |
610 self.parent.OnChar(event) #Call super.OnChar to get default behavior | |
611 return | |
612 | |
613 # End of class predTextCtrl! |