comparison orpg/tools/predTextCtrl.py @ 195:b633f4c64aae alpha

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