]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/masked/textctrl.py
wx.lib.masked: Patch from Will Sadkin. Includes Unicode fixes, plus
[wxWidgets.git] / wxPython / wx / lib / masked / textctrl.py
1 #----------------------------------------------------------------------------
2 # Name: masked.textctrl.py
3 # Authors: Jeff Childers, Will Sadkin
4 # Email: jchilders_98@yahoo.com, wsadkin@nameconnector.com
5 # Created: 02/11/2003
6 # Copyright: (c) 2003 by Jeff Childers, Will Sadkin, 2003
7 # Portions: (c) 2002 by Will Sadkin, 2002-2003
8 # RCS-ID: $Id$
9 # License: wxWidgets license
10 #----------------------------------------------------------------------------
11 #
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.
16 #
17 #----------------------------------------------------------------------------
18 """
19 Provides a generic, fully configurable masked edit text control, as well as
20 a base class from which you can derive masked controls tailored to a specific
21 function. See maskededit module overview for how to configure the control.
22 """
23
24 import wx
25 from wx.lib.masked import *
26
27 # jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
28 # be a good place to implement the 2.3 logger class
29 from wx.tools.dbg import Logger
30 ##dbg = Logger()
31 ##dbg(enable=1)
32
33
34 class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
35 """
36 This is the primary derivation from MaskedEditMixin. It provides
37 a general masked text control that can be configured with different
38 masks.
39
40 However, this is done with an extra level of inheritance, so that
41 "general" classes like masked.TextCtrl can have all possible attributes,
42 while derived classes, like masked.TimeCtrl and masked.NumCtrl
43 can prevent exposure of those optional attributes of their base
44 class that do not make sense for their derivation. Therefore,
45 we define::
46
47 BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin)
48
49 and::
50
51 masked.TextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin).
52
53 This allows us to then derive::
54
55 masked.NumCtrl( BaseMaskedTextCtrl )
56
57 and not have to expose all the same accessor functions for the
58 derived control when they don't all make sense for it.
59
60 In practice, BaseMaskedTextCtrl should never be instantiated directly,
61 but should only be used in derived classes.
62 """
63
64 def __init__( self, parent, id=-1, value = '',
65 pos = wx.DefaultPosition,
66 size = wx.DefaultSize,
67 style = wx.TE_PROCESS_TAB,
68 validator=wx.DefaultValidator, ## placeholder provided for data-transfer logic
69 name = 'maskedTextCtrl',
70 setupEventHandling = True, ## setup event handling by default
71 **kwargs):
72
73 wx.TextCtrl.__init__(self, parent, id, value='',
74 pos=pos, size = size,
75 style=style, validator=validator,
76 name=name)
77
78 self._PostInit(setupEventHandling = setupEventHandling,
79 name=name, value=value,**kwargs )
80
81
82 def _PostInit(self,setupEventHandling=True,
83 name='maskedTextCtrl' , value='', **kwargs):
84
85 self.controlInitialized = True
86 MaskedEditMixin.__init__( self, name, **kwargs )
87
88 self._SetInitialValue(value)
89
90 if setupEventHandling:
91 ## Setup event handlers
92 self.Bind(wx.EVT_SET_FOCUS, self._OnFocus ) ## defeat automatic full selection
93 self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus ) ## run internal validator
94 self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick) ## select field under cursor on dclick
95 self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu ) ## bring up an appropriate context menu
96 self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## capture control events not normally seen, eg ctrl-tab.
97 self.Bind(wx.EVT_CHAR, self._OnChar ) ## handle each keypress
98 self.Bind(wx.EVT_TEXT, self._OnTextChange ) ## color control appropriately & keep
99 ## track of previous value for undo
100
101
102 def __repr__(self):
103 return "<BaseMaskedTextCtrl: %s>" % self.GetValue()
104
105
106 def _GetSelection(self):
107 """
108 Allow mixin to get the text selection of this control.
109 REQUIRED by any class derived from MaskedEditMixin.
110 """
111 return self.GetSelection()
112
113 def _SetSelection(self, sel_start, sel_to):
114 """
115 Allow mixin to set the text selection of this control.
116 REQUIRED by any class derived from MaskedEditMixin.
117 """
118 #### dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
119 return self.SetSelection( sel_start, sel_to )
120
121 ## def SetSelection(self, sel_start, sel_to):
122 ## """
123 ## This is just for debugging...
124 ## """
125 ## dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
126 ## wx.TextCtrl.SetSelection(self, sel_start, sel_to)
127
128
129 def _GetInsertionPoint(self):
130 return self.GetInsertionPoint()
131
132 def _SetInsertionPoint(self, pos):
133 #### dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
134 self.SetInsertionPoint(pos)
135
136 ## def SetInsertionPoint(self, pos):
137 ## """
138 ## This is just for debugging...
139 ## """
140 ## dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
141 ## wx.TextCtrl.SetInsertionPoint(self, pos)
142
143
144 def IsEmpty(*args, **kw):
145 return MaskedEditMixin.IsEmpty(*args, **kw)
146
147 def _GetValue(self):
148 """
149 Allow mixin to get the raw value of the control with this function.
150 REQUIRED by any class derived from MaskedEditMixin.
151 """
152 return self.GetValue()
153
154
155 def _SetValue(self, value):
156 """
157 Allow mixin to set the raw value of the control with this function.
158 REQUIRED by any class derived from MaskedEditMixin.
159 """
160 ## dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
161 # Record current selection and insertion point, for undo
162 self._prevSelection = self._GetSelection()
163 self._prevInsertionPoint = self._GetInsertionPoint()
164 wx.TextCtrl.SetValue(self, value)
165 ## dbg(indent=0)
166
167 def SetValue(self, value):
168 """
169 This function redefines the externally accessible .SetValue() to be
170 a smart "paste" of the text in question, so as not to corrupt the
171 masked control. NOTE: this must be done in the class derived
172 from the base wx control.
173 """
174 ## dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
175
176 if not self._mask:
177 wx.TextCtrl.SetValue(self, value) # revert to base control behavior
178 return
179
180 # empty previous contents, replacing entire value:
181 self._SetInsertionPoint(0)
182 self._SetSelection(0, self._masklength)
183 if self._signOk and self._useParens:
184 signpos = value.find('-')
185 if signpos != -1:
186 value = value[:signpos] + '(' + value[signpos+1:].strip() + ')'
187 elif value.find(')') == -1 and len(value) < self._masklength:
188 value += ' ' # add place holder for reserved space for right paren
189
190 if( len(value) < self._masklength # value shorter than control
191 and (self._isFloat or self._isInt) # and it's a numeric control
192 and self._ctrl_constraints._alignRight ): # and it's a right-aligned control
193
194 ## dbg('len(value)', len(value), ' < self._masklength', self._masklength)
195 # try to intelligently "pad out" the value to the right size:
196 value = self._template[0:self._masklength - len(value)] + value
197 if self._isFloat and value.find('.') == -1:
198 value = value[1:]
199 ## dbg('padded value = "%s"' % value)
200
201 # make SetValue behave the same as if you had typed the value in:
202 try:
203 value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
204 if self._isFloat:
205 self._isNeg = False # (clear current assumptions)
206 value = self._adjustFloat(value)
207 elif self._isInt:
208 self._isNeg = False # (clear current assumptions)
209 value = self._adjustInt(value)
210 elif self._isDate and not self.IsValid(value) and self._4digityear:
211 value = self._adjustDate(value, fixcentury=True)
212 except ValueError:
213 # If date, year might be 2 digits vs. 4; try adjusting it:
214 if self._isDate and self._4digityear:
215 dateparts = value.split(' ')
216 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
217 value = string.join(dateparts, ' ')
218 ## dbg('adjusted value: "%s"' % value)
219 value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
220 else:
221 ## dbg('exception thrown', indent=0)
222 raise
223
224 self._SetValue(value) # note: to preserve similar capability, .SetValue()
225 # does not change IsModified()
226 #### dbg('queuing insertion after .SetValue', replace_to)
227 # set selection to last char replaced by paste
228 wx.CallAfter(self._SetInsertionPoint, replace_to)
229 wx.CallAfter(self._SetSelection, replace_to, replace_to)
230 ## dbg(indent=0)
231
232
233 def SetFont(self, *args, **kwargs):
234 """ Set the font, then recalculate control size, if appropriate. """
235 wx.TextCtrl.SetFont(self, *args, **kwargs)
236 if self._autofit:
237 ## dbg('calculated size:', self._CalcSize())
238 self.SetClientSize(self._CalcSize())
239 width = self.GetSize().width
240 height = self.GetBestSize().height
241 ## dbg('setting client size to:', (width, height))
242 self.SetInitialSize((width, height))
243
244
245 def Clear(self):
246 """ Blanks the current control value by replacing it with the default value."""
247 ## dbg("MaskedTextCtrl::Clear - value reset to default value (template)")
248 if self._mask:
249 self.ClearValue()
250 else:
251 wx.TextCtrl.Clear(self) # else revert to base control behavior
252
253
254 def _Refresh(self):
255 """
256 Allow mixin to refresh the base control with this function.
257 REQUIRED by any class derived from MaskedEditMixin.
258 """
259 ## dbg('MaskedTextCtrl::_Refresh', indent=1)
260 wx.TextCtrl.Refresh(self)
261 ## dbg(indent=0)
262
263
264 def Refresh(self):
265 """
266 This function redefines the externally accessible .Refresh() to
267 validate the contents of the masked control as it refreshes.
268 NOTE: this must be done in the class derived from the base wx control.
269 """
270 ## dbg('MaskedTextCtrl::Refresh', indent=1)
271 self._CheckValid()
272 self._Refresh()
273 ## dbg(indent=0)
274
275
276 def _IsEditable(self):
277 """
278 Allow mixin to determine if the base control is editable with this function.
279 REQUIRED by any class derived from MaskedEditMixin.
280 """
281 return wx.TextCtrl.IsEditable(self)
282
283
284 def Cut(self):
285 """
286 This function redefines the externally accessible .Cut to be
287 a smart "erase" of the text in question, so as not to corrupt the
288 masked control. NOTE: this must be done in the class derived
289 from the base wx control.
290 """
291 if self._mask:
292 self._Cut() # call the mixin's Cut method
293 else:
294 wx.TextCtrl.Cut(self) # else revert to base control behavior
295
296
297 def Paste(self):
298 """
299 This function redefines the externally accessible .Paste to be
300 a smart "paste" of the text in question, so as not to corrupt the
301 masked control. NOTE: this must be done in the class derived
302 from the base wx control.
303 """
304 if self._mask:
305 self._Paste() # call the mixin's Paste method
306 else:
307 wx.TextCtrl.Paste(self, value) # else revert to base control behavior
308
309
310 def Undo(self):
311 """
312 This function defines the undo operation for the control. (The default
313 undo is 1-deep.)
314 """
315 if self._mask:
316 self._Undo()
317 else:
318 wx.TextCtrl.Undo(self) # else revert to base control behavior
319
320
321 def IsModified(self):
322 """
323 This function overrides the raw wx.TextCtrl method, because the
324 masked edit mixin uses SetValue to change the value, which doesn't
325 modify the state of this attribute. So, the derived control keeps track
326 on each keystroke to see if the value changes, and if so, it's been
327 modified.
328 """
329 return wx.TextCtrl.IsModified(self) or self.modified
330
331
332 def _CalcSize(self, size=None):
333 """
334 Calculate automatic size if allowed; use base mixin function.
335 """
336 return self._calcSize(size)
337
338
339 class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
340 """
341 The "user-visible" masked text control; it is identical to the
342 BaseMaskedTextCtrl class it's derived from.
343 (This extra level of inheritance allows us to add the generic
344 set of masked edit parameters only to this class while allowing
345 other classes to derive from the "base" masked text control,
346 and provide a smaller set of valid accessor functions.)
347 See BaseMaskedTextCtrl for available methods.
348 """
349 pass
350
351
352 class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
353 """
354 This class exists to support the use of XRC subclassing.
355 """
356 # This should really be wx.EVT_WINDOW_CREATE but it is not
357 # currently delivered for native controls on all platforms, so
358 # we'll use EVT_SIZE instead. It should happen shortly after the
359 # control is created as the control is set to its "best" size.
360 _firstEventType = wx.EVT_SIZE
361
362 def __init__(self):
363 pre = wx.PreTextCtrl()
364 self.PostCreate(pre)
365 self.Bind(self._firstEventType, self.OnCreate)
366
367
368 def OnCreate(self, evt):
369 self.Unbind(self._firstEventType)
370 self._PostInit()
371
372 __i=0
373 ## CHANGELOG:
374 ## ====================
375 ## Version 1.2
376 ## - Converted docstrings to reST format, added doc for ePyDoc.
377 ## removed debugging override functions.
378 ##
379 ## Version 1.1
380 ## 1. Added .SetFont() method that properly resizes control
381 ## 2. Modified control to support construction via XRC mechanism.