]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/masked/textctrl.py
Use the right class name for caling __init__
[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 import wx
19 from wx.lib.masked import *
20
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
24 dbg = Logger()
25 ##dbg(enable=1)
26
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.)
34 #
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,
41 # ## we define
42 # ## BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin)
43 # ## and
44 # ## MaskedTextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin).
45 # ##
46 # ## This allows us to then derive:
47 # ## MaskedNumCtrl( BaseMaskedTextCtrl )
48 # ##
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.
51 # ##
52
53 class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
54 """
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
62 those derivations.
63 """
64
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
72 **kwargs):
73
74 wx.TextCtrl.__init__(self, parent, id, value='',
75 pos=pos, size = size,
76 style=style, validator=validator,
77 name=name)
78
79 self._PostInit(setupEventHandling = setupEventHandling,
80 name=name, value=value,**kwargs )
81
82
83 def _PostInit(self,setupEventHandling=True,
84 name='maskedTextCtrl' , value='', **kwargs):
85
86 self.controlInitialized = True
87 MaskedEditMixin.__init__( self, name, **kwargs )
88
89 self._SetInitialValue(value)
90
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
101
102
103 def __repr__(self):
104 return "<BaseMaskedTextCtrl: %s>" % self.GetValue()
105
106
107 def _GetSelection(self):
108 """
109 Allow mixin to get the text selection of this control.
110 REQUIRED by any class derived from MaskedEditMixin.
111 """
112 return self.GetSelection()
113
114 def _SetSelection(self, sel_start, sel_to):
115 """
116 Allow mixin to set the text selection of this control.
117 REQUIRED by any class derived from MaskedEditMixin.
118 """
119 #### dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
120 return self.SetSelection( sel_start, sel_to )
121
122 def SetSelection(self, sel_start, sel_to):
123 """
124 This is just for debugging...
125 """
126 ## dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
127 wx.TextCtrl.SetSelection(self, sel_start, sel_to)
128
129
130 def _GetInsertionPoint(self):
131 return self.GetInsertionPoint()
132
133 def _SetInsertionPoint(self, pos):
134 #### dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
135 self.SetInsertionPoint(pos)
136
137 def SetInsertionPoint(self, pos):
138 """
139 This is just for debugging...
140 """
141 ## dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
142 wx.TextCtrl.SetInsertionPoint(self, pos)
143
144
145 def _GetValue(self):
146 """
147 Allow mixin to get the raw value of the control with this function.
148 REQUIRED by any class derived from MaskedEditMixin.
149 """
150 return self.GetValue()
151
152 def _SetValue(self, value):
153 """
154 Allow mixin to set the raw value of the control with this function.
155 REQUIRED by any class derived from MaskedEditMixin.
156 """
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)
162 ## dbg(indent=0)
163
164 def SetValue(self, value):
165 """
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.
170 """
171 ## dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
172
173 if not self._mask:
174 wx.TextCtrl.SetValue(self, value) # revert to base control behavior
175 return
176
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('-')
182 if signpos != -1:
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
186
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
190
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:
195 value = value[1:]
196 ## dbg('padded value = "%s"' % value)
197
198 # make SetValue behave the same as if you had typed the value in:
199 try:
200 value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
201 if self._isFloat:
202 self._isNeg = False # (clear current assumptions)
203 value = self._adjustFloat(value)
204 elif self._isInt:
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)
209 except ValueError:
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)
217 else:
218 ## dbg('exception thrown', indent=0)
219 raise
220
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)
227 ## dbg(indent=0)
228
229 def SetFont(self, *args, **kwargs):
230 """ Set the font, then recalculate control size, if appropriate. """
231 wx.TextCtrl.SetFont(self, *args, **kwargs)
232 if self._autofit:
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.SetBestFittingSize((width, height))
239
240
241 def Clear(self):
242 """ Blanks the current control value by replacing it with the default value."""
243 ## dbg("MaskedTextCtrl::Clear - value reset to default value (template)")
244 if self._mask:
245 self.ClearValue()
246 else:
247 wx.TextCtrl.Clear(self) # else revert to base control behavior
248
249
250 def _Refresh(self):
251 """
252 Allow mixin to refresh the base control with this function.
253 REQUIRED by any class derived from MaskedEditMixin.
254 """
255 ## dbg('MaskedTextCtrl::_Refresh', indent=1)
256 wx.TextCtrl.Refresh(self)
257 ## dbg(indent=0)
258
259
260 def Refresh(self):
261 """
262 This function redefines the externally accessible .Refresh() to
263 validate the contents of the masked control as it refreshes.
264 NOTE: this must be done in the class derived from the base wx control.
265 """
266 ## dbg('MaskedTextCtrl::Refresh', indent=1)
267 self._CheckValid()
268 self._Refresh()
269 ## dbg(indent=0)
270
271
272 def _IsEditable(self):
273 """
274 Allow mixin to determine if the base control is editable with this function.
275 REQUIRED by any class derived from MaskedEditMixin.
276 """
277 return wx.TextCtrl.IsEditable(self)
278
279
280 def Cut(self):
281 """
282 This function redefines the externally accessible .Cut to be
283 a smart "erase" of the text in question, so as not to corrupt the
284 masked control. NOTE: this must be done in the class derived
285 from the base wx control.
286 """
287 if self._mask:
288 self._Cut() # call the mixin's Cut method
289 else:
290 wx.TextCtrl.Cut(self) # else revert to base control behavior
291
292
293 def Paste(self):
294 """
295 This function redefines the externally accessible .Paste to be
296 a smart "paste" of the text in question, so as not to corrupt the
297 masked control. NOTE: this must be done in the class derived
298 from the base wx control.
299 """
300 if self._mask:
301 self._Paste() # call the mixin's Paste method
302 else:
303 wx.TextCtrl.Paste(self, value) # else revert to base control behavior
304
305
306 def Undo(self):
307 """
308 This function defines the undo operation for the control. (The default
309 undo is 1-deep.)
310 """
311 if self._mask:
312 self._Undo()
313 else:
314 wx.TextCtrl.Undo(self) # else revert to base control behavior
315
316
317 def IsModified(self):
318 """
319 This function overrides the raw wxTextCtrl method, because the
320 masked edit mixin uses SetValue to change the value, which doesn't
321 modify the state of this attribute. So, we keep track on each
322 keystroke to see if the value changes, and if so, it's been
323 modified.
324 """
325 return wx.TextCtrl.IsModified(self) or self.modified
326
327
328 def _CalcSize(self, size=None):
329 """
330 Calculate automatic size if allowed; use base mixin function.
331 """
332 return self._calcSize(size)
333
334
335 class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
336 """
337 This extra level of inheritance allows us to add the generic set of
338 masked edit parameters only to this class while allowing other
339 classes to derive from the "base" masked text control, and provide
340 a smaller set of valid accessor functions.
341 """
342 pass
343
344
345 class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
346 """
347 This allows us to use XRC subclassing.
348 """
349 # This should really be wx.EVT_WINDOW_CREATE but it is not
350 # currently delivered for native controls on all platforms, so
351 # we'll use EVT_SIZE instead. It should happen shortly after the
352 # control is created as the control is set to its "best" size.
353 _firstEventType = wx.EVT_SIZE
354
355 def __init__(self):
356 pre = wx.PreTextCtrl()
357 self.PostCreate(pre)
358 self.Bind(self._firstEventType, self.OnCreate)
359
360
361 def OnCreate(self, evt):
362 self.Unbind(self._firstEventType)
363 self._PostInit()
364
365 i=0
366 ## CHANGELOG:
367 ## ====================
368 ## Version 1.1
369 ## 1. Added .SetFont() method that properly resizes control
370 ## 2. Modified control to support construction via XRC mechanism.