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
This commit is contained in:
		@@ -219,7 +219,7 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # make SetValue behave the same as if you had typed the value in:
 | 
					        # make SetValue behave the same as if you had typed the value in:
 | 
				
			||||||
        try:
 | 
					        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:
 | 
					            if self._isFloat:
 | 
				
			||||||
                self._isNeg = False     # (clear current assumptions)
 | 
					                self._isNeg = False     # (clear current assumptions)
 | 
				
			||||||
                value = self._adjustFloat(value)
 | 
					                value = self._adjustFloat(value)
 | 
				
			||||||
@@ -240,9 +240,9 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
 | 
				
			|||||||
                raise
 | 
					                raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._SetValue(value)
 | 
					        self._SetValue(value)
 | 
				
			||||||
####        dbg('queuing insertion after .SetValue', self._masklength)
 | 
					####        dbg('queuing insertion after .SetValue', replace_to)
 | 
				
			||||||
        wx.CallAfter(self._SetInsertionPoint, self._masklength)
 | 
					        wx.CallAfter(self._SetInsertionPoint, replace_to)
 | 
				
			||||||
        wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
 | 
					        wx.CallAfter(self._SetSelection, replace_to, replace_to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _Refresh(self):
 | 
					    def _Refresh(self):
 | 
				
			||||||
@@ -506,6 +506,11 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
 | 
				
			|||||||
        self._CheckValid()
 | 
					        self._CheckValid()
 | 
				
			||||||
##        dbg('field._autoCompleteIndex:', match_index)
 | 
					##        dbg('field._autoCompleteIndex:', match_index)
 | 
				
			||||||
##        dbg('self.GetSelection():', self.GetSelection())
 | 
					##        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)
 | 
					##        dbg(indent=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ):
 | 
				
			|||||||
        if not kwargs.has_key('mask'):
 | 
					        if not kwargs.has_key('mask'):
 | 
				
			||||||
           kwargs['mask'] = mask = "###.###.###.###"
 | 
					           kwargs['mask'] = mask = "###.###.###.###"
 | 
				
			||||||
        if not kwargs.has_key('formatcodes'):
 | 
					        if not kwargs.has_key('formatcodes'):
 | 
				
			||||||
            kwargs['formatcodes'] = 'F_Sr<'
 | 
					            kwargs['formatcodes'] = 'F_Sr<>'
 | 
				
			||||||
        if not kwargs.has_key('validRegex'):
 | 
					        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}"
 | 
					            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)
 | 
					            BaseMaskedTextCtrl.SetValue(self, value)
 | 
				
			||||||
