Mercurial > traipse
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! |