]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/masked/textctrl.py
f232e2266ab91aa6feaf9fc0582b4bae71f379ce
   1 #---------------------------------------------------------------------------- 
   2 # Name:         masked.textctrl.py 
   3 # Authors:      Jeff Childers, Will Sadkin 
   4 # Email:        jchilders_98@yahoo.com, wsadkin@nameconnector.com 
   6 # Copyright:    (c) 2003 by Jeff Childers, Will Sadkin, 2003 
   7 # Portions:     (c) 2002 by Will Sadkin, 2002-2003 
   9 # License:      wxWidgets license 
  10 #---------------------------------------------------------------------------- 
  12 # This file contains the most typically used generic masked control, 
  13 # masked.TextCtrl.  It also defines the BaseMaskedTextCtrl, which can 
  14 # be used to derive other "semantics-specific" classes, like masked.NumCtrl, 
  15 # masked.TimeCtrl, and masked.IpAddrCtrl. 
  17 #---------------------------------------------------------------------------- 
  19 from wx
.lib
.masked 
import * 
  21 # jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would 
  22 # be a good place to implement the 2.3 logger class 
  23 from wx
.tools
.dbg 
import Logger
 
  27 # ## TRICKY BIT: to avoid a ton of boiler-plate, and to 
  28 # ## automate the getter/setter generation for each valid 
  29 # ## control parameter so we never forget to add the 
  30 # ## functions when adding parameters, this loop 
  31 # ## programmatically adds them to the class: 
  32 # ## (This makes it easier for Designers like Boa to 
  33 # ## deal with masked controls.) 
  35 # ## To further complicate matters, this is done with an 
  36 # ## extra level of inheritance, so that "general" classes like 
  37 # ## MaskedTextCtrl can have all possible attributes, 
  38 # ## while derived classes, like TimeCtrl and MaskedNumCtrl 
  39 # ## can prevent exposure of those optional attributes of their base 
  40 # ## class that do not make sense for their derivation.  Therefore, 
  42 # ##    BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin) 
  44 # ##    MaskedTextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin). 
  46 # ## This allows us to then derive: 
  47 # ##    MaskedNumCtrl( BaseMaskedTextCtrl ) 
  49 # ## and not have to expose all the same accessor functions for the 
  50 # ## derived control when they don't all make sense for it. 
  53 class BaseMaskedTextCtrl( wx
.TextCtrl
, MaskedEditMixin 
): 
  55     This is the primary derivation from MaskedEditMixin.  It provides 
  56     a general masked text control that can be configured with different 
  57     masks.  It's actually a "base masked textCtrl", so that the 
  58     MaskedTextCtrl class can be derived from it, and add those 
  59     accessor functions to it that are appropriate to the general class, 
  60     whilst other classes can derive from BaseMaskedTextCtrl, and 
  61     only define those accessor functions that are appropriate for 
  65     def __init__( self
, parent
, id=-1, value 
= '', 
  66                   pos 
= wx
.DefaultPosition
, 
  67                   size 
= wx
.DefaultSize
, 
  68                   style 
= wx
.TE_PROCESS_TAB
, 
  69                   validator
=wx
.DefaultValidator
,     ## placeholder provided for data-transfer logic 
  70                   name 
= 'maskedTextCtrl', 
  71                   setupEventHandling 
= True,        ## setup event handling by default 
  74         wx
.TextCtrl
.__init
__(self
, parent
, id, value
='', 
  76                             style
=style
, validator
=validator
, 
  79         self
._PostInit
(setupEventHandling 
= setupEventHandling
, 
  80                       name
=name
, value
=value
,**kwargs 
) 
  83     def _PostInit(self
,setupEventHandling
=True, 
  84                  name
='maskedTextCtrl' , value
='', **kwargs
): 
  86         self
.controlInitialized 
= True 
  87         MaskedEditMixin
.__init
__( self
, name
, **kwargs 
) 
  89         self
._SetInitialValue
(value
) 
  91         if setupEventHandling
: 
  92             ## Setup event handlers 
  93             self
.Bind(wx
.EVT_SET_FOCUS
, self
._OnFocus 
)         ## defeat automatic full selection 
  94             self
.Bind(wx
.EVT_KILL_FOCUS
, self
._OnKillFocus 
)    ## run internal validator 
  95             self
.Bind(wx
.EVT_LEFT_DCLICK
, self
._OnDoubleClick
)  ## select field under cursor on dclick 
  96             self
.Bind(wx
.EVT_RIGHT_UP
, self
._OnContextMenu 
)    ## bring up an appropriate context menu 
  97             self
.Bind(wx
.EVT_KEY_DOWN
, self
._OnKeyDown 
)        ## capture control events not normally seen, eg ctrl-tab. 
  98             self
.Bind(wx
.EVT_CHAR
, self
._OnChar 
)               ## handle each keypress 
  99             self
.Bind(wx
.EVT_TEXT
, self
._OnTextChange 
)         ## color control appropriately & keep 
 100                                                                 ## track of previous value for undo 
 104         return "<BaseMaskedTextCtrl: %s>" % self
.GetValue() 
 107     def _GetSelection(self
): 
 109         Allow mixin to get the text selection of this control. 
 110         REQUIRED by any class derived from MaskedEditMixin. 
 112         return self
.GetSelection() 
 114     def _SetSelection(self
, sel_start
, sel_to
): 
 116         Allow mixin to set the text selection of this control. 
 117         REQUIRED by any class derived from MaskedEditMixin. 
 119 ####        dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals()) 
 120         return self
.SetSelection( sel_start
, sel_to 
) 
 122     def SetSelection(self
, sel_start
, sel_to
): 
 124         This is just for debugging... 
 126 ##        dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals()) 
 127         wx
.TextCtrl
.SetSelection(self
, sel_start
, sel_to
) 
 130     def _GetInsertionPoint(self
): 
 131         return self
.GetInsertionPoint() 
 133     def _SetInsertionPoint(self
, pos
): 
 134 ####        dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals()) 
 135         self
.SetInsertionPoint(pos
) 
 137     def SetInsertionPoint(self
, pos
): 
 139         This is just for debugging... 
 141 ##        dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals()) 
 142         wx
.TextCtrl
.SetInsertionPoint(self
, pos
) 
 147         Allow mixin to get the raw value of the control with this function. 
 148         REQUIRED by any class derived from MaskedEditMixin. 
 150         return self
.GetValue() 
 152     def _SetValue(self
, value
): 
 154         Allow mixin to set the raw value of the control with this function. 
 155         REQUIRED by any class derived from MaskedEditMixin. 
 157 ##        dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1) 
 158         # Record current selection and insertion point, for undo 
 159         self
._prevSelection 
= self
._GetSelection
() 
 160         self
._prevInsertionPoint 
= self
._GetInsertionPoint
() 
 161         wx
.TextCtrl
.SetValue(self
, value
) 
 164     def SetValue(self
, value
): 
 166         This function redefines the externally accessible .SetValue to be 
 167         a smart "paste" of the text in question, so as not to corrupt the 
 168         masked control.  NOTE: this must be done in the class derived 
 169         from the base wx control. 
 171 ##        dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1) 
 174             wx
.TextCtrl
.SetValue(self
, value
)    # revert to base control behavior 
 177         # empty previous contents, replacing entire value: 
 178         self
._SetInsertionPoint
(0) 
 179         self
._SetSelection
(0, self
._masklength
) 
 180         if self
._signOk 
and self
._useParens
: 
 181             signpos 
= value
.find('-') 
 183                 value 
= value
[:signpos
] + '(' + value
[signpos
+1:].strip() + ')' 
 184             elif value
.find(')') == -1 and len(value
) < self
._masklength
: 
 185                 value 
+= ' '    # add place holder for reserved space for right paren 
 187         if( len(value
) < self
._masklength                
# value shorter than control 
 188             and (self
._isFloat 
or self
._isInt
)            # and it's a numeric control 
 189             and self
._ctrl
_constraints
._alignRight 
):   # and it's a right-aligned control 
 191 ##            dbg('len(value)', len(value), ' < self._masklength', self._masklength) 
 192             # try to intelligently "pad out" the value to the right size: 
 193             value 
= self
._template
[0:self
._masklength 
- len(value
)] + value
 
 194             if self
._isFloat 
and value
.find('.') == -1: 
 196 ##            dbg('padded value = "%s"' % value) 
 198         # make SetValue behave the same as if you had typed the value in: 
 200             value
, replace_to 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 202                 self
._isNeg 
= False     # (clear current assumptions) 
 203                 value 
= self
._adjustFloat
(value
) 
 205                 self
._isNeg 
= False     # (clear current assumptions) 
 206                 value 
= self
._adjustInt
(value
) 
 207             elif self
._isDate 
and not self
.IsValid(value
) and self
._4digityear
: 
 208                 value 
= self
._adjustDate
(value
, fixcentury
=True) 
 210             # If date, year might be 2 digits vs. 4; try adjusting it: 
 211             if self
._isDate 
and self
._4digityear
: 
 212                 dateparts 
= value
.split(' ') 
 213                 dateparts
[0] = self
._adjustDate
(dateparts
[0], fixcentury
=True) 
 214                 value 
= string
.join(dateparts
, ' ') 
 215 ##                dbg('adjusted value: "%s"' % value) 
 216                 value
, replace_to 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 218 ##                dbg('exception thrown', indent=0) 
 221         self
._SetValue
(value
)   # note: to preserve similar capability, .SetValue() 
 222                                 # does not change IsModified() 
 223 ####        dbg('queuing insertion after .SetValue', replace_to) 
 224         # set selection to last char replaced by paste 
 225         wx
.CallAfter(self
._SetInsertionPoint
, replace_to
) 
 226         wx
.CallAfter(self
._SetSelection
, replace_to
, replace_to
) 
 229     def SetFont(self
, *args
, **kwargs
): 
 230         """ Set the font, then recalculate control size, if appropriate. """ 
 231         wx
.TextCtrl
.SetFont(self
, *args
, **kwargs
) 
 233 ##            dbg('calculated size:', self._CalcSize())             
 234             self
.SetClientSize(self
._CalcSize
()) 
 235             width 
= self
.GetSize().width
 
 236             height 
= self
.GetBestSize().height
 
 237 ##            dbg('setting client size to:', (width, height)) 
 238             self
.SetSize((width
, height
)) 
 239             self
.SetSizeHints((width
, height
)) 
 243         """ Blanks the current control value by replacing it with the default value.""" 
 244 ##        dbg("MaskedTextCtrl::Clear - value reset to default value (template)") 
 248             wx
.TextCtrl
.Clear(self
)    # else revert to base control behavior 
 253         Allow mixin to refresh the base control with this function. 
 254         REQUIRED by any class derived from MaskedEditMixin. 
 256 ##        dbg('MaskedTextCtrl::_Refresh', indent=1) 
 257         wx
.TextCtrl
.Refresh(self
) 
 263         This function redefines the externally accessible .Refresh() to 
 264         validate the contents of the masked control as it refreshes. 
 265         NOTE: this must be done in the class derived from the base wx control. 
 267 ##        dbg('MaskedTextCtrl::Refresh', indent=1) 
 273     def _IsEditable(self
): 
 275         Allow mixin to determine if the base control is editable with this function. 
 276         REQUIRED by any class derived from MaskedEditMixin. 
 278         return wx
.TextCtrl
.IsEditable(self
) 
 283         This function redefines the externally accessible .Cut to be 
 284         a smart "erase" of the text in question, so as not to corrupt the 
 285         masked control.  NOTE: this must be done in the class derived 
 286         from the base wx control. 
 289             self
._Cut
()             # call the mixin's Cut method 
 291             wx
.TextCtrl
.Cut(self
)    # else revert to base control behavior 
 296         This function redefines the externally accessible .Paste to be 
 297         a smart "paste" of the text in question, so as not to corrupt the 
 298         masked control.  NOTE: this must be done in the class derived 
 299         from the base wx control. 
 302             self
._Paste
()                   # call the mixin's Paste method 
 304             wx
.TextCtrl
.Paste(self
, value
)   # else revert to base control behavior 
 309         This function defines the undo operation for the control. (The default 
 315             wx
.TextCtrl
.Undo(self
)   # else revert to base control behavior 
 318     def IsModified(self
): 
 320         This function overrides the raw wxTextCtrl method, because the 
 321         masked edit mixin uses SetValue to change the value, which doesn't 
 322         modify the state of this attribute.  So, we keep track on each 
 323         keystroke to see if the value changes, and if so, it's been 
 326         return wx
.TextCtrl
.IsModified(self
) or self
.modified
 
 329     def _CalcSize(self
, size
=None): 
 331         Calculate automatic size if allowed; use base mixin function. 
 333         return self
._calcSize
(size
) 
 336 class TextCtrl( BaseMaskedTextCtrl
, MaskedEditAccessorsMixin 
): 
 338     This extra level of inheritance allows us to add the generic set of 
 339     masked edit parameters only to this class while allowing other 
 340     classes to derive from the "base" masked text control, and provide 
 341     a smaller set of valid accessor functions. 
 346 class PreMaskedTextCtrl( BaseMaskedTextCtrl
, MaskedEditAccessorsMixin 
): 
 348     This allows us to use XRC subclassing. 
 350     # This should really be wx.EVT_WINDOW_CREATE but it is not 
 351     # currently delivered for native controls on all platforms, so 
 352     # we'll use EVT_SIZE instead.  It should happen shortly after the 
 353     # control is created as the control is set to its "best" size. 
 354     _firstEventType 
= wx
.EVT_SIZE
 
 357         pre 
= wx
.PreTextCtrl() 
 359         self
.Bind(self
._firstEventType
, self
.OnCreate
) 
 362     def OnCreate(self
, evt
): 
 363         self
.Unbind(self
._firstEventType
) 
 368 ## ==================== 
 370 ##  1. Added .SetFont() method that properly resizes control 
 371 ##  2. Modified control to support construction via XRC mechanism.