X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6cffbf02c02c6df717dab7635c4b2031ed4d3414..c878ceeae8d69f231477ef0f207766093547ab86:/wxPython/wx/lib/masked/textctrl.py diff --git a/wxPython/wx/lib/masked/textctrl.py b/wxPython/wx/lib/masked/textctrl.py new file mode 100644 index 0000000000..d73fbe4a4e --- /dev/null +++ b/wxPython/wx/lib/masked/textctrl.py @@ -0,0 +1,325 @@ +#---------------------------------------------------------------------------- +# Name: masked.textctrl.py +# Authors: Jeff Childers, Will Sadkin +# Email: jchilders_98@yahoo.com, wsadkin@nameconnector.com +# Created: 02/11/2003 +# Copyright: (c) 2003 by Jeff Childers, Will Sadkin, 2003 +# Portions: (c) 2002 by Will Sadkin, 2002-2003 +# RCS-ID: $Id$ +# License: wxWidgets license +#---------------------------------------------------------------------------- +# +# This file contains the most typically used generic masked control, +# masked.TextCtrl. It also defines the BaseMaskedTextCtrl, which can +# be used to derive other "semantics-specific" classes, like masked.NumCtrl, +# masked.TimeCtrl, and masked.IpAddrCtrl. +# +#---------------------------------------------------------------------------- +import wx +from wx.lib.masked import * + +# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would +# be a good place to implement the 2.3 logger class +from wx.tools.dbg import Logger +dbg = Logger() +##dbg(enable=0) + +# ## TRICKY BIT: to avoid a ton of boiler-plate, and to +# ## automate the getter/setter generation for each valid +# ## control parameter so we never forget to add the +# ## functions when adding parameters, this loop +# ## programmatically adds them to the class: +# ## (This makes it easier for Designers like Boa to +# ## deal with masked controls.) +# +# ## To further complicate matters, this is done with an +# ## extra level of inheritance, so that "general" classes like +# ## MaskedTextCtrl can have all possible attributes, +# ## while derived classes, like TimeCtrl and MaskedNumCtrl +# ## can prevent exposure of those optional attributes of their base +# ## class that do not make sense for their derivation. Therefore, +# ## we define +# ## BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin) +# ## and +# ## MaskedTextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin). +# ## +# ## This allows us to then derive: +# ## MaskedNumCtrl( BaseMaskedTextCtrl ) +# ## +# ## and not have to expose all the same accessor functions for the +# ## derived control when they don't all make sense for it. +# ## + +class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ): + """ + This is the primary derivation from MaskedEditMixin. It provides + a general masked text control that can be configured with different + masks. It's actually a "base masked textCtrl", so that the + MaskedTextCtrl class can be derived from it, and add those + accessor functions to it that are appropriate to the general class, + whilst other classes can derive from BaseMaskedTextCtrl, and + only define those accessor functions that are appropriate for + those derivations. + """ + + def __init__( self, parent, id=-1, value = '', + pos = wx.DefaultPosition, + size = wx.DefaultSize, + style = wx.TE_PROCESS_TAB, + validator=wx.DefaultValidator, ## placeholder provided for data-transfer logic + name = 'maskedTextCtrl', + setupEventHandling = True, ## setup event handling by default + **kwargs): + + wx.TextCtrl.__init__(self, parent, id, value='', + pos=pos, size = size, + style=style, validator=validator, + name=name) + + self.controlInitialized = True + MaskedEditMixin.__init__( self, name, **kwargs ) + + self._SetInitialValue(value) + + if setupEventHandling: + ## Setup event handlers + self.Bind(wx.EVT_SET_FOCUS, self._OnFocus ) ## defeat automatic full selection + self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus ) ## run internal validator + self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick) ## select field under cursor on dclick + self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu ) ## bring up an appropriate context menu + self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## capture control events not normally seen, eg ctrl-tab. + self.Bind(wx.EVT_CHAR, self._OnChar ) ## handle each keypress + self.Bind(wx.EVT_TEXT, self._OnTextChange ) ## color control appropriately & keep + ## track of previous value for undo + + + def __repr__(self): + return "" % self.GetValue() + + + def _GetSelection(self): + """ + Allow mixin to get the text selection of this control. + REQUIRED by any class derived from MaskedEditMixin. + """ + return self.GetSelection() + + def _SetSelection(self, sel_start, sel_to): + """ + Allow mixin to set the text selection of this control. + REQUIRED by any class derived from MaskedEditMixin. + """ +#### dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals()) + return self.SetSelection( sel_start, sel_to ) + + def SetSelection(self, sel_start, sel_to): + """ + This is just for debugging... + """ +## dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals()) + wx.TextCtrl.SetSelection(self, sel_start, sel_to) + + + def _GetInsertionPoint(self): + return self.GetInsertionPoint() + + def _SetInsertionPoint(self, pos): +#### dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals()) + self.SetInsertionPoint(pos) + + def SetInsertionPoint(self, pos): + """ + This is just for debugging... + """ +## dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals()) + wx.TextCtrl.SetInsertionPoint(self, pos) + + + def _GetValue(self): + """ + Allow mixin to get the raw value of the control with this function. + REQUIRED by any class derived from MaskedEditMixin. + """ + return self.GetValue() + + def _SetValue(self, value): + """ + Allow mixin to set the raw value of the control with this function. + REQUIRED by any class derived from MaskedEditMixin. + """ +## dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1) + # Record current selection and insertion point, for undo + self._prevSelection = self._GetSelection() + self._prevInsertionPoint = self._GetInsertionPoint() + wx.TextCtrl.SetValue(self, value) +## dbg(indent=0) + + def SetValue(self, value): + """ + This function redefines the externally accessible .SetValue to be + a smart "paste" of the text in question, so as not to corrupt the + masked control. NOTE: this must be done in the class derived + from the base wx control. + """ +## dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1) + + if not self._mask: + wx.TextCtrl.SetValue(self, value) # revert to base control behavior + return + + # empty previous contents, replacing entire value: + self._SetInsertionPoint(0) + self._SetSelection(0, self._masklength) + if self._signOk and self._useParens: + signpos = value.find('-') + if signpos != -1: + value = value[:signpos] + '(' + value[signpos+1:].strip() + ')' + elif value.find(')') == -1 and len(value) < self._masklength: + value += ' ' # add place holder for reserved space for right paren + + if( len(value) < self._masklength # value shorter than control + and (self._isFloat or self._isInt) # and it's a numeric control + and self._ctrl_constraints._alignRight ): # and it's a right-aligned control + +## dbg('len(value)', len(value), ' < self._masklength', self._masklength) + # try to intelligently "pad out" the value to the right size: + value = self._template[0:self._masklength - len(value)] + value + if self._isFloat and value.find('.') == -1: + value = value[1:] +## dbg('padded value = "%s"' % value) + + # 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) + if self._isFloat: + self._isNeg = False # (clear current assumptions) + value = self._adjustFloat(value) + elif self._isInt: + self._isNeg = False # (clear current assumptions) + value = self._adjustInt(value) + elif self._isDate and not self.IsValid(value) and self._4digityear: + value = self._adjustDate(value, fixcentury=True) + except ValueError: + # If date, year might be 2 digits vs. 4; try adjusting it: + if self._isDate and self._4digityear: + dateparts = value.split(' ') + 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) + 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(indent=0) + + + def Clear(self): + """ Blanks the current control value by replacing it with the default value.""" +## dbg("MaskedTextCtrl::Clear - value reset to default value (template)") + if self._mask: + self.ClearValue() + else: + wx.TextCtrl.Clear(self) # else revert to base control behavior + + + def _Refresh(self): + """ + Allow mixin to refresh the base control with this function. + REQUIRED by any class derived from MaskedEditMixin. + """ +## dbg('MaskedTextCtrl::_Refresh', indent=1) + wx.TextCtrl.Refresh(self) +## dbg(indent=0) + + + def Refresh(self): + """ + This function redefines the externally accessible .Refresh() to + validate the contents of the masked control as it refreshes. + NOTE: this must be done in the class derived from the base wx control. + """ +## dbg('MaskedTextCtrl::Refresh', indent=1) + self._CheckValid() + self._Refresh() +## dbg(indent=0) + + + def _IsEditable(self): + """ + Allow mixin to determine if the base control is editable with this function. + REQUIRED by any class derived from MaskedEditMixin. + """ + return wx.TextCtrl.IsEditable(self) + + + def Cut(self): + """ + This function redefines the externally accessible .Cut to be + a smart "erase" of the text in question, so as not to corrupt the + masked control. NOTE: this must be done in the class derived + from the base wx control. + """ + if self._mask: + self._Cut() # call the mixin's Cut method + else: + wx.TextCtrl.Cut(self) # else revert to base control behavior + + + def Paste(self): + """ + This function redefines the externally accessible .Paste to be + a smart "paste" of the text in question, so as not to corrupt the + masked control. NOTE: this must be done in the class derived + from the base wx control. + """ + if self._mask: + self._Paste() # call the mixin's Paste method + else: + wx.TextCtrl.Paste(self, value) # else revert to base control behavior + + + def Undo(self): + """ + This function defines the undo operation for the control. (The default + undo is 1-deep.) + """ + if self._mask: + self._Undo() + else: + wx.TextCtrl.Undo(self) # else revert to base control behavior + + + def IsModified(self): + """ + This function overrides the raw wxTextCtrl method, because the + masked edit mixin uses SetValue to change the value, which doesn't + modify the state of this attribute. So, we keep track on each + keystroke to see if the value changes, and if so, it's been + modified. + """ + return wx.TextCtrl.IsModified(self) or self.modified + + + def _CalcSize(self, size=None): + """ + Calculate automatic size if allowed; use base mixin function. + """ + return self._calcSize(size) + + +class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ): + """ + This extra level of inheritance allows us to add the generic set of + masked edit parameters only to this class while allowing other + classes to derive from the "base" masked text control, and provide + a smaller set of valid accessor functions. + """ + pass + +