# o wxTimeCtrl -> TimeCtrl
#
-'''\
+"""\
====================
Masked Edit Overview
====================
Eg::
- formatcodes = ',', groupChar="'" allows 12'345.34
+ formatcodes = ',', groupChar='\'' allows 12'345.34
formatcodes = ',', groupChar='.', decimalChar=',' allows 12.345,34
(These are control-level parameters.)
If not specified as a keyword argument, the default controlType is
controlTypes.TEXT.
-'''
+"""
"""
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
self._SetKeycodeHandler(WXK_UP, self.IncrementValue)
self._SetKeyHandler('-', self._OnChangeSign)
+ (Setting a func of None removes any keyhandler for the given key.)
+
"Navigation" keys are assumed to change the cursor position, and
therefore don't cause automatic motion of the cursor as insertable
characters do.
used by the control. <func> should take the event as argument
and return False if no further action on the key is necessary.
"""
- self._keyhandlers[keycode] = func
+ if func:
+ self._keyhandlers[keycode] = func
+ elif self._keyhandlers.has_key(keycode):
+ del self._keyhandlers[keycode]
def _SetKeyHandler(self, char, func):
self._nav.append(keycode)
if handler:
self._keyhandlers[keycode] = handler
+ elif self.keyhandlers.has_key(keycode):
+ del self._keyhandlers[keycode]
+
def _AddNavKey(self, char, handler=None):
self._nav.append(keycode)
if func:
self._keyhandlers[keycode] = func
+ elif self.keyhandlers.has_key(keycode):
+ del self._keyhandlers[keycode]
def _processMask(self, mask):
keycode = ord(key)
if not self._keyhandlers.has_key(keycode):
self._SetKeyHandler(key, self._OnChangeSign)
+ elif self._isInt or self._isFloat:
+ signkeys = ['-', '+', ' ', '(', ')']
+ for key in signkeys:
+ keycode = ord(key)
+ if self._keyhandlers.has_key(keycode) and self._keyhandlers[keycode] == self._OnChangeSign:
+ self._SetKeyHandler(key, None)
self._SetKeycodeHandler(wx.WXK_UP, self._OnUpNumeric) # (adds "shift" to up arrow, and calls _OnChangeField)
# On ., truncate contents right of cursor to decimal point (if any)
- # leaves cusor after decimal point if floating point, otherwise at 0.
- if not self._keyhandlers.has_key(ord(self._decimalChar)):
+ # leaves cursor after decimal point if floating point, otherwise at 0.
+ if not self._keyhandlers.has_key(ord(self._decimalChar)) or self._keyhandlers[ord(self._decimalChar)] != self._OnDecimalPoint:
self._SetKeyHandler(self._decimalChar, self._OnDecimalPoint)
- if not self._keyhandlers.has_key(ord(self._shiftDecimalChar)):
+
+ if not self._keyhandlers.has_key(ord(self._shiftDecimalChar)) or self._keyhandlers[ord(self._shiftDecimalChar)] != self._OnChangeField:
self._SetKeyHandler(self._shiftDecimalChar, self._OnChangeField) # (Shift-'.' == '>' on US keyboards)
# Allow selective insert of groupchar in numbers:
- if not self._keyhandlers.has_key(ord(self._fields[0]._groupChar)):
+ if not self._keyhandlers.has_key(ord(self._fields[0]._groupChar)) or self._keyhandlers[ord(self._fields[0]._groupChar)] != self._OnGroupChar:
self._SetKeyHandler(self._fields[0]._groupChar, self._OnGroupChar)
## dbg(indent=0, suspend=0)
field = self._FindField(pos)
## dbg("key ='%s'" % chr(key))
-## if chr(key) == ' ':
+ if chr(key) == ' ':
## dbg('okSpaces?', field._okSpaces)
-## pass
+ pass
char = chr(key) # (must work if we got this far)
if pos == year2dig and unadjusted[year2dig] != newstr[year2dig]:
newpos = pos+2
+## dbg('queuing insertion point: (%d)' % newpos)
wx.CallAfter(self._SetInsertionPoint, newpos)
if match_field is not None:
else:
newfield = self._FindField(newpos)
if newfield != field and newfield._selectOnFieldEntry:
+## dbg('queuing insertion point: (%d)' % newfield._extent[0])
+ wx.CallAfter(self._SetInsertionPoint, newfield._extent[0])
## dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
else:
value = self._eraseSelection()
integer = self._fields[0]
start, end = integer._extent
+ sel_start, sel_to = self._GetSelection()
#### dbg('adjusted pos:', pos)
if chr(key) in ('-','+','(', ')') or (chr(key) == " " and pos == self._signpos):
cursign = self._isNeg
## dbg('cursign:', cursign)
if chr(key) in ('-','(', ')'):
- self._isNeg = (not self._isNeg) ## flip value
+ if sel_start <= self._signpos:
+ self._isNeg = True
+ else:
+ self._isNeg = (not self._isNeg) ## flip value
else:
self._isNeg = False
## dbg('isNeg?', self._isNeg)
## dbg('match found:', choice)
match = index
break
- else: dbg('choice: "%s" - no match' % choice)
+ else:
+## dbg('choice: "%s" - no match' % choice)
+ pass
if match is not None:
## dbg('matched', match)
pass
# first space for sign, and last one if using parens.
if( self._signOk
and ((pos == self._signpos and key in (ord('-'), ord('+'), ord(' ')) )
- or self._useParens and pos == self._masklength -1)):
+ or (self._useParens and pos == self._masklength -1))):
## dbg('adjusted pos:', pos, indent=0)
return pos
field = self._FindField(pos)
## dbg('field._insertRight?', field._insertRight)
+## if self._signOk: dbg('self._signpos:', self._signpos)
if field._insertRight: # if allow right-insert
start, end = field._extent
slice = self._GetValue()[start:end].strip()
## # restore selection
## self._SetSelection(sel_start, pos)
- elif self._signOk and sel_start == 0: # if selected to beginning and signed,
+ # if selected to beginning and signed, and not changing sign explicitly:
+ elif self._signOk and sel_start == 0 and key not in (ord('-'), ord('+'), ord(' ')):
# adjust to past reserved sign position:
pos = self._fields[0]._extent[0]
+## dbg('adjusting field to ', pos)
self._SetInsertionPoint(pos)
- # restore selection
- self._SetSelection(pos, sel_to)
+ # but keep original selection, to allow replacement of any sign:
+ self._SetSelection(0, sel_to)
else:
pass # leave position/selection alone
# if entire field is selected or position is at end and field is not full,
- # or if allowed to right-insert at any point in field and field is not full and cursor is not at a fillChar:
+ # or if allowed to right-insert at any point in field and field is not full and cursor is not at a fillChar
+ # or the field is a singleton integer field and is currently 0 and we're at the end:
if( (sel_start, sel_to) == field._extent
- or (pos == end and input_len < field_len)):
+ or (pos == end and ((input_len < field_len)
+ or (field_len == 1
+ and input_len == field_len
+ and field._isInt
+ and value[end-1] == '0'
+ )
+ ) ) ):
pos = end - 1
## dbg('pos = end - 1 = ', pos, 'right_insert? 1')
right_insert = True
## dbg("checking appropriate regex's")
value = self._eraseSelection(self._GetValue())
if right_insert:
+ # move the position to the right side of the insertion:
at = pos+1
else:
at = pos
if self._signOk:
text, signpos, right_signpos = self._getSignedValue()
## dbg('text: "%s", signpos:' % text, signpos)
+ if text and signpos != self._signpos:
+ self._signpos = signpos
if not text or text[signpos] not in ('-','('):
self._isNeg = False
## dbg('no valid sign found; new sign:', self._isNeg)
- if text and signpos != self._signpos:
- self._signpos = signpos
elif text and self._valid and not self._isNeg and text[signpos] in ('-', '('):
## dbg('setting _isNeg to True')
self._isNeg = True
fstr = text[start:end]
erasable_chars = [field._fillChar, ' ']
- if field._padZero:
+ # if zero padding field, or a single digit, and currently a value of 0, allow erasure of 0:
+ if field._padZero or (field._isInt and (end - start == 1) and fstr[0] == '0'):
erasable_chars.append('0')
erased = ''
#### dbg("fstr[0]:'%s'" % fstr[0])
#### dbg('field_index:', field._index)
#### dbg("fstr[0] in erasable_chars?", fstr[0] in erasable_chars)
-#### dbg("self._signOk and field._index == 0 and fstr[0] in ('-','(')?",
-## self._signOk and field._index == 0 and fstr[0] in ('-','('))
+#### dbg("self._signOk and field._index == 0 and fstr[0] in ('-','(')?", self._signOk and field._index == 0 and fstr[0] in ('-','('))
if fstr[0] in erasable_chars or (self._signOk and field._index == 0 and fstr[0] in ('-','(')):
erased = fstr[0]
#### dbg('value: "%s"' % text)
old_right_signpos = text.find(')')
if field._allowInsert and not field._insertRight and sel_to <= end and sel_start >= start:
- # inserting within a left-insert-capable field
+## dbg('inserting within a left-insert-capable field')
field_len = end - start
before = text[start:sel_start]
after = text[sel_to:end].strip()
char = char.decode(self._defaultEncoding)
newtext = left + char + right
+#### dbg('left: "%s"' % left)
+#### dbg('right: "%s"' % right)
+#### dbg('newtext: "%s"' % newtext)
if self._signOk and self._useParens:
# Balance parentheses:
if not valid:
## dbg('cannot convert string to valid time')
pass
- if valid: dbg('valid date')
+## if valid: dbg('valid date')
## dbg(indent=0)
return valid
""" Handler for EVT_KILL_FOCUS event.
"""
## dbg('MaskedEditMixin::_OnKillFocus', 'isDate=',self._isDate, indent=1)
+ if self.IsBeingDeleted() or self.GetParent().IsBeingDeleted():
+ return
if self._mask and self._IsEditable():
self._AdjustField(self._GetInsertionPoint())
self._CheckValid() ## Call valid handler
field = self._FindField(self._GetInsertionPoint())
edit_start, edit_end = field._extent
if field._selectOnFieldEntry:
+ if self._isFloat or self._isInt and field == self._fields[0]:
+ edit_start = 0
self._SetInsertionPoint(edit_start)
self._SetSelection(edit_start, edit_end)
if integer._selectOnFieldEntry:
## dbg('select on field entry:')
- self._SetInsertionPoint(edit_start)
- self._SetSelection(edit_start, edit_end)
+ self._SetInsertionPoint(0)
+ self._SetSelection(0, edit_end)
elif integer._insertRight:
## dbg('moving insertion point to end')
and sel_to >= edit_end
and not self._GetValue()[edit_start:sel_start].strip() ):
# text won't fit within selection, but left of selection is empty;
- # check to see if we can expand selection to accomodate the value:
+ # check to see if we can expand selection to accommodate the value:
empty_space = sel_start - edit_start
amount_needed = len(paste_text) - (sel_to - sel_start)
if amount_needed <= empty_space:
## CHANGELOG:
## ====================
+## Version 1.9
+## 1. Now ignores kill focus events when being destroyed.
+## 2. Added missing call to set insertion point on changing fields.
+## 3. Modified SetKeyHandler() to accept None as means of removing one.
+## 4. Fixed keyhandler processing for group and decimal character changes.
+## 5. Fixed a problem that prevented input into the integer digit of a
+## integerwidth=1 numctrl, if the current value was 0.
+## 6. Fixed logic involving processing of "_signOk" flag, to remove default
+## sign key handlers if false, so that SetAllowNegative(False) in the
+## NumCtrl works properly.
+## 7. Fixed selection logic for numeric controls so that if selectOnFieldEntry
+## is true, and the integer portion of an integer format control is selected
+## and the sign position is selected, the sign keys will always result in a
+## negative value, rather than toggling the previous sign.
+##
+##
## Version 1.8
## 1. Fixed bug involving incorrect variable name, causing combobox autocomplete to fail.
## 2. Added proper support for unicode version of wxPython