Mercurial > traipse_dev
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 |