From 5f280eaa578e05b3b85d9bcf5ab6c3dcee03a448 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 19 Jun 2004 16:25:55 +0000 Subject: [PATCH] Patch from Will Sadkin: - Fixed intra-right-insert-field erase. - Allowed right-insert in ipaddrctrl subfields. - Made _SetValue() place cursor after last non-blank character inserted, rather than end of mask. - Fixed combobox autoselect behavior to work similarly as above, so that said selection will only select the non-empty text, as per request. - Fixed some incorrect undo behavior for right-insert fields - Allowed derived classes (eg. numctrl) to pass modified values for undo processing (to handle/ignore grouping chars properly.) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@27898 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/wx/lib/masked/combobox.py | 13 +- wxPython/wx/lib/masked/ipaddrctrl.py | 6 +- wxPython/wx/lib/masked/maskededit.py | 184 ++++++++++++++++++++++----- wxPython/wx/lib/masked/numctrl.py | 156 +++++++++++++++++++++-- wxPython/wx/lib/masked/textctrl.py | 11 +- 5 files changed, 310 insertions(+), 60 deletions(-) diff --git a/wxPython/wx/lib/masked/combobox.py b/wxPython/wx/lib/masked/combobox.py index 8a78f9b651..c42db0c945 100644 --- a/wxPython/wx/lib/masked/combobox.py +++ b/wxPython/wx/lib/masked/combobox.py @@ -219,7 +219,7 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): # make SetValue behave the same as if you had typed the value in: try: - value = self._Paste(value, raise_on_invalid=True, just_return_value=True) + value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True) if self._isFloat: self._isNeg = False # (clear current assumptions) value = self._adjustFloat(value) @@ -240,9 +240,9 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): raise self._SetValue(value) -#### dbg('queuing insertion after .SetValue', self._masklength) - wx.CallAfter(self._SetInsertionPoint, self._masklength) - wx.CallAfter(self._SetSelection, self._masklength, self._masklength) +#### dbg('queuing insertion after .SetValue', replace_to) + wx.CallAfter(self._SetInsertionPoint, replace_to) + wx.CallAfter(self._SetSelection, replace_to, replace_to) def _Refresh(self): @@ -506,6 +506,11 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ): self._CheckValid() ## dbg('field._autoCompleteIndex:', match_index) ## dbg('self.GetSelection():', self.GetSelection()) + end = self._goEnd(getPosOnly=True) +## dbg('scheduling set of end position to:', end) + # work around bug in wx 2.5 + wx.CallAfter(self.SetInsertionPoint, 0) + wx.CallAfter(self.SetInsertionPoint, end) ## dbg(indent=0) diff --git a/wxPython/wx/lib/masked/ipaddrctrl.py b/wxPython/wx/lib/masked/ipaddrctrl.py index 9fea97faa6..4182c68281 100644 --- a/wxPython/wx/lib/masked/ipaddrctrl.py +++ b/wxPython/wx/lib/masked/ipaddrctrl.py @@ -83,7 +83,7 @@ class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ): if not kwargs.has_key('mask'): kwargs['mask'] = mask = "###.###.###.###" if not kwargs.has_key('formatcodes'): - kwargs['formatcodes'] = 'F_Sr<' + kwargs['formatcodes'] = 'F_Sr<>' if not kwargs.has_key('validRegex'): kwargs['validRegex'] = "( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))(\.( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))){3}" @@ -184,4 +184,6 @@ class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ): BaseMaskedTextCtrl.SetValue(self, value) ## dbg(indent=0) - +i=0 +## Version 1.1 +## Made ipaddrctrls allow right-insert in subfields, now that insert/cut/paste works better diff --git a/wxPython/wx/lib/masked/maskededit.py b/wxPython/wx/lib/masked/maskededit.py index ec0d1de1fc..a271fc4a7b 100644 --- a/wxPython/wx/lib/masked/maskededit.py +++ b/wxPython/wx/lib/masked/maskededit.py @@ -3098,7 +3098,7 @@ class MaskedEditMixin: return False - def _OnErase(self, event=None): + def _OnErase(self, event=None, just_return_value=False): """ Handles backspace and delete keypress in control. Should return False to skip other processing.""" ## dbg("MaskedEditMixin::_OnErase", indent=1) sel_start, sel_to = self._GetSelection() ## check for a range of selected text @@ -3316,6 +3316,11 @@ class MaskedEditMixin: ## dbg(indent=0) return False + if just_return_value: +## dbg(indent=0) + return newstr + + # else... ## dbg('setting value (later) to', newstr) wx.CallAfter(self._SetValue, newstr) ## dbg('setting insertion point (later) to', pos) @@ -4710,11 +4715,28 @@ class MaskedEditMixin: newtext = "" newpos = pos + # if >= 2 chars selected in a right-insert field, do appropriate erase on field, + # then set selection to end, and do usual right insert. + if sel_start != sel_to and sel_to >= sel_start+2: + field = self._FindField(sel_start) + if( field._insertRight # if right-insert + and field._allowInsert # and allow insert at any point in field + and field == self._FindField(sel_to) ): # and selection all in same field + text = self._OnErase(just_return_value=True) # remove selection before insert +## dbg('text after (left)erase: "%s"' % text) + pos = sel_start = sel_to + if pos != sel_start and sel_start == sel_to: # adjustpos must have moved the position; make selection match: sel_start = sel_to = pos ## dbg('field._insertRight?', field._insertRight) +## dbg('field._allowInsert?', field._allowInsert) +## dbg('sel_start, end', sel_start, end) + if sel_start < end: +## dbg('text[sel_start] != field._fillChar?', text[sel_start] != field._fillChar) + pass + if( field._insertRight # field allows right insert and ((sel_start, sel_to) == field._extent # and whole field selected or (sel_start == sel_to # or nothing selected @@ -4870,7 +4892,8 @@ class MaskedEditMixin: if match_index is not None and partial_match: matched_str = newtext newtext = self._ctrl_constraints._choices[match_index] - new_select_to = self._ctrl_constraints._extent[1] + edit_end = self._ctrl_constraints._extent[1] + new_select_to = min(edit_end, len(newvalue.rstrip())) match_field = self._ctrl_constraints if self._ctrl_constraints._insertRight: # adjust position to just after partial match in control: @@ -5423,11 +5446,31 @@ class MaskedEditMixin: field = self._FindField(sel_start) edit_start, edit_end = field._extent new_pos = None - if field._allowInsert and sel_to <= edit_end and sel_start + len(paste_text) < edit_end: + if field._allowInsert and sel_to <= edit_end and (sel_start + len(paste_text) < edit_end or field._insertRight): + if field._insertRight: + # want to paste to the left; see if it will fit: + left_text = self._GetValue()[edit_start:sel_start].lstrip() +## dbg('len(left_text):', len(left_text)) +## dbg('len(paste_text):', len(paste_text)) +## dbg('sel_start - (len(left_text) + len(paste_text)) >= edit_start?', sel_start - (len(left_text) + len(paste_text)) >= edit_start) + if sel_start - (len(left_text) - (sel_to - sel_start) + len(paste_text)) >= edit_start: + # will fit! create effective paste text, and move cursor back to do so: + paste_text = left_text + paste_text + sel_start -= len(left_text) + paste_text = paste_text.rjust(sel_to - sel_start) +## dbg('modified paste_text to be: "%s"' % paste_text) +## dbg('modified selection to:', (sel_start, sel_to)) + else: +## dbg("won't fit left;", 'paste text remains: "%s"' % paste_text) + pass + else: + paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip() +## dbg("allow insert, but not insert right;", 'paste text set to: "%s"' % paste_text) + + new_pos = sel_start + len(paste_text) # store for subsequent positioning - paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip() ## dbg('paste within insertable field; adjusted paste_text: "%s"' % paste_text, 'end:', edit_end) - sel_to = sel_start + len(paste_text) +## dbg('expanded selection to:', (sel_start, sel_to)) # Another special case: paste won't fit, but it's a right-insert field where entire # non-empty value is selected, and there's room if the selection is expanded leftward: @@ -5490,7 +5533,7 @@ class MaskedEditMixin: if not wx.Validator_IsSilent(): wx.Bell() ## dbg(indent=0) - return False + return None, -1 # else... text = self._eraseSelection() @@ -5521,17 +5564,19 @@ class MaskedEditMixin: wx.CallAfter(self._SetInsertionPoint, new_pos) else: ## dbg(indent=0) - return new_text + return new_text, replace_to elif just_return_value: ## dbg(indent=0) - return self._GetValue() + return self._GetValue(), sel_to ## dbg(indent=0) - def _Undo(self): + def _Undo(self, value=None, prev=None, just_return_results=False): """ Provides an Undo() method in base controls. """ ## dbg("MaskedEditMixin::_Undo", indent=1) - value = self._GetValue() - prev = self._prevValue + if value is None: + value = self._GetValue() + if prev is None: + prev = self._prevValue ## dbg('current value: "%s"' % value) ## dbg('previous value: "%s"' % prev) if prev is None: @@ -5588,6 +5633,7 @@ class MaskedEditMixin: for next_op in range(len(code_5tuples)-1, -1, -1): op, i1, i2, j1, j2 = code_5tuples[next_op] ## dbg('value[i1:i2]: "%s"' % value[i1:i2], 'template[i1:i2] "%s"' % self._template[i1:i2]) + field = self._FindField(i2) if op == 'insert' and prev[j1:j2] != self._template[j1:j2]: ## dbg('insert found: selection =>', (j1, j2)) sel_start = j1 @@ -5595,9 +5641,8 @@ class MaskedEditMixin: diff_found = True break elif op == 'delete' and value[i1:i2] != self._template[i1:i2]: - field = self._FindField(i2) edit_start, edit_end = field._extent - if field._insertRight and i2 == edit_end: + if field._insertRight and (field._allowInsert or i2 == edit_end): sel_start = i2 sel_to = i2 else: @@ -5607,23 +5652,39 @@ class MaskedEditMixin: diff_found = True break elif op == 'replace': -## dbg('replace found: selection =>', (j1, j2)) - sel_start = j1 - sel_to = j2 + if not prev[i1:i2].strip() and field._insertRight: + sel_start = sel_to = j2 + else: + sel_start = j1 + sel_to = j2 +## dbg('replace found: selection =>', (sel_start, sel_to)) diff_found = True break if diff_found: # now go forwards, looking for earlier changes: +## dbg('searching forward...') for next_op in range(len(code_5tuples)): op, i1, i2, j1, j2 = code_5tuples[next_op] field = self._FindField(i1) if op == 'equal': continue elif op == 'replace': -## dbg('setting sel_start to', i1) - sel_start = i1 + if field._insertRight: + # if replace with spaces in an insert-right control, ignore "forward" replace + if not prev[i1:i2].strip(): + continue + elif j1 < i1: +## dbg('setting sel_start to', j1) + sel_start = j1 + else: +## dbg('setting sel_start to', i1) + sel_start = i1 + else: +## dbg('setting sel_start to', i1) + sel_start = i1 +## dbg('saw replace; breaking') break elif op == 'insert' and not value[i1:i2]: ## dbg('forward %s found' % op) @@ -5635,9 +5696,21 @@ class MaskedEditMixin: ## dbg('setting sel_start to inserted space:', j1) sel_start = j1 break - elif op == 'delete' and field._insertRight and not value[i1:i2].lstrip(): - continue + elif op == 'delete': +## dbg('delete; field._insertRight?', field._insertRight, 'value[%d:%d].lstrip: "%s"' % (i1,i2,value[i1:i2].lstrip())) + if field._insertRight: + if value[i1:i2].lstrip(): +## dbg('setting sel_start to ', j1) + sel_start = j1 +## dbg('breaking loop') + break + else: + continue + else: +## dbg('saw delete; breaking') + break else: +## dbg('unknown code!') # we've got what we need break @@ -5691,15 +5764,22 @@ class MaskedEditMixin: prev_sel_start, prev_sel_to = self._prevSelection field = self._FindField(sel_start) - - if self._signOk and (self._prevValue[sel_start] in ('-', '(', ')') - or self._curValue[sel_start] in ('-', '(', ')')): + if( self._signOk + and sel_start < self._masklength + and (prev[sel_start] in ('-', '(', ')') + or value[sel_start] in ('-', '(', ')')) ): # change of sign; leave cursor alone... +## dbg("prev[sel_start] in ('-', '(', ')')?", prev[sel_start] in ('-', '(', ')')) +## dbg("value[sel_start] in ('-', '(', ')')?", value[sel_start] in ('-', '(', ')')) +## dbg('setting selection to previous one') sel_start, sel_to = self._prevSelection - elif field._groupdigits and (self._curValue[sel_start:sel_to] == field._groupChar - or self._prevValue[sel_start:sel_to] == field._groupChar): + elif field._groupdigits and (value[sel_start:sel_to] == field._groupChar + or prev[sel_start:sel_to] == field._groupChar): # do not highlight grouping changes +## dbg('value[sel_start:sel_to] == field._groupChar?', value[sel_start:sel_to] == field._groupChar) +## dbg('prev[sel_start:sel_to] == field._groupChar?', prev[sel_start:sel_to] == field._groupChar) +## dbg('setting selection to previous one') sel_start, sel_to = self._prevSelection else: @@ -5711,7 +5791,13 @@ class MaskedEditMixin: if prev_select_len >= calc_select_len: # old selection was bigger; trust it: - sel_start, sel_to = self._prevSelection +## dbg('prev_select_len >= calc_select_len?', prev_select_len >= calc_select_len) + if not field._insertRight: +## dbg('setting selection to previous one') + sel_start, sel_to = self._prevSelection + else: + sel_to = self._prevSelection[1] +## dbg('setting selection to', (sel_start, sel_to)) elif( sel_to > prev_sel_to # calculated select past last selection and prev_sel_to < len(self._template) # and prev_sel_to not at end of control @@ -5742,27 +5828,44 @@ class MaskedEditMixin: test_sel_start, test_sel_to = prev_sel_start, prev_sel_to ## dbg('test selection:', (test_sel_start, test_sel_to)) -## dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to]) -## dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to]) +## dbg('calc change: "%s"' % prev[sel_start:sel_to]) +## dbg('test change: "%s"' % prev[test_sel_start:test_sel_to]) # if calculated selection spans characters, and same characters # "before" the previous insertion point are present there as well, # select the ones related to the last known selection instead. if( sel_start != sel_to and test_sel_to < len(self._template) - and self._prevValue[test_sel_start:test_sel_to] == self._prevValue[sel_start:sel_to] ): + and prev[test_sel_start:test_sel_to] == prev[sel_start:sel_to] ): sel_start, sel_to = test_sel_start, test_sel_to + # finally, make sure that the old and new values are + # different where we say they're different: + while( sel_to - 1 > 0 + and sel_to > sel_start + and value[sel_to-1:] == prev[sel_to-1:]): + sel_to -= 1 + while( sel_start + 1 < self._masklength + and sel_start < sel_to + and value[:sel_start+1] == prev[:sel_start+1]): + sel_start += 1 + ## dbg('sel_start, sel_to:', sel_start, sel_to) -## dbg('previous value: "%s"' % self._prevValue) - self._SetValue(self._prevValue) +## dbg('previous value: "%s"' % prev) +## dbg(indent=0) + if just_return_results: + return prev, (sel_start, sel_to) + # else... + self._SetValue(prev) self._SetInsertionPoint(sel_start) self._SetSelection(sel_start, sel_to) + else: ## dbg('no difference between previous value') - pass -## dbg(indent=0) +## dbg(indent=0) + if just_return_results: + return prev, self._GetSelection() def _OnClear(self, event): @@ -5790,8 +5893,8 @@ class MaskedEditMixin: wx.EVT_MENU(menu, wx.ID_SELECTALL, self._OnCtrl_A) # ## WSS: The base control apparently handles - # enable/disable of wID_CUT, wxID_COPY, wxID_PASTE - # and wxID_CLEAR menu items even if the menu is one + # enable/disable of wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE + # and wx.ID_CLEAR menu items even if the menu is one # we created. However, it doesn't do undo properly, # so we're keeping track of previous values ourselves. # Therefore, we have to override the default update for @@ -6283,6 +6386,17 @@ i=1 ## CHANGELOG: ## ==================== +## Version 1.7 +## 1. Fixed intra-right-insert-field erase, such that it doesn't leave a hole, but instead +## shifts the text to the left accordingly. +## 2. Fixed _SetValue() to place cursor after last character inserted, rather than end of +## mask. +## 3. Fixed some incorrect undo behavior for right-insert fields, and allowed derived classes +## (eg. numctrl) to pass modified values for undo processing (to handle/ignore grouping +## chars properly.) +## 4. Fixed autoselect behavior to work similarly to (2) above, so that combobox +## selection will only select the non-empty text, as per request. +## ## Version 1.6 ## 1. Reorganized masked controls into separate package, renamed things accordingly ## 2. Split actual controls out of this file into their own files. diff --git a/wxPython/wx/lib/masked/numctrl.py b/wxPython/wx/lib/masked/numctrl.py index 17c95888d7..f8f1934e1a 100644 --- a/wxPython/wx/lib/masked/numctrl.py +++ b/wxPython/wx/lib/masked/numctrl.py @@ -1050,18 +1050,20 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): ## dbg(indent=0) - def _OnErase( self, event ): + def _OnErase( self, event=None, just_return_value=False ): """ This overrides the base control _OnErase, so that erasing around grouping characters auto selects the digit before or after the grouping character, so that the erasure does the right thing. """ ## dbg('NumCtrl::_OnErase', indent=1) - + if event is None: # called as action routine from Cut() operation. + key = wx.WXK_DELETE + else: + key = event.GetKeyCode() #if grouping digits, make sure deletes next to group char always # delete next digit to appropriate side: if self._groupDigits: - key = event.GetKeyCode() value = BaseMaskedTextCtrl.GetValue(self) sel_start, sel_to = self._GetSelection() @@ -1088,7 +1090,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): self.SetInsertionPoint(sel_start) self.SetSelection(sel_start, sel_to+1) - BaseMaskedTextCtrl._OnErase(self, event) + return BaseMaskedTextCtrl._OnErase(self, event, just_return_value) ## dbg(indent=0) @@ -1522,19 +1524,141 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin): paste_text = self._getClipboardContents() else: paste_text = value - - # treat paste as "replace number", if appropriate: sel_start, sel_to = self._GetSelection() - if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent: - paste_text = self._toGUI(paste_text) - self._SetSelection(0, len(self._mask)) + orig_sel_start = sel_start + orig_sel_to = sel_to +## dbg('selection:', (sel_start, sel_to)) + old_value = self._GetValue() - return MaskedEditMixin._Paste(self, + # + field = self._FindField(sel_start) + edit_start, edit_end = field._extent + paste_text = paste_text.replace(self._groupChar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')','') + if field._insertRight and self._groupDigits: + # want to paste to the left; see if it will fit: + left_text = old_value[edit_start:sel_start].lstrip() +## dbg('len(left_text):', len(left_text)) +## dbg('len(paste_text):', len(paste_text)) +## dbg('sel_start - (len(left_text) + len(paste_text)) >= edit_start?', sel_start - (len(left_text) + len(paste_text)) >= edit_start) + if sel_start - (len(left_text) + len(paste_text)) >= edit_start: + # will fit! create effective paste text, and move cursor back to do so: + paste_text = left_text + paste_text + sel_start -= len(paste_text) + sel_start += sel_to - orig_sel_start # decrease by amount selected + else: +## dbg("won't fit left;", 'paste text remains: "%s"' % paste_text) +## dbg('adjusted start before accounting for grouping:', sel_start) +## dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text) + pass + if self._groupDigits and sel_start != orig_sel_start: + left_len = len(old_value[:sel_to].lstrip()) + # remove group chars from adjusted paste string, and left pad to wipe out + # old characters, so that selection will remove the right chars, and + # readjust will do the right thing: + paste_text = paste_text.replace(self._groupChar,'') + adjcount = left_len - len(paste_text) + paste_text = ' ' * adjcount + paste_text + sel_start = sel_to - len(paste_text) +## dbg('adjusted start after accounting for grouping:', sel_start) +## dbg('adjusted paste_text after accounting for grouping: "%s"' % paste_text) + self.SetInsertionPoint(sel_to) + self.SetSelection(sel_start, sel_to) + +## # treat paste as "replace number", if appropriate: +## sel_start, sel_to = self._GetSelection() +## if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent: +## paste_text = self._toGUI(paste_text) +## self._SetSelection(0, len(self._mask)) + + new_text, replace_to = MaskedEditMixin._Paste(self, paste_text, raise_on_invalid=raise_on_invalid, - just_return_value=just_return_value) - - + just_return_value=True) + self._SetInsertionPoint(orig_sel_to) + self._SetSelection(orig_sel_start, orig_sel_to) + if not just_return_value and new_text is not None: + if new_text != self._GetValue(): + self.modified = True + if new_text == '': + self.ClearValue() + else: + wx.CallAfter(self._SetValue, new_text) + wx.CallAfter(self._SetInsertionPoint, replace_to) +## dbg(indent=0) + else: +## dbg(indent=0) + return new_text, replace_to + + def _Undo(self, value=None, prev=None): + '''numctrl's undo is more complicated than the base control's, due to + grouping characters; we don't want to consider them when calculating + the undone portion.''' +## dbg('NumCtrl::_Undo', indent=1) + if value is None: value = self._GetValue() + if prev is None: prev = self._prevValue + if not self._groupDigits: + ignore, (new_sel_start, new_sel_to) = BaseMaskedTextCtrl._Undo(self, value, prev, just_return_results = True) + self._SetValue(prev) + self._SetInsertionPoint(new_sel_start) + self._SetSelection(new_sel_start, new_sel_to) + self._prevSelection = (new_sel_start, new_sel_to) +## dbg('resetting "prev selection" to', self._prevSelection) +## dbg(indent=0) + return + # else... + sel_start, sel_to = self._prevSelection + edit_start, edit_end = self._FindFieldExtent(0) + + adjvalue = self._GetNumValue(value).rjust(self._masklength) + adjprev = self._GetNumValue(prev ).rjust(self._masklength) + + # move selection to account for "ungrouped" value: + left_text = value[sel_start:].lstrip() + numleftgroups = len(left_text) - len(left_text.replace(self._groupChar, '')) + adjsel_start = sel_start + numleftgroups + right_text = value[sel_to:].lstrip() + numrightgroups = len(right_text) - len(right_text.replace(self._groupChar, '')) + adjsel_to = sel_to + numrightgroups +## dbg('adjusting "previous" selection from', (sel_start, sel_to), 'to:', (adjsel_start, adjsel_to)) + self._prevSelection = (adjsel_start, adjsel_to) + + # determine appropriate selection for ungrouped undo + ignore, (new_sel_start, new_sel_to) = BaseMaskedTextCtrl._Undo(self, adjvalue, adjprev, just_return_results = True) + + # adjust new selection based on grouping: + left_len = edit_end - new_sel_start + numleftgroups = left_len / 3 + new_sel_start -= numleftgroups + if numleftgroups and left_len % 3 == 0: + new_sel_start += 1 + + if new_sel_start < self._masklength and prev[new_sel_start] == self._groupChar: + new_sel_start += 1 + + right_len = edit_end - new_sel_to + numrightgroups = right_len / 3 + new_sel_to -= numrightgroups + + if new_sel_to and prev[new_sel_to-1] == self._groupChar: + new_sel_to -= 1 + + if new_sel_start > new_sel_to: + new_sel_to = new_sel_start + + # for numbers, we don't care about leading whitespace; adjust selection if + # it includes leading space. + prev_stripped = prev.lstrip() + prev_start = self._masklength - len(prev_stripped) + if new_sel_start < prev_start: + new_sel_start = prev_start + +## dbg('adjusted selection accounting for grouping:', (new_sel_start, new_sel_to)) + self._SetValue(prev) + self._SetInsertionPoint(new_sel_start) + self._SetSelection(new_sel_start, new_sel_to) + self._prevSelection = (new_sel_start, new_sel_to) +## dbg('resetting "prev selection" to', self._prevSelection) +## dbg(indent=0) #=========================================================================== @@ -1606,6 +1730,10 @@ i=0 ## 1. Add support for printf-style format specification. ## 2. Add option for repositioning on 'illegal' insertion point. ## +## Version 1.2 +## 1. Allowed select/replace digits. +## 2. Fixed undo to ignore grouping chars. +## ## Version 1.1 ## 1. Fixed .SetIntegerWidth() and .SetFractionWidth() functions. ## 2. Added autoSize parameter, to allow manual sizing of the control. @@ -1613,4 +1741,4 @@ i=0 ## nonsensical parameter methods from the control, so it will work ## properly with Boa. ## 4. Fixed allowNone bug found by user sameerc1@grandecom.net - +## diff --git a/wxPython/wx/lib/masked/textctrl.py b/wxPython/wx/lib/masked/textctrl.py index d73fbe4a4e..5f42128e81 100644 --- a/wxPython/wx/lib/masked/textctrl.py +++ b/wxPython/wx/lib/masked/textctrl.py @@ -190,7 +190,7 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ): # make SetValue behave the same as if you had typed the value in: try: - value = self._Paste(value, raise_on_invalid=True, just_return_value=True) + value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True) if self._isFloat: self._isNeg = False # (clear current assumptions) value = self._adjustFloat(value) @@ -206,16 +206,17 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ): dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True) value = string.join(dateparts, ' ') ## dbg('adjusted value: "%s"' % value) - value = self._Paste(value, raise_on_invalid=True, just_return_value=True) + value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True) else: ## dbg('exception thrown', indent=0) raise self._SetValue(value) # note: to preserve similar capability, .SetValue() # does not change IsModified() -#### dbg('queuing insertion after .SetValue', self._masklength) - wx.CallAfter(self._SetInsertionPoint, self._masklength) - wx.CallAfter(self._SetSelection, self._masklength, self._masklength) +#### dbg('queuing insertion after .SetValue', replace_to) + # set selection to last char replaced by paste + wx.CallAfter(self._SetInsertionPoint, replace_to) + wx.CallAfter(self._SetSelection, replace_to, replace_to) ## dbg(indent=0) -- 2.45.2