comparison orpg/tools/predTextCtrl.py @ 28:ff154cf3350c ornery-orc

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