2004-04-19 19:24:37 -04:00
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# Name: masked.ipaddrctrl.py
|
|
|
|
# Authors: Will Sadkin
|
|
|
|
# Email: wsadkin@nameconnector.com
|
|
|
|
# Created: 02/11/2003
|
|
|
|
# Copyright: (c) 2003 by Will Sadkin, 2003
|
|
|
|
# RCS-ID: $Id$
|
|
|
|
# License: wxWidgets license
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# NOTE:
|
|
|
|
# Masked.IpAddrCtrl is a minor modification to masked.TextCtrl, that is
|
|
|
|
# specifically tailored for entering IP addresses. It allows for
|
|
|
|
# right-insert fields and provides an accessor to obtain the entered
|
|
|
|
# address with extra whitespace removed.
|
|
|
|
#
|
|
|
|
#----------------------------------------------------------------------------
|
2004-10-11 18:13:18 -04:00
|
|
|
"""
|
|
|
|
Provides a smart text input control that understands the structure and
|
|
|
|
limits of IP Addresses, and allows automatic field navigation as the
|
|
|
|
user hits '.' when typing.
|
|
|
|
"""
|
2004-04-19 19:24:37 -04:00
|
|
|
|
2004-10-11 18:13:18 -04:00
|
|
|
import wx, types, string
|
2004-04-19 19:24:37 -04:00
|
|
|
from wx.lib.masked import BaseMaskedTextCtrl
|
|
|
|
|
|
|
|
# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
|
|
|
|
# be a good place to implement the 2.3 logger class
|
|
|
|
from wx.tools.dbg import Logger
|
2004-10-11 18:13:18 -04:00
|
|
|
##dbg = Logger()
|
2004-04-19 19:24:37 -04:00
|
|
|
##dbg(enable=0)
|
|
|
|
|
|
|
|
class IpAddrCtrlAccessorsMixin:
|
2004-10-11 18:13:18 -04:00
|
|
|
"""
|
|
|
|
Defines IpAddrCtrl's list of attributes having their own
|
|
|
|
Get/Set functions, exposing only those that make sense for
|
|
|
|
an IP address control.
|
|
|
|
"""
|
2004-04-19 19:24:37 -04:00
|
|
|
|
|
|
|
exposed_basectrl_params = (
|
|
|
|
'fields',
|
|
|
|
'retainFieldValidation',
|
|
|
|
'formatcodes',
|
|
|
|
'fillChar',
|
|
|
|
'defaultValue',
|
|
|
|
'description',
|
|
|
|
|
|
|
|
'useFixedWidthFont',
|
|
|
|
'signedForegroundColour',
|
|
|
|
'emptyBackgroundColour',
|
|
|
|
'validBackgroundColour',
|
|
|
|
'invalidBackgroundColour',
|
|
|
|
|
|
|
|
'emptyInvalid',
|
|
|
|
'validFunc',
|
|
|
|
'validRequired',
|
|
|
|
)
|
|
|
|
|
|
|
|
for param in exposed_basectrl_params:
|
|
|
|
propname = param[0].upper() + param[1:]
|
|
|
|
exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
|
|
|
|
exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
|
|
|
|
|
|
|
|
if param.find('Colour') != -1:
|
|
|
|
# add non-british spellings, for backward-compatibility
|
|
|
|
propname.replace('Colour', 'Color')
|
|
|
|
|
|
|
|
exec('def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param))
|
|
|
|
exec('def Get%s(self): return self.GetCtrlParameter("%s")''' % (propname, param))
|
|
|
|
|
|
|
|
|
|
|
|
class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ):
|
|
|
|
"""
|
|
|
|
This class is a particular type of MaskedTextCtrl that accepts
|
|
|
|
and understands the semantics of IP addresses, reformats input
|
|
|
|
as you move from field to field, and accepts '.' as a navigation
|
|
|
|
character, so that typing an IP address can be done naturally.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__( self, parent, id=-1, value = '',
|
|
|
|
pos = wx.DefaultPosition,
|
|
|
|
size = wx.DefaultSize,
|
|
|
|
style = wx.TE_PROCESS_TAB,
|
|
|
|
validator = wx.DefaultValidator,
|
|
|
|
name = 'IpAddrCtrl',
|
|
|
|
setupEventHandling = True, ## setup event handling by default
|
|
|
|
**kwargs):
|
|
|
|
|
|
|
|
if not kwargs.has_key('mask'):
|
|
|
|
kwargs['mask'] = mask = "###.###.###.###"
|
|
|
|
if not kwargs.has_key('formatcodes'):
|
2004-06-19 12:25:55 -04:00
|
|
|
kwargs['formatcodes'] = 'F_Sr<>'
|
2004-04-19 19:24:37 -04:00
|
|
|
if not kwargs.has_key('validRegex'):
|
|
|
|
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}"
|
|
|
|
|
|
|
|
|
|
|
|
BaseMaskedTextCtrl.__init__(
|
|
|
|
self, parent, id=id, value = value,
|
|
|
|
pos=pos, size=size,
|
|
|
|
style = style,
|
|
|
|
validator = validator,
|
|
|
|
name = name,
|
|
|
|
setupEventHandling = setupEventHandling,
|
|
|
|
**kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
# set up individual field parameters as well:
|
|
|
|
field_params = {}
|
|
|
|
field_params['validRegex'] = "( | \d| \d |\d | \d\d|\d\d |\d \d|(1\d\d|2[0-4]\d|25[0-5]))"
|
|
|
|
|
|
|
|
# require "valid" string; this prevents entry of any value > 255, but allows
|
|
|
|
# intermediate constructions; overall control validation requires well-formatted value.
|
|
|
|
field_params['formatcodes'] = 'V'
|
|
|
|
|
|
|
|
if field_params:
|
|
|
|
for i in self._field_indices:
|
|
|
|
self.SetFieldParameters(i, **field_params)
|
|
|
|
|
|
|
|
# This makes '.' act like tab:
|
|
|
|
self._AddNavKey('.', handler=self.OnDot)
|
|
|
|
self._AddNavKey('>', handler=self.OnDot) # for "shift-."
|
|
|
|
|
|
|
|
|
|
|
|
def OnDot(self, event):
|
2004-10-11 18:13:18 -04:00
|
|
|
"""
|
|
|
|
Defines what action to take when the '.' character is typed in the
|
|
|
|
control. By default, the current field is right-justified, and the
|
|
|
|
cursor is placed in the next field.
|
|
|
|
"""
|
2004-04-19 19:24:37 -04:00
|
|
|
## dbg('IpAddrCtrl::OnDot', indent=1)
|
|
|
|
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
|
|
|
|
oldvalue = self.GetValue()
|
|
|
|
edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
|
|
|
|
if not event.ShiftDown():
|
|
|
|
if pos > edit_start and pos < edit_end:
|
|
|
|
# clip data in field to the right of pos, if adjusting fields
|
|
|
|
# when not at delimeter; (assumption == they hit '.')
|
|
|
|
newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
|
|
|
|
self._SetValue(newvalue)
|
|
|
|
self._SetInsertionPoint(pos)
|
|
|
|
## dbg(indent=0)
|
|
|
|
return self._OnChangeField(event)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetAddress(self):
|
2004-10-11 18:13:18 -04:00
|
|
|
"""
|
|
|
|
Returns the control value, with any spaces removed.
|
|
|
|
"""
|
2004-04-19 19:24:37 -04:00
|
|
|
value = BaseMaskedTextCtrl.GetValue(self)
|
|
|
|
return value.replace(' ','') # remove spaces from the value
|
|
|
|
|
|
|
|
|
|
|
|
def _OnCtrl_S(self, event):
|
|
|
|
## dbg("IpAddrCtrl::_OnCtrl_S")
|
|
|
|
if self._demo:
|
|
|
|
print "value:", self.GetAddress()
|
|
|
|
return False
|
|
|
|
|
|
|
|
def SetValue(self, value):
|
2004-10-11 18:13:18 -04:00
|
|
|
"""
|
|
|
|
Takes a string value, validates it for a valid IP address,
|
|
|
|
splits it into an array of 4 fields, justifies it
|
|
|
|
appropriately, and inserts it into the control.
|
|
|
|
Invalid values will raise a ValueError exception.
|
|
|
|
"""
|
2004-04-19 19:24:37 -04:00
|
|
|
## dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1)
|
|
|
|
if type(value) not in (types.StringType, types.UnicodeType):
|
|
|
|
## dbg(indent=0)
|
|
|
|
raise ValueError('%s must be a string', str(value))
|
|
|
|
|
|
|
|
bValid = True # assume True
|
|
|
|
parts = value.split('.')
|
2004-10-11 18:13:18 -04:00
|
|
|
|
2004-04-19 19:24:37 -04:00
|
|
|
if len(parts) != 4:
|
|
|
|
bValid = False
|
|
|
|
else:
|
|
|
|
for i in range(4):
|
|
|
|
part = parts[i]
|
|
|
|
if not 0 <= len(part) <= 3:
|
|
|
|
bValid = False
|
|
|
|
break
|
|
|
|
elif part.strip(): # non-empty part
|
|
|
|
try:
|
|
|
|
j = string.atoi(part)
|
|
|
|
if not 0 <= j <= 255:
|
|
|
|
bValid = False
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
parts[i] = '%3d' % j
|
|
|
|
except:
|
|
|
|
bValid = False
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
# allow empty sections for SetValue (will result in "invalid" value,
|
|
|
|
# but this may be useful for initializing the control:
|
|
|
|
parts[i] = ' ' # convert empty field to 3-char length
|
|
|
|
|
|
|
|
if not bValid:
|
|
|
|
## dbg(indent=0)
|
|
|
|
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))
|
|
|
|
else:
|
|
|
|
## dbg('parts:', parts)
|
|
|
|
value = string.join(parts, '.')
|
|
|
|
BaseMaskedTextCtrl.SetValue(self, value)
|
|
|
|
## dbg(indent=0)
|
|
|
|
|
2004-10-11 18:13:18 -04:00
|
|
|
__i=0
|
|
|
|
## CHANGELOG:
|
|
|
|
## ====================
|
|
|
|
## Version 1.2
|
|
|
|
## 1. Fixed bugs involving missing imports now that these classes are in
|
|
|
|
## their own module.
|
|
|
|
## 2. Added doc strings for ePyDoc.
|
|
|
|
## 3. Renamed helper functions, vars etc. not intended to be visible in public
|
|
|
|
## interface to code.
|
|
|
|
##
|
2004-06-19 12:25:55 -04:00
|
|
|
## Version 1.1
|
|
|
|
## Made ipaddrctrls allow right-insert in subfields, now that insert/cut/paste works better
|