]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/masked/numctrl.py
"wxWindows" --> "wxWidgets"
[wxWidgets.git] / wxPython / wx / lib / masked / numctrl.py
index 17c95888d7878f173085e84dddd08006abc54c7d..cfb64b22a22222f905d14066b847e7b2c5dc103f 100644 (file)
@@ -386,7 +386,7 @@ MININT = -maxint-1
 from wx.tools.dbg import Logger
 from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
 dbg = Logger()
-##dbg(enable=0)
+##dbg(enable=1)
 
 #----------------------------------------------------------------------------
 
@@ -654,14 +654,26 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 
             maskededit_kwargs['mask'] = intmask+fracmask
 
-        if kwargs.has_key('groupChar'):
+        if kwargs.has_key('groupChar') or kwargs.has_key('decimalChar'):
             old_groupchar = self._groupChar     # save so we can reformat properly
-##            dbg("old_groupchar: '%s'" % old_groupchar)
-            maskededit_kwargs['groupChar'] = kwargs['groupChar']
-        if kwargs.has_key('decimalChar'):
             old_decimalchar = self._decimalChar
+##            dbg("old_groupchar: '%s'" % old_groupchar)
 ##            dbg("old_decimalchar: '%s'" % old_decimalchar)
-            maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
+            groupchar = old_groupchar
+            decimalchar = old_decimalchar
+
+            if kwargs.has_key('groupChar'):
+                maskededit_kwargs['groupChar'] = kwargs['groupChar']
+                groupchar = kwargs['groupChar']
+            if kwargs.has_key('decimalChar'):
+                maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
+                decimalchar = kwargs['decimalChar']
+
+            # Add sanity check to make sure these are distinct, and if not,
+            # raise attribute error
+            if groupchar == decimalchar:
+                raise AttributeError('groupChar and decimalChar must be distinct')
+
 
         # for all other parameters, assign keyword args as appropriate:
         for key, param_value in kwargs.items():
@@ -1050,18 +1062,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()
 
@@ -1087,9 +1101,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
                            and value[sel_start:sel_to] == self._groupChar ):
                     self.SetInsertionPoint(sel_start)
                     self.SetSelection(sel_start, sel_to+1)
-
-        BaseMaskedTextCtrl._OnErase(self, event)
 ##        dbg(indent=0)
+        return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
 
 
     def OnTextChange( self, event ):
@@ -1144,7 +1157,9 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         A ValueError exception will be raised if an invalid value
         is specified.
         """
+##        dbg('NumCtrl::SetValue(%s)' % value, indent=1)
         BaseMaskedTextCtrl.SetValue( self, self._toGUI(value) )
+##        dbg(indent=0)
 
 
     def SetIntegerWidth(self, value):
@@ -1517,24 +1532,140 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         Preprocessor for base control paste; if value needs to be right-justified
         to fit in control, do so prior to paste:
         """
-##        dbg('NumCtrl::_Paste (value = "%s")' % value)
+##        dbg('NumCtrl::_Paste (value = "%s")' % value, indent=1)
         if value is None:
             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('(', '-').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)
+
+        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 +1737,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 +1748,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
-
+##