]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/masked/ipaddrctrl.py
wx.lib.masked: Patch from Will Sadkin. Includes Unicode fixes, plus
[wxWidgets.git] / wxPython / wx / lib / masked / ipaddrctrl.py
1 #----------------------------------------------------------------------------
2 # Name: masked.ipaddrctrl.py
3 # Authors: Will Sadkin
4 # Email: wsadkin@nameconnector.com
5 # Created: 02/11/2003
6 # Copyright: (c) 2003 by Will Sadkin, 2003
7 # RCS-ID: $Id$
8 # License: wxWidgets license
9 #----------------------------------------------------------------------------
10 # NOTE:
11 # Masked.IpAddrCtrl is a minor modification to masked.TextCtrl, that is
12 # specifically tailored for entering IP addresses. It allows for
13 # right-insert fields and provides an accessor to obtain the entered
14 # address with extra whitespace removed.
15 #
16 #----------------------------------------------------------------------------
17 """
18 Provides a smart text input control that understands the structure and
19 limits of IP Addresses, and allows automatic field navigation as the
20 user hits '.' when typing.
21 """
22
23 import wx, types, string
24 from wx.lib.masked import BaseMaskedTextCtrl
25
26 # jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
27 # be a good place to implement the 2.3 logger class
28 from wx.tools.dbg import Logger
29 ##dbg = Logger()
30 ##dbg(enable=0)
31
32 class IpAddrCtrlAccessorsMixin:
33 """
34 Defines IpAddrCtrl's list of attributes having their own
35 Get/Set functions, exposing only those that make sense for
36 an IP address control.
37 """
38
39 exposed_basectrl_params = (
40 'fields',
41 'retainFieldValidation',
42 'formatcodes',
43 'fillChar',
44 'defaultValue',
45 'description',
46
47 'useFixedWidthFont',
48 'signedForegroundColour',
49 'emptyBackgroundColour',
50 'validBackgroundColour',
51 'invalidBackgroundColour',
52
53 'emptyInvalid',
54 'validFunc',
55 'validRequired',
56 )
57
58 for param in exposed_basectrl_params:
59 propname = param[0].upper() + param[1:]
60 exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
61 exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
62
63 if param.find('Colour') != -1:
64 # add non-british spellings, for backward-compatibility
65 propname.replace('Colour', 'Color')
66
67 exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
68 exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
69
70
71 class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ):
72 """
73 This class is a particular type of MaskedTextCtrl that accepts
74 and understands the semantics of IP addresses, reformats input
75 as you move from field to field, and accepts '.' as a navigation
76 character, so that typing an IP address can be done naturally.
77 """
78
79
80
81 def __init__( self, parent, id=-1, value = '',
82 pos = wx.DefaultPosition,
83 size = wx.DefaultSize,
84 style = wx.TE_PROCESS_TAB,
85 validator = wx.DefaultValidator,
86 name = 'IpAddrCtrl',
87 setupEventHandling = True, ## setup event handling by default
88 **kwargs):
89
90 if not kwargs.has_key('mask'):
91 kwargs['mask'] = mask = "###.###.###.###"
92 if not kwargs.has_key('formatcodes'):
93 kwargs['formatcodes'] = 'F_Sr<>'
94 if not kwargs.has_key('validRegex'):
95 kwargs['validRegex'] = "( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))(\.( \d| \d\d|(1\d\d|2[0-4]\d|25[0-5]))){3}"
96
97
98 BaseMaskedTextCtrl.__init__(
99 self, parent, id=id, value = value,
100 pos=pos, size=size,
101 style = style,
102 validator = validator,
103 name = name,
104 setupEventHandling = setupEventHandling,
105 **kwargs)
106
107
108 # set up individual field parameters as well:
109 field_params = {}
110 field_params['validRegex'] = "( | \d| \d |\d | \d\d|\d\d |\d \d|(1\d\d|2[0-4]\d|25[0-5]))"
111
112 # require "valid" string; this prevents entry of any value > 255, but allows
113 # intermediate constructions; overall control validation requires well-formatted value.
114 field_params['formatcodes'] = 'V'
115
116 if field_params:
117 for i in self._field_indices:
118 self.SetFieldParameters(i, **field_params)
119
120 # This makes '.' act like tab:
121 self._AddNavKey('.', handler=self.OnDot)
122 self._AddNavKey('>', handler=self.OnDot) # for "shift-."
123
124
125 def OnDot(self, event):
126 """
127 Defines what action to take when the '.' character is typed in the
128 control. By default, the current field is right-justified, and the
129 cursor is placed in the next field.
130 """
131 ## dbg('IpAddrCtrl::OnDot', indent=1)
132 pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
133 oldvalue = self.GetValue()
134 edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
135 if not event.ShiftDown():
136 if pos > edit_start and pos < edit_end:
137 # clip data in field to the right of pos, if adjusting fields
138 # when not at delimeter; (assumption == they hit '.')
139 newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
140 self._SetValue(newvalue)
141 self._SetInsertionPoint(pos)
142 ## dbg(indent=0)
143 return self._OnChangeField(event)
144
145
146
147 def GetAddress(self):
148 """
149 Returns the control value, with any spaces removed.
150 """
151 value = BaseMaskedTextCtrl.GetValue(self)
152 return value.replace(' ','') # remove spaces from the value
153
154
155 def _OnCtrl_S(self, event):
156 ## dbg("IpAddrCtrl::_OnCtrl_S")
157 if self._demo:
158 print "value:", self.GetAddress()
159 return False
160
161 def SetValue(self, value):
162 """
163 Takes a string value, validates it for a valid IP address,
164 splits it into an array of 4 fields, justifies it
165 appropriately, and inserts it into the control.
166 Invalid values will raise a ValueError exception.
167 """
168 ## dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1)
169 if type(value) not in (types.StringType, types.UnicodeType):
170 ## dbg(indent=0)
171 raise ValueError('%s must be a string', str(value))
172
173 bValid = True # assume True
174 parts = value.split('.')
175
176 if len(parts) != 4:
177 bValid = False
178 else:
179 for i in range(4):
180 part = parts[i]
181 if not 0 <= len(part) <= 3:
182 bValid = False
183 break
184 elif part.strip(): # non-empty part
185 try:
186 j = string.atoi(part)
187 if not 0 <= j <= 255:
188 bValid = False
189 break
190 else:
191 parts[i] = '%3d' % j
192 except:
193 bValid = False
194 break
195 else:
196 # allow empty sections for SetValue (will result in "invalid" value,
197 # but this may be useful for initializing the control:
198 parts[i] = ' ' # convert empty field to 3-char length
199
200 if not bValid:
201 ## dbg(indent=0)
202 raise ValueError('value (%s) must be a string of form n.n.n.n where n is empty or in range 0-255' % str(value))
203 else:
204 ## dbg('parts:', parts)
205 value = string.join(parts, '.')
206 BaseMaskedTextCtrl.SetValue(self, value)
207 ## dbg(indent=0)
208
209 __i=0
210 ## CHANGELOG:
211 ## ====================
212 ## Version 1.2
213 ## 1. Fixed bugs involving missing imports now that these classes are in
214 ## their own module.
215 ## 2. Added doc strings for ePyDoc.
216 ## 3. Renamed helper functions, vars etc. not intended to be visible in public
217 ## interface to code.
218 ##
219 ## Version 1.1
220 ## Made ipaddrctrls allow right-insert in subfields, now that insert/cut/paste works better