wxWidgets/wxPython/wx/lib/masked/ipaddrctrl.py
Robin Dunn c878ceeae8 Added new MaskedEditControl code from Will Sadkin. The modules are
now locaed in their own sub-package, wx.lib.masked.  Demos updated.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26874 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2004-04-19 23:24:37 +00:00

188 lines
6.8 KiB
Python

#----------------------------------------------------------------------------
# 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.
#
#----------------------------------------------------------------------------
import wx
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
dbg = Logger()
##dbg(enable=0)
class IpAddrCtrlAccessorsMixin:
# Define IpAddrCtrl's list of attributes having their own
# Get/Set functions, exposing only those that make sense for
# an IP address control.
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'):
kwargs['formatcodes'] = 'F_Sr<'
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):
## 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):
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):
## 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('.')
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)