]>
Commit | Line | Data |
---|---|---|
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 | import wx | |
19 | from wx.lib.masked import BaseMaskedTextCtrl | |
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=0) | |
26 | ||
27 | class IpAddrCtrlAccessorsMixin: | |
28 | # Define IpAddrCtrl's list of attributes having their own | |
29 | # Get/Set functions, exposing only those that make sense for | |
30 | # an IP address control. | |
31 | ||
32 | exposed_basectrl_params = ( | |
33 | 'fields', | |
34 | 'retainFieldValidation', | |
35 | 'formatcodes', | |
36 | 'fillChar', | |
37 | 'defaultValue', | |
38 | 'description', | |
39 | ||
40 | 'useFixedWidthFont', | |
41 | 'signedForegroundColour', | |
42 | 'emptyBackgroundColour', | |
43 | 'validBackgroundColour', | |
44 | 'invalidBackgroundColour', | |
45 | ||
46 | 'emptyInvalid', | |
47 | 'validFunc', | |
48 | 'validRequired', | |
49 | ) | |
50 | ||
51 | for param in exposed_basectrl_params: | |
52 | propname = param[0].upper() + param[1:] | |
53 | exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param)) | |
54 | exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param)) | |
55 | ||
56 | if param.find('Colour') != -1: | |
57 | # add non-british spellings, for backward-compatibility | |
58 | propname.replace('Colour', 'Color') | |
59 | ||
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 | ||
64 | class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ): | |
65 | """ | |
66 | This class is a particular type of MaskedTextCtrl that accepts | |
67 | and understands the semantics of IP addresses, reformats input | |
68 | as you move from field to field, and accepts '.' as a navigation | |
69 | character, so that typing an IP address can be done naturally. | |
70 | """ | |
71 | ||
72 | ||
73 | ||
74 | def __init__( self, parent, id=-1, value = '', | |
75 | pos = wx.DefaultPosition, | |
76 | size = wx.DefaultSize, | |
77 | style = wx.TE_PROCESS_TAB, | |
78 | validator = wx.DefaultValidator, | |
79 | name = 'IpAddrCtrl', | |
80 | setupEventHandling = True, ## setup event handling by default | |
81 | **kwargs): | |
82 | ||
83 | if not kwargs.has_key('mask'): | |
84 | kwargs['mask'] = mask = "###.###.###.###" | |
85 | if not kwargs.has_key('formatcodes'): | |
86 | kwargs['formatcodes'] = 'F_Sr<' | |
87 | if not kwargs.has_key('validRegex'): | |
88 | 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}" | |
89 | ||
90 | ||
91 | BaseMaskedTextCtrl.__init__( | |
92 | self, parent, id=id, value = value, | |
93 | pos=pos, size=size, | |
94 | style = style, | |
95 | validator = validator, | |
96 | name = name, | |
97 | setupEventHandling = setupEventHandling, | |
98 | **kwargs) | |
99 | ||
100 | ||
101 | # set up individual field parameters as well: | |
102 | field_params = {} | |
103 | field_params['validRegex'] = "( | \d| \d |\d | \d\d|\d\d |\d \d|(1\d\d|2[0-4]\d|25[0-5]))" | |
104 | ||
105 | # require "valid" string; this prevents entry of any value > 255, but allows | |
106 | # intermediate constructions; overall control validation requires well-formatted value. | |
107 | field_params['formatcodes'] = 'V' | |
108 | ||
109 | if field_params: | |
110 | for i in self._field_indices: | |
111 | self.SetFieldParameters(i, **field_params) | |
112 | ||
113 | # This makes '.' act like tab: | |
114 | self._AddNavKey('.', handler=self.OnDot) | |
115 | self._AddNavKey('>', handler=self.OnDot) # for "shift-." | |
116 | ||
117 | ||
118 | def OnDot(self, event): | |
119 | ## dbg('IpAddrCtrl::OnDot', indent=1) | |
120 | pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode()) | |
121 | oldvalue = self.GetValue() | |
122 | edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True) | |
123 | if not event.ShiftDown(): | |
124 | if pos > edit_start and pos < edit_end: | |
125 | # clip data in field to the right of pos, if adjusting fields | |
126 | # when not at delimeter; (assumption == they hit '.') | |
127 | newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:] | |
128 | self._SetValue(newvalue) | |
129 | self._SetInsertionPoint(pos) | |
130 | ## dbg(indent=0) | |
131 | return self._OnChangeField(event) | |
132 | ||
133 | ||
134 | ||
135 | def GetAddress(self): | |
136 | value = BaseMaskedTextCtrl.GetValue(self) | |
137 | return value.replace(' ','') # remove spaces from the value | |
138 | ||
139 | ||
140 | def _OnCtrl_S(self, event): | |
141 | ## dbg("IpAddrCtrl::_OnCtrl_S") | |
142 | if self._demo: | |
143 | print "value:", self.GetAddress() | |
144 | return False | |
145 | ||
146 | def SetValue(self, value): | |
147 | ## dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1) | |
148 | if type(value) not in (types.StringType, types.UnicodeType): | |
149 | ## dbg(indent=0) | |
150 | raise ValueError('%s must be a string', str(value)) | |
151 | ||
152 | bValid = True # assume True | |
153 | parts = value.split('.') | |
154 | if len(parts) != 4: | |
155 | bValid = False | |
156 | else: | |
157 | for i in range(4): | |
158 | part = parts[i] | |
159 | if not 0 <= len(part) <= 3: | |
160 | bValid = False | |
161 | break | |
162 | elif part.strip(): # non-empty part | |
163 | try: | |
164 | j = string.atoi(part) | |
165 | if not 0 <= j <= 255: | |
166 | bValid = False | |
167 | break | |
168 | else: | |
169 | parts[i] = '%3d' % j | |
170 | except: | |
171 | bValid = False | |
172 | break | |
173 | else: | |
174 | # allow empty sections for SetValue (will result in "invalid" value, | |
175 | # but this may be useful for initializing the control: | |
176 | parts[i] = ' ' # convert empty field to 3-char length | |
177 | ||
178 | if not bValid: | |
179 | ## dbg(indent=0) | |
180 | 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)) | |
181 | else: | |
182 | ## dbg('parts:', parts) | |
183 | value = string.join(parts, '.') | |
184 | BaseMaskedTextCtrl.SetValue(self, value) | |
185 | ## dbg(indent=0) | |
186 | ||
187 |