##        dbg(indent=0)
 | 
					##        dbg(indent=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					i=0
 | 
				
			||||||
 | 
					## Version 1.1
 | 
				
			||||||
 | 
					##  Made ipaddrctrls allow right-insert in subfields, now that insert/cut/paste works better
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3098,7 +3098,7 @@ class MaskedEditMixin:
 | 
				
			|||||||
        return False
 | 
					        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."""
 | 
					        """ Handles backspace and delete keypress in control. Should return False to skip other processing."""
 | 
				
			||||||
##        dbg("MaskedEditMixin::_OnErase", indent=1)
 | 
					##        dbg("MaskedEditMixin::_OnErase", indent=1)
 | 
				
			||||||
        sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
 | 
					        sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
 | 
				
			||||||
@@ -3316,6 +3316,11 @@ class MaskedEditMixin:
 | 
				
			|||||||
##            dbg(indent=0)
 | 
					##            dbg(indent=0)
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if just_return_value:
 | 
				
			||||||
 | 
					##            dbg(indent=0)
 | 
				
			||||||
 | 
					            return newstr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # else...
 | 
				
			||||||
##        dbg('setting value (later) to', newstr)
 | 
					##        dbg('setting value (later) to', newstr)
 | 
				
			||||||
        wx.CallAfter(self._SetValue, newstr)
 | 
					        wx.CallAfter(self._SetValue, newstr)
 | 
				
			||||||
##        dbg('setting insertion point (later) to', pos)
 | 
					##        dbg('setting insertion point (later) to', pos)
 | 
				
			||||||
@@ -4710,11 +4715,28 @@ class MaskedEditMixin:
 | 
				
			|||||||
        newtext = ""
 | 
					        newtext = ""
 | 
				
			||||||
        newpos = pos
 | 
					        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:
 | 
					        if pos != sel_start and sel_start == sel_to:
 | 
				
			||||||
            # adjustpos must have moved the position; make selection match:
 | 
					            # adjustpos must have moved the position; make selection match:
 | 
				
			||||||
            sel_start = sel_to = pos
 | 
					            sel_start = sel_to = pos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##        dbg('field._insertRight?', field._insertRight)
 | 
					##        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
 | 
					        if( field._insertRight                                  # field allows right insert
 | 
				
			||||||
            and ((sel_start, sel_to) == field._extent           # and whole field selected
 | 
					            and ((sel_start, sel_to) == field._extent           # and whole field selected
 | 
				
			||||||
                 or (sel_start == sel_to                        # or nothing selected
 | 
					                 or (sel_start == sel_to                        # or nothing selected
 | 
				
			||||||
@@ -4870,7 +4892,8 @@ class MaskedEditMixin:
 | 
				
			|||||||
                if match_index is not None and partial_match:
 | 
					                if match_index is not None and partial_match:
 | 
				
			||||||
                    matched_str = newtext
 | 
					                    matched_str = newtext
 | 
				
			||||||
                    newtext = self._ctrl_constraints._choices[match_index]
 | 
					                    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
 | 
					                    match_field = self._ctrl_constraints
 | 
				
			||||||
                    if self._ctrl_constraints._insertRight:
 | 
					                    if self._ctrl_constraints._insertRight:
 | 
				
			||||||
                        # adjust position to just after partial match in control:
 | 
					                        # adjust position to just after partial match in control:
 | 
				
			||||||
@@ -5423,11 +5446,31 @@ class MaskedEditMixin:
 | 
				
			|||||||
            field = self._FindField(sel_start)
 | 
					            field = self._FindField(sel_start)
 | 
				
			||||||
            edit_start, edit_end = field._extent
 | 
					            edit_start, edit_end = field._extent
 | 
				
			||||||
            new_pos = None
 | 
					            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
 | 
					                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)
 | 
					##                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
 | 
					            # 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:
 | 
					            # 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():
 | 
					                if not wx.Validator_IsSilent():
 | 
				
			||||||
                    wx.Bell()
 | 
					                    wx.Bell()
 | 
				
			||||||
##                dbg(indent=0)
 | 
					##                dbg(indent=0)
 | 
				
			||||||
                return False
 | 
					                return None, -1
 | 
				
			||||||
            # else...
 | 
					            # else...
 | 
				
			||||||
            text = self._eraseSelection()
 | 
					            text = self._eraseSelection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5521,17 +5564,19 @@ class MaskedEditMixin:
 | 
				
			|||||||
                    wx.CallAfter(self._SetInsertionPoint, new_pos)
 | 
					                    wx.CallAfter(self._SetInsertionPoint, new_pos)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
##                dbg(indent=0)
 | 
					##                dbg(indent=0)
 | 
				
			||||||
                return new_text
 | 
					                return new_text, replace_to
 | 
				
			||||||
        elif just_return_value:
 | 
					        elif just_return_value:
 | 
				
			||||||
##            dbg(indent=0)
 | 
					##            dbg(indent=0)
 | 
				
			||||||
            return self._GetValue()
 | 
					            return self._GetValue(), sel_to
 | 
				
			||||||
##        dbg(indent=0)
 | 
					##        dbg(indent=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _Undo(self):
 | 
					    def _Undo(self, value=None, prev=None, just_return_results=False):
 | 
				
			||||||
        """ Provides an Undo() method in base controls. """
 | 
					        """ Provides an Undo() method in base controls. """
 | 
				
			||||||
##        dbg("MaskedEditMixin::_Undo", indent=1)
 | 
					##        dbg("MaskedEditMixin::_Undo", indent=1)
 | 
				
			||||||
        value = self._GetValue()
 | 
					        if value is None:
 | 
				
			||||||
        prev = self._prevValue
 | 
					            value = self._GetValue()
 | 
				
			||||||
 | 
					        if prev is None:
 | 
				
			||||||
 | 
					            prev = self._prevValue
 | 
				
			||||||
##        dbg('current value:  "%s"' % value)
 | 
					##        dbg('current value:  "%s"' % value)
 | 
				
			||||||
##        dbg('previous value: "%s"' % prev)
 | 
					##        dbg('previous value: "%s"' % prev)
 | 
				
			||||||
        if prev is None:
 | 
					        if prev is None:
 | 
				
			||||||
@@ -5588,6 +5633,7 @@ class MaskedEditMixin:
 | 
				
			|||||||
                for next_op in range(len(code_5tuples)-1, -1, -1):
 | 
					                for next_op in range(len(code_5tuples)-1, -1, -1):
 | 
				
			||||||
                    op, i1, i2, j1, j2 = code_5tuples[next_op]
 | 
					                    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])
 | 
					##                    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]:
 | 
					                    if op == 'insert' and prev[j1:j2] != self._template[j1:j2]:
 | 
				
			||||||
##                        dbg('insert found: selection =>', (j1, j2))
 | 
					##                        dbg('insert found: selection =>', (j1, j2))
 | 
				
			||||||
                        sel_start = j1
 | 
					                        sel_start = j1
 | 
				
			||||||
@@ -5595,9 +5641,8 @@ class MaskedEditMixin:
 | 
				
			|||||||
                        diff_found = True
 | 
					                        diff_found = True
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
                    elif op == 'delete' and value[i1:i2] != self._template[i1:i2]:
 | 
					                    elif op == 'delete' and value[i1:i2] != self._template[i1:i2]:
 | 
				
			||||||
                        field = self._FindField(i2)
 | 
					 | 
				
			||||||
                        edit_start, edit_end = field._extent
 | 
					                        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_start = i2
 | 
				
			||||||
                            sel_to = i2
 | 
					                            sel_to = i2
 | 
				
			||||||
                        else:
 | 
					                        else:
 | 
				
			||||||
@@ -5607,23 +5652,39 @@ class MaskedEditMixin:
 | 
				
			|||||||
                        diff_found = True
 | 
					                        diff_found = True
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
                    elif op == 'replace':
 | 
					                    elif op == 'replace':
 | 
				
			||||||
##                        dbg('replace found: selection =>', (j1, j2))
 | 
					                        if not prev[i1:i2].strip() and field._insertRight:
 | 
				
			||||||
                        sel_start = j1
 | 
					                            sel_start = sel_to = j2
 | 
				
			||||||
                        sel_to = j2
 | 
					                        else:
 | 
				
			||||||
 | 
					                            sel_start = j1
 | 
				
			||||||
 | 
					                            sel_to = j2
 | 
				
			||||||
 | 
					##                        dbg('replace found: selection =>', (sel_start, sel_to))
 | 
				
			||||||
                        diff_found = True
 | 
					                        diff_found = True
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if diff_found:
 | 
					                if diff_found:
 | 
				
			||||||
                    # now go forwards, looking for earlier changes:
 | 
					                    # now go forwards, looking for earlier changes:
 | 
				
			||||||
 | 
					##                    dbg('searching forward...')
 | 
				
			||||||
                    for next_op in range(len(code_5tuples)):
 | 
					                    for next_op in range(len(code_5tuples)):
 | 
				
			||||||
                        op, i1, i2, j1, j2 = code_5tuples[next_op]
 | 
					                        op, i1, i2, j1, j2 = code_5tuples[next_op]
 | 
				
			||||||
                        field = self._FindField(i1)
 | 
					                        field = self._FindField(i1)
 | 
				
			||||||
                        if op == 'equal':
 | 
					                        if op == 'equal':
 | 
				
			||||||
                            continue
 | 
					                            continue
 | 
				
			||||||
                        elif op == 'replace':
 | 
					                        elif op == 'replace':
 | 
				
			||||||
##                            dbg('setting sel_start to', i1)
 | 
					                            if field._insertRight:
 | 
				
			||||||
                            sel_start = i1
 | 
					                                # 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
 | 
					                            break
 | 
				
			||||||
                        elif op == 'insert' and not value[i1:i2]:
 | 
					                        elif op == 'insert' and not value[i1:i2]:
 | 
				
			||||||
##                            dbg('forward %s found' % op)
 | 
					##                            dbg('forward %s found' % op)
 | 
				
			||||||
@@ -5635,9 +5696,21 @@ class MaskedEditMixin:
 | 
				
			|||||||
##                                dbg('setting sel_start to inserted space:', j1)
 | 
					##                                dbg('setting sel_start to inserted space:', j1)
 | 
				
			||||||
                                sel_start = j1
 | 
					                                sel_start = j1
 | 
				
			||||||
                                break
 | 
					                                break
 | 
				
			||||||
                        elif op == 'delete' and field._insertRight and not value[i1:i2].lstrip():
 | 
					                        elif op == 'delete':
 | 
				
			||||||
                            continue
 | 
					##                            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:
 | 
					                        else:
 | 
				
			||||||
 | 
					##                            dbg('unknown code!')
 | 
				
			||||||
                            # we've got what we need
 | 
					                            # we've got what we need
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5691,15 +5764,22 @@ class MaskedEditMixin:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                prev_sel_start, prev_sel_to = self._prevSelection
 | 
					                prev_sel_start, prev_sel_to = self._prevSelection
 | 
				
			||||||
                field = self._FindField(sel_start)
 | 
					                field = self._FindField(sel_start)
 | 
				
			||||||
 | 
					                if( self._signOk
 | 
				
			||||||
                if self._signOk and (self._prevValue[sel_start] in ('-', '(', ')')
 | 
					                      and sel_start < self._masklength
 | 
				
			||||||
                                     or self._curValue[sel_start] in ('-', '(', ')')):
 | 
					                      and (prev[sel_start] in ('-', '(', ')')
 | 
				
			||||||
 | 
					                                     or value[sel_start] in ('-', '(', ')')) ):
 | 
				
			||||||
                    # change of sign; leave cursor alone...
 | 
					                    # 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
 | 
					                    sel_start, sel_to = self._prevSelection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                elif field._groupdigits and (self._curValue[sel_start:sel_to] == field._groupChar
 | 
					                elif field._groupdigits and (value[sel_start:sel_to] == field._groupChar
 | 
				
			||||||
                                             or self._prevValue[sel_start:sel_to] == field._groupChar):
 | 
					                                             or prev[sel_start:sel_to] == field._groupChar):
 | 
				
			||||||
                    # do not highlight grouping changes
 | 
					                    # 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
 | 
					                    sel_start, sel_to = self._prevSelection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
@@ -5711,7 +5791,13 @@ class MaskedEditMixin:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if prev_select_len >= calc_select_len:
 | 
					                    if prev_select_len >= calc_select_len:
 | 
				
			||||||
                        # old selection was bigger; trust it:
 | 
					                        # 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
 | 
					                    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
 | 
					                          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
 | 
					                            test_sel_start, test_sel_to = prev_sel_start, prev_sel_to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##                        dbg('test selection:', (test_sel_start, test_sel_to))
 | 
					##                        dbg('test selection:', (test_sel_start, test_sel_to))
 | 
				
			||||||
##                        dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to])
 | 
					##                        dbg('calc change: "%s"' % prev[sel_start:sel_to])
 | 
				
			||||||
##                        dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to])
 | 
					##                        dbg('test change: "%s"' % prev[test_sel_start:test_sel_to])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        # if calculated selection spans characters, and same characters
 | 
					                        # if calculated selection spans characters, and same characters
 | 
				
			||||||
                        # "before" the previous insertion point are present there as well,
 | 
					                        # "before" the previous insertion point are present there as well,
 | 
				
			||||||
                        # select the ones related to the last known selection instead.
 | 
					                        # select the ones related to the last known selection instead.
 | 
				
			||||||
                        if( sel_start != sel_to
 | 
					                        if( sel_start != sel_to
 | 
				
			||||||
                            and test_sel_to < len(self._template)
 | 
					                            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
 | 
					                            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('sel_start, sel_to:', sel_start, sel_to)
 | 
				
			||||||
##            dbg('previous value: "%s"' % self._prevValue)
 | 
					##            dbg('previous value: "%s"' % prev)
 | 
				
			||||||
            self._SetValue(self._prevValue)
 | 
					##            dbg(indent=0)
 | 
				
			||||||
 | 
					            if just_return_results:
 | 
				
			||||||
 | 
					                return prev, (sel_start, sel_to)
 | 
				
			||||||
 | 
					            # else...
 | 
				
			||||||
 | 
					            self._SetValue(prev)
 | 
				
			||||||
            self._SetInsertionPoint(sel_start)
 | 
					            self._SetInsertionPoint(sel_start)
 | 
				
			||||||
            self._SetSelection(sel_start, sel_to)
 | 
					            self._SetSelection(sel_start, sel_to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
##            dbg('no difference between previous value')
 | 
					##            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):
 | 
					    def _OnClear(self, event):
 | 
				
			||||||
@@ -5790,8 +5893,8 @@ class MaskedEditMixin:
 | 
				
			|||||||
        wx.EVT_MENU(menu, wx.ID_SELECTALL, self._OnCtrl_A)
 | 
					        wx.EVT_MENU(menu, wx.ID_SELECTALL, self._OnCtrl_A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # ## WSS: The base control apparently handles
 | 
					        # ## WSS: The base control apparently handles
 | 
				
			||||||
        # enable/disable of wID_CUT, wxID_COPY, wxID_PASTE
 | 
					        # enable/disable of wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE
 | 
				
			||||||
        # and wxID_CLEAR menu items even if the menu is one
 | 
					        # and wx.ID_CLEAR menu items even if the menu is one
 | 
				
			||||||
        # we created.  However, it doesn't do undo properly,
 | 
					        # we created.  However, it doesn't do undo properly,
 | 
				
			||||||
        # so we're keeping track of previous values ourselves.
 | 
					        # so we're keeping track of previous values ourselves.
 | 
				
			||||||
        # Therefore, we have to override the default update for
 | 
					        # Therefore, we have to override the default update for
 | 
				
			||||||
@@ -6283,6 +6386,17 @@ i=1
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## CHANGELOG:
 | 
					## 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
 | 
					##  Version 1.6
 | 
				
			||||||
##  1. Reorganized masked controls into separate package, renamed things accordingly
 | 
					##  1. Reorganized masked controls into separate package, renamed things accordingly
 | 
				
			||||||
##  2. Split actual controls out of this file into their own files.
 | 
					##  2. Split actual controls out of this file into their own files.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1050,18 +1050,20 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 | 
				
			|||||||
##        dbg(indent=0)
 | 
					##        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
 | 
					        This overrides the base control _OnErase, so that erasing around
 | 
				
			||||||
        grouping characters auto selects the digit before or after the
 | 
					        grouping characters auto selects the digit before or after the
 | 
				
			||||||
        grouping character, so that the erasure does the right thing.
 | 
					        grouping character, so that the erasure does the right thing.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
##        dbg('NumCtrl::_OnErase', indent=1)
 | 
					##        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
 | 
					        #if grouping digits, make sure deletes next to group char always
 | 
				
			||||||
        # delete next digit to appropriate side:
 | 
					        # delete next digit to appropriate side:
 | 
				
			||||||
        if self._groupDigits:
 | 
					        if self._groupDigits:
 | 
				
			||||||
            key = event.GetKeyCode()
 | 
					 | 
				
			||||||
            value = BaseMaskedTextCtrl.GetValue(self)
 | 
					            value = BaseMaskedTextCtrl.GetValue(self)
 | 
				
			||||||
            sel_start, sel_to = self._GetSelection()
 | 
					            sel_start, sel_to = self._GetSelection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1088,7 +1090,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 | 
				
			|||||||
                    self.SetInsertionPoint(sel_start)
 | 
					                    self.SetInsertionPoint(sel_start)
 | 
				
			||||||
                    self.SetSelection(sel_start, sel_to+1)
 | 
					                    self.SetSelection(sel_start, sel_to+1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        BaseMaskedTextCtrl._OnErase(self, event)
 | 
					        return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
 | 
				
			||||||
##        dbg(indent=0)
 | 
					##        dbg(indent=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1522,19 +1524,141 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 | 
				
			|||||||
            paste_text = self._getClipboardContents()
 | 
					            paste_text = self._getClipboardContents()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            paste_text = value
 | 
					            paste_text = value
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # treat paste as "replace number", if appropriate:
 | 
					 | 
				
			||||||
        sel_start, sel_to = self._GetSelection()
 | 
					        sel_start, sel_to = self._GetSelection()
 | 
				
			||||||
        if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent:
 | 
					        orig_sel_start = sel_start
 | 
				
			||||||
            paste_text = self._toGUI(paste_text)
 | 
					        orig_sel_to = sel_to
 | 
				
			||||||
            self._SetSelection(0, len(self._mask))
 | 
					##        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,
 | 
					                                        paste_text,
 | 
				
			||||||
                                        raise_on_invalid=raise_on_invalid,
 | 
					                                        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.
 | 
					##   1. Add support for printf-style format specification.
 | 
				
			||||||
##   2. Add option for repositioning on 'illegal' insertion point.
 | 
					##   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
 | 
					## Version 1.1
 | 
				
			||||||
##   1. Fixed .SetIntegerWidth() and .SetFractionWidth() functions.
 | 
					##   1. Fixed .SetIntegerWidth() and .SetFractionWidth() functions.
 | 
				
			||||||
##   2. Added autoSize parameter, to allow manual sizing of the control.
 | 
					##   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
 | 
					##      nonsensical parameter methods from the control, so it will work
 | 
				
			||||||
##      properly with Boa.
 | 
					##      properly with Boa.
 | 
				
			||||||
##   4. Fixed allowNone bug found by user sameerc1@grandecom.net
 | 
					##   4. Fixed allowNone bug found by user sameerc1@grandecom.net
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -190,7 +190,7 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # make SetValue behave the same as if you had typed the value in:
 | 
					        # make SetValue behave the same as if you had typed the value in:
 | 
				
			||||||
        try:
 | 
					        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:
 | 
					            if self._isFloat:
 | 
				
			||||||
                self._isNeg = False     # (clear current assumptions)
 | 
					                self._isNeg = False     # (clear current assumptions)
 | 
				
			||||||
                value = self._adjustFloat(value)
 | 
					                value = self._adjustFloat(value)
 | 
				
			||||||
@@ -206,16 +206,17 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
 | 
				
			|||||||
                dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
 | 
					                dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
 | 
				
			||||||
                value = string.join(dateparts, ' ')
 | 
					                value = string.join(dateparts, ' ')
 | 
				
			||||||
##                dbg('adjusted value: "%s"' % value)
 | 
					##                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:
 | 
					            else:
 | 
				
			||||||
##                dbg('exception thrown', indent=0)
 | 
					##                dbg('exception thrown', indent=0)
 | 
				
			||||||
                raise
 | 
					                raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._SetValue(value)   # note: to preserve similar capability, .SetValue()
 | 
					        self._SetValue(value)   # note: to preserve similar capability, .SetValue()
 | 
				
			||||||
                                # does not change IsModified()
 | 
					                                # does not change IsModified()
 | 
				
			||||||
####        dbg('queuing insertion after .SetValue', self._masklength)
 | 
					####        dbg('queuing insertion after .SetValue', replace_to)
 | 
				
			||||||
        wx.CallAfter(self._SetInsertionPoint, self._masklength)
 | 
					        # set selection to last char replaced by paste
 | 
				
			||||||
        wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
 | 
					        wx.CallAfter(self._SetInsertionPoint, replace_to)
 | 
				
			||||||
 | 
					        wx.CallAfter(self._SetSelection, replace_to, replace_to)
 | 
				
			||||||
##        dbg(indent=0)
 | 
					##        dbg(indent=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user