]>
Commit | Line | Data |
---|---|---|
c878ceea RD |
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 | #---------------------------------------------------------------------------- | |
f54a36bb RD |
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 | """ | |
c878ceea | 22 | |
f54a36bb | 23 | import wx, types, string |
c878ceea RD |
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 | |
f54a36bb | 29 | ##dbg = Logger() |
c878ceea RD |
30 | ##dbg(enable=0) |
31 | ||
32 | class IpAddrCtrlAccessorsMixin: | |
f54a36bb RD |
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 | """ | |
c878ceea RD |
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'): | |
5f280eaa | 93 | kwargs['formatcodes'] = 'F_Sr<>' |
c878ceea RD |
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): | |
f54a36bb RD |
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 | """ | |
c878ceea RD |
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): | |
f54a36bb RD |
148 | """ |
149 | Returns the control value, with any spaces removed. | |
150 | """ | |
c878ceea RD |
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): | |
f54a36bb RD |
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 | """ | |
c878ceea RD |
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('.') | |
f54a36bb | 175 | |
c878ceea RD |
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 | ||
f54a36bb RD |
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 | ## | |
5f280eaa RD |
219 | ## Version 1.1 |
220 | ## Made ipaddrctrls allow right-insert in subfields, now that insert/cut/paste works better |