]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/maskededit.py
Moved config.py to the root because if wxPython hasn't been built yet
[wxWidgets.git] / wxPython / wx / lib / maskededit.py
index 3b1630e153609a3870240e444f5253d1eb00e5e0..bdc463543e5eb03557c3b5924e51a88b88c24c30 100644 (file)
@@ -3,15 +3,12 @@
 # Authors:      Jeff Childers, Will Sadkin
 # Email:        jchilders_98@yahoo.com, wsadkin@nameconnector.com
 # Created:      02/11/2003
 # Authors:      Jeff Childers, Will Sadkin
 # Email:        jchilders_98@yahoo.com, wsadkin@nameconnector.com
 # Created:      02/11/2003
-# Copyright:    (c) 2003 by Jeff Childers, 2003
+# Copyright:    (c) 2003 by Jeff Childers, Will Sadkin, 2003
 # Portions:     (c) 2002 by Will Sadkin, 2002-2003
 # RCS-ID:       $Id$
 # Portions:     (c) 2002 by Will Sadkin, 2002-2003
 # RCS-ID:       $Id$
-# License:      wxWindows license
+# License:      wxWidgets license
 #----------------------------------------------------------------------------
 # NOTE:
 #----------------------------------------------------------------------------
 # NOTE:
-#   This was written way it is because of the lack of masked edit controls
-#   in wxWindows/wxPython.
-#
 #   MaskedEdit controls are based on a suggestion made on [wxPython-Users] by
 #   Jason Hihn, and borrows liberally from Will Sadkin's original masked edit
 #   control for time entry, TimeCtrl (which is now rewritten using this
 #   MaskedEdit controls are based on a suggestion made on [wxPython-Users] by
 #   Jason Hihn, and borrows liberally from Will Sadkin's original masked edit
 #   control for time entry, TimeCtrl (which is now rewritten using this
@@ -433,6 +430,11 @@ decimalChar=</b>
           ctl.GetChoiceRequired()
           ctl.GetFormatcodes()
 
           ctl.GetChoiceRequired()
           ctl.GetFormatcodes()
 
+  <b><i>Note:</i></b> After any change in parameters, the choices for the
+      control are reevaluated to ensure that they are still legal.  If you
+      have large choice lists, it is therefore more efficient to set parameters
+      before setting the choices available.
+
   <b>.SetFieldParameters(field_index, **kwargs)</b>
                     This function allows you to specify change individual field
                     parameters after construction. (Indices are 0-based.)
   <b>.SetFieldParameters(field_index, **kwargs)</b>
                     This function allows you to specify change individual field
                     parameters after construction. (Indices are 0-based.)
@@ -446,7 +448,8 @@ The control detects certain common constructions. In order to use the signed fea
 decimal point. Without a decimal (e.g. '######', the control will treat it as an integer
 value. With a decimal (e.g. '###.##'), the control will act as a floating point control
 (i.e. press decimal to 'tab' to the decimal position). Pressing decimal in the
 decimal point. Without a decimal (e.g. '######', the control will treat it as an integer
 value. With a decimal (e.g. '###.##'), the control will act as a floating point control
 (i.e. press decimal to 'tab' to the decimal position). Pressing decimal in the
-integer control truncates the value.
+integer control truncates the value.  However, for a true numeric control,
+MaskedNumCtrl provides all this, and true numeric input/output support as well.
 
 
 Check your controls by calling each control's .IsValid() function and the
 
 
 Check your controls by calling each control's .IsValid() function and the
@@ -700,6 +703,34 @@ Event Handling
         _OnHome(event)
         _OnEnd(event)
 
         _OnHome(event)
         _OnEnd(event)
 
+    The following routine provides a hook back to any class derivations, so that
+    they can react to parameter changes before any value is set/reset as a result of
+    those changes.  (eg. MaskedComboBox needs to detect when the choices list is
+    modified, either implicitly or explicitly, so it can reset the base control
+    to have the appropriate choice list *before* the initial value is reset to match.)
+
+        _OnCtrlParametersChanged()
+
+Accessor Functions
+------------------
+    For convenience, each class derived from MaskedEditMixin should
+    define an accessors mixin, so that it exposes only those parameters
+    that make sense for the derivation.  This is done with an intermediate
+    level of inheritance, ie:
+
+    class BaseMaskedTextCtrl( TextCtrl, MaskedEditMixin ):
+
+    class MaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
+    class MaskedNumCtrl( BaseMaskedTextCtrl, MaskedNumCtrlAccessorsMixin ):
+    class IpAddrCtrl( BaseMaskedTextCtrl, IpAddrCtrlAccessorsMixin ):
+    class TimeCtrl( BaseMaskedTextCtrl, TimeCtrlAccessorsMixin ):
+
+    etc.
+
+    Each accessors mixin defines Get/Set functions for the base class parameters
+    that are appropriate for that derivation.
+    This allows the base classes to be "more generic," exposing the widest
+    set of options, while not requiring derived classes to be so general.
 """
 
 import  copy
 """
 
 import  copy
@@ -715,7 +746,7 @@ import  wx
 from wx.tools.dbg import Logger 
 
 dbg = Logger()
 from wx.tools.dbg import Logger 
 
 dbg = Logger()
-dbg(enable=0)
+##dbg(enable=0)
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
@@ -847,17 +878,17 @@ masktags = {
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
            'description': "US Date + Time\n(w/hypens)"
            },
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
            'description': "US Date + Time\n(w/hypens)"
            },
-       "USDATEMILTIMEMMDDYYYY/HHMMSS": {
+       "USDATE24HRTIMEMMDDYYYY/HHMMSS": {           
            'mask': "##/##/#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "##/##/#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "US Date + Military Time"
+           'description': "US Date + 24Hr (Military) Time"           
            },
            },
-       "USDATEMILTIMEMMDDYYYY-HHMMSS": {
+       "USDATE24HRTIMEMMDDYYYY-HHMMSS": {
            'mask': "##-##-#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "##-##-#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "US Date + Military Time\n(w/hypens)"
+           'description': "US Date + 24Hr Time\n(w/hypens)"           
            },
        "USDATETIMEMMDDYYYY/HHMM": {
            'mask': "##/##/#### ##:## AM",
            },
        "USDATETIMEMMDDYYYY/HHMM": {
            'mask': "##/##/#### ##:## AM",
@@ -866,11 +897,11 @@ masktags = {
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + hours + ':' + minutes + ' (A|P)M',
            'description': "US Date + Time\n(without seconds)"
            },
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + hours + ':' + minutes + ' (A|P)M',
            'description': "US Date + Time\n(without seconds)"
            },
-       "USDATEMILTIMEMMDDYYYY/HHMM": {
+       "USDATE24HRTIMEMMDDYYYY/HHMM": {
            'mask': "##/##/#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + milhours + ':' + minutes,
            'mask': "##/##/#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '/' + days + '/' + '\d{4} ' + milhours + ':' + minutes,
-           'description': "US Date + Military Time\n(without seconds)"
+           'description': "US Date + 24Hr Time\n(without seconds)"           
            },
        "USDATETIMEMMDDYYYY-HHMM": {
            'mask': "##-##-#### ##:## AM",
            },
        "USDATETIMEMMDDYYYY-HHMM": {
            'mask': "##-##-#### ##:## AM",
@@ -879,11 +910,11 @@ masktags = {
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + hours + ':' + minutes + ' (A|P)M',
            'description': "US Date + Time\n(w/hypens and w/o secs)"
            },
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + hours + ':' + minutes + ' (A|P)M',
            'description': "US Date + Time\n(w/hypens and w/o secs)"
            },
-       "USDATEMILTIMEMMDDYYYY-HHMM": {
+       "USDATE24HRTIMEMMDDYYYY-HHMM": {
            'mask': "##-##-#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + milhours + ':' + minutes,
            'mask': "##-##-#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + months + '-' + days + '-' + '\d{4} ' + milhours + ':' + minutes,
-           'description': "US Date + Military Time\n(w/hyphens and w/o seconds)"
+           'description': "US Date + 24Hr Time\n(w/hyphens and w/o seconds)"
            },
        "USDATEMMDDYYYY/": {
            'mask': "##/##/####",
            },
        "USDATEMMDDYYYY/": {
            'mask': "##/##/####",
@@ -999,52 +1030,52 @@ masktags = {
            'description': "DD.MM.YYYY HH:MM"
            },
 
            'description': "DD.MM.YYYY HH:MM"
            },
 
-       "EUDATEMILTIMEYYYYMMDD/HHMMSS": {
+       "EUDATE24HRTIMEYYYYMMDD/HHMMSS": {
            'mask': "####/##/## ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '/' + months + '/' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "####/##/## ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '/' + months + '/' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "YYYY/MM/DD Mil. Time"
+           'description': "YYYY/MM/DD 24Hr Time"
            },
            },
-       "EUDATEMILTIMEYYYYMMDD.HHMMSS": {
+       "EUDATE24HRTIMEYYYYMMDD.HHMMSS": {
            'mask': "####.##.## ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '.' + months + '.' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "####.##.## ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '.' + months + '.' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "YYYY.MM.DD Mil. Time"
+           'description': "YYYY.MM.DD 24Hr Time"
            },
            },
-       "EUDATEMILTIMEDDMMYYYY/HHMMSS": {
+       "EUDATE24HRTIMEDDMMYYYY/HHMMSS": {
            'mask': "##/##/#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '/' + months + '/' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "##/##/#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '/' + months + '/' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "DD/MM/YYYY Mil. Time"
+           'description': "DD/MM/YYYY 24Hr Time"
            },
            },
-       "EUDATEMILTIMEDDMMYYYY.HHMMSS": {
+       "EUDATE24HRTIMEDDMMYYYY.HHMMSS": {
            'mask': "##.##.#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '.' + months + '.' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
            'mask': "##.##.#### ##:##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '.' + months + '.' + '\d{4} ' + milhours + ':' + minutes + ':' + seconds,
-           'description': "DD.MM.YYYY Mil. Time"
+           'description': "DD.MM.YYYY 24Hr Time"
            },
            },
-       "EUDATEMILTIMEYYYYMMDD/HHMM": {
+       "EUDATE24HRTIMEYYYYMMDD/HHMM": {
            'mask': "####/##/## ##:##",
            'formatcodes': 'DF','validRegex': '^' + '\d{4}'+ '/' + months + '/' + days + ' ' + milhours + ':' + minutes,
            'mask': "####/##/## ##:##",
            'formatcodes': 'DF','validRegex': '^' + '\d{4}'+ '/' + months + '/' + days + ' ' + milhours + ':' + minutes,
-           'description': "YYYY/MM/DD Mil. Time\n(w/o seconds)"
+           'description': "YYYY/MM/DD 24Hr Time\n(w/o seconds)"
            },
            },
-       "EUDATEMILTIMEYYYYMMDD.HHMM": {
+       "EUDATE24HRTIMEYYYYMMDD.HHMM": {
            'mask': "####.##.## ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '.' + months + '.' + days + ' ' + milhours + ':' + minutes,
            'mask': "####.##.## ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + '\d{4}'+ '.' + months + '.' + days + ' ' + milhours + ':' + minutes,
-           'description': "YYYY.MM.DD Mil. Time\n(w/o seconds)"
+           'description': "YYYY.MM.DD 24Hr Time\n(w/o seconds)"
            },
            },
-       "EUDATEMILTIMEDDMMYYYY/HHMM": {
+       "EUDATE24HRTIMEDDMMYYYY/HHMM": {
            'mask': "##/##/#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '/' + months + '/' + '\d{4} ' + milhours + ':' + minutes,
            'mask': "##/##/#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '/' + months + '/' + '\d{4} ' + milhours + ':' + minutes,
-           'description': "DD/MM/YYYY Mil. Time\n(w/o seconds)"
+           'description': "DD/MM/YYYY 24Hr Time\n(w/o seconds)"
            },
            },
-       "EUDATEMILTIMEDDMMYYYY.HHMM": {
+       "EUDATE24HRTIMEDDMMYYYY.HHMM": {
            'mask': "##.##.#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '.' + months + '.' + '\d{4} ' + milhours + ':' + minutes,
            'mask': "##.##.#### ##:##",
            'formatcodes': 'DF',
            'validRegex': '^' + days + '.' + months + '.' + '\d{4} ' + milhours + ':' + minutes,
-           'description': "DD.MM.YYYY Mil. Time\n(w/o seconds)"
+           'description': "DD.MM.YYYY 24Hr Time\n(w/o seconds)"
            },
 
        "TIMEHHMMSS": {
            },
 
        "TIMEHHMMSS": {
@@ -1061,17 +1092,17 @@ masktags = {
            'validRegex': '^' + hours + ':' + minutes + ' (A|P)M',
            'description': "HH:MM (A|P)M\n(see TimeCtrl)"
            },
            'validRegex': '^' + hours + ':' + minutes + ' (A|P)M',
            'description': "HH:MM (A|P)M\n(see TimeCtrl)"
            },
-       "MILTIMEHHMMSS": {
+       "24HRTIMEHHMMSS": {
            'mask': "##:##:##",
            'formatcodes': 'TF',
            'validRegex': '^' + milhours + ':' + minutes + ':' + seconds,
            'mask': "##:##:##",
            'formatcodes': 'TF',
            'validRegex': '^' + milhours + ':' + minutes + ':' + seconds,
-           'description': "Military HH:MM:SS\n(see TimeCtrl)"
+           'description': "24Hr HH:MM:SS\n(see TimeCtrl)"
            },
            },
-       "MILTIMEHHMM": {
+       "24HRTIMEHHMM": {
            'mask': "##:##",
            'formatcodes': 'TF',
            'validRegex': '^' + milhours + ':' + minutes,
            'mask': "##:##",
            'formatcodes': 'TF',
            'validRegex': '^' + milhours + ':' + minutes,
-           'description': "Military HH:MM\n(see TimeCtrl)"
+           'description': "24Hr HH:MM\n(see TimeCtrl)"
            },
        "USSOCIALSEC": {
            'mask': "###-##-####",
            },
        "USSOCIALSEC": {
            'mask': "###-##-####",
@@ -1174,11 +1205,11 @@ class Field:
         This is the "constructor" for setting up parameters for fields.
         a field_index of -1 is used to indicate "the entire control."
         """
         This is the "constructor" for setting up parameters for fields.
         a field_index of -1 is used to indicate "the entire control."
         """
-##        dbg('Field::Field', indent=1)
+####        dbg('Field::Field', indent=1)
         # Validate legitimate set of parameters:
         for key in kwargs.keys():
             if key not in Field.valid_params.keys():
         # Validate legitimate set of parameters:
         for key in kwargs.keys():
             if key not in Field.valid_params.keys():
-##                dbg(indent=0)
+####                dbg(indent=0)
                 raise TypeError('invalid parameter "%s"' % (key))
 
         # Set defaults for each parameter for this instance, and fully
                 raise TypeError('invalid parameter "%s"' % (key))
 
         # Set defaults for each parameter for this instance, and fully
@@ -1192,7 +1223,7 @@ class Field:
         self._SetParameters(**kwargs)
         self._ValidateParameters(**kwargs)
 
         self._SetParameters(**kwargs)
         self._ValidateParameters(**kwargs)
 
-##        dbg(indent=0)
+####        dbg(indent=0)
 
 
     def _SetParameters(self, **kwargs):
 
 
     def _SetParameters(self, **kwargs):
@@ -1200,19 +1231,21 @@ class Field:
         This function can be used to set individual or multiple parameters for
         a masked edit field parameter after construction.
         """
         This function can be used to set individual or multiple parameters for
         a masked edit field parameter after construction.
         """
-        dbg(suspend=1)
-        dbg('maskededit.Field::_SetParameters', indent=1)
+##        dbg(suspend=1)
+##        dbg('maskededit.Field::_SetParameters', indent=1)
         # Validate keyword arguments:
         for key in kwargs.keys():
             if key not in Field.valid_params.keys():
         # Validate keyword arguments:
         for key in kwargs.keys():
             if key not in Field.valid_params.keys():
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 raise AttributeError('invalid keyword argument "%s"' % key)
 
         if self._index is not None: dbg('field index:', self._index)
                 raise AttributeError('invalid keyword argument "%s"' % key)
 
         if self._index is not None: dbg('field index:', self._index)
-        dbg('parameters:', indent=1)
+##        dbg('parameters:', indent=1)
         for key, value in kwargs.items():
         for key, value in kwargs.items():
-            dbg('%s:' % key, value)
-        dbg(indent=0)
+##            dbg('%s:' % key, value)
+            pass
+##        dbg(indent=0)
+
 
         old_fillChar = self._fillChar   # store so we can change choice lists accordingly if it changes
 
 
         old_fillChar = self._fillChar   # store so we can change choice lists accordingly if it changes
 
@@ -1246,13 +1279,13 @@ class Field:
 
         if kwargs.has_key('fillChar'):
             self._old_fillChar = old_fillChar
 
         if kwargs.has_key('fillChar'):
             self._old_fillChar = old_fillChar
-##            dbg("self._old_fillChar: '%s'" % self._old_fillChar)
+####            dbg("self._old_fillChar: '%s'" % self._old_fillChar)
 
         if kwargs.has_key('mask') or kwargs.has_key('validRegex'):  # (set/changed)
             self._isInt = isInteger(self._mask)
 
         if kwargs.has_key('mask') or kwargs.has_key('validRegex'):  # (set/changed)
             self._isInt = isInteger(self._mask)
-            dbg('isInt?', self._isInt, 'self._mask:"%s"' % self._mask)
+##            dbg('isInt?', self._isInt, 'self._mask:"%s"' % self._mask)
 
 
-        dbg(indent=0, suspend=0)
+##        dbg(indent=0, suspend=0)
 
 
     def _ValidateParameters(self, **kwargs):
 
 
     def _ValidateParameters(self, **kwargs):
@@ -1260,18 +1293,18 @@ class Field:
         This function can be used to validate individual or multiple parameters for
         a masked edit field parameter after construction.
         """
         This function can be used to validate individual or multiple parameters for
         a masked edit field parameter after construction.
         """
-        dbg(suspend=1)
-        dbg('maskededit.Field::_ValidateParameters', indent=1)
+##        dbg(suspend=1)
+##        dbg('maskededit.Field::_ValidateParameters', indent=1)
         if self._index is not None: dbg('field index:', self._index)
         if self._index is not None: dbg('field index:', self._index)
-##        dbg('parameters:', indent=1)
+####        dbg('parameters:', indent=1)
 ##        for key, value in kwargs.items():
 ##        for key, value in kwargs.items():
-##            dbg('%s:' % key, value)
-##        dbg(indent=0)
-##        dbg("self._old_fillChar: '%s'" % self._old_fillChar)
+####            dbg('%s:' % key, value)
+####        dbg(indent=0)
+####        dbg("self._old_fillChar: '%s'" % self._old_fillChar)
 
         # Verify proper numeric format params:
         if self._groupdigits and self._groupChar == self._decimalChar:
 
         # Verify proper numeric format params:
         if self._groupdigits and self._groupChar == self._decimalChar:
-            dbg(indent=0, suspend=0)
+##            dbg(indent=0, suspend=0)
             raise AttributeError("groupChar '%s' cannot be the same as decimalChar '%s'" % (self._groupChar, self._decimalChar))
 
 
             raise AttributeError("groupChar '%s' cannot be the same as decimalChar '%s'" % (self._groupChar, self._decimalChar))
 
 
@@ -1295,7 +1328,7 @@ class Field:
                     else:
                         self._filter = re.compile(self._validRegex)
                 except:
                     else:
                         self._filter = re.compile(self._validRegex)
                 except:
-                    dbg(indent=0, suspend=0)
+##                    dbg(indent=0, suspend=0)
                     raise TypeError('%s: validRegex "%s" not a legal regular expression' % (str(self._index), self._validRegex))
             else:
                 self._filter = None
                     raise TypeError('%s: validRegex "%s" not a legal regular expression' % (str(self._index), self._validRegex))
             else:
                 self._filter = None
@@ -1306,7 +1339,7 @@ class Field:
             self._rangeLow  = 0
             if self._validRange:
                 if type(self._validRange) != types.TupleType or len( self._validRange )!= 2 or self._validRange[0] > self._validRange[1]:
             self._rangeLow  = 0
             if self._validRange:
                 if type(self._validRange) != types.TupleType or len( self._validRange )!= 2 or self._validRange[0] > self._validRange[1]:
-                    dbg(indent=0, suspend=0)
+##                    dbg(indent=0, suspend=0)
                     raise TypeError('%s: validRange %s parameter must be tuple of form (a,b) where a <= b'
                                     % (str(self._index), repr(self._validRange)) )
 
                     raise TypeError('%s: validRange %s parameter must be tuple of form (a,b) where a <= b'
                                     % (str(self._index), repr(self._validRange)) )
 
@@ -1317,16 +1350,16 @@ class Field:
         if kwargs.has_key('choices') or (len(self._choices) and len(self._choices[0]) != len(self._mask)):       # (set/changed)
             self._hasList   = False
             if self._choices and type(self._choices) not in (types.TupleType, types.ListType):
         if kwargs.has_key('choices') or (len(self._choices) and len(self._choices[0]) != len(self._mask)):       # (set/changed)
             self._hasList   = False
             if self._choices and type(self._choices) not in (types.TupleType, types.ListType):
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
             elif len( self._choices) > 0:
                 for choice in self._choices:
                     if type(choice) not in (types.StringType, types.UnicodeType):
                 raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
             elif len( self._choices) > 0:
                 for choice in self._choices:
                     if type(choice) not in (types.StringType, types.UnicodeType):
-                        dbg(indent=0, suspend=0)
+##                        dbg(indent=0, suspend=0)
                         raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
 
                 length = len(self._mask)
                         raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
 
                 length = len(self._mask)
-                dbg('len(%s)' % self._mask, length, 'len(self._choices):', len(self._choices), 'length:', length, 'self._alignRight?', self._alignRight)
+##                dbg('len(%s)' % self._mask, length, 'len(self._choices):', len(self._choices), 'length:', length, 'self._alignRight?', self._alignRight)
                 if len(self._choices) and length:
                     if len(self._choices[0]) > length:
                         # changed mask without respecifying choices; readjust the width as appropriate:
                 if len(self._choices) and length:
                     if len(self._choices[0]) > length:
                         # changed mask without respecifying choices; readjust the width as appropriate:
@@ -1335,7 +1368,7 @@ class Field:
                         self._choices = [choice.rjust( length ) for choice in self._choices]
                     else:
                         self._choices = [choice.ljust( length ) for choice in self._choices]
                         self._choices = [choice.rjust( length ) for choice in self._choices]
                     else:
                         self._choices = [choice.ljust( length ) for choice in self._choices]
-                    dbg('aligned choices:', self._choices)
+##                    dbg('aligned choices:', self._choices)
 
                 if hasattr(self, '_template'):
                     # Verify each choice specified is valid:
 
                 if hasattr(self, '_template'):
                     # Verify each choice specified is valid:
@@ -1344,28 +1377,28 @@ class Field:
                             # allow empty values even if invalid, (just colored differently)
                             continue
                         if not self.IsValid(choice):
                             # allow empty values even if invalid, (just colored differently)
                             continue
                         if not self.IsValid(choice):
-                            dbg(indent=0, suspend=0)
+##                            dbg(indent=0, suspend=0)
                             raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self._index), choice))
                 self._hasList = True
 
                             raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self._index), choice))
                 self._hasList = True
 
-##        dbg("kwargs.has_key('fillChar')?", kwargs.has_key('fillChar'), "len(self._choices) > 0?", len(self._choices) > 0)
-##        dbg("self._old_fillChar:'%s'" % self._old_fillChar, "self._fillChar: '%s'" % self._fillChar)
+####        dbg("kwargs.has_key('fillChar')?", kwargs.has_key('fillChar'), "len(self._choices) > 0?", len(self._choices) > 0)
+####        dbg("self._old_fillChar:'%s'" % self._old_fillChar, "self._fillChar: '%s'" % self._fillChar)
         if kwargs.has_key('fillChar') and len(self._choices) > 0:
             if kwargs['fillChar'] != ' ':
                 self._choices = [choice.replace(' ', self._fillChar) for choice in self._choices]
             else:
                 self._choices = [choice.replace(self._old_fillChar, self._fillChar) for choice in self._choices]
         if kwargs.has_key('fillChar') and len(self._choices) > 0:
             if kwargs['fillChar'] != ' ':
                 self._choices = [choice.replace(' ', self._fillChar) for choice in self._choices]
             else:
                 self._choices = [choice.replace(self._old_fillChar, self._fillChar) for choice in self._choices]
-            dbg('updated choices:', self._choices)
+##            dbg('updated choices:', self._choices)
 
 
         if kwargs.has_key('autoSelect') and kwargs['autoSelect']:
             if not self._hasList:
 
 
         if kwargs.has_key('autoSelect') and kwargs['autoSelect']:
             if not self._hasList:
-                dbg('no list to auto complete; ignoring "autoSelect=True"')
+##                dbg('no list to auto complete; ignoring "autoSelect=True"')
                 self._autoSelect = False
 
         # reset field validity assumption:
         self._valid = True
                 self._autoSelect = False
 
         # reset field validity assumption:
         self._valid = True
-        dbg(indent=0, suspend=0)
+##        dbg(indent=0, suspend=0)
 
 
     def _GetParameter(self, paramname):
 
 
     def _GetParameter(self, paramname):
@@ -1383,28 +1416,28 @@ class Field:
         Indicates whether the specified slice is considered empty for the
         field.
         """
         Indicates whether the specified slice is considered empty for the
         field.
         """
-        dbg('Field::IsEmpty("%s")' % slice, indent=1)
+##        dbg('Field::IsEmpty("%s")' % slice, indent=1)
         if not hasattr(self, '_template'):
         if not hasattr(self, '_template'):
-            dbg(indent=0)
+##            dbg(indent=0)
             raise AttributeError('_template')
 
             raise AttributeError('_template')
 
-        dbg('self._template: "%s"' % self._template)
-        dbg('self._defaultValue: "%s"' % str(self._defaultValue))
+##        dbg('self._template: "%s"' % self._template)
+##        dbg('self._defaultValue: "%s"' % str(self._defaultValue))
         if slice == self._template and not self._defaultValue:
         if slice == self._template and not self._defaultValue:
-            dbg(indent=0)
+##            dbg(indent=0)
             return True
 
         elif slice == self._template:
             empty = True
             for pos in range(len(self._template)):
             return True
 
         elif slice == self._template:
             empty = True
             for pos in range(len(self._template)):
-##                dbg('slice[%(pos)d] != self._fillChar?' %locals(), slice[pos] != self._fillChar[pos])
+####                dbg('slice[%(pos)d] != self._fillChar?' %locals(), slice[pos] != self._fillChar[pos])
                 if slice[pos] not in (' ', self._fillChar):
                     empty = False
                     break
                 if slice[pos] not in (' ', self._fillChar):
                     empty = False
                     break
-            dbg("IsEmpty? %(empty)d (do all mask chars == fillChar?)" % locals(), indent=0)
+##            dbg("IsEmpty? %(empty)d (do all mask chars == fillChar?)" % locals(), indent=0)
             return empty
         else:
             return empty
         else:
-            dbg("IsEmpty? 0 (slice doesn't match template)", indent=0)
+##            dbg("IsEmpty? 0 (slice doesn't match template)", indent=0)
             return False
 
 
             return False
 
 
@@ -1413,23 +1446,23 @@ class Field:
         Indicates whether the specified slice is considered a valid value for the
         field.
         """
         Indicates whether the specified slice is considered a valid value for the
         field.
         """
-        dbg(suspend=1)
-        dbg('Field[%s]::IsValid("%s")' % (str(self._index), slice), indent=1)
+##        dbg(suspend=1)
+##        dbg('Field[%s]::IsValid("%s")' % (str(self._index), slice), indent=1)
         valid = True    # assume true to start
 
         if self.IsEmpty(slice):
         valid = True    # assume true to start
 
         if self.IsEmpty(slice):
-            dbg(indent=0, suspend=0)
+##            dbg(indent=0, suspend=0)
             if self._emptyInvalid:
                 return False
             else:
                 return True
 
         elif self._hasList and self._choiceRequired:
             if self._emptyInvalid:
                 return False
             else:
                 return True
 
         elif self._hasList and self._choiceRequired:
-            dbg("(member of list required)")
+##            dbg("(member of list required)")
             # do case-insensitive match on list; strip surrounding whitespace from slice (already done for choices):
             if self._fillChar != ' ':
                 slice = slice.replace(self._fillChar, ' ')
             # do case-insensitive match on list; strip surrounding whitespace from slice (already done for choices):
             if self._fillChar != ' ':
                 slice = slice.replace(self._fillChar, ' ')
-                dbg('updated slice:"%s"' % slice)
+##                dbg('updated slice:"%s"' % slice)
             compareStr = slice.strip()
 
             if self._compareNoCase:
             compareStr = slice.strip()
 
             if self._compareNoCase:
@@ -1437,7 +1470,7 @@ class Field:
             valid = compareStr in self._compareChoices
 
         elif self._hasRange and not self.IsEmpty(slice):
             valid = compareStr in self._compareChoices
 
         elif self._hasRange and not self.IsEmpty(slice):
-            dbg('validating against range')
+##            dbg('validating against range')
             try:
                 # allow float as well as int ranges (int comparisons for free.)
                 valid = self._rangeLow <= float(slice) <= self._rangeHigh
             try:
                 # allow float as well as int ranges (int comparisons for free.)
                 valid = self._rangeLow <= float(slice) <= self._rangeHigh
@@ -1445,22 +1478,22 @@ class Field:
                 valid = False
 
         elif self._validRegex and self._filter:
                 valid = False
 
         elif self._validRegex and self._filter:
-            dbg('validating against regex')
+##            dbg('validating against regex')
             valid = (re.match( self._filter, slice) is not None)
 
         if valid and self._validFunc:
             valid = (re.match( self._filter, slice) is not None)
 
         if valid and self._validFunc:
-            dbg('validating against supplied function')
+##            dbg('validating against supplied function')
             valid = self._validFunc(slice)
             valid = self._validFunc(slice)
-        dbg('valid?', valid, indent=0, suspend=0)
+##        dbg('valid?', valid, indent=0, suspend=0)
         return valid
 
 
     def _AdjustField(self, slice):
         """ 'Fixes' an integer field. Right or left-justifies, as required."""
         return valid
 
 
     def _AdjustField(self, slice):
         """ 'Fixes' an integer field. Right or left-justifies, as required."""
-        dbg('Field::_AdjustField("%s")' % slice, indent=1)
+##        dbg('Field::_AdjustField("%s")' % slice, indent=1)
         length = len(self._mask)
         length = len(self._mask)
-##        dbg('length(self._mask):', length)
-##        dbg('self._useParensForNegatives?', self._useParensForNegatives)
+####        dbg('length(self._mask):', length)
+####        dbg('self._useParensForNegatives?', self._useParensForNegatives)
         if self._isInt:
             if self._useParensForNegatives:
                 signpos = slice.find('(')
         if self._isInt:
             if self._useParensForNegatives:
                 signpos = slice.find('(')
@@ -1475,13 +1508,13 @@ class Field:
             intStr = string.replace(intStr,self._fillChar,"")       # drop extra fillchars
             intStr = string.replace(intStr,"-","")                  # drop sign, if any
             intStr = string.replace(intStr, self._groupChar, "")    # lose commas/dots
             intStr = string.replace(intStr,self._fillChar,"")       # drop extra fillchars
             intStr = string.replace(intStr,"-","")                  # drop sign, if any
             intStr = string.replace(intStr, self._groupChar, "")    # lose commas/dots
-##            dbg('intStr:"%s"' % intStr)
+####            dbg('intStr:"%s"' % intStr)
             start, end = self._extent
             field_len = end - start
             if not self._padZero and len(intStr) != field_len and intStr.strip():
                 intStr = str(long(intStr))
             start, end = self._extent
             field_len = end - start
             if not self._padZero and len(intStr) != field_len and intStr.strip():
                 intStr = str(long(intStr))
-##            dbg('raw int str: "%s"' % intStr)
-##            dbg('self._groupdigits:', self._groupdigits, 'self._formatcodes:', self._formatcodes)
+####            dbg('raw int str: "%s"' % intStr)
+####            dbg('self._groupdigits:', self._groupdigits, 'self._formatcodes:', self._formatcodes)
             if self._groupdigits:
                 new = ''
                 cnt = 1
             if self._groupdigits:
                 new = ''
                 cnt = 1
@@ -1497,8 +1530,8 @@ class Field:
                     intStr = new
                 # else... leave it without the commas...
 
                     intStr = new
                 # else... leave it without the commas...
 
-            dbg('padzero?', self._padZero)
-            dbg('len(intStr):', len(intStr), 'field length:', length)
+##            dbg('padzero?', self._padZero)
+##            dbg('len(intStr):', len(intStr), 'field length:', length)
             if self._padZero and len(intStr) < length:
                 intStr = '0' * (length - len(intStr)) + intStr
                 if signpos != -1:   # we had a sign before; restore it
             if self._padZero and len(intStr) < length:
                 intStr = '0' * (length - len(intStr)) + intStr
                 if signpos != -1:   # we had a sign before; restore it
@@ -1528,7 +1561,7 @@ class Field:
             slice = slice.ljust( length )
         if self._fillChar != ' ':
             slice = slice.replace(' ', self._fillChar)
             slice = slice.ljust( length )
         if self._fillChar != ' ':
             slice = slice.replace(' ', self._fillChar)
-        dbg('adjusted slice: "%s"' % slice, indent=0)
+##        dbg('adjusted slice: "%s"' % slice, indent=0)
         return slice
 
 
         return slice
 
 
@@ -1645,7 +1678,7 @@ class MaskedEditMixin:
         for key, value in MaskedEditMixin.valid_ctrl_params.items():
             setattr(self, '_' + key, copy.copy(value))
             if not kwargs.has_key(key):
         for key, value in MaskedEditMixin.valid_ctrl_params.items():
             setattr(self, '_' + key, copy.copy(value))
             if not kwargs.has_key(key):
-##                dbg('%s: "%s"' % (key, repr(value)))
+####                dbg('%s: "%s"' % (key, repr(value)))
                 kwargs[key] = copy.copy(value)
 
         # Create a "field" that holds global parameters for control constraints
                 kwargs[key] = copy.copy(value)
 
         # Create a "field" that holds global parameters for control constraints
@@ -1659,12 +1692,12 @@ class MaskedEditMixin:
         This public function can be used to set individual or multiple masked edit
         parameters after construction.
         """
         This public function can be used to set individual or multiple masked edit
         parameters after construction.
         """
-        dbg(suspend=1)
-        dbg('MaskedEditMixin::SetCtrlParameters', indent=1)
-##        dbg('kwargs:', indent=1)
+##        dbg(suspend=1)
+##        dbg('MaskedEditMixin::SetCtrlParameters', indent=1)
+####        dbg('kwargs:', indent=1)
 ##        for key, value in kwargs.items():
 ##        for key, value in kwargs.items():
-##            dbg(key, '=', value)
-##        dbg(indent=0)
+####            dbg(key, '=', value)
+####        dbg(indent=0)
 
         # Validate keyword arguments:
         constraint_kwargs = {}
 
         # Validate keyword arguments:
         constraint_kwargs = {}
@@ -1672,7 +1705,7 @@ class MaskedEditMixin:
         for key, value in kwargs.items():
             key = key.replace('Color', 'Colour')    # for b-c, and standard wxPython spelling
             if key not in MaskedEditMixin.valid_ctrl_params.keys() + Field.valid_params.keys():
         for key, value in kwargs.items():
             key = key.replace('Color', 'Colour')    # for b-c, and standard wxPython spelling
             if key not in MaskedEditMixin.valid_ctrl_params.keys() + Field.valid_params.keys():
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 raise TypeError('Invalid keyword argument "%s" for control "%s"' % (key, self.name))
             elif key in Field.valid_params.keys():
                 constraint_kwargs[key] = value
                 raise TypeError('Invalid keyword argument "%s" for control "%s"' % (key, self.name))
             elif key in Field.valid_params.keys():
                 constraint_kwargs[key] = value
@@ -1687,8 +1720,12 @@ class MaskedEditMixin:
         else:
             autoformat = None
 
         else:
             autoformat = None
 
+        # handle "parochial name" backward compatibility:
+        if autoformat and autoformat.find('MILTIME') != -1 and autoformat not in masktags.keys():
+            autoformat = autoformat.replace('MILTIME', '24HRTIME')
+
         if autoformat != self._autoformat and autoformat in masktags.keys():
         if autoformat != self._autoformat and autoformat in masktags.keys():
-            dbg('autoformat:', autoformat)
+##            dbg('autoformat:', autoformat)
             self._autoformat                  = autoformat
             mask                              = masktags[self._autoformat]['mask']
             # gather rest of any autoformat parameters:
             self._autoformat                  = autoformat
             mask                              = masktags[self._autoformat]['mask']
             # gather rest of any autoformat parameters:
@@ -1699,17 +1736,17 @@ class MaskedEditMixin:
         elif autoformat and not autoformat in masktags.keys():
             raise AttributeError('invalid value for autoformat parameter: %s' % repr(autoformat))
         else:
         elif autoformat and not autoformat in masktags.keys():
             raise AttributeError('invalid value for autoformat parameter: %s' % repr(autoformat))
         else:
-            dbg('autoformat not selected')
+##            dbg('autoformat not selected')
             if kwargs.has_key('mask'):
                 mask = kwargs['mask']
             if kwargs.has_key('mask'):
                 mask = kwargs['mask']
-                dbg('mask:', mask)
+##                dbg('mask:', mask)
 
         ## Assign style flags
         if mask is None:
 
         ## Assign style flags
         if mask is None:
-            dbg('preserving previous mask')
+##            dbg('preserving previous mask')
             mask = self._previous_mask   # preserve previous mask
         else:
             mask = self._previous_mask   # preserve previous mask
         else:
-            dbg('mask (re)set')
+##            dbg('mask (re)set')
             reset_args['reset_mask'] = mask
             constraint_kwargs['mask'] = mask
 
             reset_args['reset_mask'] = mask
             constraint_kwargs['mask'] = mask
 
@@ -1725,25 +1762,25 @@ class MaskedEditMixin:
                 for i in range(len(fields)):
                     field = fields[i]
                     if not isinstance(field, Field):
                 for i in range(len(fields)):
                     field = fields[i]
                     if not isinstance(field, Field):
-                        dbg(indent=0, suspend=0)
+##                        dbg(indent=0, suspend=0)
                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
                     self._fields[i] = field
 
             elif type(fields) == types.DictionaryType:
                 for index, field in fields.items():
                     if not isinstance(field, Field):
                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
                     self._fields[i] = field
 
             elif type(fields) == types.DictionaryType:
                 for index, field in fields.items():
                     if not isinstance(field, Field):
-                        dbg(indent=0, suspend=0)
+##                        dbg(indent=0, suspend=0)
                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
                     self._fields[index] = field
             else:
                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
                     self._fields[index] = field
             else:
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 raise AttributeError('fields parameter must be a list or dictionary; not %s' % repr(fields))
 
         # Assign constraint parameters for entire control:
                 raise AttributeError('fields parameter must be a list or dictionary; not %s' % repr(fields))
 
         # Assign constraint parameters for entire control:
-##        dbg('control constraints:', indent=1)
+####        dbg('control constraints:', indent=1)
 ##        for key, value in constraint_kwargs.items():
 ##        for key, value in constraint_kwargs.items():
-##            dbg('%s:' % key, value)
-##        dbg(indent=0)
+####            dbg('%s:' % key, value)
+####        dbg(indent=0)
 
         # determine if changing parameters that should affect the entire control:
         for key in MaskedEditMixin.valid_ctrl_params.keys():
 
         # determine if changing parameters that should affect the entire control:
         for key in MaskedEditMixin.valid_ctrl_params.keys():
@@ -1770,15 +1807,15 @@ class MaskedEditMixin:
                     raise TypeError('%s not a legal color specification for %s' % (repr(ctrl_kwargs[key]), key))
 
 
                     raise TypeError('%s not a legal color specification for %s' % (repr(ctrl_kwargs[key]), key))
 
 
-        dbg('self._retainFieldValidation:', self._retainFieldValidation)
+##        dbg('self._retainFieldValidation:', self._retainFieldValidation)
         if not self._retainFieldValidation:
             # Build dictionary of any changing parameters which should be propagated to the
             # component fields:
             for arg in Field.propagating_params:
         if not self._retainFieldValidation:
             # Build dictionary of any changing parameters which should be propagated to the
             # component fields:
             for arg in Field.propagating_params:
-##                dbg('kwargs.has_key(%s)?' % arg, kwargs.has_key(arg))
-##                dbg('getattr(self._ctrl_constraints, _%s)?' % arg, getattr(self._ctrl_constraints, '_'+arg))
+####                dbg('kwargs.has_key(%s)?' % arg, kwargs.has_key(arg))
+####                dbg('getattr(self._ctrl_constraints, _%s)?' % arg, getattr(self._ctrl_constraints, '_'+arg))
                 reset_args[arg] = kwargs.has_key(arg) and kwargs[arg] != getattr(self._ctrl_constraints, '_'+arg)
                 reset_args[arg] = kwargs.has_key(arg) and kwargs[arg] != getattr(self._ctrl_constraints, '_'+arg)
-##                dbg('reset_args[%s]?' % arg, reset_args[arg])
+####                dbg('reset_args[%s]?' % arg, reset_args[arg])
 
         # Set the control-level constraints:
         self._ctrl_constraints._SetParameters(**constraint_kwargs)
 
         # Set the control-level constraints:
         self._ctrl_constraints._SetParameters(**constraint_kwargs)
@@ -1795,7 +1832,7 @@ class MaskedEditMixin:
         # Validate that all choices for given fields are at least of the
         # necessary length, and that they all would be valid pastes if pasted
         # into their respective fields:
         # Validate that all choices for given fields are at least of the
         # necessary length, and that they all would be valid pastes if pasted
         # into their respective fields:
-##        dbg('validating choices')
+####        dbg('validating choices')
         self._validateChoices()
 
 
         self._validateChoices()
 
 
@@ -1822,6 +1859,9 @@ class MaskedEditMixin:
             elif self._autoformat.find('DMMY')  != -1: self._datestyle = 'DMY'
             elif self._autoformat.find('DMMMY') != -1: self._datestyle = 'DMY'
 
             elif self._autoformat.find('DMMY')  != -1: self._datestyle = 'DMY'
             elif self._autoformat.find('DMMMY') != -1: self._datestyle = 'DMY'
 
+        # Give derived controls a chance to react to parameter changes before
+        # potentially changing current value of the control.
+        self._OnCtrlParametersChanged()
 
         if self.controlInitialized:
             # Then the base control is available for configuration;
 
         if self.controlInitialized:
             # Then the base control is available for configuration;
@@ -1831,18 +1871,18 @@ class MaskedEditMixin:
                 self._setFont()
 
             if reset_args.has_key('reset_mask'):
                 self._setFont()
 
             if reset_args.has_key('reset_mask'):
-                dbg('reset mask')
+##                dbg('reset mask')
                 curvalue = self._GetValue()
                 if curvalue.strip():
                     try:
                 curvalue = self._GetValue()
                 if curvalue.strip():
                     try:
-                        dbg('attempting to _SetInitialValue(%s)' % self._GetValue())
+##                        dbg('attempting to _SetInitialValue(%s)' % self._GetValue())
                         self._SetInitialValue(self._GetValue())
                     except Exception, e:
                         self._SetInitialValue(self._GetValue())
                     except Exception, e:
-                        dbg('exception caught:', e)
-                        dbg("current value doesn't work; attempting to reset to template")
+##                        dbg('exception caught:', e)
+##                        dbg("current value doesn't work; attempting to reset to template")
                         self._SetInitialValue()
                 else:
                         self._SetInitialValue()
                 else:
-                    dbg('attempting to _SetInitialValue() with template')
+##                    dbg('attempting to _SetInitialValue() with template')
                     self._SetInitialValue()
 
             elif kwargs.has_key('useParensForNegatives'):
                     self._SetInitialValue()
 
             elif kwargs.has_key('useParensForNegatives'):
@@ -1856,19 +1896,19 @@ class MaskedEditMixin:
                         if newvalue[-1] in (' ', ')'):
                             newvalue = newvalue[:-1]
 
                         if newvalue[-1] in (' ', ')'):
                             newvalue = newvalue[:-1]
 
-                    dbg('reconfiguring value for parens:"%s"' % newvalue)
+##                    dbg('reconfiguring value for parens:"%s"' % newvalue)
                     self._SetValue(newvalue)
 
                     if self._prevValue != newvalue:
                         self._prevValue = newvalue  # disallow undo of sign type
 
             if self._autofit:
                     self._SetValue(newvalue)
 
                     if self._prevValue != newvalue:
                         self._prevValue = newvalue  # disallow undo of sign type
 
             if self._autofit:
-                dbg('setting client size to:', self._CalcSize())
+##                dbg('setting client size to:', self._CalcSize())
                 self.SetClientSize(self._CalcSize())
 
             # Set value/type-specific formatting
             self._applyFormatting()
                 self.SetClientSize(self._CalcSize())
 
             # Set value/type-specific formatting
             self._applyFormatting()
-        dbg(indent=0, suspend=0)
+##        dbg(indent=0, suspend=0)
 
     def SetMaskParameters(self, **kwargs):
         """ old name for this function """
 
     def SetMaskParameters(self, **kwargs):
         """ old name for this function """
@@ -1891,23 +1931,30 @@ class MaskedEditMixin:
         return self.GetCtrlParameter(paramname)
 
 
         return self.GetCtrlParameter(paramname)
 
 
-    # ## TRICKY BIT: to avoid a ton of boiler-plate, and to
-    # ## automate the getter/setter generation for each valid
-    # ## control parameter so we never forget to add the
-    # ## functions when adding parameters, this loop
-    # ## programmatically adds them to the class:
-    # ## (This makes it easier for Designers like Boa to
-    # ## deal with masked controls.)
-    # ##
-    for param in valid_ctrl_params.keys() + Field.valid_params.keys():
-        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))
+## This idea worked, but Boa was unable to use this solution...
+##    def _attachMethod(self, func):
+##        import new
+##        setattr(self, func.__name__, new.instancemethod(func, self, self.__class__))
+##
+##
+##    def _DefinePropertyFunctions(exposed_params):
+##        for param in exposed_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))
+##            self._attachMethod(locals()['Set%s' % propname])
+##            self._attachMethod(locals()['Get%s' % propname])
+##
+##            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))
+##                self._attachMethod(locals()['Set%s' % propname])
+##                self._attachMethod(locals()['Get%s' % propname])
+##
 
 
     def SetFieldParameters(self, field_index, **kwargs):
 
 
     def SetFieldParameters(self, field_index, **kwargs):
@@ -2013,7 +2060,7 @@ class MaskedEditMixin:
         of booleans indicating whether or not a given position in the mask is
         a mask character or not.
         """
         of booleans indicating whether or not a given position in the mask is
         a mask character or not.
         """
-        dbg('_processMask: mask', mask, indent=1)
+##        dbg('_processMask: mask', mask, indent=1)
         # regular expression for parsing c{n} syntax:
         rex = re.compile('([' +string.join(maskchars,"") + '])\{(\d+)\}')
         s = mask
         # regular expression for parsing c{n} syntax:
         rex = re.compile('([' +string.join(maskchars,"") + '])\{(\d+)\}')
         s = mask
@@ -2033,8 +2080,8 @@ class MaskedEditMixin:
         self._signOk     = '-' in self._ctrl_constraints._formatcodes and (self._isFloat or self._isInt)
         self._useParens  = self._ctrl_constraints._useParensForNegatives
         self._isNeg      = False
         self._signOk     = '-' in self._ctrl_constraints._formatcodes and (self._isFloat or self._isInt)
         self._useParens  = self._ctrl_constraints._useParensForNegatives
         self._isNeg      = False
-##        dbg('self._signOk?', self._signOk, 'self._useParens?', self._useParens)
-##        dbg('isFloatingPoint(%s)?' % (s), isFloatingPoint(s),
+####        dbg('self._signOk?', self._signOk, 'self._useParens?', self._useParens)
+####        dbg('isFloatingPoint(%s)?' % (s), isFloatingPoint(s),
 ##            'ctrl regex:', self._ctrl_constraints._validRegex)
 
         if self._signOk and s[0] != ' ':
 ##            'ctrl regex:', self._ctrl_constraints._validRegex)
 
         if self._signOk and s[0] != ' ':
@@ -2061,10 +2108,10 @@ class MaskedEditMixin:
                         s = s[:i] + s[i+1:]     # elide the 2nd '\' as well
             else:                       # else if special char, mark position accordingly
                 ismasked[i] = s[i] in maskchars
                         s = s[:i] + s[i+1:]     # elide the 2nd '\' as well
             else:                       # else if special char, mark position accordingly
                 ismasked[i] = s[i] in maskchars
-##            dbg('ismasked[%d]:' % i, ismasked[i], s)
+####            dbg('ismasked[%d]:' % i, ismasked[i], s)
             i += 1                      # increment to next char
             i += 1                      # increment to next char
-##        dbg('ismasked:', ismasked)
-        dbg('new mask: "%s"' % s, indent=0)
+####        dbg('ismasked:', ismasked)
+##        dbg('new mask: "%s"' % s, indent=0)
 
         return s, ismasked
 
 
         return s, ismasked
 
@@ -2101,7 +2148,7 @@ class MaskedEditMixin:
                     self._fields[1] = Field()
 
                 self._decimalpos = string.find( self._mask, '.')
                     self._fields[1] = Field()
 
                 self._decimalpos = string.find( self._mask, '.')
-                dbg('decimal pos =', self._decimalpos)
+##                dbg('decimal pos =', self._decimalpos)
 
                 formatcodes = self._fields[0]._GetParameter('formatcodes')
                 if 'R' not in formatcodes: formatcodes += 'R'
 
                 formatcodes = self._fields[0]._GetParameter('formatcodes')
                 if 'R' not in formatcodes: formatcodes += 'R'
@@ -2142,18 +2189,18 @@ class MaskedEditMixin:
                     pos = i       # figure out field for 1st editable space:
 
                 while i <= len(self._mask):
                     pos = i       # figure out field for 1st editable space:
 
                 while i <= len(self._mask):
-##                    dbg('searching: outer field loop: i = ', i)
+####                    dbg('searching: outer field loop: i = ', i)
                     if self._isMaskChar(i):
                     if self._isMaskChar(i):
-##                        dbg('1st char is mask char; recording edit_start=', i)
+####                        dbg('1st char is mask char; recording edit_start=', i)
                         edit_start = i
                         # Skip to end of editable part of current field:
                         while i < len(self._mask) and self._isMaskChar(i):
                             self._lookupField[i] = field_index
                             i += 1
                         edit_start = i
                         # Skip to end of editable part of current field:
                         while i < len(self._mask) and self._isMaskChar(i):
                             self._lookupField[i] = field_index
                             i += 1
-##                        dbg('edit_end =', i)
+####                        dbg('edit_end =', i)
                         edit_end = i
                         self._lookupField[i] = field_index
                         edit_end = i
                         self._lookupField[i] = field_index
-##                        dbg('self._fields.has_key(%d)?' % field_index, self._fields.has_key(field_index))
+####                        dbg('self._fields.has_key(%d)?' % field_index, self._fields.has_key(field_index))
                         if not self._fields.has_key(field_index):
                             kwargs = Field.valid_params.copy()
                             kwargs['index'] = field_index
                         if not self._fields.has_key(field_index):
                             kwargs = Field.valid_params.copy()
                             kwargs['index'] = field_index
@@ -2174,15 +2221,15 @@ class MaskedEditMixin:
                         break           # if past end, we're done
                     else:
                         field_index += 1
                         break           # if past end, we're done
                     else:
                         field_index += 1
-##                        dbg('next field:', field_index)
+####                        dbg('next field:', field_index)
 
         indices = self._fields.keys()
         indices.sort()
         self._field_indices = indices[1:]
 
         indices = self._fields.keys()
         indices.sort()
         self._field_indices = indices[1:]
-##        dbg('lookupField map:', indent=1)
+####        dbg('lookupField map:', indent=1)
 ##        for i in range(len(self._mask)):
 ##        for i in range(len(self._mask)):
-##            dbg('pos %d:' % i, self._lookupField[i])
-##        dbg(indent=0)
+####            dbg('pos %d:' % i, self._lookupField[i])
+####        dbg(indent=0)
 
         # Verify that all field indices specified are valid for mask:
         for index in self._fields.keys():
 
         # Verify that all field indices specified are valid for mask:
         for index in self._fields.keys():
@@ -2203,7 +2250,7 @@ class MaskedEditMixin:
             for field in self._fields.values():
                 if field._defaultValue and not reset_default:
                     default_set = True
             for field in self._fields.values():
                 if field._defaultValue and not reset_default:
                     default_set = True
-        dbg('default set?', default_set)
+##        dbg('default set?', default_set)
 
         # Determine overall new template for control, and keep track of previous
         # values, so that current control value can be modified as appropriate:
 
         # Determine overall new template for control, and keep track of previous
         # values, so that current control value can be modified as appropriate:
@@ -2225,9 +2272,9 @@ class MaskedEditMixin:
             field._template = ""
 
         for pos in range(len(self._mask)):
             field._template = ""
 
         for pos in range(len(self._mask)):
-##            dbg('pos:', pos)
+####            dbg('pos:', pos)
             field = self._FindField(pos)
             field = self._FindField(pos)
-##            dbg('field:', field._index)
+####            dbg('field:', field._index)
             start, end = field._extent
 
             if pos == 0 and self._signOk:
             start, end = field._extent
 
             if pos == 0 and self._signOk:
@@ -2250,32 +2297,32 @@ class MaskedEditMixin:
                         curvalue[pos] = fillChar
 
                 if not field._defaultValue and not self._ctrl_constraints._defaultValue:
                         curvalue[pos] = fillChar
 
                 if not field._defaultValue and not self._ctrl_constraints._defaultValue:
-##                    dbg('no default value')
+####                    dbg('no default value')
                     self._template += fillChar
                     field._template += fillChar
 
                 elif field._defaultValue and not reset_default:
                     self._template += fillChar
                     field._template += fillChar
 
                 elif field._defaultValue and not reset_default:
-##                    dbg('len(field._defaultValue):', len(field._defaultValue))
-##                    dbg('pos-start:', pos-start)
+####                    dbg('len(field._defaultValue):', len(field._defaultValue))
+####                    dbg('pos-start:', pos-start)
                     if len(field._defaultValue) > pos-start:
                     if len(field._defaultValue) > pos-start:
-##                        dbg('field._defaultValue[pos-start]: "%s"' % field._defaultValue[pos-start])
+####                        dbg('field._defaultValue[pos-start]: "%s"' % field._defaultValue[pos-start])
                         self._template += field._defaultValue[pos-start]
                         field._template += field._defaultValue[pos-start]
                     else:
                         self._template += field._defaultValue[pos-start]
                         field._template += field._defaultValue[pos-start]
                     else:
-##                        dbg('field default not long enough; using fillChar')
+####                        dbg('field default not long enough; using fillChar')
                         self._template += fillChar
                         field._template += fillChar
                 else:
                     if len(self._ctrl_constraints._defaultValue) > pos:
                         self._template += fillChar
                         field._template += fillChar
                 else:
                     if len(self._ctrl_constraints._defaultValue) > pos:
-##                        dbg('using control default')
+####                        dbg('using control default')
                         self._template += self._ctrl_constraints._defaultValue[pos]
                         field._template += self._ctrl_constraints._defaultValue[pos]
                     else:
                         self._template += self._ctrl_constraints._defaultValue[pos]
                         field._template += self._ctrl_constraints._defaultValue[pos]
                     else:
-##                        dbg('ctrl default not long enough; using fillChar')
+####                        dbg('ctrl default not long enough; using fillChar')
                         self._template += fillChar
                         field._template += fillChar
                         self._template += fillChar
                         field._template += fillChar
-##                dbg('field[%d]._template now "%s"' % (field._index, field._template))
-##                dbg('self._template now "%s"' % self._template)
+####                dbg('field[%d]._template now "%s"' % (field._index, field._template))
+####                dbg('self._template now "%s"' % self._template)
             else:
                 self._template += self._mask[pos]
 
             else:
                 self._template += self._mask[pos]
 
@@ -2288,9 +2335,9 @@ class MaskedEditMixin:
 
         if default_set:
             self._defaultValue = self._template
 
         if default_set:
             self._defaultValue = self._template
-            dbg('self._defaultValue:', self._defaultValue)
+##            dbg('self._defaultValue:', self._defaultValue)
             if not self.IsEmpty(self._defaultValue) and not self.IsValid(self._defaultValue):
             if not self.IsEmpty(self._defaultValue) and not self.IsValid(self._defaultValue):
-##                dbg(indent=0)
+####                dbg(indent=0)
                 raise ValueError('Default value of "%s" is not a valid value for control "%s"' % (self._defaultValue, self.name))
 
             # if no fillchar change, but old value == old template, replace it:
                 raise ValueError('Default value of "%s" is not a valid value for control "%s"' % (self._defaultValue, self.name))
 
             # if no fillchar change, but old value == old template, replace it:
@@ -2301,7 +2348,7 @@ class MaskedEditMixin:
             self._defaultValue = None
 
         if reset_value:
             self._defaultValue = None
 
         if reset_value:
-            dbg('resetting value to: "%s"' % newvalue)
+##            dbg('resetting value to: "%s"' % newvalue)
             pos = self._GetInsertionPoint()
             sel_start, sel_to = self._GetSelection()
             self._SetValue(newvalue)
             pos = self._GetInsertionPoint()
             sel_start, sel_to = self._GetSelection()
             self._SetValue(newvalue)
@@ -2347,11 +2394,11 @@ class MaskedEditMixin:
                 inherit_args['defaultValue'] = ""   # (reset for field)
 
             for param in Field.propagating_params:
                 inherit_args['defaultValue'] = ""   # (reset for field)
 
             for param in Field.propagating_params:
-##                dbg('reset_args.has_key(%s)?' % param, reset_args.has_key(param))
-##                dbg('reset_args.has_key(%(param)s) and reset_args[%(param)s]?' % locals(), reset_args.has_key(param) and reset_args[param])
+####                dbg('reset_args.has_key(%s)?' % param, reset_args.has_key(param))
+####                dbg('reset_args.has_key(%(param)s) and reset_args[%(param)s]?' % locals(), reset_args.has_key(param) and reset_args[param])
                 if reset_args.has_key(param):
                     inherit_args[param] = self.GetCtrlParameter(param)
                 if reset_args.has_key(param):
                     inherit_args[param] = self.GetCtrlParameter(param)
-##                    dbg('inherit_args[%s]' % param, inherit_args[param])
+####                    dbg('inherit_args[%s]' % param, inherit_args[param])
 
             if inherit_args:
                 field._SetParameters(**inherit_args)
 
             if inherit_args:
                 field._SetParameters(**inherit_args)
@@ -2368,22 +2415,22 @@ class MaskedEditMixin:
             if field._choices:
                 index = field._index
                 if len(self._field_indices) == 1 and index == 0 and field._choices == self._ctrl_constraints._choices:
             if field._choices:
                 index = field._index
                 if len(self._field_indices) == 1 and index == 0 and field._choices == self._ctrl_constraints._choices:
-                    dbg('skipping (duplicate) choice validation of field 0')
+##                    dbg('skipping (duplicate) choice validation of field 0')
                     continue
                     continue
-##                dbg('checking for choices for field', field._index)
+####                dbg('checking for choices for field', field._index)
                 start, end = field._extent
                 field_length = end - start
                 start, end = field._extent
                 field_length = end - start
-##                dbg('start, end, length:', start, end, field_length)
+####                dbg('start, end, length:', start, end, field_length)
                 for choice in field._choices:
                 for choice in field._choices:
-##                    dbg('testing "%s"' % choice)
+####                    dbg('testing "%s"' % choice)
                     valid_paste, ignore, replace_to = self._validatePaste(choice, start, end)
                     if not valid_paste:
                     valid_paste, ignore, replace_to = self._validatePaste(choice, start, end)
                     if not valid_paste:
-##                        dbg(indent=0)
+####                        dbg(indent=0)
                         raise ValueError('"%s" could not be entered into field %d of control "%s"' % (choice, index, self.name))
                     elif replace_to > end:
                         raise ValueError('"%s" could not be entered into field %d of control "%s"' % (choice, index, self.name))
                     elif replace_to > end:
-##                        dbg(indent=0)
+####                        dbg(indent=0)
                         raise ValueError('"%s" will not fit into field %d of control "%s"' (choice, index, self.name))
                         raise ValueError('"%s" will not fit into field %d of control "%s"' (choice, index, self.name))
-##                    dbg(choice, 'valid in field', index)
+####                    dbg(choice, 'valid in field', index)
 
 
     def _configure(self, mask, **reset_args):
 
 
     def _configure(self, mask, **reset_args):
@@ -2401,19 +2448,19 @@ class MaskedEditMixin:
         whole control.
 
         """
         whole control.
 
         """
-        dbg(suspend=1)
-        dbg('MaskedEditMixin::_configure("%s")' % mask, indent=1)
+##        dbg(suspend=1)
+##        dbg('MaskedEditMixin::_configure("%s")' % mask, indent=1)
 
         # Preprocess specified mask to expand {n} syntax, handle escaped
         # mask characters, etc and build the resulting positionally keyed
         # dictionary for which positions are mask vs. template characters:
         self._mask, self.ismasked = self._processMask(mask)
         self._masklength = len(self._mask)
 
         # Preprocess specified mask to expand {n} syntax, handle escaped
         # mask characters, etc and build the resulting positionally keyed
         # dictionary for which positions are mask vs. template characters:
         self._mask, self.ismasked = self._processMask(mask)
         self._masklength = len(self._mask)
-##        dbg('processed mask:', self._mask)
+####        dbg('processed mask:', self._mask)
 
         # Preserve original mask specified, for subsequent reprocessing
         # if parameters change.
 
         # Preserve original mask specified, for subsequent reprocessing
         # if parameters change.
-        dbg('mask: "%s"' % self._mask, 'previous mask: "%s"' % self._previous_mask)
+##        dbg('mask: "%s"' % self._mask, 'previous mask: "%s"' % self._previous_mask)
         self._previous_mask = mask    # save unexpanded mask for next time
             # Set expanded mask and extent of field -1 to width of entire control:
         self._ctrl_constraints._SetParameters(mask = self._mask, extent=(0,self._masklength))
         self._previous_mask = mask    # save unexpanded mask for next time
             # Set expanded mask and extent of field -1 to width of entire control:
         self._ctrl_constraints._SetParameters(mask = self._mask, extent=(0,self._masklength))
@@ -2422,7 +2469,7 @@ class MaskedEditMixin:
         # instances as necessary, configure them with those extents, and
         # build lookup table mapping each position for control to its corresponding
         # field.
         # instances as necessary, configure them with those extents, and
         # build lookup table mapping each position for control to its corresponding
         # field.
-##        dbg('calculating field extents')
+####        dbg('calculating field extents')
 
         self._calcFieldExtents()
 
 
         self._calcFieldExtents()
 
@@ -2432,13 +2479,13 @@ class MaskedEditMixin:
         reset_fillchar = reset_args.has_key('fillChar') and reset_args['fillChar']
         reset_default = reset_args.has_key('defaultValue') and reset_args['defaultValue']
 
         reset_fillchar = reset_args.has_key('fillChar') and reset_args['fillChar']
         reset_default = reset_args.has_key('defaultValue') and reset_args['defaultValue']
 
-##        dbg('calculating template')
+####        dbg('calculating template')
         self._calcTemplate(reset_fillchar, reset_default)
 
         # Propagate control-level formatting and character constraints to each
         # field if they don't already have them; if only one field, propagate
         # control-level validation constraints to field as well:
         self._calcTemplate(reset_fillchar, reset_default)
 
         # Propagate control-level formatting and character constraints to each
         # field if they don't already have them; if only one field, propagate
         # control-level validation constraints to field as well:
-##        dbg('propagating constraints')
+####        dbg('propagating constraints')
         self._propagateConstraints(**reset_args)
 
 
         self._propagateConstraints(**reset_args)
 
 
@@ -2446,10 +2493,10 @@ class MaskedEditMixin:
             raise AttributeError('groupChar (%s) and decimalChar (%s) must be distinct.' %
                                  (self._fields[0]._groupChar, self._decimalChar) )
 
             raise AttributeError('groupChar (%s) and decimalChar (%s) must be distinct.' %
                                  (self._fields[0]._groupChar, self._decimalChar) )
 
-##        dbg('fields:', indent=1)
+####        dbg('fields:', indent=1)
 ##        for i in [-1] + self._field_indices:
 ##        for i in [-1] + self._field_indices:
-##            dbg('field %d:' % i, self._fields[i].__dict__)
-##        dbg(indent=0)
+####            dbg('field %d:' % i, self._fields[i].__dict__)
+####        dbg(indent=0)
 
         # Set up special parameters for numeric control, if appropriate:
         if self._signOk:
 
         # Set up special parameters for numeric control, if appropriate:
         if self._signOk:
@@ -2467,7 +2514,7 @@ class MaskedEditMixin:
         if self._isFloat or self._isInt:
             if self.controlInitialized:
                 value = self._GetValue()
         if self._isFloat or self._isInt:
             if self.controlInitialized:
                 value = self._GetValue()
-##                dbg('value: "%s"' % value, 'len(value):', len(value),
+####                dbg('value: "%s"' % value, 'len(value):', len(value),
 ##                    'len(self._ctrl_constraints._mask):',len(self._ctrl_constraints._mask))
                 if len(value) < len(self._ctrl_constraints._mask):
                     newvalue = value
 ##                    'len(self._ctrl_constraints._mask):',len(self._ctrl_constraints._mask))
                 if len(value) < len(self._ctrl_constraints._mask):
                     newvalue = value
@@ -2480,12 +2527,12 @@ class MaskedEditMixin:
                             newvalue = newvalue.rjust(len(self._ctrl_constraints._mask))
                         else:
                             newvalue = newvalue.ljust(len(self._ctrl_constraints._mask))
                             newvalue = newvalue.rjust(len(self._ctrl_constraints._mask))
                         else:
                             newvalue = newvalue.ljust(len(self._ctrl_constraints._mask))
-                    dbg('old value: "%s"' % value)
-                    dbg('new value: "%s"' % newvalue)
+##                    dbg('old value: "%s"' % value)
+##                    dbg('new value: "%s"' % newvalue)
                     try:
                         self._SetValue(newvalue)
                     except Exception, e:
                     try:
                         self._SetValue(newvalue)
                     except Exception, e:
-                        dbg('exception raised:', e, 'resetting to initial value')
+##                        dbg('exception raised:', e, 'resetting to initial value')
                         self._SetInitialValue()
 
                 elif len(value) > len(self._ctrl_constraints._mask):
                         self._SetInitialValue()
 
                 elif len(value) > len(self._ctrl_constraints._mask):
@@ -2497,27 +2544,27 @@ class MaskedEditMixin:
                     if not self._signOk:
                         newvalue, signpos, right_signpos = self._getSignedValue(newvalue)
 
                     if not self._signOk:
                         newvalue, signpos, right_signpos = self._getSignedValue(newvalue)
 
-                    dbg('old value: "%s"' % value)
-                    dbg('new value: "%s"' % newvalue)
+##                    dbg('old value: "%s"' % value)
+##                    dbg('new value: "%s"' % newvalue)
                     try:
                         self._SetValue(newvalue)
                     except Exception, e:
                     try:
                         self._SetValue(newvalue)
                     except Exception, e:
-                        dbg('exception raised:', e, 'resetting to initial value')
+##                        dbg('exception raised:', e, 'resetting to initial value')
                         self._SetInitialValue()
                 elif not self._signOk and ('(' in value or '-' in value):
                     newvalue, signpos, right_signpos = self._getSignedValue(value)
                         self._SetInitialValue()
                 elif not self._signOk and ('(' in value or '-' in value):
                     newvalue, signpos, right_signpos = self._getSignedValue(value)
-                    dbg('old value: "%s"' % value)
-                    dbg('new value: "%s"' % newvalue)
+##                    dbg('old value: "%s"' % value)
+##                    dbg('new value: "%s"' % newvalue)
                     try:
                         self._SetValue(newvalue)
                     except e:
                     try:
                         self._SetValue(newvalue)
                     except e:
-                        dbg('exception raised:', e, 'resetting to initial value')
+##                        dbg('exception raised:', e, 'resetting to initial value')
                         self._SetInitialValue()
 
             # Replace up/down arrow default handling:
             # make down act like tab, up act like shift-tab:
 
                         self._SetInitialValue()
 
             # Replace up/down arrow default handling:
             # make down act like tab, up act like shift-tab:
 
-##            dbg('Registering numeric navigation and control handlers (if not already set)')
+####            dbg('Registering numeric navigation and control handlers (if not already set)')
             if not self._keyhandlers.has_key(wx.WXK_DOWN):
                 self._SetKeycodeHandler(wx.WXK_DOWN, self._OnChangeField)
             if not self._keyhandlers.has_key(wx.WXK_UP):
             if not self._keyhandlers.has_key(wx.WXK_DOWN):
                 self._SetKeycodeHandler(wx.WXK_DOWN, self._OnChangeField)
             if not self._keyhandlers.has_key(wx.WXK_UP):
@@ -2534,7 +2581,7 @@ class MaskedEditMixin:
             if not self._keyhandlers.has_key(ord(self._fields[0]._groupChar)):
                 self._SetKeyHandler(self._fields[0]._groupChar, self._OnGroupChar)
 
             if not self._keyhandlers.has_key(ord(self._fields[0]._groupChar)):
                 self._SetKeyHandler(self._fields[0]._groupChar, self._OnGroupChar)
 
-        dbg(indent=0, suspend=0)
+##        dbg(indent=0, suspend=0)
 
 
     def _SetInitialValue(self, value=""):
 
 
     def _SetInitialValue(self, value=""):
@@ -2543,7 +2590,7 @@ class MaskedEditMixin:
         It will also set/reset the font if necessary and apply
         formatting to the control at this time.
         """
         It will also set/reset the font if necessary and apply
         formatting to the control at this time.
         """
-        dbg('MaskedEditMixin::_SetInitialValue("%s")' % value, indent=1)
+##        dbg('MaskedEditMixin::_SetInitialValue("%s")' % value, indent=1)
         if not value:
             self._prevValue = self._curValue = self._template
             # don't apply external validation rules in this case, as template may
         if not value:
             self._prevValue = self._curValue = self._template
             # don't apply external validation rules in this case, as template may
@@ -2551,27 +2598,27 @@ class MaskedEditMixin:
             try:
                 self._SetValue(self._curValue)  # note the use of "raw" ._SetValue()...
             except Exception, e:
             try:
                 self._SetValue(self._curValue)  # note the use of "raw" ._SetValue()...
             except Exception, e:
-                dbg('exception thrown:', e, indent=0)
+##                dbg('exception thrown:', e, indent=0)
                 raise
         else:
             # Otherwise apply validation as appropriate to passed value:
                 raise
         else:
             # Otherwise apply validation as appropriate to passed value:
-##            dbg('value = "%s", length:' % value, len(value))
+####            dbg('value = "%s", length:' % value, len(value))
             self._prevValue = self._curValue = value
             try:
                 self.SetValue(value)            # use public (validating) .SetValue()
             except Exception, e:
             self._prevValue = self._curValue = value
             try:
                 self.SetValue(value)            # use public (validating) .SetValue()
             except Exception, e:
-                dbg('exception thrown:', e, indent=0)
+##                dbg('exception thrown:', e, indent=0)
                 raise
 
 
         # Set value/type-specific formatting
         self._applyFormatting()
                 raise
 
 
         # Set value/type-specific formatting
         self._applyFormatting()
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _calcSize(self, size=None):
         """ Calculate automatic size if allowed; must be called after the base control is instantiated"""
 
 
     def _calcSize(self, size=None):
         """ Calculate automatic size if allowed; must be called after the base control is instantiated"""
-##        dbg('MaskedEditMixin::_calcSize', indent=1)
+####        dbg('MaskedEditMixin::_calcSize', indent=1)
         cont = (size is None or size == wx.DefaultSize)
 
         if cont and self._autofit:
         cont = (size is None or size == wx.DefaultSize)
 
         if cont and self._autofit:
@@ -2580,29 +2627,29 @@ class MaskedEditMixin:
                 sizing_text += 'M'
             if wx.Platform == "__WXMAC__":   # give it even a little more...
                 sizing_text += 'M'
                 sizing_text += 'M'
             if wx.Platform == "__WXMAC__":   # give it even a little more...
                 sizing_text += 'M'
-##            dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text)
+####            dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text)
             w, h = self.GetTextExtent(sizing_text)
             size = (w+4, self.GetClientSize().height)
             w, h = self.GetTextExtent(sizing_text)
             size = (w+4, self.GetClientSize().height)
-##            dbg('size:', size, indent=0)
+####            dbg('size:', size, indent=0)
         return size
 
 
     def _setFont(self):
         """ Set the control's font typeface -- pass the font name as str."""
         return size
 
 
     def _setFont(self):
         """ Set the control's font typeface -- pass the font name as str."""
-##        dbg('MaskedEditMixin::_setFont', indent=1)
+####        dbg('MaskedEditMixin::_setFont', indent=1)
         if not self._useFixedWidthFont:
             self._font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
         else:
             font = self.GetFont()   # get size, weight, etc from current font
 
         if not self._useFixedWidthFont:
             self._font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
         else:
             font = self.GetFont()   # get size, weight, etc from current font
 
-            # Set to teletype font (guaranteed to be mappable to all wxWindows
+            # Set to teletype font (guaranteed to be mappable to all wxWidgets
             # platforms:
             self._font = wx.Font( font.GetPointSize(), wx.TELETYPE, font.GetStyle(),
                                  font.GetWeight(), font.GetUnderlined())
             # platforms:
             self._font = wx.Font( font.GetPointSize(), wx.TELETYPE, font.GetStyle(),
                                  font.GetWeight(), font.GetUnderlined())
-##            dbg('font string: "%s"' % font.GetNativeFontInfo().ToString())
+####            dbg('font string: "%s"' % font.GetNativeFontInfo().ToString())
 
         self.SetFont(self._font)
 
         self.SetFont(self._font)
-##        dbg(indent=0)
+####        dbg(indent=0)
 
 
     def _OnTextChange(self, event):
 
 
     def _OnTextChange(self, event):
@@ -2616,31 +2663,32 @@ class MaskedEditMixin:
         EVT_TEXT events for the same change.)
         """
         newvalue = self._GetValue()
         EVT_TEXT events for the same change.)
         """
         newvalue = self._GetValue()
-        dbg('MaskedEditMixin::_OnTextChange: value: "%s"' % newvalue, indent=1)
+##        dbg('MaskedEditMixin::_OnTextChange: value: "%s"' % newvalue, indent=1)
         bValid = False
         if self._ignoreChange:      # ie. if an "intermediate text change event"
         bValid = False
         if self._ignoreChange:      # ie. if an "intermediate text change event"
-            dbg(indent=0)
+##            dbg(indent=0)
             return bValid
 
         ##! WS: For some inexplicable reason, every wxTextCtrl.SetValue
         ## call is generating two (2) EVT_TEXT events.
         ## This is the only mechanism I can find to mask this problem:
         if newvalue == self._curValue:
             return bValid
 
         ##! WS: For some inexplicable reason, every wxTextCtrl.SetValue
         ## call is generating two (2) EVT_TEXT events.
         ## This is the only mechanism I can find to mask this problem:
         if newvalue == self._curValue:
-            dbg('ignoring bogus text change event', indent=0)
+##            dbg('ignoring bogus text change event', indent=0)
+            pass
         else:
         else:
-            dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
+##            dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
             if self._Change():
                 if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
             if self._Change():
                 if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
-                    dbg('clearing self._isNeg')
+##                    dbg('clearing self._isNeg')
                     self._isNeg = False
                     text, self._signpos, self._right_signpos = self._getSignedValue()
                 self._CheckValid()  # Recolor control as appropriate
                     self._isNeg = False
                     text, self._signpos, self._right_signpos = self._getSignedValue()
                 self._CheckValid()  # Recolor control as appropriate
-            dbg('calling event.Skip()')
+##            dbg('calling event.Skip()')
             event.Skip()
             bValid = True
         self._prevValue = self._curValue    # save for undo
         self._curValue = newvalue           # Save last seen value for next iteration
             event.Skip()
             bValid = True
         self._prevValue = self._curValue    # save for undo
         self._curValue = newvalue           # Save last seen value for next iteration
-        dbg(indent=0)
+##        dbg(indent=0)
         return bValid
 
 
         return bValid
 
 
@@ -2654,7 +2702,7 @@ class MaskedEditMixin:
         if key in self._nav and event.ControlDown():
             # then this is the only place we will likely see these events;
             # process them now:
         if key in self._nav and event.ControlDown():
             # then this is the only place we will likely see these events;
             # process them now:
-            dbg('MaskedEditMixin::OnKeyDown: calling _OnChar')
+##            dbg('MaskedEditMixin::OnKeyDown: calling _OnChar')
             self._OnChar(event)
             return
         # else allow regular EVT_CHAR key processing
             self._OnChar(event)
             return
         # else allow regular EVT_CHAR key processing
@@ -2666,25 +2714,25 @@ class MaskedEditMixin:
         This is the engine of wxMaskedEdit controls.  It examines each keystroke,
         decides if it's allowed, where it should go or what action to take.
         """
         This is the engine of wxMaskedEdit controls.  It examines each keystroke,
         decides if it's allowed, where it should go or what action to take.
         """
-        dbg('MaskedEditMixin::_OnChar', indent=1)
+##        dbg('MaskedEditMixin::_OnChar', indent=1)
 
         # Get keypress value, adjusted by control options (e.g. convert to upper etc)
         key = event.GetKeyCode()
         orig_pos = self._GetInsertionPoint()
         orig_value = self._GetValue()
 
         # Get keypress value, adjusted by control options (e.g. convert to upper etc)
         key = event.GetKeyCode()
         orig_pos = self._GetInsertionPoint()
         orig_value = self._GetValue()
-        dbg('keycode = ', key)
-        dbg('current pos = ', orig_pos)
-        dbg('current selection = ', self._GetSelection())
+##        dbg('keycode = ', key)
+##        dbg('current pos = ', orig_pos)
+##        dbg('current selection = ', self._GetSelection())
 
         if not self._Keypress(key):
 
         if not self._Keypress(key):
-            dbg(indent=0)
+##            dbg(indent=0)
             return
 
         # If no format string for this control, or the control is marked as "read-only",
         # skip the rest of the special processing, and just "do the standard thing:"
         if not self._mask or not self._IsEditable():
             event.Skip()
             return
 
         # If no format string for this control, or the control is marked as "read-only",
         # skip the rest of the special processing, and just "do the standard thing:"
         if not self._mask or not self._IsEditable():
             event.Skip()
-            dbg(indent=0)
+##            dbg(indent=0)
             return
 
         # Process navigation and control keys first, with
             return
 
         # Process navigation and control keys first, with
@@ -2695,77 +2743,77 @@ class MaskedEditMixin:
                 if self._GetValue() != orig_value:
                     self.modified = True
                 if not keep_processing:
                 if self._GetValue() != orig_value:
                     self.modified = True
                 if not keep_processing:
-                    dbg(indent=0)
+##                    dbg(indent=0)
                     return
                 self._applyFormatting()
                     return
                 self._applyFormatting()
-                dbg(indent=0)
+##                dbg(indent=0)
                 return
 
         # Else... adjust the position as necessary for next input key,
         # and determine resulting selection:
         pos = self._adjustPos( orig_pos, key )    ## get insertion position, adjusted as needed
         sel_start, sel_to = self._GetSelection()                ## check for a range of selected text
                 return
 
         # Else... adjust the position as necessary for next input key,
         # and determine resulting selection:
         pos = self._adjustPos( orig_pos, key )    ## get insertion position, adjusted as needed
         sel_start, sel_to = self._GetSelection()                ## check for a range of selected text
-        dbg("pos, sel_start, sel_to:", pos, sel_start, sel_to)
+##        dbg("pos, sel_start, sel_to:", pos, sel_start, sel_to)
 
         keep_processing = True
         # Capture user past end of format field
         if pos > len(self.maskdict):
 
         keep_processing = True
         # Capture user past end of format field
         if pos > len(self.maskdict):
-            dbg("field length exceeded:",pos)
+##            dbg("field length exceeded:",pos)
             keep_processing = False
 
         if keep_processing:
             if self._isMaskChar(pos):  ## Get string of allowed characters for validation
                 okchars = self._getAllowedChars(pos)
             else:
             keep_processing = False
 
         if keep_processing:
             if self._isMaskChar(pos):  ## Get string of allowed characters for validation
                 okchars = self._getAllowedChars(pos)
             else:
-                dbg('Not a valid position: pos = ', pos,"chars=",maskchars)
+##                dbg('Not a valid position: pos = ', pos,"chars=",maskchars)
                 okchars = ""
 
         key = self._adjustKey(pos, key)     # apply formatting constraints to key:
 
         if self._keyhandlers.has_key(key):
             # there's an override for default behavior; use override function instead
                 okchars = ""
 
         key = self._adjustKey(pos, key)     # apply formatting constraints to key:
 
         if self._keyhandlers.has_key(key):
             # there's an override for default behavior; use override function instead
-            dbg('using supplied key handler:', self._keyhandlers[key])
+##            dbg('using supplied key handler:', self._keyhandlers[key])
             keep_processing = self._keyhandlers[key](event)
             if self._GetValue() != orig_value:
                 self.modified = True
             if not keep_processing:
             keep_processing = self._keyhandlers[key](event)
             if self._GetValue() != orig_value:
                 self.modified = True
             if not keep_processing:
-                dbg(indent=0)
+##                dbg(indent=0)
                 return
             # else skip default processing, but do final formatting
         if key < wx.WXK_SPACE or key > 255:
                 return
             # else skip default processing, but do final formatting
         if key < wx.WXK_SPACE or key > 255:
-            dbg('key < WXK_SPACE or key > 255')
+##            dbg('key < WXK_SPACE or key > 255')
             event.Skip()                # non alphanumeric
             keep_processing = False
         else:
             field = self._FindField(pos)
             event.Skip()                # non alphanumeric
             keep_processing = False
         else:
             field = self._FindField(pos)
-            dbg("key ='%s'" % chr(key))
+##            dbg("key ='%s'" % chr(key))
             if chr(key) == ' ':
             if chr(key) == ' ':
-                dbg('okSpaces?', field._okSpaces)
-
+##                dbg('okSpaces?', field._okSpaces)
+                pass
 
 
             if chr(key) in field._excludeChars + self._ctrl_constraints._excludeChars:
                 keep_processing = False
 
             if keep_processing and self._isCharAllowed( chr(key), pos, checkRegex = True ):
 
 
             if chr(key) in field._excludeChars + self._ctrl_constraints._excludeChars:
                 keep_processing = False
 
             if keep_processing and self._isCharAllowed( chr(key), pos, checkRegex = True ):
-                dbg("key allowed by mask")
+##                dbg("key allowed by mask")
                 # insert key into candidate new value, but don't change control yet:
                 oldstr = self._GetValue()
                 newstr, newpos, new_select_to, match_field, match_index = self._insertKey(
                                 chr(key), pos, sel_start, sel_to, self._GetValue(), allowAutoSelect = True)
                 # insert key into candidate new value, but don't change control yet:
                 oldstr = self._GetValue()
                 newstr, newpos, new_select_to, match_field, match_index = self._insertKey(
                                 chr(key), pos, sel_start, sel_to, self._GetValue(), allowAutoSelect = True)
-                dbg("str with '%s' inserted:" % chr(key), '"%s"' % newstr)
+##                dbg("str with '%s' inserted:" % chr(key), '"%s"' % newstr)
                 if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
                 if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
-                    dbg('not valid; checking to see if adjusted string is:')
+##                    dbg('not valid; checking to see if adjusted string is:')
                     keep_processing = False
                     if self._isFloat and newstr != self._template:
                         newstr = self._adjustFloat(newstr)
                     keep_processing = False
                     if self._isFloat and newstr != self._template:
                         newstr = self._adjustFloat(newstr)
-                        dbg('adjusted str:', newstr)
+##                        dbg('adjusted str:', newstr)
                         if self.IsValid(newstr):
                         if self.IsValid(newstr):
-                            dbg("it is!")
+##                            dbg("it is!")
                             keep_processing = True
                             wx.CallAfter(self._SetInsertionPoint, self._decimalpos)
                     if not keep_processing:
                             keep_processing = True
                             wx.CallAfter(self._SetInsertionPoint, self._decimalpos)
                     if not keep_processing:
-                        dbg("key disallowed by validation")
+##                        dbg("key disallowed by validation")
                         if not wx.Validator_IsSilent() and orig_pos == pos:
                             wx.Bell()
 
                         if not wx.Validator_IsSilent() and orig_pos == pos:
                             wx.Bell()
 
@@ -2775,7 +2823,7 @@ class MaskedEditMixin:
                     # special case: adjust date value as necessary:
                     if self._isDate and newstr != self._template:
                         newstr = self._adjustDate(newstr)
                     # special case: adjust date value as necessary:
                     if self._isDate and newstr != self._template:
                         newstr = self._adjustDate(newstr)
-                    dbg('adjusted newstr:', newstr)
+##                    dbg('adjusted newstr:', newstr)
 
                     if newstr != orig_value:
                         self.modified = True
 
                     if newstr != orig_value:
                         self.modified = True
@@ -2791,21 +2839,21 @@ class MaskedEditMixin:
                     wx.CallAfter(self._SetInsertionPoint, newpos)
 
                     if match_field is not None:
                     wx.CallAfter(self._SetInsertionPoint, newpos)
 
                     if match_field is not None:
-                        dbg('matched field')
+##                        dbg('matched field')
                         self._OnAutoSelect(match_field, match_index)
 
                     if new_select_to != newpos:
                         self._OnAutoSelect(match_field, match_index)
 
                     if new_select_to != newpos:
-                        dbg('queuing selection: (%d, %d)' % (newpos, new_select_to))
+##                        dbg('queuing selection: (%d, %d)' % (newpos, new_select_to))
                         wx.CallAfter(self._SetSelection, newpos, new_select_to)
                     else:
                         newfield = self._FindField(newpos)
                         if newfield != field and newfield._selectOnFieldEntry:
                         wx.CallAfter(self._SetSelection, newpos, new_select_to)
                     else:
                         newfield = self._FindField(newpos)
                         if newfield != field and newfield._selectOnFieldEntry:
-                            dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
+##                            dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
                             wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
                     keep_processing = False
 
             elif keep_processing:
                             wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
                     keep_processing = False
 
             elif keep_processing:
-                dbg('char not allowed')
+##                dbg('char not allowed')
                 keep_processing = False
                 if (not wx.Validator_IsSilent()) and orig_pos == pos:
                     wx.Bell()
                 keep_processing = False
                 if (not wx.Validator_IsSilent()) and orig_pos == pos:
                     wx.Bell()
@@ -2817,12 +2865,12 @@ class MaskedEditMixin:
             pos = self._GetInsertionPoint()
             next_entry = self._findNextEntry( pos )
             if pos != next_entry:
             pos = self._GetInsertionPoint()
             next_entry = self._findNextEntry( pos )
             if pos != next_entry:
-                dbg("moving from %(pos)d to next valid entry: %(next_entry)d" % locals())
+##                dbg("moving from %(pos)d to next valid entry: %(next_entry)d" % locals())
                 wx.CallAfter(self._SetInsertionPoint, next_entry )
 
             if self._isTemplateChar(pos):
                 self._AdjustField(pos)
                 wx.CallAfter(self._SetInsertionPoint, next_entry )
 
             if self._isTemplateChar(pos):
                 self._AdjustField(pos)
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _FindFieldExtent(self, pos=None, getslice=False, value=None):
 
 
     def _FindFieldExtent(self, pos=None, getslice=False, value=None):
@@ -2846,8 +2894,7 @@ class MaskedEditMixin:
         10, 14
         etc.
         """
         10, 14
         etc.
         """
-        dbg('MaskedEditMixin::_FindFieldExtent(pos=%s, getslice=%s)' % (
-                str(pos), str(getslice)) ,indent=1)
+##        dbg('MaskedEditMixin::_FindFieldExtent(pos=%s, getslice=%s)' % (str(pos), str(getslice)) ,indent=1)
 
         field = self._FindField(pos)
         if not field:
 
         field = self._FindField(pos)
         if not field:
@@ -2859,12 +2906,12 @@ class MaskedEditMixin:
         if getslice:
             if value is None: value = self._GetValue()
             slice = value[edit_start:edit_end]
         if getslice:
             if value is None: value = self._GetValue()
             slice = value[edit_start:edit_end]
-            dbg('edit_start:', edit_start, 'edit_end:', edit_end, 'slice: "%s"' % slice)
-            dbg(indent=0)
+##            dbg('edit_start:', edit_start, 'edit_end:', edit_end, 'slice: "%s"' % slice)
+##            dbg(indent=0)
             return edit_start, edit_end, slice
         else:
             return edit_start, edit_end, slice
         else:
-            dbg('edit_start:', edit_start, 'edit_end:', edit_end)
-            dbg(indent=0)
+##            dbg('edit_start:', edit_start, 'edit_end:', edit_end)
+##            dbg(indent=0)
             return edit_start, edit_end
 
 
             return edit_start, edit_end
 
 
@@ -2876,23 +2923,23 @@ class MaskedEditMixin:
         when calculating the current field.
 
         """
         when calculating the current field.
 
         """
-##        dbg('MaskedEditMixin::_FindField(pos=%s)' % str(pos) ,indent=1)
+####        dbg('MaskedEditMixin::_FindField(pos=%s)' % str(pos) ,indent=1)
         if pos is None: pos = self._GetInsertionPoint()
         elif pos < 0 or pos > self._masklength:
             raise IndexError('position %s out of range of control' % str(pos))
 
         if len(self._fields) == 0:
         if pos is None: pos = self._GetInsertionPoint()
         elif pos < 0 or pos > self._masklength:
             raise IndexError('position %s out of range of control' % str(pos))
 
         if len(self._fields) == 0:
-            dbg(indent=0)
+##            dbg(indent=0)
             return None
 
         # else...
             return None
 
         # else...
-##        dbg(indent=0)
+####        dbg(indent=0)
         return self._fields[self._lookupField[pos]]
 
 
     def ClearValue(self):
         """ Blanks the current control value by replacing it with the default value."""
         return self._fields[self._lookupField[pos]]
 
 
     def ClearValue(self):
         """ Blanks the current control value by replacing it with the default value."""
-        dbg("MaskedEditMixin::ClearValue - value reset to default value (template)")
+##        dbg("MaskedEditMixin::ClearValue - value reset to default value (template)")
         self._SetValue( self._template )
         self._SetInsertionPoint(0)
         self.Refresh()
         self._SetValue( self._template )
         self._SetInsertionPoint(0)
         self.Refresh()
@@ -2911,11 +2958,11 @@ class MaskedEditMixin:
         Makes up-arrow act like shift-tab should; ie. take you to start of
         previous field.
         """
         Makes up-arrow act like shift-tab should; ie. take you to start of
         previous field.
         """
-        dbg('MaskedEditMixin::_OnUpNumeric', indent=1)
+##        dbg('MaskedEditMixin::_OnUpNumeric', indent=1)
         event.m_shiftDown = 1
         event.m_shiftDown = 1
-        dbg('event.ShiftDown()?', event.ShiftDown())
+##        dbg('event.ShiftDown()?', event.ShiftDown())
         self._OnChangeField(event)
         self._OnChangeField(event)
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _OnArrow(self, event):
 
 
     def _OnArrow(self, event):
@@ -2923,7 +2970,7 @@ class MaskedEditMixin:
         Used in response to left/right navigation keys; makes these actions skip
         over mask template chars.
         """
         Used in response to left/right navigation keys; makes these actions skip
         over mask template chars.
         """
-        dbg("MaskedEditMixin::_OnArrow", indent=1)
+##        dbg("MaskedEditMixin::_OnArrow", indent=1)
         pos = self._GetInsertionPoint()
         keycode = event.GetKeyCode()
         sel_start, sel_to = self._GetSelection()
         pos = self._GetInsertionPoint()
         keycode = event.GetKeyCode()
         sel_start, sel_to = self._GetSelection()
@@ -2931,12 +2978,12 @@ class MaskedEditMixin:
         if keycode in (wx.WXK_RIGHT, wx.WXK_DOWN):
             if( ( not self._isTemplateChar(pos) and pos+1 > entry_end)
                 or ( self._isTemplateChar(pos) and pos >= entry_end) ):
         if keycode in (wx.WXK_RIGHT, wx.WXK_DOWN):
             if( ( not self._isTemplateChar(pos) and pos+1 > entry_end)
                 or ( self._isTemplateChar(pos) and pos >= entry_end) ):
-                dbg("can't advance", indent=0)
+##                dbg("can't advance", indent=0)
                 return False
             elif self._isTemplateChar(pos):
                 self._AdjustField(pos)
         elif keycode in (wx.WXK_LEFT,wx.WXK_UP) and sel_start == sel_to and pos > 0 and self._isTemplateChar(pos-1):
                 return False
             elif self._isTemplateChar(pos):
                 self._AdjustField(pos)
         elif keycode in (wx.WXK_LEFT,wx.WXK_UP) and sel_start == sel_to and pos > 0 and self._isTemplateChar(pos-1):
-            dbg('adjusting field')
+##            dbg('adjusting field')
             self._AdjustField(pos)
 
         # treat as shifted up/down arrows as tab/reverse tab:
             self._AdjustField(pos)
 
         # treat as shifted up/down arrows as tab/reverse tab:
@@ -2967,7 +3014,7 @@ class MaskedEditMixin:
             else:
                 # treat arrows as normal, allowing selection
                 # as appropriate:
             else:
                 # treat arrows as normal, allowing selection
                 # as appropriate:
-                dbg('using base ctrl event processing')
+##                dbg('using base ctrl event processing')
                 event.Skip()
         else:
             if( (sel_to == self._fields[0]._extent[0] and keycode == wx.WXK_LEFT)
                 event.Skip()
         else:
             if( (sel_to == self._fields[0]._extent[0] and keycode == wx.WXK_LEFT)
@@ -2977,17 +3024,17 @@ class MaskedEditMixin:
             else:
                 # treat arrows as normal, allowing selection
                 # as appropriate:
             else:
                 # treat arrows as normal, allowing selection
                 # as appropriate:
-                dbg('using base event processing')
+##                dbg('using base event processing')
                 event.Skip()
 
         keep_processing = False
                 event.Skip()
 
         keep_processing = False
-        dbg(indent=0)
+##        dbg(indent=0)
         return keep_processing
 
 
     def _OnCtrl_S(self, event):
         """ Default Ctrl-S handler; prints value information if demo enabled. """
         return keep_processing
 
 
     def _OnCtrl_S(self, event):
         """ Default Ctrl-S handler; prints value information if demo enabled. """
-        dbg("MaskedEditMixin::_OnCtrl_S")
+##        dbg("MaskedEditMixin::_OnCtrl_S")
         if self._demo:
             print 'MaskedEditMixin.GetValue()       = "%s"\nMaskedEditMixin.GetPlainValue() = "%s"' % (self.GetValue(), self.GetPlainValue())
             print "Valid? => " + str(self.IsValid())
         if self._demo:
             print 'MaskedEditMixin.GetValue()       = "%s"\nMaskedEditMixin.GetPlainValue() = "%s"' % (self.GetValue(), self.GetPlainValue())
             print "Valid? => " + str(self.IsValid())
@@ -2998,9 +3045,9 @@ class MaskedEditMixin:
     def _OnCtrl_X(self, event=None):
         """ Handles ctrl-x keypress in control and Cut operation on context menu.
             Should return False to skip other processing. """
     def _OnCtrl_X(self, event=None):
         """ Handles ctrl-x keypress in control and Cut operation on context menu.
             Should return False to skip other processing. """
-        dbg("MaskedEditMixin::_OnCtrl_X", indent=1)
+##        dbg("MaskedEditMixin::_OnCtrl_X", indent=1)
         self.Cut()
         self.Cut()
-        dbg(indent=0)
+##        dbg(indent=0)
         return False
 
     def _OnCtrl_C(self, event=None):
         return False
 
     def _OnCtrl_C(self, event=None):
@@ -3012,17 +3059,17 @@ class MaskedEditMixin:
     def _OnCtrl_V(self, event=None):
         """ Handles ctrl-V keypress in control and Paste operation on context menu.
             Should return False to skip other processing. """
     def _OnCtrl_V(self, event=None):
         """ Handles ctrl-V keypress in control and Paste operation on context menu.
             Should return False to skip other processing. """
-        dbg("MaskedEditMixin::_OnCtrl_V", indent=1)
+##        dbg("MaskedEditMixin::_OnCtrl_V", indent=1)
         self.Paste()
         self.Paste()
-        dbg(indent=0)
+##        dbg(indent=0)
         return False
 
     def _OnCtrl_Z(self, event=None):
         """ Handles ctrl-Z keypress in control and Undo operation on context menu.
             Should return False to skip other processing. """
         return False
 
     def _OnCtrl_Z(self, event=None):
         """ Handles ctrl-Z keypress in control and Undo operation on context menu.
             Should return False to skip other processing. """
-        dbg("MaskedEditMixin::_OnCtrl_Z", indent=1)
+##        dbg("MaskedEditMixin::_OnCtrl_Z", indent=1)
         self.Undo()
         self.Undo()
-        dbg(indent=0)
+##        dbg(indent=0)
         return False
 
     def _OnCtrl_A(self,event=None):
         return False
 
     def _OnCtrl_A(self,event=None):
@@ -3039,7 +3086,7 @@ class MaskedEditMixin:
 
     def _OnErase(self, event=None):
         """ Handles backspace and delete keypress in control. Should return False to skip other processing."""
 
     def _OnErase(self, event=None):
         """ Handles backspace and delete keypress in control. Should return False to skip other processing."""
-        dbg("MaskedEditMixin::_OnErase", indent=1)
+##        dbg("MaskedEditMixin::_OnErase", indent=1)
         sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
 
         if event is None:   # called as action routine from Cut() operation.
         sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
 
         if event is None:   # called as action routine from Cut() operation.
@@ -3062,7 +3109,7 @@ class MaskedEditMixin:
                 and value[sel_to] == ' ' and key == wx.WXK_DELETE and not field._insertRight) ):
             if not wx.Validator_IsSilent():
                 wx.Bell()
                 and value[sel_to] == ' ' and key == wx.WXK_DELETE and not field._insertRight) ):
             if not wx.Validator_IsSilent():
                 wx.Bell()
-            dbg(indent=0)
+##            dbg(indent=0)
             return False
 
 
             return False
 
 
@@ -3077,7 +3124,7 @@ class MaskedEditMixin:
                     and (sel_to == end                          # and selection ends at right edge
                          or sel_to < end and field._allowInsert)) ) ):  # or allow right insert at any point in field
 
                     and (sel_to == end                          # and selection ends at right edge
                          or sel_to < end and field._allowInsert)) ) ):  # or allow right insert at any point in field
 
-            dbg('delete left')
+##            dbg('delete left')
             # if backspace but left of cursor is empty, adjust cursor right before deleting
             while( key == wx.WXK_BACK
                    and sel_start == sel_to
             # if backspace but left of cursor is empty, adjust cursor right before deleting
             while( key == wx.WXK_BACK
                    and sel_start == sel_to
@@ -3086,7 +3133,7 @@ class MaskedEditMixin:
                 sel_start += 1
                 sel_to = sel_start
 
                 sel_start += 1
                 sel_to = sel_start
 
-            dbg('sel_start, start:', sel_start, start)
+##            dbg('sel_start, start:', sel_start, start)
 
             if sel_start == sel_to:
                 keep = sel_start -1
 
             if sel_start == sel_to:
                 keep = sel_start -1
@@ -3100,7 +3147,7 @@ class MaskedEditMixin:
                 signchar = value[0]
                 newfield = signchar + newfield
                 move_sign_into_field = True
                 signchar = value[0]
                 newfield = signchar + newfield
                 move_sign_into_field = True
-            dbg('cut newfield: "%s"' % newfield)
+##            dbg('cut newfield: "%s"' % newfield)
 
             # handle what should fill in from the left:
             left = ""
 
             # handle what should fill in from the left:
             left = ""
@@ -3114,7 +3161,7 @@ class MaskedEditMixin:
                 else:
                     left += self._template[i]   # this can produce strange results in combination with default values...
             newfield = left + newfield
                 else:
                     left += self._template[i]   # this can produce strange results in combination with default values...
             newfield = left + newfield
-            dbg('filled newfield: "%s"' % newfield)
+##            dbg('filled newfield: "%s"' % newfield)
 
             newstr = value[:start] + newfield + value[end:]
 
 
             newstr = value[:start] + newfield + value[end:]
 
@@ -3164,7 +3211,7 @@ class MaskedEditMixin:
                     erase_len = erase_to - newpos
 
                     left = value[start:newpos]
                     erase_len = erase_to - newpos
 
                     left = value[start:newpos]
-                    dbg("retained ='%s'" % value[erase_to:end], 'sel_to:', sel_to, "fill: '%s'" % self._template[end - erase_len:end])
+##                    dbg("retained ='%s'" % value[erase_to:end], 'sel_to:', sel_to, "fill: '%s'" % self._template[end - erase_len:end])
                     right = value[erase_to:end] + self._template[end-erase_len:end]
                     pos_adjust = 0
                     if field._alignRight:
                     right = value[erase_to:end] + self._template[end-erase_len:end]
                     pos_adjust = 0
                     if field._alignRight:
@@ -3182,24 +3229,24 @@ class MaskedEditMixin:
                     if pos_adjust:
                         newfield = newfield.rjust(end-start)
                         newpos += pos_adjust
                     if pos_adjust:
                         newfield = newfield.rjust(end-start)
                         newpos += pos_adjust
-                    dbg("left='%s', right ='%s', newfield='%s'" %(left, right, newfield))
+##                    dbg("left='%s', right ='%s', newfield='%s'" %(left, right, newfield))
                     newstr = value[:start] + newfield + value[end:]
 
                 pos = newpos
 
             else:
                 if sel_start == sel_to:
                     newstr = value[:start] + newfield + value[end:]
 
                 pos = newpos
 
             else:
                 if sel_start == sel_to:
-                    dbg("current sel_start, sel_to:", sel_start, sel_to)
+##                    dbg("current sel_start, sel_to:", sel_start, sel_to)
                     if key == wx.WXK_BACK:
                         sel_start, sel_to = sel_to-1, sel_to-1
                     if key == wx.WXK_BACK:
                         sel_start, sel_to = sel_to-1, sel_to-1
-                        dbg("new sel_start, sel_to:", sel_start, sel_to)
+##                        dbg("new sel_start, sel_to:", sel_start, sel_to)
 
                     if field._padZero and not value[start:sel_to].replace('0', '').replace(' ','').replace(field._fillChar, ''):
                         # preceding chars (if any) are zeros, blanks or fillchar; new char should be 0:
                         newchar = '0'
                     else:
                         newchar = self._template[sel_to] ## get an original template character to "clear" the current char
 
                     if field._padZero and not value[start:sel_to].replace('0', '').replace(' ','').replace(field._fillChar, ''):
                         # preceding chars (if any) are zeros, blanks or fillchar; new char should be 0:
                         newchar = '0'
                     else:
                         newchar = self._template[sel_to] ## get an original template character to "clear" the current char
-                    dbg('value = "%s"' % value, 'value[%d] = "%s"' %(sel_start, value[sel_start]))
+##                    dbg('value = "%s"' % value, 'value[%d] = "%s"' %(sel_start, value[sel_start]))
 
                     if self._isTemplateChar(sel_to):
                         if sel_to == 0 and self._signOk and value[sel_to] == '-':   # erasing "template" sign char
 
                     if self._isTemplateChar(sel_to):
                         if sel_to == 0 and self._signOk and value[sel_to] == '-':   # erasing "template" sign char
@@ -3236,36 +3283,38 @@ class MaskedEditMixin:
                 # erased right-sign marker; get rid of left-sign marker:
                 newstr = newstr[:left_signpos] + ' ' + newstr[left_signpos+1:]
 
                 # erased right-sign marker; get rid of left-sign marker:
                 newstr = newstr[:left_signpos] + ' ' + newstr[left_signpos+1:]
 
-        dbg("oldstr:'%s'" % value, 'oldpos:', oldstart)
-        dbg("newstr:'%s'" % newstr, 'pos:', pos)
+##        dbg("oldstr:'%s'" % value, 'oldpos:', oldstart)
+##        dbg("newstr:'%s'" % newstr, 'pos:', pos)
 
         # if erasure results in an invalid field, disallow it:
 
         # if erasure results in an invalid field, disallow it:
-        dbg('field._validRequired?', field._validRequired)
-        dbg('field.IsValid("%s")?' % newstr[start:end], field.IsValid(newstr[start:end]))
+##        dbg('field._validRequired?', field._validRequired)
+##        dbg('field.IsValid("%s")?' % newstr[start:end], field.IsValid(newstr[start:end]))
         if field._validRequired and not field.IsValid(newstr[start:end]):
             if not wx.Validator_IsSilent():
                 wx.Bell()
         if field._validRequired and not field.IsValid(newstr[start:end]):
             if not wx.Validator_IsSilent():
                 wx.Bell()
-            dbg(indent=0)
+##            dbg(indent=0)
             return False
 
         # if erasure results in an invalid value, disallow it:
         if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
             if not wx.Validator_IsSilent():
                 wx.Bell()
             return False
 
         # if erasure results in an invalid value, disallow it:
         if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
             if not wx.Validator_IsSilent():
                 wx.Bell()
-            dbg(indent=0)
+##            dbg(indent=0)
             return False
 
             return False
 
-        dbg('setting value (later) to', newstr)
+##        dbg('setting value (later) to', newstr)
         wx.CallAfter(self._SetValue, newstr)
         wx.CallAfter(self._SetValue, newstr)
-        dbg('setting insertion point (later) to', pos)
+##        dbg('setting insertion point (later) to', pos)
         wx.CallAfter(self._SetInsertionPoint, pos)
         wx.CallAfter(self._SetInsertionPoint, pos)
-        dbg(indent=0)
+##        dbg(indent=0)
+        if newstr != value:
+            self.modified = True
         return False
 
 
     def _OnEnd(self,event):
         """ Handles End keypress in control. Should return False to skip other processing. """
         return False
 
 
     def _OnEnd(self,event):
         """ Handles End keypress in control. Should return False to skip other processing. """
-        dbg("MaskedEditMixin::_OnEnd", indent=1)
+##        dbg("MaskedEditMixin::_OnEnd", indent=1)
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         if not event.ControlDown():
             end = self._masklength  # go to end of control
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         if not event.ControlDown():
             end = self._masklength  # go to end of control
@@ -3282,48 +3331,51 @@ class MaskedEditMixin:
             # - cursor not in same field
             # - or at or past last input already
             # - or current selection = end of current field:
             # - cursor not in same field
             # - or at or past last input already
             # - or current selection = end of current field:
-##            dbg('field != field_end?', field != field_end)
-##            dbg('sel_to >= end_of_input?', sel_to >= end_of_input)
+####            dbg('field != field_end?', field != field_end)
+####            dbg('sel_to >= end_of_input?', sel_to >= end_of_input)
             if field != field_end or sel_to >= end_of_input:
                 edit_start, edit_end = field._extent
             if field != field_end or sel_to >= end_of_input:
                 edit_start, edit_end = field._extent
-##                dbg('edit_end:', edit_end)
-##                dbg('sel_to:', sel_to)
-##                dbg('sel_to == edit_end?', sel_to == edit_end)
-##                dbg('field._index < self._field_indices[-1]?', field._index < self._field_indices[-1])
+####                dbg('edit_end:', edit_end)
+####                dbg('sel_to:', sel_to)
+####                dbg('sel_to == edit_end?', sel_to == edit_end)
+####                dbg('field._index < self._field_indices[-1]?', field._index < self._field_indices[-1])
 
                 if sel_to == edit_end and field._index < self._field_indices[-1]:
                     edit_start, edit_end = self._FindFieldExtent(self._findNextEntry(edit_end))  # go to end of next field:
                     end = edit_end
 
                 if sel_to == edit_end and field._index < self._field_indices[-1]:
                     edit_start, edit_end = self._FindFieldExtent(self._findNextEntry(edit_end))  # go to end of next field:
                     end = edit_end
-                    dbg('end moved to', end)
+##                    dbg('end moved to', end)
 
                 elif sel_to == edit_end and field._index == self._field_indices[-1]:
                     # already at edit end of last field; select to end of control:
                     end = self._masklength
 
                 elif sel_to == edit_end and field._index == self._field_indices[-1]:
                     # already at edit end of last field; select to end of control:
                     end = self._masklength
-                    dbg('end moved to', end)
+##                    dbg('end moved to', end)
                 else:
                     end = edit_end  # select to end of current field
                 else:
                     end = edit_end  # select to end of current field
-                    dbg('end moved to ', end)
+##                    dbg('end moved to ', end)
             else:
                 # select to current end of input
                 end = end_of_input
 
 
             else:
                 # select to current end of input
                 end = end_of_input
 
 
-##        dbg('pos:', pos, 'end:', end)
+####        dbg('pos:', pos, 'end:', end)
 
         if event.ShiftDown():
             if not event.ControlDown():
 
         if event.ShiftDown():
             if not event.ControlDown():
-                dbg("shift-end; select to end of control")
+##                dbg("shift-end; select to end of control")
+                pass
             else:
             else:
-                dbg("shift-ctrl-end; select to end of non-whitespace")
+##                dbg("shift-ctrl-end; select to end of non-whitespace")
+                pass                
             wx.CallAfter(self._SetInsertionPoint, pos)
             wx.CallAfter(self._SetSelection, pos, end)
         else:
             if not event.ControlDown():
             wx.CallAfter(self._SetInsertionPoint, pos)
             wx.CallAfter(self._SetSelection, pos, end)
         else:
             if not event.ControlDown():
-                dbg('go to end of control:')
+##                dbg('go to end of control:')
+                pass
             wx.CallAfter(self._SetInsertionPoint, end)
             wx.CallAfter(self._SetSelection, end, end)
 
             wx.CallAfter(self._SetInsertionPoint, end)
             wx.CallAfter(self._SetSelection, end, end)
 
-        dbg(indent=0)
+##        dbg(indent=0)
         return False
 
 
         return False
 
 
@@ -3332,14 +3384,14 @@ class MaskedEditMixin:
          Changes the event to look like a tab event, so we can then call
          event.Skip() on it, and have the parent form "do the right thing."
          """
          Changes the event to look like a tab event, so we can then call
          event.Skip() on it, and have the parent form "do the right thing."
          """
-         dbg('MaskedEditMixin::OnReturn')
+##         dbg('MaskedEditMixin::OnReturn')
          event.m_keyCode = wx.WXK_TAB
          event.Skip()
 
 
     def _OnHome(self,event):
         """ Handles Home keypress in control. Should return False to skip other processing."""
          event.m_keyCode = wx.WXK_TAB
          event.Skip()
 
 
     def _OnHome(self,event):
         """ Handles Home keypress in control. Should return False to skip other processing."""
-        dbg("MaskedEditMixin::_OnHome", indent=1)
+##        dbg("MaskedEditMixin::_OnHome", indent=1)
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         sel_start, sel_to = self._GetSelection()
 
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         sel_start, sel_to = self._GetSelection()
 
@@ -3348,13 +3400,13 @@ class MaskedEditMixin:
         # 1) shift: select from start of control to end of current
         #    selection.
         if event.ShiftDown() and not event.ControlDown():
         # 1) shift: select from start of control to end of current
         #    selection.
         if event.ShiftDown() and not event.ControlDown():
-            dbg("shift-home; select to start of control")
+##            dbg("shift-home; select to start of control")
             start = 0
             end = sel_start
 
         # 2) no shift, no control: move cursor to beginning of control.
         elif not event.ControlDown():
             start = 0
             end = sel_start
 
         # 2) no shift, no control: move cursor to beginning of control.
         elif not event.ControlDown():
-            dbg("home; move to start of control")
+##            dbg("home; move to start of control")
             start = 0
             end = 0
 
             start = 0
             end = 0
 
@@ -3380,10 +3432,10 @@ class MaskedEditMixin:
                 start = 0
 
             if not event.ShiftDown():
                 start = 0
 
             if not event.ShiftDown():
-                dbg("ctrl-home; move to beginning of field")
+##                dbg("ctrl-home; move to beginning of field")
                 end = start
             else:
                 end = start
             else:
-                dbg("shift-ctrl-home; select to beginning of field")
+##                dbg("shift-ctrl-home; select to beginning of field")
                 end = sel_to
 
         else:
                 end = sel_to
 
         else:
@@ -3402,12 +3454,12 @@ class MaskedEditMixin:
             else:
                 end = start
                 end_of_field = False
             else:
                 end = start
                 end_of_field = False
-            dbg("shift-ctrl-home; unselect to beginning of field")
+##            dbg("shift-ctrl-home; unselect to beginning of field")
 
 
-        dbg('queuing new sel_start, sel_to:', (start, end))
+##        dbg('queuing new sel_start, sel_to:', (start, end))
         wx.CallAfter(self._SetInsertionPoint, start)
         wx.CallAfter(self._SetSelection, start, end)
         wx.CallAfter(self._SetInsertionPoint, start)
         wx.CallAfter(self._SetSelection, start, end)
-        dbg(indent=0)
+##        dbg(indent=0)
         return False
 
 
         return False
 
 
@@ -3419,19 +3471,19 @@ class MaskedEditMixin:
         control-shift-TAB, these events are not sent to the controls
         by the framework.
         """
         control-shift-TAB, these events are not sent to the controls
         by the framework.
         """
-        dbg('MaskedEditMixin::_OnChangeField', indent = 1)
+##        dbg('MaskedEditMixin::_OnChangeField', indent = 1)
         # determine end of current field:
         pos = self._GetInsertionPoint()
         # determine end of current field:
         pos = self._GetInsertionPoint()
-        dbg('current pos:', pos)
+##        dbg('current pos:', pos)
         sel_start, sel_to = self._GetSelection()
 
         if self._masklength < 0:   # no fields; process tab normally
             self._AdjustField(pos)
             if event.GetKeyCode() == wx.WXK_TAB:
         sel_start, sel_to = self._GetSelection()
 
         if self._masklength < 0:   # no fields; process tab normally
             self._AdjustField(pos)
             if event.GetKeyCode() == wx.WXK_TAB:
-                dbg('tab to next ctrl')
+##                dbg('tab to next ctrl')
                 event.Skip()
             #else: do nothing
                 event.Skip()
             #else: do nothing
-            dbg(indent=0)
+##            dbg(indent=0)
             return False
 
 
             return False
 
 
@@ -3446,41 +3498,41 @@ class MaskedEditMixin:
             index = field._index
             field_start = field._extent[0]
             if pos < field_start:
             index = field._index
             field_start = field._extent[0]
             if pos < field_start:
-                dbg('cursor before 1st field; cannot change to a previous field')
+##                dbg('cursor before 1st field; cannot change to a previous field')
                 if not wx.Validator_IsSilent():
                     wx.Bell()
                 return False
 
             if event.ControlDown():
                 if not wx.Validator_IsSilent():
                     wx.Bell()
                 return False
 
             if event.ControlDown():
-                dbg('queuing select to beginning of field:', field_start, pos)
+##                dbg('queuing select to beginning of field:', field_start, pos)
                 wx.CallAfter(self._SetInsertionPoint, field_start)
                 wx.CallAfter(self._SetSelection, field_start, pos)
                 wx.CallAfter(self._SetInsertionPoint, field_start)
                 wx.CallAfter(self._SetSelection, field_start, pos)
-                dbg(indent=0)
+##                dbg(indent=0)
                 return False
 
             elif index == 0:
                   # We're already in the 1st field; process shift-tab normally:
                 self._AdjustField(pos)
                 if event.GetKeyCode() == wx.WXK_TAB:
                 return False
 
             elif index == 0:
                   # We're already in the 1st field; process shift-tab normally:
                 self._AdjustField(pos)
                 if event.GetKeyCode() == wx.WXK_TAB:
-                    dbg('tab to previous ctrl')
+##                    dbg('tab to previous ctrl')
                     event.Skip()
                 else:
                     event.Skip()
                 else:
-                    dbg('position at beginning')
+##                    dbg('position at beginning')
                     wx.CallAfter(self._SetInsertionPoint, field_start)
                     wx.CallAfter(self._SetInsertionPoint, field_start)
-                dbg(indent=0)
+##                dbg(indent=0)
                 return False
             else:
                 # find beginning of previous field:
                 begin_prev = self._FindField(field_start-1)._extent[0]
                 self._AdjustField(pos)
                 return False
             else:
                 # find beginning of previous field:
                 begin_prev = self._FindField(field_start-1)._extent[0]
                 self._AdjustField(pos)
-                dbg('repositioning to', begin_prev)
+##                dbg('repositioning to', begin_prev)
                 wx.CallAfter(self._SetInsertionPoint, begin_prev)
                 if self._FindField(begin_prev)._selectOnFieldEntry:
                     edit_start, edit_end = self._FindFieldExtent(begin_prev)
                 wx.CallAfter(self._SetInsertionPoint, begin_prev)
                 if self._FindField(begin_prev)._selectOnFieldEntry:
                     edit_start, edit_end = self._FindFieldExtent(begin_prev)
-                    dbg('queuing selection to (%d, %d)' % (edit_start, edit_end))
+##                    dbg('queuing selection to (%d, %d)' % (edit_start, edit_end))
                     wx.CallAfter(self._SetInsertionPoint, edit_start)
                     wx.CallAfter(self._SetSelection, edit_start, edit_end)
                     wx.CallAfter(self._SetInsertionPoint, edit_start)
                     wx.CallAfter(self._SetSelection, edit_start, edit_end)
-                dbg(indent=0)
+##                dbg(indent=0)
                 return False
 
         else:
                 return False
 
         else:
@@ -3488,14 +3540,14 @@ class MaskedEditMixin:
             field = self._FindField(sel_to)
             field_start, field_end = field._extent
             if event.ControlDown():
             field = self._FindField(sel_to)
             field_start, field_end = field._extent
             if event.ControlDown():
-                dbg('queuing select to end of field:', pos, field_end)
+##                dbg('queuing select to end of field:', pos, field_end)
                 wx.CallAfter(self._SetInsertionPoint, pos)
                 wx.CallAfter(self._SetSelection, pos, field_end)
                 wx.CallAfter(self._SetInsertionPoint, pos)
                 wx.CallAfter(self._SetSelection, pos, field_end)
-                dbg(indent=0)
+##                dbg(indent=0)
                 return False
             else:
                 if pos < field_start:
                 return False
             else:
                 if pos < field_start:
-                    dbg('cursor before 1st field; go to start of field')
+##                    dbg('cursor before 1st field; go to start of field')
                     wx.CallAfter(self._SetInsertionPoint, field_start)
                     if field._selectOnFieldEntry:
                         wx.CallAfter(self._SetSelection, field_start, field_end)
                     wx.CallAfter(self._SetInsertionPoint, field_start)
                     if field._selectOnFieldEntry:
                         wx.CallAfter(self._SetSelection, field_start, field_end)
@@ -3503,29 +3555,29 @@ class MaskedEditMixin:
                         wx.CallAfter(self._SetSelection, field_start, field_start)
                     return False
                 # else...
                         wx.CallAfter(self._SetSelection, field_start, field_start)
                     return False
                 # else...
-                dbg('end of current field:', field_end)
-                dbg('go to next field')
+##                dbg('end of current field:', field_end)
+##                dbg('go to next field')
                 if field_end == self._fields[self._field_indices[-1]]._extent[1]:
                     self._AdjustField(pos)
                     if event.GetKeyCode() == wx.WXK_TAB:
                 if field_end == self._fields[self._field_indices[-1]]._extent[1]:
                     self._AdjustField(pos)
                     if event.GetKeyCode() == wx.WXK_TAB:
-                        dbg('tab to next ctrl')
+##                        dbg('tab to next ctrl')
                         event.Skip()
                     else:
                         event.Skip()
                     else:
-                        dbg('position at end')
+##                        dbg('position at end')
                         wx.CallAfter(self._SetInsertionPoint, field_end)
                         wx.CallAfter(self._SetInsertionPoint, field_end)
-                    dbg(indent=0)
+##                    dbg(indent=0)
                     return False
                 else:
                     # we have to find the start of the next field
                     next_pos = self._findNextEntry(field_end)
                     if next_pos == field_end:
                     return False
                 else:
                     # we have to find the start of the next field
                     next_pos = self._findNextEntry(field_end)
                     if next_pos == field_end:
-                        dbg('already in last field')
+##                        dbg('already in last field')
                         self._AdjustField(pos)
                         if event.GetKeyCode() == wx.WXK_TAB:
                         self._AdjustField(pos)
                         if event.GetKeyCode() == wx.WXK_TAB:
-                            dbg('tab to next ctrl')
+##                            dbg('tab to next ctrl')
                             event.Skip()
                         #else: do nothing
                             event.Skip()
                         #else: do nothing
-                        dbg(indent=0)
+##                        dbg(indent=0)
                         return False
                     else:
                         self._AdjustField( pos )
                         return False
                     else:
                         self._AdjustField( pos )
@@ -3534,31 +3586,31 @@ class MaskedEditMixin:
                         field = self._FindField(next_pos)
                         edit_start, edit_end = field._extent
                         if field._selectOnFieldEntry:
                         field = self._FindField(next_pos)
                         edit_start, edit_end = field._extent
                         if field._selectOnFieldEntry:
-                            dbg('move to ', next_pos)
+##                            dbg('move to ', next_pos)
                             wx.CallAfter(self._SetInsertionPoint, next_pos)
                             edit_start, edit_end = self._FindFieldExtent(next_pos)
                             wx.CallAfter(self._SetInsertionPoint, next_pos)
                             edit_start, edit_end = self._FindFieldExtent(next_pos)
-                            dbg('queuing select', edit_start, edit_end)
+##                            dbg('queuing select', edit_start, edit_end)
                             wx.CallAfter(self._SetSelection, edit_start, edit_end)
                         else:
                             if field._insertRight:
                                 next_pos = field._extent[1]
                             wx.CallAfter(self._SetSelection, edit_start, edit_end)
                         else:
                             if field._insertRight:
                                 next_pos = field._extent[1]
-                            dbg('move to ', next_pos)
+##                            dbg('move to ', next_pos)
                             wx.CallAfter(self._SetInsertionPoint, next_pos)
                             wx.CallAfter(self._SetInsertionPoint, next_pos)
-                        dbg(indent=0)
+##                        dbg(indent=0)
                         return False
 
 
     def _OnDecimalPoint(self, event):
                         return False
 
 
     def _OnDecimalPoint(self, event):
-        dbg('MaskedEditMixin::_OnDecimalPoint', indent=1)
+##        dbg('MaskedEditMixin::_OnDecimalPoint', indent=1)
 
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
 
         if self._isFloat:       ## handle float value, move to decimal place
 
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
 
         if self._isFloat:       ## handle float value, move to decimal place
-            dbg('key == Decimal tab; decimal pos:', self._decimalpos)
+##            dbg('key == Decimal tab; decimal pos:', self._decimalpos)
             value = self._GetValue()
             if pos < self._decimalpos:
                 clipped_text = value[0:pos] + self._decimalChar + value[self._decimalpos+1:]
             value = self._GetValue()
             if pos < self._decimalpos:
                 clipped_text = value[0:pos] + self._decimalChar + value[self._decimalpos+1:]
-                dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
+##                dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
                 newstr = self._adjustFloat(clipped_text)
             else:
                 newstr = self._adjustFloat(value)
                 newstr = self._adjustFloat(clipped_text)
             else:
                 newstr = self._adjustFloat(value)
@@ -3567,46 +3619,46 @@ class MaskedEditMixin:
             start, end = fraction._extent
             wx.CallAfter(self._SetInsertionPoint, start)
             if fraction._selectOnFieldEntry:
             start, end = fraction._extent
             wx.CallAfter(self._SetInsertionPoint, start)
             if fraction._selectOnFieldEntry:
-                dbg('queuing selection after decimal point to:', (start, end))
+##                dbg('queuing selection after decimal point to:', (start, end))
                 wx.CallAfter(self._SetSelection, start, end)
             keep_processing = False
 
         if self._isInt:      ## handle integer value, truncate from current position
                 wx.CallAfter(self._SetSelection, start, end)
             keep_processing = False
 
         if self._isInt:      ## handle integer value, truncate from current position
-            dbg('key == Integer decimal event')
+##            dbg('key == Integer decimal event')
             value = self._GetValue()
             clipped_text = value[0:pos]
             value = self._GetValue()
             clipped_text = value[0:pos]
-            dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
+##            dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
             newstr = self._adjustInt(clipped_text)
             newstr = self._adjustInt(clipped_text)
-            dbg('newstr: "%s"' % newstr)
+##            dbg('newstr: "%s"' % newstr)
             wx.CallAfter(self._SetValue, newstr)
             newpos = len(newstr.rstrip())
             if newstr.find(')') != -1:
                 newpos -= 1     # (don't move past right paren)
             wx.CallAfter(self._SetInsertionPoint, newpos)
             keep_processing = False
             wx.CallAfter(self._SetValue, newstr)
             newpos = len(newstr.rstrip())
             if newstr.find(')') != -1:
                 newpos -= 1     # (don't move past right paren)
             wx.CallAfter(self._SetInsertionPoint, newpos)
             keep_processing = False
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _OnChangeSign(self, event):
 
 
     def _OnChangeSign(self, event):
-        dbg('MaskedEditMixin::_OnChangeSign', indent=1)
+##        dbg('MaskedEditMixin::_OnChangeSign', indent=1)
         key = event.GetKeyCode()
         pos = self._adjustPos(self._GetInsertionPoint(), key)
         value = self._eraseSelection()
         integer = self._fields[0]
         start, end = integer._extent
 
         key = event.GetKeyCode()
         pos = self._adjustPos(self._GetInsertionPoint(), key)
         value = self._eraseSelection()
         integer = self._fields[0]
         start, end = integer._extent
 
-##        dbg('adjusted pos:', pos)
+####        dbg('adjusted pos:', pos)
         if chr(key) in ('-','+','(', ')') or (chr(key) == " " and pos == self._signpos):
             cursign = self._isNeg
         if chr(key) in ('-','+','(', ')') or (chr(key) == " " and pos == self._signpos):
             cursign = self._isNeg
-            dbg('cursign:', cursign)
+##            dbg('cursign:', cursign)
             if chr(key) in ('-','(', ')'):
                 self._isNeg = (not self._isNeg)   ## flip value
             else:
                 self._isNeg = False
             if chr(key) in ('-','(', ')'):
                 self._isNeg = (not self._isNeg)   ## flip value
             else:
                 self._isNeg = False
-            dbg('isNeg?', self._isNeg)
+##            dbg('isNeg?', self._isNeg)
 
             text, self._signpos, self._right_signpos = self._getSignedValue(candidate=value)
 
             text, self._signpos, self._right_signpos = self._getSignedValue(candidate=value)
-            dbg('text:"%s"' % text, 'signpos:', self._signpos, 'right_signpos:', self._right_signpos)
+##            dbg('text:"%s"' % text, 'signpos:', self._signpos, 'right_signpos:', self._right_signpos)
             if text is None:
                 text = value
 
             if text is None:
                 text = value
 
@@ -3616,17 +3668,17 @@ class MaskedEditMixin:
                 else:
                     text = text[:self._signpos] + '-' + text[self._signpos+1:]
             else:
                 else:
                     text = text[:self._signpos] + '-' + text[self._signpos+1:]
             else:
-##                dbg('self._isNeg?', self._isNeg, 'self.IsValid(%s)' % text, self.IsValid(text))
+####                dbg('self._isNeg?', self._isNeg, 'self.IsValid(%s)' % text, self.IsValid(text))
                 if self._useParens:
                     text = text[:self._signpos] + ' ' + text[self._signpos+1:self._right_signpos] + ' ' + text[self._right_signpos+1:]
                 else:
                     text = text[:self._signpos] + ' ' + text[self._signpos+1:]
                 if self._useParens:
                     text = text[:self._signpos] + ' ' + text[self._signpos+1:self._right_signpos] + ' ' + text[self._right_signpos+1:]
                 else:
                     text = text[:self._signpos] + ' ' + text[self._signpos+1:]
-                dbg('clearing self._isNeg')
+##                dbg('clearing self._isNeg')
                 self._isNeg = False
 
             wx.CallAfter(self._SetValue, text)
             wx.CallAfter(self._applyFormatting)
                 self._isNeg = False
 
             wx.CallAfter(self._SetValue, text)
             wx.CallAfter(self._applyFormatting)
-            dbg('pos:', pos, 'signpos:', self._signpos)
+##            dbg('pos:', pos, 'signpos:', self._signpos)
             if pos == self._signpos or integer.IsEmpty(text[start:end]):
                 wx.CallAfter(self._SetInsertionPoint, self._signpos+1)
             else:
             if pos == self._signpos or integer.IsEmpty(text[start:end]):
                 wx.CallAfter(self._SetInsertionPoint, self._signpos+1)
             else:
@@ -3635,7 +3687,7 @@ class MaskedEditMixin:
             keep_processing = False
         else:
             keep_processing = True
             keep_processing = False
         else:
             keep_processing = True
-        dbg(indent=0)
+##        dbg(indent=0)
         return keep_processing
 
 
         return keep_processing
 
 
@@ -3644,7 +3696,7 @@ class MaskedEditMixin:
         This handler is only registered if the mask is a numeric mask.
         It allows the insertion of ',' or '.' if appropriate.
         """
         This handler is only registered if the mask is a numeric mask.
         It allows the insertion of ',' or '.' if appropriate.
         """
-        dbg('MaskedEditMixin::_OnGroupChar', indent=1)
+##        dbg('MaskedEditMixin::_OnGroupChar', indent=1)
         keep_processing = True
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         sel_start, sel_to = self._GetSelection()
         keep_processing = True
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         sel_start, sel_to = self._GetSelection()
@@ -3656,7 +3708,7 @@ class MaskedEditMixin:
 
         if keep_processing:
             newstr, newpos = self._insertKey(groupchar, pos, sel_start, sel_to, self._GetValue() )
 
         if keep_processing:
             newstr, newpos = self._insertKey(groupchar, pos, sel_start, sel_to, self._GetValue() )
-            dbg("str with '%s' inserted:" % groupchar, '"%s"' % newstr)
+##            dbg("str with '%s' inserted:" % groupchar, '"%s"' % newstr)
             if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
                 keep_processing = False
                 if not wx.Validator_IsSilent():
             if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
                 keep_processing = False
                 if not wx.Validator_IsSilent():
@@ -3666,7 +3718,7 @@ class MaskedEditMixin:
             wx.CallAfter(self._SetValue, newstr)
             wx.CallAfter(self._SetInsertionPoint, newpos)
         keep_processing = False
             wx.CallAfter(self._SetValue, newstr)
             wx.CallAfter(self._SetInsertionPoint, newpos)
         keep_processing = False
-        dbg(indent=0)
+##        dbg(indent=0)
         return keep_processing
 
 
         return keep_processing
 
 
@@ -3700,7 +3752,7 @@ class MaskedEditMixin:
 
 
     def _OnAutoCompleteField(self, event):
 
 
     def _OnAutoCompleteField(self, event):
-        dbg('MaskedEditMixin::_OnAutoCompleteField', indent =1)
+##        dbg('MaskedEditMixin::_OnAutoCompleteField', indent =1)
         pos = self._GetInsertionPoint()
         field = self._FindField(pos)
         edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
         pos = self._GetInsertionPoint()
         field = self._FindField(pos)
         edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
@@ -3714,10 +3766,10 @@ class MaskedEditMixin:
             text = slice
         text = text.strip()
         keep_processing = True  # (assume True to start)
             text = slice
         text = text.strip()
         keep_processing = True  # (assume True to start)
-        dbg('field._hasList?', field._hasList)
+##        dbg('field._hasList?', field._hasList)
         if field._hasList:
         if field._hasList:
-            dbg('choices:', field._choices)
-            dbg('compareChoices:', field._compareChoices)
+##            dbg('choices:', field._choices)
+##            dbg('compareChoices:', field._compareChoices)
             choices, choice_required = field._compareChoices, field._choiceRequired
             if keycode in (wx.WXK_PRIOR, wx.WXK_UP):
                 direction = -1
             choices, choice_required = field._compareChoices, field._choiceRequired
             if keycode in (wx.WXK_PRIOR, wx.WXK_UP):
                 direction = -1
@@ -3736,10 +3788,10 @@ class MaskedEditMixin:
                       or (keycode == wx.WXK_DOWN and partial_match) ) ):
 
                 # We're allowed to auto-complete:
                       or (keycode == wx.WXK_DOWN and partial_match) ) ):
 
                 # We're allowed to auto-complete:
-                dbg('match found')
+##                dbg('match found')
                 value = self._GetValue()
                 newvalue = value[:edit_start] + field._choices[match_index] + value[edit_end:]
                 value = self._GetValue()
                 newvalue = value[:edit_start] + field._choices[match_index] + value[edit_end:]
-                dbg('setting value to "%s"' % newvalue)
+##                dbg('setting value to "%s"' % newvalue)
                 self._SetValue(newvalue)
                 self._SetInsertionPoint(min(edit_end, len(newvalue.rstrip())))
                 self._OnAutoSelect(field, match_index)
                 self._SetValue(newvalue)
                 self._SetInsertionPoint(min(edit_end, len(newvalue.rstrip())))
                 self._OnAutoSelect(field, match_index)
@@ -3757,7 +3809,7 @@ class MaskedEditMixin:
                 keep_processing = self._OnArrow(event)
         # else some other key; keep processing the key
 
                 keep_processing = self._OnArrow(event)
         # else some other key; keep processing the key
 
-        dbg('keep processing?', keep_processing, indent=0)
+##        dbg('keep processing?', keep_processing, indent=0)
         return keep_processing
 
 
         return keep_processing
 
 
@@ -3766,7 +3818,7 @@ class MaskedEditMixin:
         Function called if autoselect feature is enabled and entire control
         is selected:
         """
         Function called if autoselect feature is enabled and entire control
         is selected:
         """
-        dbg('MaskedEditMixin::OnAutoSelect', field._index)
+##        dbg('MaskedEditMixin::OnAutoSelect', field._index)
         if match_index is not None:
             field._autoCompleteIndex = match_index
 
         if match_index is not None:
             field._autoCompleteIndex = match_index
 
@@ -3783,9 +3835,9 @@ class MaskedEditMixin:
         The function returns a 2-tuple, with the 2nd element being a boolean
         that indicates if partial match was necessary.
         """
         The function returns a 2-tuple, with the 2nd element being a boolean
         that indicates if partial match was necessary.
         """
-        dbg('autoComplete(direction=', direction, 'choices=',choices, 'value=',value,'compareNoCase?', compareNoCase, 'current_index:', current_index, indent=1)
+##        dbg('autoComplete(direction=', direction, 'choices=',choices, 'value=',value,'compareNoCase?', compareNoCase, 'current_index:', current_index, indent=1)
         if value is None:
         if value is None:
-            dbg('nothing to match against', indent=0)
+##            dbg('nothing to match against', indent=0)
             return (None, False)
 
         partial_match = False
             return (None, False)
 
         partial_match = False
@@ -3795,28 +3847,28 @@ class MaskedEditMixin:
 
         last_index = len(choices) - 1
         if value in choices:
 
         last_index = len(choices) - 1
         if value in choices:
-            dbg('"%s" in', choices)
+##            dbg('"%s" in', choices)
             if current_index is not None and choices[current_index] == value:
                 index = current_index
             else:
                 index = choices.index(value)
 
             if current_index is not None and choices[current_index] == value:
                 index = current_index
             else:
                 index = choices.index(value)
 
-            dbg('matched "%s" (%d)' % (choices[index], index))
+##            dbg('matched "%s" (%d)' % (choices[index], index))
             if direction == -1:
             if direction == -1:
-                dbg('going to previous')
+##                dbg('going to previous')
                 if index == 0: index = len(choices) - 1
                 else: index -= 1
             else:
                 if index == len(choices) - 1: index = 0
                 else: index += 1
                 if index == 0: index = len(choices) - 1
                 else: index -= 1
             else:
                 if index == len(choices) - 1: index = 0
                 else: index += 1
-            dbg('change value to "%s" (%d)' % (choices[index], index))
+##            dbg('change value to "%s" (%d)' % (choices[index], index))
             match = index
         else:
             partial_match = True
             value = value.strip()
             match = index
         else:
             partial_match = True
             value = value.strip()
-            dbg('no match; try to auto-complete:')
+##            dbg('no match; try to auto-complete:')
             match = None
             match = None
-            dbg('searching for "%s"' % value)
+##            dbg('searching for "%s"' % value)
             if current_index is None:
                 indices = range(len(choices))
                 if direction == -1:
             if current_index is None:
                 indices = range(len(choices))
                 if direction == -1:
@@ -3824,23 +3876,25 @@ class MaskedEditMixin:
             else:
                 if direction == 1:
                     indices = range(current_index +1, len(choices)) + range(current_index+1)
             else:
                 if direction == 1:
                     indices = range(current_index +1, len(choices)) + range(current_index+1)
-                    dbg('range(current_index+1 (%d), len(choices) (%d)) + range(%d):' % (current_index+1, len(choices), current_index+1), indices)
+##                    dbg('range(current_index+1 (%d), len(choices) (%d)) + range(%d):' % (current_index+1, len(choices), current_index+1), indices)
                 else:
                     indices = range(current_index-1, -1, -1) + range(len(choices)-1, current_index-1, -1)
                 else:
                     indices = range(current_index-1, -1, -1) + range(len(choices)-1, current_index-1, -1)
-                    dbg('range(current_index-1 (%d), -1) + range(len(choices)-1 (%d)), current_index-1 (%d):' % (current_index-1, len(choices)-1, current_index-1), indices)
-##            dbg('indices:', indices)
+##                    dbg('range(current_index-1 (%d), -1) + range(len(choices)-1 (%d)), current_index-1 (%d):' % (current_index-1, len(choices)-1, current_index-1), indices)
+####            dbg('indices:', indices)
             for index in indices:
                 choice = choices[index]
                 if choice.find(value, 0) == 0:
             for index in indices:
                 choice = choices[index]
                 if choice.find(value, 0) == 0:
-                    dbg('match found:', choice)
+##                    dbg('match found:', choice)
                     match = index
                     break
                 else: dbg('choice: "%s" - no match' % choice)
             if match is not None:
                     match = index
                     break
                 else: dbg('choice: "%s" - no match' % choice)
             if match is not None:
-                dbg('matched', match)
+##                dbg('matched', match)
+                pass
             else:
             else:
-                dbg('no match found')
-        dbg(indent=0)
+##                dbg('no match found')
+                pass
+##        dbg(indent=0)
         return (match, partial_match)
 
 
         return (match, partial_match)
 
 
@@ -3897,31 +3951,31 @@ class MaskedEditMixin:
         Checks the current insertion point position and adjusts it if
         necessary to skip over non-editable characters.
         """
         Checks the current insertion point position and adjusts it if
         necessary to skip over non-editable characters.
         """
-        dbg('_adjustPos', pos, key, indent=1)
+##        dbg('_adjustPos', pos, key, indent=1)
         sel_start, sel_to = self._GetSelection()
         # If a numeric or decimal mask, and negatives allowed, reserve the
         # first space for sign, and last one if using parens.
         if( self._signOk
             and ((pos == self._signpos and key in (ord('-'), ord('+'), ord(' ')) )
                  or self._useParens and pos == self._masklength -1)):
         sel_start, sel_to = self._GetSelection()
         # If a numeric or decimal mask, and negatives allowed, reserve the
         # first space for sign, and last one if using parens.
         if( self._signOk
             and ((pos == self._signpos and key in (ord('-'), ord('+'), ord(' ')) )
                  or self._useParens and pos == self._masklength -1)):
-            dbg('adjusted pos:', pos, indent=0)
+##            dbg('adjusted pos:', pos, indent=0)
             return pos
 
         if key not in self._nav:
             field = self._FindField(pos)
 
             return pos
 
         if key not in self._nav:
             field = self._FindField(pos)
 
-            dbg('field._insertRight?', field._insertRight)
+##            dbg('field._insertRight?', field._insertRight)
             if field._insertRight:              # if allow right-insert
                 start, end = field._extent
                 slice = self._GetValue()[start:end].strip()
                 field_len = end - start
                 if pos == end:                      # if cursor at right edge of field
                     # if not filled or supposed to stay in field, keep current position
             if field._insertRight:              # if allow right-insert
                 start, end = field._extent
                 slice = self._GetValue()[start:end].strip()
                 field_len = end - start
                 if pos == end:                      # if cursor at right edge of field
                     # if not filled or supposed to stay in field, keep current position
-##                    dbg('pos==end')
-##                    dbg('len (slice):', len(slice))
-##                    dbg('field_len?', field_len)
-##                    dbg('pos==end; len (slice) < field_len?', len(slice) < field_len)
-##                    dbg('not field._moveOnFieldFull?', not field._moveOnFieldFull)
+####                    dbg('pos==end')
+####                    dbg('len (slice):', len(slice))
+####                    dbg('field_len?', field_len)
+####                    dbg('pos==end; len (slice) < field_len?', len(slice) < field_len)
+####                    dbg('not field._moveOnFieldFull?', not field._moveOnFieldFull)
                     if len(slice) == field_len and field._moveOnFieldFull:
                         # move cursor to next field:
                         pos = self._findNextEntry(pos)
                     if len(slice) == field_len and field._moveOnFieldFull:
                         # move cursor to next field:
                         pos = self._findNextEntry(pos)
@@ -3967,7 +4021,7 @@ class MaskedEditMixin:
                     self._SetInsertionPoint(pos)
                     if pos < sel_to:    # restore selection
                         self._SetSelection(pos, sel_to)
                     self._SetInsertionPoint(pos)
                     if pos < sel_to:    # restore selection
                         self._SetSelection(pos, sel_to)
-        dbg('adjusted pos:', pos, indent=0)
+##        dbg('adjusted pos:', pos, indent=0)
         return pos
 
 
         return pos
 
 
@@ -3975,24 +4029,24 @@ class MaskedEditMixin:
         """
         'Fixes' an floating point control. Collapses spaces, right-justifies, etc.
         """
         """
         'Fixes' an floating point control. Collapses spaces, right-justifies, etc.
         """
-        dbg('MaskedEditMixin::_adjustFloat, candidate = "%s"' % candidate, indent=1)
+##        dbg('MaskedEditMixin::_adjustFloat, candidate = "%s"' % candidate, indent=1)
         lenInt,lenFraction  = [len(s) for s in self._mask.split('.')]  ## Get integer, fraction lengths
 
         if candidate is None: value = self._GetValue()
         else: value = candidate
         lenInt,lenFraction  = [len(s) for s in self._mask.split('.')]  ## Get integer, fraction lengths
 
         if candidate is None: value = self._GetValue()
         else: value = candidate
-        dbg('value = "%(value)s"' % locals(), 'len(value):', len(value))
+##        dbg('value = "%(value)s"' % locals(), 'len(value):', len(value))
         intStr, fracStr = value.split(self._decimalChar)
 
         intStr = self._fields[0]._AdjustField(intStr)
         intStr, fracStr = value.split(self._decimalChar)
 
         intStr = self._fields[0]._AdjustField(intStr)
-        dbg('adjusted intStr: "%s"' % intStr)
+##        dbg('adjusted intStr: "%s"' % intStr)
         lenInt = len(intStr)
         fracStr = fracStr + ('0'*(lenFraction-len(fracStr)))  # add trailing spaces to decimal
 
         lenInt = len(intStr)
         fracStr = fracStr + ('0'*(lenFraction-len(fracStr)))  # add trailing spaces to decimal
 
-        dbg('intStr "%(intStr)s"' % locals())
-        dbg('lenInt:', lenInt)
+##        dbg('intStr "%(intStr)s"' % locals())
+##        dbg('lenInt:', lenInt)
 
         intStr = string.rjust( intStr[-lenInt:], lenInt)
 
         intStr = string.rjust( intStr[-lenInt:], lenInt)
-        dbg('right-justifed intStr = "%(intStr)s"' % locals())
+##        dbg('right-justifed intStr = "%(intStr)s"' % locals())
         newvalue = intStr + self._decimalChar + fracStr
 
         if self._signOk:
         newvalue = intStr + self._decimalChar + fracStr
 
         if self._signOk:
@@ -4014,23 +4068,23 @@ class MaskedEditMixin:
             else:
                 newvalue = newvalue[:-1] + ' '
 
             else:
                 newvalue = newvalue[:-1] + ' '
 
-        dbg('newvalue = "%s"' % newvalue)
+##        dbg('newvalue = "%s"' % newvalue)
         if candidate is None:
             wx.CallAfter(self._SetValue, newvalue)
         if candidate is None:
             wx.CallAfter(self._SetValue, newvalue)
-        dbg(indent=0)
+##        dbg(indent=0)
         return newvalue
 
 
     def _adjustInt(self, candidate=None):
         """ 'Fixes' an integer control. Collapses spaces, right or left-justifies."""
         return newvalue
 
 
     def _adjustInt(self, candidate=None):
         """ 'Fixes' an integer control. Collapses spaces, right or left-justifies."""
-        dbg("MaskedEditMixin::_adjustInt", candidate)
+##        dbg("MaskedEditMixin::_adjustInt", candidate)
         lenInt = self._masklength
         if candidate is None: value = self._GetValue()
         else: value = candidate
 
         intStr = self._fields[0]._AdjustField(value)
         intStr = intStr.strip() # drop extra spaces
         lenInt = self._masklength
         if candidate is None: value = self._GetValue()
         else: value = candidate
 
         intStr = self._fields[0]._AdjustField(value)
         intStr = intStr.strip() # drop extra spaces
-        dbg('adjusted field: "%s"' % intStr)
+##        dbg('adjusted field: "%s"' % intStr)
 
         if self._isNeg and intStr.find('-') == -1 and intStr.find('(') == -1:
             if self._useParens:
 
         if self._isNeg and intStr.find('-') == -1 and intStr.find('(') == -1:
             if self._useParens:
@@ -4065,20 +4119,20 @@ class MaskedEditMixin:
         'Fixes' a date control, expanding the year if it can.
         Applies various self-formatting options.
         """
         'Fixes' a date control, expanding the year if it can.
         Applies various self-formatting options.
         """
-        dbg("MaskedEditMixin::_adjustDate", indent=1)
+##        dbg("MaskedEditMixin::_adjustDate", indent=1)
         if candidate is None: text    = self._GetValue()
         else: text = candidate
         if candidate is None: text    = self._GetValue()
         else: text = candidate
-        dbg('text=', text)
+##        dbg('text=', text)
         if self._datestyle == "YMD":
             year_field = 0
         else:
             year_field = 2
 
         if self._datestyle == "YMD":
             year_field = 0
         else:
             year_field = 2
 
-        dbg('getYear: "%s"' % getYear(text, self._datestyle))
+##        dbg('getYear: "%s"' % getYear(text, self._datestyle))
         year    = string.replace( getYear( text, self._datestyle),self._fields[year_field]._fillChar,"")  # drop extra fillChars
         month   = getMonth( text, self._datestyle)
         day     = getDay( text, self._datestyle)
         year    = string.replace( getYear( text, self._datestyle),self._fields[year_field]._fillChar,"")  # drop extra fillChars
         month   = getMonth( text, self._datestyle)
         day     = getDay( text, self._datestyle)
-        dbg('self._datestyle:', self._datestyle, 'year:', year, 'Month', month, 'day:', day)
+##        dbg('self._datestyle:', self._datestyle, 'year:', year, 'Month', month, 'day:', day)
 
         yearVal = None
         yearstart = self._dateExtent - 4
 
         yearVal = None
         yearstart = self._dateExtent - 4
@@ -4092,7 +4146,7 @@ class MaskedEditMixin:
             try:
                 yearVal = int(year)
             except:
             try:
                 yearVal = int(year)
             except:
-                dbg('bad year=', year)
+##                dbg('bad year=', year)
                 year = text[yearstart:self._dateExtent]
 
         if len(year) < 4 and yearVal:
                 year = text[yearstart:self._dateExtent]
 
         if len(year) < 4 and yearVal:
@@ -4119,22 +4173,22 @@ class MaskedEditMixin:
                 year = "%04d" % yearVal
             if self._4digityear or force4digit_year:
                 text = makeDate(year, month, day, self._datestyle, text) + text[self._dateExtent:]
                 year = "%04d" % yearVal
             if self._4digityear or force4digit_year:
                 text = makeDate(year, month, day, self._datestyle, text) + text[self._dateExtent:]
-        dbg('newdate: "%s"' % text, indent=0)
+##        dbg('newdate: "%s"' % text, indent=0)
         return text
 
 
     def _goEnd(self, getPosOnly=False):
         """ Moves the insertion point to the end of user-entry """
         return text
 
 
     def _goEnd(self, getPosOnly=False):
         """ Moves the insertion point to the end of user-entry """
-        dbg("MaskedEditMixin::_goEnd; getPosOnly:", getPosOnly, indent=1)
+##        dbg("MaskedEditMixin::_goEnd; getPosOnly:", getPosOnly, indent=1)
         text = self._GetValue()
         text = self._GetValue()
-##        dbg('text: "%s"' % text)
+####        dbg('text: "%s"' % text)
         i = 0
         if len(text.rstrip()):
             for i in range( min( self._masklength-1, len(text.rstrip())), -1, -1):
         i = 0
         if len(text.rstrip()):
             for i in range( min( self._masklength-1, len(text.rstrip())), -1, -1):
-##                dbg('i:', i, 'self._isMaskChar(%d)' % i, self._isMaskChar(i))
+####                dbg('i:', i, 'self._isMaskChar(%d)' % i, self._isMaskChar(i))
                 if self._isMaskChar(i):
                     char = text[i]
                 if self._isMaskChar(i):
                     char = text[i]
-##                    dbg("text[%d]: '%s'" % (i, char))
+####                    dbg("text[%d]: '%s'" % (i, char))
                     if char != ' ':
                         i += 1
                         break
                     if char != ' ':
                         i += 1
                         break
@@ -4148,8 +4202,8 @@ class MaskedEditMixin:
         start, end = field._extent
         if field._insertRight and pos < end:
             pos = end
         start, end = field._extent
         if field._insertRight and pos < end:
             pos = end
-        dbg('next pos:', pos)
-        dbg(indent=0)
+##        dbg('next pos:', pos)
+##        dbg(indent=0)
         if getPosOnly:
             return pos
         else:
         if getPosOnly:
             return pos
         else:
@@ -4158,13 +4212,13 @@ class MaskedEditMixin:
 
     def _goHome(self, getPosOnly=False):
         """ Moves the insertion point to the beginning of user-entry """
 
     def _goHome(self, getPosOnly=False):
         """ Moves the insertion point to the beginning of user-entry """
-        dbg("MaskedEditMixin::_goHome; getPosOnly:", getPosOnly, indent=1)
+##        dbg("MaskedEditMixin::_goHome; getPosOnly:", getPosOnly, indent=1)
         text = self._GetValue()
         for i in range(self._masklength):
             if self._isMaskChar(i):
                 break
         pos = max(i, 0)
         text = self._GetValue()
         for i in range(self._masklength):
             if self._isMaskChar(i):
                 break
         pos = max(i, 0)
-        dbg(indent=0)
+##        dbg(indent=0)
         if getPosOnly:
             return pos
         else:
         if getPosOnly:
             return pos
         else:
@@ -4183,7 +4237,7 @@ class MaskedEditMixin:
             okchars += " "
         if okchars and field._includeChars:      ## any additional included characters?
             okchars += field._includeChars
             okchars += " "
         if okchars and field._includeChars:      ## any additional included characters?
             okchars += field._includeChars
-##        dbg('okchars[%d]:' % pos, okchars)
+####        dbg('okchars[%d]:' % pos, okchars)
         return okchars
 
 
         return okchars
 
 
@@ -4207,7 +4261,7 @@ class MaskedEditMixin:
 
     def _isCharAllowed(self, char, pos, checkRegex=False, allowAutoSelect=True, ignoreInsertRight=False):
         """ Returns True if character is allowed at the specific position, otherwise False."""
 
     def _isCharAllowed(self, char, pos, checkRegex=False, allowAutoSelect=True, ignoreInsertRight=False):
         """ Returns True if character is allowed at the specific position, otherwise False."""
-        dbg('_isCharAllowed', char, pos, checkRegex, indent=1)
+##        dbg('_isCharAllowed', char, pos, checkRegex, indent=1)
         field = self._FindField(pos)
         right_insert = False
 
         field = self._FindField(pos)
         right_insert = False
 
@@ -4238,21 +4292,21 @@ class MaskedEditMixin:
             if( (sel_start, sel_to) == field._extent
                 or (pos == end and input_len < field_len)):
                 pos = end - 1
             if( (sel_start, sel_to) == field._extent
                 or (pos == end and input_len < field_len)):
                 pos = end - 1
-                dbg('pos = end - 1 = ', pos, 'right_insert? 1')
+##                dbg('pos = end - 1 = ', pos, 'right_insert? 1')
                 right_insert = True
             elif( field._allowInsert and sel_start == sel_to
                   and (sel_to == end or (sel_to < self._masklength and value[sel_start] != field._fillChar))
                   and input_len < field_len ):
                 pos = sel_to - 1    # where character will go
                 right_insert = True
             elif( field._allowInsert and sel_start == sel_to
                   and (sel_to == end or (sel_to < self._masklength and value[sel_start] != field._fillChar))
                   and input_len < field_len ):
                 pos = sel_to - 1    # where character will go
-                dbg('pos = sel_to - 1 = ', pos, 'right_insert? 1')
+##                dbg('pos = sel_to - 1 = ', pos, 'right_insert? 1')
                 right_insert = True
             # else leave pos alone...
             else:
                 right_insert = True
             # else leave pos alone...
             else:
-                dbg('pos stays ', pos, 'right_insert? 0')
-
+##                dbg('pos stays ', pos, 'right_insert? 0')
+                pass
 
         if self._isTemplateChar( pos ):  ## if a template character, return empty
 
         if self._isTemplateChar( pos ):  ## if a template character, return empty
-            dbg('%d is a template character; returning False' % pos, indent=0)
+##            dbg('%d is a template character; returning False' % pos, indent=0)
             return False
 
         if self._isMaskChar( pos ):
             return False
 
         if self._isMaskChar( pos ):
@@ -4269,11 +4323,11 @@ class MaskedEditMixin:
                 elif self._useParens and (self._isInt or (self._isFloat and pos > self._decimalpos)):
                     okChars += ')'
 
                 elif self._useParens and (self._isInt or (self._isFloat and pos > self._decimalpos)):
                     okChars += ')'
 
-##            dbg('%s in %s?' % (char, okChars), char in okChars)
+####            dbg('%s in %s?' % (char, okChars), char in okChars)
             approved = char in okChars
 
             if approved and checkRegex:
             approved = char in okChars
 
             if approved and checkRegex:
-                dbg("checking appropriate regex's")
+##                dbg("checking appropriate regex's")
                 value = self._eraseSelection(self._GetValue())
                 if right_insert:
                     at = pos+1
                 value = self._eraseSelection(self._GetValue())
                 if right_insert:
                     at = pos+1
@@ -4283,21 +4337,21 @@ class MaskedEditMixin:
                     newvalue, ignore, ignore, ignore, ignore = self._insertKey(char, at, sel_start, sel_to, value, allowAutoSelect=True)
                 else:
                     newvalue, ignore = self._insertKey(char, at, sel_start, sel_to, value)
                     newvalue, ignore, ignore, ignore, ignore = self._insertKey(char, at, sel_start, sel_to, value, allowAutoSelect=True)
                 else:
                     newvalue, ignore = self._insertKey(char, at, sel_start, sel_to, value)
-                dbg('newvalue: "%s"' % newvalue)
+##                dbg('newvalue: "%s"' % newvalue)
 
                 fields = [self._FindField(pos)] + [self._ctrl_constraints]
                 for field in fields:    # includes fields[-1] == "ctrl_constraints"
                     if field._regexMask and field._filter:
 
                 fields = [self._FindField(pos)] + [self._ctrl_constraints]
                 for field in fields:    # includes fields[-1] == "ctrl_constraints"
                     if field._regexMask and field._filter:
-                        dbg('checking vs. regex')
+##                        dbg('checking vs. regex')
                         start, end = field._extent
                         slice = newvalue[start:end]
                         approved = (re.match( field._filter, slice) is not None)
                         start, end = field._extent
                         slice = newvalue[start:end]
                         approved = (re.match( field._filter, slice) is not None)
-                        dbg('approved?', approved)
+##                        dbg('approved?', approved)
                     if not approved: break
                     if not approved: break
-            dbg(indent=0)
+##            dbg(indent=0)
             return approved
         else:
             return approved
         else:
-            dbg('%d is a !???! character; returning False', indent=0)
+##            dbg('%d is a !???! character; returning False', indent=0)
             return False
 
 
             return False
 
 
@@ -4306,22 +4360,22 @@ class MaskedEditMixin:
             Need to find a way to call this whenever the value changes, in case the control's
             value has been changed or set programatically.
         """
             Need to find a way to call this whenever the value changes, in case the control's
             value has been changed or set programatically.
         """
-        dbg(suspend=1)
-        dbg('MaskedEditMixin::_applyFormatting', indent=1)
+##        dbg(suspend=1)
+##        dbg('MaskedEditMixin::_applyFormatting', indent=1)
 
         # Handle negative numbers
         if self._signOk:
             text, signpos, right_signpos = self._getSignedValue()
 
         # Handle negative numbers
         if self._signOk:
             text, signpos, right_signpos = self._getSignedValue()
-            dbg('text: "%s", signpos:' % text, signpos)
+##            dbg('text: "%s", signpos:' % text, signpos)
             if not text or text[signpos] not in ('-','('):
                 self._isNeg = False
             if not text or text[signpos] not in ('-','('):
                 self._isNeg = False
-                dbg('no valid sign found; new sign:', self._isNeg)
+##                dbg('no valid sign found; new sign:', self._isNeg)
                 if text and signpos != self._signpos:
                     self._signpos = signpos
             elif text and self._valid and not self._isNeg and text[signpos] in ('-', '('):
                 if text and signpos != self._signpos:
                     self._signpos = signpos
             elif text and self._valid and not self._isNeg and text[signpos] in ('-', '('):
-                dbg('setting _isNeg to True')
+##                dbg('setting _isNeg to True')
                 self._isNeg = True
                 self._isNeg = True
-            dbg('self._isNeg:', self._isNeg)
+##            dbg('self._isNeg:', self._isNeg)
 
         if self._signOk and self._isNeg:
             fc = self._signedForegroundColour
 
         if self._signOk and self._isNeg:
             fc = self._signedForegroundColour
@@ -4332,32 +4386,32 @@ class MaskedEditMixin:
             c =fc._name
         else:
             c = fc
             c =fc._name
         else:
             c = fc
-        dbg('setting foreground to', c)
+##        dbg('setting foreground to', c)
         self.SetForegroundColour(fc)
 
         if self._valid:
         self.SetForegroundColour(fc)
 
         if self._valid:
-            dbg('valid')
+##            dbg('valid')
             if self.IsEmpty():
                 bc = self._emptyBackgroundColour
             else:
                 bc = self._validBackgroundColour
         else:
             if self.IsEmpty():
                 bc = self._emptyBackgroundColour
             else:
                 bc = self._validBackgroundColour
         else:
-            dbg('invalid')
+##            dbg('invalid')
             bc = self._invalidBackgroundColour
         if hasattr(bc, '_name'):
             c =bc._name
         else:
             c = bc
             bc = self._invalidBackgroundColour
         if hasattr(bc, '_name'):
             c =bc._name
         else:
             c = bc
-        dbg('setting background to', c)
+##        dbg('setting background to', c)
         self.SetBackgroundColour(bc)
         self._Refresh()
         self.SetBackgroundColour(bc)
         self._Refresh()
-        dbg(indent=0, suspend=0)
+##        dbg(indent=0, suspend=0)
 
 
     def _getAbsValue(self, candidate=None):
         """ Return an unsigned value (i.e. strip the '-' prefix if any), and sign position(s).
         """
 
 
     def _getAbsValue(self, candidate=None):
         """ Return an unsigned value (i.e. strip the '-' prefix if any), and sign position(s).
         """
-        dbg('MaskedEditMixin::_getAbsValue; candidate="%s"' % candidate, indent=1)
+##        dbg('MaskedEditMixin::_getAbsValue; candidate="%s"' % candidate, indent=1)
         if candidate is None: text = self._GetValue()
         else: text = candidate
         right_signpos = text.find(')')
         if candidate is None: text = self._GetValue()
         else: text = candidate
         right_signpos = text.find(')')
@@ -4366,14 +4420,15 @@ class MaskedEditMixin:
             if self._ctrl_constraints._alignRight and self._fields[0]._fillChar == ' ':
                 signpos = text.find('-')
                 if signpos == -1:
             if self._ctrl_constraints._alignRight and self._fields[0]._fillChar == ' ':
                 signpos = text.find('-')
                 if signpos == -1:
-                    dbg('no - found; searching for (')
+##                    dbg('no - found; searching for (')
                     signpos = text.find('(')
                 elif signpos != -1:
                     signpos = text.find('(')
                 elif signpos != -1:
-                    dbg('- found at', signpos)
+##                    dbg('- found at', signpos)
+                    pass
 
                 if signpos == -1:
 
                 if signpos == -1:
-                    dbg('signpos still -1')
-                    dbg('len(%s) (%d) < len(%s) (%d)?' % (text, len(text), self._mask, self._masklength), len(text) < self._masklength)
+##                    dbg('signpos still -1')
+##                    dbg('len(%s) (%d) < len(%s) (%d)?' % (text, len(text), self._mask, self._masklength), len(text) < self._masklength)
                     if len(text) < self._masklength:
                         text = ' ' + text
                     if len(text) < self._masklength:
                     if len(text) < self._masklength:
                         text = ' ' + text
                     if len(text) < self._masklength:
@@ -4381,13 +4436,13 @@ class MaskedEditMixin:
                     if len(text) > self._masklength and text[-1] in (')', ' '):
                         text = text[:-1]
                     else:
                     if len(text) > self._masklength and text[-1] in (')', ' '):
                         text = text[:-1]
                     else:
-                        dbg('len(%s) (%d), len(%s) (%d)' % (text, len(text), self._mask, self._masklength))
-                        dbg('len(%s) - (len(%s) + 1):' % (text, text.lstrip()) , len(text) - (len(text.lstrip()) + 1))
+##                        dbg('len(%s) (%d), len(%s) (%d)' % (text, len(text), self._mask, self._masklength))
+##                        dbg('len(%s) - (len(%s) + 1):' % (text, text.lstrip()) , len(text) - (len(text.lstrip()) + 1))
                         signpos = len(text) - (len(text.lstrip()) + 1)
 
                         if self._useParens and not text.strip():
                             signpos -= 1    # empty value; use penultimate space
                         signpos = len(text) - (len(text.lstrip()) + 1)
 
                         if self._useParens and not text.strip():
                             signpos -= 1    # empty value; use penultimate space
-                dbg('signpos:', signpos)
+##                dbg('signpos:', signpos)
                 if signpos >= 0:
                     text = text[:signpos] + ' ' + text[signpos+1:]
 
                 if signpos >= 0:
                     text = text[:signpos] + ' ' + text[signpos+1:]
 
@@ -4410,25 +4465,25 @@ class MaskedEditMixin:
                 # figure out where it ought to go:
                 right_signpos = self._masklength - 1     # initial guess
                 if not self._ctrl_constraints._alignRight:
                 # figure out where it ought to go:
                 right_signpos = self._masklength - 1     # initial guess
                 if not self._ctrl_constraints._alignRight:
-                    dbg('not right-aligned')
+##                    dbg('not right-aligned')
                     if len(text.strip()) == 0:
                         right_signpos = signpos + 1
                     elif len(text.strip()) < self._masklength:
                         right_signpos = len(text.rstrip())
                     if len(text.strip()) == 0:
                         right_signpos = signpos + 1
                     elif len(text.strip()) < self._masklength:
                         right_signpos = len(text.rstrip())
-                dbg('right_signpos:', right_signpos)
+##                dbg('right_signpos:', right_signpos)
 
             groupchar = self._fields[0]._groupChar
             try:
                 value = long(text.replace(groupchar,'').replace('(','-').replace(')','').replace(' ', ''))
             except:
 
             groupchar = self._fields[0]._groupChar
             try:
                 value = long(text.replace(groupchar,'').replace('(','-').replace(')','').replace(' ', ''))
             except:
-                dbg('invalid number', indent=0)
+##                dbg('invalid number', indent=0)
                 return None, signpos, right_signpos
 
         else:   # float value
             try:
                 groupchar = self._fields[0]._groupChar
                 value = float(text.replace(groupchar,'').replace(self._decimalChar, '.').replace('(', '-').replace(')','').replace(' ', ''))
                 return None, signpos, right_signpos
 
         else:   # float value
             try:
                 groupchar = self._fields[0]._groupChar
                 value = float(text.replace(groupchar,'').replace(self._decimalChar, '.').replace('(', '-').replace(')','').replace(' ', ''))
-                dbg('value:', value)
+##                dbg('value:', value)
             except:
                 value = None
 
             except:
                 value = None
 
@@ -4440,15 +4495,16 @@ class MaskedEditMixin:
                 text = text[:signpos] + self._template[signpos] + text[signpos+1:]
             else:
                 # look forwards up to the decimal point for the 1st non-digit
                 text = text[:signpos] + self._template[signpos] + text[signpos+1:]
             else:
                 # look forwards up to the decimal point for the 1st non-digit
-                dbg('decimal pos:', self._decimalpos)
-                dbg('text: "%s"' % text)
+##                dbg('decimal pos:', self._decimalpos)
+##                dbg('text: "%s"' % text)
                 if self._signOk:
                     signpos = self._decimalpos - (len(text[:self._decimalpos].lstrip()) + 1)
                 if self._signOk:
                     signpos = self._decimalpos - (len(text[:self._decimalpos].lstrip()) + 1)
-                    if text[signpos+1] in ('-','('):
+                    # prevent checking for empty string - Tomo - Wed 14 Jan 2004 03:19:09 PM CET
+                    if len(text) >= signpos+1 and  text[signpos+1] in ('-','('):
                         signpos += 1
                 else:
                     signpos = -1
                         signpos += 1
                 else:
                     signpos = -1
-                dbg('signpos:', signpos)
+##                dbg('signpos:', signpos)
 
             if self._useParens:
                 if self._signOk:
 
             if self._useParens:
                 if self._signOk:
@@ -4463,11 +4519,11 @@ class MaskedEditMixin:
                         right_signpos = -1
 
             if value is None:
                         right_signpos = -1
 
             if value is None:
-                dbg('invalid number')
+##                dbg('invalid number')
                 text = None
 
                 text = None
 
-        dbg('abstext = "%s"' % text, 'signpos:', signpos, 'right_signpos:', right_signpos)
-        dbg(indent=0)
+##        dbg('abstext = "%s"' % text, 'signpos:', signpos, 'right_signpos:', right_signpos)
+##        dbg(indent=0)
         return text, signpos, right_signpos
 
 
         return text, signpos, right_signpos
 
 
@@ -4475,7 +4531,7 @@ class MaskedEditMixin:
         """ Return a signed value by adding a "-" prefix if the value
             is set to negative, or a space if positive.
         """
         """ Return a signed value by adding a "-" prefix if the value
             is set to negative, or a space if positive.
         """
-        dbg('MaskedEditMixin::_getSignedValue; candidate="%s"' % candidate, indent=1)
+##        dbg('MaskedEditMixin::_getSignedValue; candidate="%s"' % candidate, indent=1)
         if candidate is None: text = self._GetValue()
         else: text = candidate
 
         if candidate is None: text = self._GetValue()
         else: text = candidate
 
@@ -4483,7 +4539,7 @@ class MaskedEditMixin:
         abstext, signpos, right_signpos = self._getAbsValue(text)
         if self._signOk:
             if abstext is None:
         abstext, signpos, right_signpos = self._getAbsValue(text)
         if self._signOk:
             if abstext is None:
-                dbg(indent=0)
+##                dbg(indent=0)
                 return abstext, signpos, right_signpos
 
             if self._isNeg or text[signpos] in ('-', '('):
                 return abstext, signpos, right_signpos
 
             if self._isNeg or text[signpos] in ('-', '('):
@@ -4503,8 +4559,8 @@ class MaskedEditMixin:
                 text = text[:right_signpos] + ')' + text[right_signpos+1:]
         else:
             text = abstext
                 text = text[:right_signpos] + ')' + text[right_signpos+1:]
         else:
             text = abstext
-        dbg('signedtext = "%s"' % text, 'signpos:', signpos, 'right_signpos', right_signpos)
-        dbg(indent=0)
+##        dbg('signedtext = "%s"' % text, 'signpos:', signpos, 'right_signpos', right_signpos)
+##        dbg(indent=0)
         return text, signpos, right_signpos
 
 
         return text, signpos, right_signpos
 
 
@@ -4512,13 +4568,13 @@ class MaskedEditMixin:
         """ Returns control's value stripped of the template text.
             plainvalue = MaskedEditMixin.GetPlainValue()
         """
         """ Returns control's value stripped of the template text.
             plainvalue = MaskedEditMixin.GetPlainValue()
         """
-        dbg('MaskedEditMixin::GetPlainValue; candidate="%s"' % candidate, indent=1)
+##        dbg('MaskedEditMixin::GetPlainValue; candidate="%s"' % candidate, indent=1)
 
         if candidate is None: text = self._GetValue()
         else: text = candidate
 
         if self.IsEmpty():
 
         if candidate is None: text = self._GetValue()
         else: text = candidate
 
         if self.IsEmpty():
-            dbg('returned ""', indent=0)
+##            dbg('returned ""', indent=0)
             return ""
         else:
             plain = ""
             return ""
         else:
             plain = ""
@@ -4527,9 +4583,9 @@ class MaskedEditMixin:
                     plain += text[idx]
 
             if self._isFloat or self._isInt:
                     plain += text[idx]
 
             if self._isFloat or self._isInt:
-                dbg('plain so far: "%s"' % plain)
+##                dbg('plain so far: "%s"' % plain)
                 plain = plain.replace('(', '-').replace(')', ' ')
                 plain = plain.replace('(', '-').replace(')', ' ')
-                dbg('plain after sign regularization: "%s"' % plain)
+##                dbg('plain after sign regularization: "%s"' % plain)
 
                 if self._signOk and self._isNeg and plain.count('-') == 0:
                     # must be in reserved position; add to "plain value"
 
                 if self._signOk and self._isNeg and plain.count('-') == 0:
                     # must be in reserved position; add to "plain value"
@@ -4540,9 +4596,9 @@ class MaskedEditMixin:
                     plain = ' ' * lpad + plain.replace(',','')
                 else:
                     plain = plain.replace(',','')
                     plain = ' ' * lpad + plain.replace(',','')
                 else:
                     plain = plain.replace(',','')
-                dbg('plain after pad and group:"%s"' % plain)
+##                dbg('plain after pad and group:"%s"' % plain)
 
 
-            dbg('returned "%s"' % plain.rstrip(), indent=0)
+##            dbg('returned "%s"' % plain.rstrip(), indent=0)
             return plain.rstrip()
 
 
             return plain.rstrip()
 
 
@@ -4553,19 +4609,19 @@ class MaskedEditMixin:
         """
         if value is None: value = self._GetValue()
         if value == self._template and not self._defaultValue:
         """
         if value is None: value = self._GetValue()
         if value == self._template and not self._defaultValue:
-##            dbg("IsEmpty? 1 (value == self._template and not self._defaultValue)")
+####            dbg("IsEmpty? 1 (value == self._template and not self._defaultValue)")
             return True     # (all mask chars == fillChar by defn)
         elif value == self._template:
             empty = True
             for pos in range(len(self._template)):
             return True     # (all mask chars == fillChar by defn)
         elif value == self._template:
             empty = True
             for pos in range(len(self._template)):
-##                dbg('isMaskChar(%(pos)d)?' % locals(), self._isMaskChar(pos))
-##                dbg('value[%(pos)d] != self._fillChar?' %locals(), value[pos] != self._fillChar[pos])
+####                dbg('isMaskChar(%(pos)d)?' % locals(), self._isMaskChar(pos))
+####                dbg('value[%(pos)d] != self._fillChar?' %locals(), value[pos] != self._fillChar[pos])
                 if self._isMaskChar(pos) and value[pos] not in (' ', self._fillChar[pos]):
                     empty = False
                 if self._isMaskChar(pos) and value[pos] not in (' ', self._fillChar[pos]):
                     empty = False
-##            dbg("IsEmpty? %(empty)d (do all mask chars == fillChar?)" % locals())
+####            dbg("IsEmpty? %(empty)d (do all mask chars == fillChar?)" % locals())
             return empty
         else:
             return empty
         else:
-##            dbg("IsEmpty? 0 (value doesn't match template)")
+####            dbg("IsEmpty? 0 (value doesn't match template)")
             return False
 
 
             return False
 
 
@@ -4581,26 +4637,26 @@ class MaskedEditMixin:
     def IsValid(self, value=None):
         """ Indicates whether the value specified (or the current value of the control
         if not specified) is considered valid."""
     def IsValid(self, value=None):
         """ Indicates whether the value specified (or the current value of the control
         if not specified) is considered valid."""
-##        dbg('MaskedEditMixin::IsValid("%s")' % value, indent=1)
+####        dbg('MaskedEditMixin::IsValid("%s")' % value, indent=1)
         if value is None: value = self._GetValue()
         ret = self._CheckValid(value)
         if value is None: value = self._GetValue()
         ret = self._CheckValid(value)
-##        dbg(indent=0)
+####        dbg(indent=0)
         return ret
 
 
     def _eraseSelection(self, value=None, sel_start=None, sel_to=None):
         """ Used to blank the selection when inserting a new character. """
         return ret
 
 
     def _eraseSelection(self, value=None, sel_start=None, sel_to=None):
         """ Used to blank the selection when inserting a new character. """
-        dbg("MaskedEditMixin::_eraseSelection", indent=1)
+##        dbg("MaskedEditMixin::_eraseSelection", indent=1)
         if value is None: value = self._GetValue()
         if sel_start is None or sel_to is None:
             sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
         if value is None: value = self._GetValue()
         if sel_start is None or sel_to is None:
             sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
-        dbg('value: "%s"' % value)
-        dbg("current sel_start, sel_to:", sel_start, sel_to)
+##        dbg('value: "%s"' % value)
+##        dbg("current sel_start, sel_to:", sel_start, sel_to)
 
         newvalue = list(value)
         for i in range(sel_start, sel_to):
             if self._signOk and newvalue[i] in ('-', '(', ')'):
 
         newvalue = list(value)
         for i in range(sel_start, sel_to):
             if self._signOk and newvalue[i] in ('-', '(', ')'):
-                dbg('found sign (%s) at' % newvalue[i], i)
+##                dbg('found sign (%s) at' % newvalue[i], i)
 
                 # balance parentheses:
                 if newvalue[i] == '(':
 
                 # balance parentheses:
                 if newvalue[i] == '(':
@@ -4623,14 +4679,14 @@ class MaskedEditMixin:
                     newvalue[i] = self._template[i]
 
         value = string.join(newvalue,"")
                     newvalue[i] = self._template[i]
 
         value = string.join(newvalue,"")
-        dbg('new value: "%s"' % value)
-        dbg(indent=0)
+##        dbg('new value: "%s"' % value)
+##        dbg(indent=0)
         return value
 
 
     def _insertKey(self, char, pos, sel_start, sel_to, value, allowAutoSelect=False):
         """ Handles replacement of the character at the current insertion point."""
         return value
 
 
     def _insertKey(self, char, pos, sel_start, sel_to, value, allowAutoSelect=False):
         """ Handles replacement of the character at the current insertion point."""
-        dbg('MaskedEditMixin::_insertKey', "\'" + char + "\'", pos, sel_start, sel_to, '"%s"' % value, indent=1)
+##        dbg('MaskedEditMixin::_insertKey', "\'" + char + "\'", pos, sel_start, sel_to, '"%s"' % value, indent=1)
 
         text = self._eraseSelection(value)
         field = self._FindField(pos)
 
         text = self._eraseSelection(value)
         field = self._FindField(pos)
@@ -4642,7 +4698,7 @@ class MaskedEditMixin:
             # adjustpos must have moved the position; make selection match:
             sel_start = sel_to = pos
 
             # adjustpos must have moved the position; make selection match:
             sel_start = sel_to = pos
 
-        dbg('field._insertRight?', field._insertRight)
+##        dbg('field._insertRight?', field._insertRight)
         if( field._insertRight                                  # field allows right insert
             and ((sel_start, sel_to) == field._extent           # and whole field selected
                  or (sel_start == sel_to                        # or nothing selected
         if( field._insertRight                                  # field allows right insert
             and ((sel_start, sel_to) == field._extent           # and whole field selected
                  or (sel_start == sel_to                        # or nothing selected
@@ -4650,7 +4706,7 @@ class MaskedEditMixin:
                           or (field._allowInsert                # or field allows right-insert
                               and sel_start < end               # next to other char in field:
                               and text[sel_start] != field._fillChar) ) ) ) ):
                           or (field._allowInsert                # or field allows right-insert
                               and sel_start < end               # next to other char in field:
                               and text[sel_start] != field._fillChar) ) ) ) ):
-            dbg('insertRight')
+##            dbg('insertRight')
             fstr = text[start:end]
             erasable_chars = [field._fillChar, ' ']
 
             fstr = text[start:end]
             erasable_chars = [field._fillChar, ' ']
 
@@ -4658,42 +4714,42 @@ class MaskedEditMixin:
                 erasable_chars.append('0')
 
             erased = ''
                 erasable_chars.append('0')
 
             erased = ''
-##            dbg("fstr[0]:'%s'" % fstr[0])
-##            dbg('field_index:', field._index)
-##            dbg("fstr[0] in erasable_chars?", fstr[0] in erasable_chars)
-##            dbg("self._signOk and field._index == 0 and fstr[0] in ('-','(')?",
+####            dbg("fstr[0]:'%s'" % fstr[0])
+####            dbg('field_index:', field._index)
+####            dbg("fstr[0] in erasable_chars?", fstr[0] in erasable_chars)
+####            dbg("self._signOk and field._index == 0 and fstr[0] in ('-','(')?",
 ##                 self._signOk and field._index == 0 and fstr[0] in ('-','('))
             if fstr[0] in erasable_chars or (self._signOk and field._index == 0 and fstr[0] in ('-','(')):
                 erased = fstr[0]
 ##                 self._signOk and field._index == 0 and fstr[0] in ('-','('))
             if fstr[0] in erasable_chars or (self._signOk and field._index == 0 and fstr[0] in ('-','(')):
                 erased = fstr[0]
-##                dbg('value:      "%s"' % text)
-##                dbg('fstr:       "%s"' % fstr)
-##                dbg("erased:     '%s'" % erased)
+####                dbg('value:      "%s"' % text)
+####                dbg('fstr:       "%s"' % fstr)
+####                dbg("erased:     '%s'" % erased)
                 field_sel_start = sel_start - start
                 field_sel_to = sel_to - start
                 field_sel_start = sel_start - start
                 field_sel_to = sel_to - start
-                dbg('left fstr:  "%s"' % fstr[1:field_sel_start])
-                dbg('right fstr: "%s"' % fstr[field_sel_to:end])
+##                dbg('left fstr:  "%s"' % fstr[1:field_sel_start])
+##                dbg('right fstr: "%s"' % fstr[field_sel_to:end])
                 fstr = fstr[1:field_sel_start] + char + fstr[field_sel_to:end]
             if field._alignRight and sel_start != sel_to:
                 field_len = end - start
 ##                pos += (field_len - len(fstr))    # move cursor right by deleted amount
                 pos = sel_to
                 fstr = fstr[1:field_sel_start] + char + fstr[field_sel_to:end]
             if field._alignRight and sel_start != sel_to:
                 field_len = end - start
 ##                pos += (field_len - len(fstr))    # move cursor right by deleted amount
                 pos = sel_to
-                dbg('setting pos to:', pos)
+##                dbg('setting pos to:', pos)
                 if field._padZero:
                     fstr = '0' * (field_len - len(fstr)) + fstr
                 else:
                     fstr = fstr.rjust(field_len)   # adjust the field accordingly
                 if field._padZero:
                     fstr = '0' * (field_len - len(fstr)) + fstr
                 else:
                     fstr = fstr.rjust(field_len)   # adjust the field accordingly
-            dbg('field str: "%s"' % fstr)
+##            dbg('field str: "%s"' % fstr)
 
             newtext = text[:start] + fstr + text[end:]
             if erased in ('-', '(') and self._signOk:
                 newtext = erased + newtext[1:]
 
             newtext = text[:start] + fstr + text[end:]
             if erased in ('-', '(') and self._signOk:
                 newtext = erased + newtext[1:]
-            dbg('newtext: "%s"' % newtext)
+##            dbg('newtext: "%s"' % newtext)
 
             if self._signOk and field._index == 0:
                 start -= 1             # account for sign position
 
 
             if self._signOk and field._index == 0:
                 start -= 1             # account for sign position
 
-##            dbg('field._moveOnFieldFull?', field._moveOnFieldFull)
-##            dbg('len(fstr.lstrip()) == end-start?', len(fstr.lstrip()) == end-start)
+####            dbg('field._moveOnFieldFull?', field._moveOnFieldFull)
+####            dbg('len(fstr.lstrip()) == end-start?', len(fstr.lstrip()) == end-start)
             if( field._moveOnFieldFull and pos == end
                 and len(fstr.lstrip()) == end-start):   # if field now full
                 newpos = self._findNextEntry(end)       #   go to next field
             if( field._moveOnFieldFull and pos == end
                 and len(fstr.lstrip()) == end-start):   # if field now full
                 newpos = self._findNextEntry(end)       #   go to next field
@@ -4701,9 +4757,10 @@ class MaskedEditMixin:
                 newpos = pos                            # else keep cursor at current position
 
         if not newtext:
                 newpos = pos                            # else keep cursor at current position
 
         if not newtext:
-            dbg('not newtext')
+##            dbg('not newtext')
             if newpos != pos:
             if newpos != pos:
-                dbg('newpos:', newpos)
+##                dbg('newpos:', newpos)
+                pass
             if self._signOk and self._useParens:
                 old_right_signpos = text.find(')')
 
             if self._signOk and self._useParens:
                 old_right_signpos = text.find(')')
 
@@ -4712,10 +4769,10 @@ class MaskedEditMixin:
                 field_len = end - start
                 before = text[start:sel_start]
                 after = text[sel_to:end].strip()
                 field_len = end - start
                 before = text[start:sel_start]
                 after = text[sel_to:end].strip()
-##                dbg("current field:'%s'" % text[start:end])
-##                dbg("before:'%s'" % before, "after:'%s'" % after)
+####                dbg("current field:'%s'" % text[start:end])
+####                dbg("before:'%s'" % before, "after:'%s'" % after)
                 new_len = len(before) + len(after) + 1 # (for inserted char)
                 new_len = len(before) + len(after) + 1 # (for inserted char)
-##                dbg('new_len:', new_len)
+####                dbg('new_len:', new_len)
 
                 if new_len < field_len:
                     retained = after + self._template[end-(field_len-new_len):end]
 
                 if new_len < field_len:
                     retained = after + self._template[end-(field_len-new_len):end]
@@ -4725,7 +4782,7 @@ class MaskedEditMixin:
                     retained = after
 
                 left = text[0:start] + before
                     retained = after
 
                 left = text[0:start] + before
-##                dbg("left:'%s'" % left, "retained:'%s'" % retained)
+####                dbg("left:'%s'" % left, "retained:'%s'" % retained)
                 right   = retained + text[end:]
             else:
                 left  = text[0:pos]
                 right   = retained + text[end:]
             else:
                 left  = text[0:pos]
@@ -4754,16 +4811,16 @@ class MaskedEditMixin:
                             else:
                                 rstripped_text = newtext.rstrip()
                                 right_signpos = len(rstripped_text)
                             else:
                                 rstripped_text = newtext.rstrip()
                                 right_signpos = len(rstripped_text)
-                                dbg('old_right_signpos:', old_right_signpos, 'right signpos now:', right_signpos)
+##                                dbg('old_right_signpos:', old_right_signpos, 'right signpos now:', right_signpos)
                                 newtext = newtext[:right_signpos] + ')' + newtext[right_signpos+1:]
 
             if( field._insertRight                                  # if insert-right field (but we didn't start at right edge)
                 and field._moveOnFieldFull                          # and should move cursor when full
                 and len(newtext[start:end].strip()) == end-start):  # and field now full
                 newpos = self._findNextEntry(end)                   #   go to next field
                                 newtext = newtext[:right_signpos] + ')' + newtext[right_signpos+1:]
 
             if( field._insertRight                                  # if insert-right field (but we didn't start at right edge)
                 and field._moveOnFieldFull                          # and should move cursor when full
                 and len(newtext[start:end].strip()) == end-start):  # and field now full
                 newpos = self._findNextEntry(end)                   #   go to next field
-                dbg('newpos = nextentry =', newpos)
+##                dbg('newpos = nextentry =', newpos)
             else:
             else:
-                dbg('pos:', pos, 'newpos:', pos+1)
+##                dbg('pos:', pos, 'newpos:', pos+1)
                 newpos = pos+1
 
 
                 newpos = pos+1
 
 
@@ -4803,12 +4860,12 @@ class MaskedEditMixin:
                         # adjust position to just after partial match in control:
                         newpos = self._masklength - (len(self._ctrl_constraints._choices[match_index].strip()) - len(matched_str.strip()))
 
                         # adjust position to just after partial match in control:
                         newpos = self._masklength - (len(self._ctrl_constraints._choices[match_index].strip()) - len(matched_str.strip()))
 
-            dbg('newtext: "%s"' % newtext, 'newpos:', newpos, 'new_select_to:', new_select_to)
-            dbg(indent=0)
+##            dbg('newtext: "%s"' % newtext, 'newpos:', newpos, 'new_select_to:', new_select_to)
+##            dbg(indent=0)
             return newtext, newpos, new_select_to, match_field, match_index
         else:
             return newtext, newpos, new_select_to, match_field, match_index
         else:
-            dbg('newtext: "%s"' % newtext, 'newpos:', newpos)
-            dbg(indent=0)
+##            dbg('newtext: "%s"' % newtext, 'newpos:', newpos)
+##            dbg(indent=0)
             return newtext, newpos
 
 
             return newtext, newpos
 
 
@@ -4822,7 +4879,7 @@ class MaskedEditMixin:
         preserve the correct selection when the focus event is not due to tab,
         we need to pull the following trick:
         """
         preserve the correct selection when the focus event is not due to tab,
         we need to pull the following trick:
         """
-        dbg('MaskedEditMixin::_OnFocus')
+##        dbg('MaskedEditMixin::_OnFocus')
         wx.CallAfter(self._fixSelection)
         event.Skip()
         self.Refresh()
         wx.CallAfter(self._fixSelection)
         event.Skip()
         self.Refresh()
@@ -4834,41 +4891,42 @@ class MaskedEditMixin:
         current value of the control is a "valid value," and has the side
         effect of coloring the control appropriately.
         """
         current value of the control is a "valid value," and has the side
         effect of coloring the control appropriately.
         """
-        dbg(suspend=1)
-        dbg('MaskedEditMixin::_CheckValid: candidate="%s"' % candidate, indent=1)
+##        dbg(suspend=1)
+##        dbg('MaskedEditMixin::_CheckValid: candidate="%s"' % candidate, indent=1)
         oldValid = self._valid
         if candidate is None: value = self._GetValue()
         else: value = candidate
         oldValid = self._valid
         if candidate is None: value = self._GetValue()
         else: value = candidate
-        dbg('value: "%s"' % value)
+##        dbg('value: "%s"' % value)
         oldvalue = value
         valid = True    # assume True
 
         if not self.IsDefault(value) and self._isDate:                    ## Date type validation
             valid = self._validateDate(value)
         oldvalue = value
         valid = True    # assume True
 
         if not self.IsDefault(value) and self._isDate:                    ## Date type validation
             valid = self._validateDate(value)
-            dbg("valid date?", valid)
+##            dbg("valid date?", valid)
 
         elif not self.IsDefault(value) and self._isTime:
             valid = self._validateTime(value)
 
         elif not self.IsDefault(value) and self._isTime:
             valid = self._validateTime(value)
-            dbg("valid time?", valid)
+##            dbg("valid time?", valid)
 
         elif not self.IsDefault(value) and (self._isInt or self._isFloat):  ## Numeric type
             valid = self._validateNumeric(value)
 
         elif not self.IsDefault(value) and (self._isInt or self._isFloat):  ## Numeric type
             valid = self._validateNumeric(value)
-            dbg("valid Number?", valid)
+##            dbg("valid Number?", valid)
 
         if valid:   # and not self.IsDefault(value):    ## generic validation accounts for IsDefault()
             ## valid so far; ensure also allowed by any list or regex provided:
             valid = self._validateGeneric(value)
 
         if valid:   # and not self.IsDefault(value):    ## generic validation accounts for IsDefault()
             ## valid so far; ensure also allowed by any list or regex provided:
             valid = self._validateGeneric(value)
-            dbg("valid value?", valid)
+##            dbg("valid value?", valid)
 
 
-        dbg('valid?', valid)
+##        dbg('valid?', valid)
 
         if not candidate:
             self._valid = valid
             self._applyFormatting()
             if self._valid != oldValid:
 
         if not candidate:
             self._valid = valid
             self._applyFormatting()
             if self._valid != oldValid:
-                dbg('validity changed: oldValid =',oldValid,'newvalid =', self._valid)
-                dbg('oldvalue: "%s"' % oldvalue, 'newvalue: "%s"' % self._GetValue())
-        dbg(indent=0, suspend=0)
+##                dbg('validity changed: oldValid =',oldValid,'newvalid =', self._valid)
+##                dbg('oldvalue: "%s"' % oldvalue, 'newvalue: "%s"' % self._GetValue())
+                pass
+##        dbg(indent=0, suspend=0)
         return valid
 
 
         return valid
 
 
@@ -4907,47 +4965,47 @@ class MaskedEditMixin:
                         require_digit_at = self._fields[0]._extent[1]-1
                     else:
                         require_digit_at = self._fields[0]._extent[0]
                         require_digit_at = self._fields[0]._extent[1]-1
                     else:
                         require_digit_at = self._fields[0]._extent[0]
-                    dbg('require_digit_at:', require_digit_at)
-                    dbg("value[rda]: '%s'" % value[require_digit_at])
+##                    dbg('require_digit_at:', require_digit_at)
+##                    dbg("value[rda]: '%s'" % value[require_digit_at])
                     if value[require_digit_at] not in list(string.digits):
                         valid = False
                         return valid
                 # else...
                     if value[require_digit_at] not in list(string.digits):
                         valid = False
                         return valid
                 # else...
-            dbg('number:', number)
+##            dbg('number:', number)
             if self._ctrl_constraints._hasRange:
                 valid = self._ctrl_constraints._rangeLow <= number <= self._ctrl_constraints._rangeHigh
             else:
                 valid = True
             groupcharpos = value.rfind(groupchar)
             if groupcharpos != -1:  # group char present
             if self._ctrl_constraints._hasRange:
                 valid = self._ctrl_constraints._rangeLow <= number <= self._ctrl_constraints._rangeHigh
             else:
                 valid = True
             groupcharpos = value.rfind(groupchar)
             if groupcharpos != -1:  # group char present
-                dbg('groupchar found at', groupcharpos)
+##                dbg('groupchar found at', groupcharpos)
                 if self._isFloat and groupcharpos > self._decimalpos:
                     # 1st one found on right-hand side is past decimal point
                 if self._isFloat and groupcharpos > self._decimalpos:
                     # 1st one found on right-hand side is past decimal point
-                    dbg('groupchar in fraction; illegal')
+##                    dbg('groupchar in fraction; illegal')
                     valid = False
                 elif self._isFloat:
                     integer = value[:self._decimalpos].strip()
                 else:
                     integer = value.strip()
                     valid = False
                 elif self._isFloat:
                     integer = value[:self._decimalpos].strip()
                 else:
                     integer = value.strip()
-                dbg("integer:'%s'" % integer)
+##                dbg("integer:'%s'" % integer)
                 if integer[0] in ('-', '('):
                     integer = integer[1:]
                 if integer[-1] == ')':
                     integer = integer[:-1]
 
                 parts = integer.split(groupchar)
                 if integer[0] in ('-', '('):
                     integer = integer[1:]
                 if integer[-1] == ')':
                     integer = integer[:-1]
 
                 parts = integer.split(groupchar)
-                dbg('parts:', parts)
+##                dbg('parts:', parts)
                 for i in range(len(parts)):
                     if i == 0 and abs(int(parts[0])) > 999:
                 for i in range(len(parts)):
                     if i == 0 and abs(int(parts[0])) > 999:
-                        dbg('group 0 too long; illegal')
+##                        dbg('group 0 too long; illegal')
                         valid = False
                         break
                     elif i > 0 and (len(parts[i]) != 3 or ' ' in parts[i]):
                         valid = False
                         break
                     elif i > 0 and (len(parts[i]) != 3 or ' ' in parts[i]):
-                        dbg('group %i (%s) not right size; illegal' % (i, parts[i]))
+##                        dbg('group %i (%s) not right size; illegal' % (i, parts[i]))
                         valid = False
                         break
         except ValueError:
                         valid = False
                         break
         except ValueError:
-            dbg('value not a valid number')
+##            dbg('value not a valid number')
             valid = False
         return valid
 
             valid = False
         return valid
 
@@ -4956,12 +5014,12 @@ class MaskedEditMixin:
         """ Validate the current date value using the provided Regex filter.
             Generally used for character types.BufferType
         """
         """ Validate the current date value using the provided Regex filter.
             Generally used for character types.BufferType
         """
-        dbg('MaskedEditMixin::_validateDate', indent=1)
+##        dbg('MaskedEditMixin::_validateDate', indent=1)
         if candidate is None: value = self._GetValue()
         else: value = candidate
         if candidate is None: value = self._GetValue()
         else: value = candidate
-        dbg('value = "%s"' % value)
+##        dbg('value = "%s"' % value)
         text = self._adjustDate(value, force4digit_year=True)     ## Fix the date up before validating it
         text = self._adjustDate(value, force4digit_year=True)     ## Fix the date up before validating it
-        dbg('text =', text)
+##        dbg('text =', text)
         valid = True   # assume True until proven otherwise
 
         try:
         valid = True   # assume True until proven otherwise
 
         try:
@@ -4976,19 +5034,19 @@ class MaskedEditMixin:
 
             year, month, day = getDateParts( datestr, self._datestyle)
             year = int(year)
 
             year, month, day = getDateParts( datestr, self._datestyle)
             year = int(year)
-            dbg('self._dateExtent:', self._dateExtent)
+##            dbg('self._dateExtent:', self._dateExtent)
             if self._dateExtent == 11:
                 month = charmonths_dict[month.lower()]
             else:
                 month = int(month)
             day = int(day)
             if self._dateExtent == 11:
                 month = charmonths_dict[month.lower()]
             else:
                 month = int(month)
             day = int(day)
-            dbg('year, month, day:', year, month, day)
+##            dbg('year, month, day:', year, month, day)
 
         except ValueError:
 
         except ValueError:
-            dbg('cannot convert string to integer parts')
+##            dbg('cannot convert string to integer parts')
             valid = False
         except KeyError:
             valid = False
         except KeyError:
-            dbg('cannot convert string to integer month')
+##            dbg('cannot convert string to integer month')
             valid = False
 
         if valid:
             valid = False
 
         if valid:
@@ -5000,12 +5058,12 @@ class MaskedEditMixin:
             else:
                 month -= 1
                 try:
             else:
                 month -= 1
                 try:
-                    dbg("trying to create date from values day=%d, month=%d, year=%d" % (day,month,year))
+##                    dbg("trying to create date from values day=%d, month=%d, year=%d" % (day,month,year))
                     dateHandler = wx.DateTimeFromDMY(day,month,year)
                     dateHandler = wx.DateTimeFromDMY(day,month,year)
-                    dbg("succeeded")
+##                    dbg("succeeded")
                     dateOk = True
                 except:
                     dateOk = True
                 except:
-                    dbg('cannot convert string to valid date')
+##                    dbg('cannot convert string to valid date')
                     dateOk = False
                 if not dateOk:
                     valid = False
                     dateOk = False
                 if not dateOk:
                     valid = False
@@ -5015,16 +5073,17 @@ class MaskedEditMixin:
                 # so we eliminate them here:
                 timeStr     = text[self._dateExtent+1:].strip()         ## time portion of the string
                 if timeStr:
                 # so we eliminate them here:
                 timeStr     = text[self._dateExtent+1:].strip()         ## time portion of the string
                 if timeStr:
-                    dbg('timeStr: "%s"' % timeStr)
+##                    dbg('timeStr: "%s"' % timeStr)
                     try:
                         checkTime    = dateHandler.ParseTime(timeStr)
                         valid = checkTime == len(timeStr)
                     except:
                         valid = False
                     if not valid:
                     try:
                         checkTime    = dateHandler.ParseTime(timeStr)
                         valid = checkTime == len(timeStr)
                     except:
                         valid = False
                     if not valid:
-                        dbg('cannot convert string to valid time')
+##                        dbg('cannot convert string to valid time')
+                        pass                        
         if valid: dbg('valid date')
         if valid: dbg('valid date')
-        dbg(indent=0)
+##        dbg(indent=0)
         return valid
 
 
         return valid
 
 
@@ -5032,40 +5091,41 @@ class MaskedEditMixin:
         """ Validate the current time value using the provided Regex filter.
             Generally used for character types.BufferType
         """
         """ Validate the current time value using the provided Regex filter.
             Generally used for character types.BufferType
         """
-        dbg('MaskedEditMixin::_validateTime', indent=1)
+##        dbg('MaskedEditMixin::_validateTime', indent=1)
         # wxDateTime doesn't take kindly to leading/trailing spaces when parsing,
         # so we eliminate them here:
         if candidate is None: value = self._GetValue().strip()
         else: value = candidate.strip()
         # wxDateTime doesn't take kindly to leading/trailing spaces when parsing,
         # so we eliminate them here:
         if candidate is None: value = self._GetValue().strip()
         else: value = candidate.strip()
-        dbg('value = "%s"' % value)
+##        dbg('value = "%s"' % value)
         valid = True   # assume True until proven otherwise
 
         dateHandler = wx.DateTime_Today()
         try:
             checkTime    = dateHandler.ParseTime(value)
         valid = True   # assume True until proven otherwise
 
         dateHandler = wx.DateTime_Today()
         try:
             checkTime    = dateHandler.ParseTime(value)
-            dbg('checkTime:', checkTime, 'len(value)', len(value))
+##            dbg('checkTime:', checkTime, 'len(value)', len(value))
             valid = checkTime == len(value)
         except:
             valid = False
 
         if not valid:
             valid = checkTime == len(value)
         except:
             valid = False
 
         if not valid:
-            dbg('cannot convert string to valid time')
+##            dbg('cannot convert string to valid time')
+            pass
         if valid: dbg('valid time')
         if valid: dbg('valid time')
-        dbg(indent=0)
+##        dbg(indent=0)
         return valid
 
 
     def _OnKillFocus(self,event):
         """ Handler for EVT_KILL_FOCUS event.
         """
         return valid
 
 
     def _OnKillFocus(self,event):
         """ Handler for EVT_KILL_FOCUS event.
         """
-        dbg('MaskedEditMixin::_OnKillFocus', 'isDate=',self._isDate, indent=1)
+##        dbg('MaskedEditMixin::_OnKillFocus', 'isDate=',self._isDate, indent=1)
         if self._mask and self._IsEditable():
             self._AdjustField(self._GetInsertionPoint())
             self._CheckValid()   ## Call valid handler
 
         self._LostFocus()    ## Provided for subclass use
         event.Skip()
         if self._mask and self._IsEditable():
             self._AdjustField(self._GetInsertionPoint())
             self._CheckValid()   ## Call valid handler
 
         self._LostFocus()    ## Provided for subclass use
         event.Skip()
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _fixSelection(self):
 
 
     def _fixSelection(self):
@@ -5082,20 +5142,20 @@ class MaskedEditMixin:
         we can assume the cause, change the insertion point to the start of
         the control, and deselect.
         """
         we can assume the cause, change the insertion point to the start of
         the control, and deselect.
         """
-        dbg('MaskedEditMixin::_fixSelection', indent=1)
+##        dbg('MaskedEditMixin::_fixSelection', indent=1)
         if not self._mask or not self._IsEditable():
         if not self._mask or not self._IsEditable():
-            dbg(indent=0)
+##            dbg(indent=0)
             return
 
         sel_start, sel_to = self._GetSelection()
             return
 
         sel_start, sel_to = self._GetSelection()
-        dbg('sel_start, sel_to:', sel_start, sel_to, 'self.IsEmpty()?', self.IsEmpty())
+##        dbg('sel_start, sel_to:', sel_start, sel_to, 'self.IsEmpty()?', self.IsEmpty())
 
         if( sel_start == 0 and sel_to >= len( self._mask )   #(can be greater in numeric controls because of reserved space)
             and (not self._ctrl_constraints._autoSelect or self.IsEmpty() or self.IsDefault() ) ):
             # This isn't normally allowed, and so assume we got here by the new
             # "tab traversal" behavior, so we need to reset the selection
             # and insertion point:
 
         if( sel_start == 0 and sel_to >= len( self._mask )   #(can be greater in numeric controls because of reserved space)
             and (not self._ctrl_constraints._autoSelect or self.IsEmpty() or self.IsDefault() ) ):
             # This isn't normally allowed, and so assume we got here by the new
             # "tab traversal" behavior, so we need to reset the selection
             # and insertion point:
-            dbg('entire text selected; resetting selection to start of control')
+##            dbg('entire text selected; resetting selection to start of control')
             self._goHome()
             field = self._FindField(self._GetInsertionPoint())
             edit_start, edit_end = field._extent
             self._goHome()
             field = self._FindField(self._GetInsertionPoint())
             edit_start, edit_end = field._extent
@@ -5115,25 +5175,26 @@ class MaskedEditMixin:
                 edit_start, edit_end = integer._extent
 
                 if integer._selectOnFieldEntry:
                 edit_start, edit_end = integer._extent
 
                 if integer._selectOnFieldEntry:
-                    dbg('select on field entry:')
+##                    dbg('select on field entry:')
                     self._SetInsertionPoint(edit_start)
                     self._SetSelection(edit_start, edit_end)
 
                 elif integer._insertRight:
                     self._SetInsertionPoint(edit_start)
                     self._SetSelection(edit_start, edit_end)
 
                 elif integer._insertRight:
-                    dbg('moving insertion point to end')
+##                    dbg('moving insertion point to end')
                     self._SetInsertionPoint(edit_end)
                     self._SetSelection(edit_end, edit_end)
                 else:
                     self._SetInsertionPoint(edit_end)
                     self._SetSelection(edit_end, edit_end)
                 else:
-                    dbg('numeric ctrl is empty; start at beginning after sign')
+##                    dbg('numeric ctrl is empty; start at beginning after sign')
                     self._SetInsertionPoint(signpos+1)   ## Move past minus sign space if signed
                     self._SetSelection(signpos+1, signpos+1)
 
         elif sel_start > self._goEnd(getPosOnly=True):
                     self._SetInsertionPoint(signpos+1)   ## Move past minus sign space if signed
                     self._SetSelection(signpos+1, signpos+1)
 
         elif sel_start > self._goEnd(getPosOnly=True):
-            dbg('cursor beyond the end of the user input; go to end of it')
+##            dbg('cursor beyond the end of the user input; go to end of it')
             self._goEnd()
         else:
             self._goEnd()
         else:
-            dbg('sel_start, sel_to:', sel_start, sel_to, 'self._masklength:', self._masklength)
-        dbg(indent=0)
+##            dbg('sel_start, sel_to:', sel_start, sel_to, 'self._masklength:', self._masklength)
+            pass
+##        dbg(indent=0)
 
 
     def _Keypress(self,key):
 
 
     def _Keypress(self,key):
@@ -5175,11 +5236,11 @@ class MaskedEditMixin:
         derived control because the mixin functions can't override a method of
         a sibling class.
         """
         derived control because the mixin functions can't override a method of
         a sibling class.
         """
-        dbg("MaskedEditMixin::_Cut", indent=1)
+##        dbg("MaskedEditMixin::_Cut", indent=1)
         value = self._GetValue()
         value = self._GetValue()
-        dbg('current value: "%s"' % value)
+##        dbg('current value: "%s"' % value)
         sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
         sel_start, sel_to = self._GetSelection()                   ## check for a range of selected text
-        dbg('selected text: "%s"' % value[sel_start:sel_to].strip())
+##        dbg('selected text: "%s"' % value[sel_start:sel_to].strip())
         do = wxTextDataObject()
         do.SetText(value[sel_start:sel_to].strip())
         wxTheClipboard.Open()
         do = wxTextDataObject()
         do.SetText(value[sel_start:sel_to].strip())
         wxTheClipboard.Open()
@@ -5188,7 +5249,7 @@ class MaskedEditMixin:
 
         if sel_to - sel_start != 0:
             self._OnErase()
 
         if sel_to - sel_start != 0:
             self._OnErase()
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
 # WS Note: overriding Copy is no longer necessary given that you
 
 
 # WS Note: overriding Copy is no longer necessary given that you
@@ -5232,32 +5293,32 @@ class MaskedEditMixin:
         returns validity, replacement text, and extent of paste in
         template.
         """
         returns validity, replacement text, and extent of paste in
         template.
         """
-        dbg(suspend=1)
-        dbg('MaskedEditMixin::_validatePaste("%(paste_text)s", %(sel_start)d, %(sel_to)d), raise_on_invalid? %(raise_on_invalid)d' % locals(), indent=1)
+##        dbg(suspend=1)
+##        dbg('MaskedEditMixin::_validatePaste("%(paste_text)s", %(sel_start)d, %(sel_to)d), raise_on_invalid? %(raise_on_invalid)d' % locals(), indent=1)
         select_length = sel_to - sel_start
         maxlength = select_length
         select_length = sel_to - sel_start
         maxlength = select_length
-        dbg('sel_to - sel_start:', maxlength)
+##        dbg('sel_to - sel_start:', maxlength)
         if maxlength == 0:
             maxlength = self._masklength - sel_start
             item = 'control'
         else:
             item = 'selection'
         if maxlength == 0:
             maxlength = self._masklength - sel_start
             item = 'control'
         else:
             item = 'selection'
-        dbg('maxlength:', maxlength)
+##        dbg('maxlength:', maxlength)
         length_considered = len(paste_text)
         if length_considered > maxlength:
         length_considered = len(paste_text)
         if length_considered > maxlength:
-            dbg('paste text will not fit into the %s:' % item, indent=0)
+##            dbg('paste text will not fit into the %s:' % item, indent=0)
             if raise_on_invalid:
             if raise_on_invalid:
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 if item == 'control':
                     raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
                 else:
                     raise ValueError('"%s" will not fit into the selection' % paste_text)
             else:
                 if item == 'control':
                     raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
                 else:
                     raise ValueError('"%s" will not fit into the selection' % paste_text)
             else:
-                dbg(indent=0, suspend=0)
+##                dbg(indent=0, suspend=0)
                 return False, None, None
 
         text = self._template
                 return False, None, None
 
         text = self._template
-        dbg('length_considered:', length_considered)
+##        dbg('length_considered:', length_considered)
 
         valid_paste = True
         replacement_text = ""
 
         valid_paste = True
         replacement_text = ""
@@ -5266,7 +5327,7 @@ class MaskedEditMixin:
         while valid_paste and i < length_considered and replace_to < self._masklength:
             if paste_text[i:] == self._template[replace_to:length_considered]:
                 # remainder of paste matches template; skip char-by-char analysis
         while valid_paste and i < length_considered and replace_to < self._masklength:
             if paste_text[i:] == self._template[replace_to:length_considered]:
                 # remainder of paste matches template; skip char-by-char analysis
-                dbg('remainder paste_text[%d:] (%s) matches template[%d:%d]' % (i, paste_text[i:], replace_to, length_considered))
+##                dbg('remainder paste_text[%d:] (%s) matches template[%d:%d]' % (i, paste_text[i:], replace_to, length_considered))
                 replacement_text += paste_text[i:]
                 replace_to = i = length_considered
                 continue
                 replacement_text += paste_text[i:]
                 replace_to = i = length_considered
                 continue
@@ -5277,12 +5338,12 @@ class MaskedEditMixin:
                 if field._forceupper:   char = char.upper()
                 elif field._forcelower: char = char.lower()
 
                 if field._forceupper:   char = char.upper()
                 elif field._forcelower: char = char.lower()
 
-            dbg('char:', "'"+char+"'", 'i =', i, 'replace_to =', replace_to)
-            dbg('self._isTemplateChar(%d)?' % replace_to, self._isTemplateChar(replace_to))
+##            dbg('char:', "'"+char+"'", 'i =', i, 'replace_to =', replace_to)
+##            dbg('self._isTemplateChar(%d)?' % replace_to, self._isTemplateChar(replace_to))
             if not self._isTemplateChar(replace_to) and self._isCharAllowed( char, replace_to, allowAutoSelect=False, ignoreInsertRight=True):
                 replacement_text += char
             if not self._isTemplateChar(replace_to) and self._isCharAllowed( char, replace_to, allowAutoSelect=False, ignoreInsertRight=True):
                 replacement_text += char
-                dbg("not template(%(replace_to)d) and charAllowed('%(char)s',%(replace_to)d)" % locals())
-                dbg("replacement_text:", '"'+replacement_text+'"')
+##                dbg("not template(%(replace_to)d) and charAllowed('%(char)s',%(replace_to)d)" % locals())
+##                dbg("replacement_text:", '"'+replacement_text+'"')
                 i += 1
                 replace_to += 1
             elif( char == self._template[replace_to]
                 i += 1
                 replace_to += 1
             elif( char == self._template[replace_to]
@@ -5290,8 +5351,8 @@ class MaskedEditMixin:
                           ( (i == 0 and (char == '-' or (self._useParens and char == '(')))
                             or (i == self._masklength - 1 and self._useParens and char == ')') ) ) ):
                 replacement_text += char
                           ( (i == 0 and (char == '-' or (self._useParens and char == '(')))
                             or (i == self._masklength - 1 and self._useParens and char == ')') ) ) ):
                 replacement_text += char
-                dbg("'%(char)s' == template(%(replace_to)d)" % locals())
-                dbg("replacement_text:", '"'+replacement_text+'"')
+##                dbg("'%(char)s' == template(%(replace_to)d)" % locals())
+##                dbg("replacement_text:", '"'+replacement_text+'"')
                 i += 1
                 replace_to += 1
             else:
                 i += 1
                 replace_to += 1
             else:
@@ -5300,24 +5361,25 @@ class MaskedEditMixin:
                     valid_paste = False
                 else:
                     replacement_text += self._template[replace_to:next_entry]
                     valid_paste = False
                 else:
                     replacement_text += self._template[replace_to:next_entry]
-                    dbg("skipping template; next_entry =", next_entry)
-                    dbg("replacement_text:", '"'+replacement_text+'"')
+##                    dbg("skipping template; next_entry =", next_entry)
+##                    dbg("replacement_text:", '"'+replacement_text+'"')
                     replace_to = next_entry  # so next_entry will be considered on next loop
 
         if not valid_paste and raise_on_invalid:
                     replace_to = next_entry  # so next_entry will be considered on next loop
 
         if not valid_paste and raise_on_invalid:
-            dbg('raising exception', indent=0, suspend=0)
+##            dbg('raising exception', indent=0, suspend=0)
             raise ValueError('"%s" cannot be inserted into the control "%s"' % (paste_text, self.name))
 
         elif i < len(paste_text):
             valid_paste = False
             if raise_on_invalid:
             raise ValueError('"%s" cannot be inserted into the control "%s"' % (paste_text, self.name))
 
         elif i < len(paste_text):
             valid_paste = False
             if raise_on_invalid:
-                dbg('raising exception', indent=0, suspend=0)
+##                dbg('raising exception', indent=0, suspend=0)
                 raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
 
                 raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
 
-        dbg('valid_paste?', valid_paste)
+##        dbg('valid_paste?', valid_paste)
         if valid_paste:
         if valid_paste:
-            dbg('replacement_text: "%s"' % replacement_text, 'replace to:', replace_to)
-        dbg(indent=0, suspend=0)
+##            dbg('replacement_text: "%s"' % replacement_text, 'replace to:', replace_to)
+            pass
+##        dbg(indent=0, suspend=0)
         return valid_paste, replacement_text, replace_to
 
 
         return valid_paste, replacement_text, replace_to
 
 
@@ -5329,17 +5391,17 @@ class MaskedEditMixin:
         derived control because the mixin functions can't override a
         method of a sibling class.
         """
         derived control because the mixin functions can't override a
         method of a sibling class.
         """
-        dbg('MaskedEditMixin::_Paste (value = "%s")' % value, indent=1)
+##        dbg('MaskedEditMixin::_Paste (value = "%s")' % value, indent=1)
         if value is None:
             paste_text = self._getClipboardContents()
         else:
             paste_text = value
 
         if paste_text is not None:
         if value is None:
             paste_text = self._getClipboardContents()
         else:
             paste_text = value
 
         if paste_text is not None:
-            dbg('paste text: "%s"' % paste_text)
+##            dbg('paste text: "%s"' % paste_text)
             # (conversion will raise ValueError if paste isn't legal)
             sel_start, sel_to = self._GetSelection()
             # (conversion will raise ValueError if paste isn't legal)
             sel_start, sel_to = self._GetSelection()
-            dbg('selection:', (sel_start, sel_to))
+##            dbg('selection:', (sel_start, sel_to))
 
             # special case: handle allowInsert fields properly
             field = self._FindField(sel_start)
 
             # special case: handle allowInsert fields properly
             field = self._FindField(sel_start)
@@ -5348,7 +5410,7 @@ class MaskedEditMixin:
             if field._allowInsert and sel_to <= edit_end and sel_start + len(paste_text) < edit_end:
                 new_pos = sel_start + len(paste_text)   # store for subsequent positioning
                 paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip()
             if field._allowInsert and sel_to <= edit_end and sel_start + len(paste_text) < edit_end:
                 new_pos = sel_start + len(paste_text)   # store for subsequent positioning
                 paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip()
-                dbg('paste within insertable field; adjusted paste_text: "%s"' % paste_text, 'end:', edit_end)
+##                dbg('paste within insertable field; adjusted paste_text: "%s"' % paste_text, 'end:', edit_end)
                 sel_to = sel_start + len(paste_text)
 
             # Another special case: paste won't fit, but it's a right-insert field where entire
                 sel_to = sel_start + len(paste_text)
 
             # Another special case: paste won't fit, but it's a right-insert field where entire
@@ -5364,7 +5426,7 @@ class MaskedEditMixin:
                 amount_needed = len(paste_text) - (sel_to - sel_start)
                 if amount_needed <= empty_space:
                     sel_start -= amount_needed
                 amount_needed = len(paste_text) - (sel_to - sel_start)
                 if amount_needed <= empty_space:
                     sel_start -= amount_needed
-                    dbg('expanded selection to:', (sel_start, sel_to))
+##                    dbg('expanded selection to:', (sel_start, sel_to))
 
 
             # another special case: deal with signed values properly:
 
 
             # another special case: deal with signed values properly:
@@ -5375,10 +5437,10 @@ class MaskedEditMixin:
                     paste_signpos = paste_text.find('(')
 
                 # if paste text will result in signed value:
                     paste_signpos = paste_text.find('(')
 
                 # if paste text will result in signed value:
-##                dbg('paste_signpos != -1?', paste_signpos != -1)
-##                dbg('sel_start:', sel_start, 'signpos:', signpos)
-##                dbg('field._insertRight?', field._insertRight)
-##                dbg('sel_start - len(paste_text) >= signpos?', sel_start - len(paste_text) <= signpos)
+####                dbg('paste_signpos != -1?', paste_signpos != -1)
+####                dbg('sel_start:', sel_start, 'signpos:', signpos)
+####                dbg('field._insertRight?', field._insertRight)
+####                dbg('sel_start - len(paste_text) >= signpos?', sel_start - len(paste_text) <= signpos)
                 if paste_signpos != -1 and (sel_start <= signpos
                                             or (field._insertRight and sel_start - len(paste_text) <= signpos)):
                     signed = True
                 if paste_signpos != -1 and (sel_start <= signpos
                                             or (field._insertRight and sel_start - len(paste_text) <= signpos)):
                     signed = True
@@ -5386,32 +5448,32 @@ class MaskedEditMixin:
                     signed = False
                 # remove "sign" from paste text, so we can auto-adjust for sign type after paste:
                 paste_text = paste_text.replace('-', ' ').replace('(',' ').replace(')','')
                     signed = False
                 # remove "sign" from paste text, so we can auto-adjust for sign type after paste:
                 paste_text = paste_text.replace('-', ' ').replace('(',' ').replace(')','')
-                dbg('unsigned paste text: "%s"' % paste_text)
+##                dbg('unsigned paste text: "%s"' % paste_text)
             else:
                 signed = False
 
             # another special case: deal with insert-right fields when selection is empty and
             # cursor is at end of field:
             else:
                 signed = False
 
             # another special case: deal with insert-right fields when selection is empty and
             # cursor is at end of field:
-##            dbg('field._insertRight?', field._insertRight)
-##            dbg('sel_start == edit_end?', sel_start == edit_end)
-##            dbg('sel_start', sel_start, 'sel_to', sel_to)
+####            dbg('field._insertRight?', field._insertRight)
+####            dbg('sel_start == edit_end?', sel_start == edit_end)
+####            dbg('sel_start', sel_start, 'sel_to', sel_to)
             if field._insertRight and sel_start == edit_end and sel_start == sel_to:
                 sel_start -= len(paste_text)
                 if sel_start < 0:
                     sel_start = 0
             if field._insertRight and sel_start == edit_end and sel_start == sel_to:
                 sel_start -= len(paste_text)
                 if sel_start < 0:
                     sel_start = 0
-                dbg('adjusted selection:', (sel_start, sel_to))
+##                dbg('adjusted selection:', (sel_start, sel_to))
 
             try:
                 valid_paste, replacement_text, replace_to = self._validatePaste(paste_text, sel_start, sel_to, raise_on_invalid)
             except:
 
             try:
                 valid_paste, replacement_text, replace_to = self._validatePaste(paste_text, sel_start, sel_to, raise_on_invalid)
             except:
-                dbg('exception thrown', indent=0)
+##                dbg('exception thrown', indent=0)
                 raise
 
             if not valid_paste:
                 raise
 
             if not valid_paste:
-                dbg('paste text not legal for the selection or portion of the control following the cursor;')
+##                dbg('paste text not legal for the selection or portion of the control following the cursor;')
                 if not wx.Validator_IsSilent():
                     wx.Bell()
                 if not wx.Validator_IsSilent():
                     wx.Bell()
-                dbg(indent=0)
+##                dbg(indent=0)
                 return False
             # else...
             text = self._eraseSelection()
                 return False
             # else...
             text = self._eraseSelection()
@@ -5429,9 +5491,11 @@ class MaskedEditMixin:
                     if not self._isNeg:
                         self._isNeg = 1
 
                     if not self._isNeg:
                         self._isNeg = 1
 
-            dbg("new_text:", '"'+new_text+'"')
+##            dbg("new_text:", '"'+new_text+'"')
 
             if not just_return_value:
 
             if not just_return_value:
+                if new_text != self._GetValue():
+                    self.modified = True
                 if new_text == '':
                     self.ClearValue()
                 else:
                 if new_text == '':
                     self.ClearValue()
                 else:
@@ -5440,22 +5504,22 @@ class MaskedEditMixin:
                         new_pos = sel_start + len(replacement_text)
                     wx.CallAfter(self._SetInsertionPoint, new_pos)
             else:
                         new_pos = sel_start + len(replacement_text)
                     wx.CallAfter(self._SetInsertionPoint, new_pos)
             else:
-                dbg(indent=0)
+##                dbg(indent=0)
                 return new_text
         elif just_return_value:
                 return new_text
         elif just_return_value:
-            dbg(indent=0)
+##            dbg(indent=0)
             return self._GetValue()
             return self._GetValue()
-        dbg(indent=0)
+##        dbg(indent=0)
 
     def _Undo(self):
         """ Provides an Undo() method in base controls. """
 
     def _Undo(self):
         """ Provides an Undo() method in base controls. """
-        dbg("MaskedEditMixin::_Undo", indent=1)
+##        dbg("MaskedEditMixin::_Undo", indent=1)
         value = self._GetValue()
         prev = self._prevValue
         value = self._GetValue()
         prev = self._prevValue
-        dbg('current value:  "%s"' % value)
-        dbg('previous value: "%s"' % prev)
+##        dbg('current value:  "%s"' % value)
+##        dbg('previous value: "%s"' % prev)
         if prev is None:
         if prev is None:
-            dbg('no previous value', indent=0)
+##            dbg('no previous value', indent=0)
             return
 
         elif value != prev:
             return
 
         elif value != prev:
@@ -5492,24 +5556,24 @@ class MaskedEditMixin:
             # Determine where they stop differing in "undo" result:
             sm = difflib.SequenceMatcher(None, a=value, b=prev)
             i, j, k = sm.find_longest_match(sel_start, length, sel_start, length)
             # Determine where they stop differing in "undo" result:
             sm = difflib.SequenceMatcher(None, a=value, b=prev)
             i, j, k = sm.find_longest_match(sel_start, length, sel_start, length)
-            dbg('i,j,k = ', (i,j,k), 'value[i:i+k] = "%s"' % value[i:i+k], 'prev[j:j+k] = "%s"' % prev[j:j+k] )
+##            dbg('i,j,k = ', (i,j,k), 'value[i:i+k] = "%s"' % value[i:i+k], 'prev[j:j+k] = "%s"' % prev[j:j+k] )
 
             if k == 0:                              # no match found; select to end
                 sel_to = length
             else:
                 code_5tuples = sm.get_opcodes()
                 for op, i1, i2, j1, j2 in code_5tuples:
 
             if k == 0:                              # no match found; select to end
                 sel_to = length
             else:
                 code_5tuples = sm.get_opcodes()
                 for op, i1, i2, j1, j2 in code_5tuples:
-                    dbg("%7s value[%d:%d] (%s) prev[%d:%d] (%s)" %
-                            (op, i1, i2, value[i1:i2], j1, j2, prev[j1:j2]))
+##                    dbg("%7s value[%d:%d] (%s) prev[%d:%d] (%s)" % (op, i1, i2, value[i1:i2], j1, j2, prev[j1:j2]))
+                    pass
 
                 diff_found = False
                 # look backward through operations needed to produce "previous" value;
                 # first change wins:
                 for next_op in range(len(code_5tuples)-1, -1, -1):
                     op, i1, i2, j1, j2 = code_5tuples[next_op]
 
                 diff_found = False
                 # look backward through operations needed to produce "previous" value;
                 # first change wins:
                 for next_op in range(len(code_5tuples)-1, -1, -1):
                     op, i1, i2, j1, j2 = code_5tuples[next_op]
-                    dbg('value[i1:i2]: "%s"' % value[i1:i2], 'template[i1:i2] "%s"' % self._template[i1:i2])
+##                    dbg('value[i1:i2]: "%s"' % value[i1:i2], 'template[i1:i2] "%s"' % self._template[i1:i2])
                     if op == 'insert' and prev[j1:j2] != self._template[j1:j2]:
                     if op == 'insert' and prev[j1:j2] != self._template[j1:j2]:
-                        dbg('insert found: selection =>', (j1, j2))
+##                        dbg('insert found: selection =>', (j1, j2))
                         sel_start = j1
                         sel_to = j2
                         diff_found = True
                         sel_start = j1
                         sel_to = j2
                         diff_found = True
@@ -5523,11 +5587,11 @@ class MaskedEditMixin:
                         else:
                             sel_start = i1
                             sel_to = j1
                         else:
                             sel_start = i1
                             sel_to = j1
-                        dbg('delete found: selection =>', (sel_start, sel_to))
+##                        dbg('delete found: selection =>', (sel_start, sel_to))
                         diff_found = True
                         break
                     elif op == 'replace':
                         diff_found = True
                         break
                     elif op == 'replace':
-                        dbg('replace found: selection =>', (j1, j2))
+##                        dbg('replace found: selection =>', (j1, j2))
                         sel_start = j1
                         sel_to = j2
                         diff_found = True
                         sel_start = j1
                         sel_to = j2
                         diff_found = True
@@ -5542,17 +5606,17 @@ class MaskedEditMixin:
                         if op == 'equal':
                             continue
                         elif op == 'replace':
                         if op == 'equal':
                             continue
                         elif op == 'replace':
-                            dbg('setting sel_start to', i1)
+##                            dbg('setting sel_start to', i1)
                             sel_start = i1
                             break
                         elif op == 'insert' and not value[i1:i2]:
                             sel_start = i1
                             break
                         elif op == 'insert' and not value[i1:i2]:
-                            dbg('forward %s found' % op)
+##                            dbg('forward %s found' % op)
                             if prev[j1:j2].strip():
                             if prev[j1:j2].strip():
-                                dbg('item to insert non-empty; setting sel_start to', j1)
+##                                dbg('item to insert non-empty; setting sel_start to', j1)
                                 sel_start = j1
                                 break
                             elif not field._insertRight:
                                 sel_start = j1
                                 break
                             elif not field._insertRight:
-                                dbg('setting sel_start to inserted space:', j1)
+##                                dbg('setting sel_start to inserted space:', j1)
                                 sel_start = j1
                                 break
                         elif op == 'delete' and field._insertRight and not value[i1:i2].lstrip():
                                 sel_start = j1
                                 break
                         elif op == 'delete' and field._insertRight and not value[i1:i2].lstrip():
@@ -5563,7 +5627,7 @@ class MaskedEditMixin:
 
 
                 if not diff_found:
 
 
                 if not diff_found:
-                    dbg('no insert,delete or replace found (!)')
+##                    dbg('no insert,delete or replace found (!)')
                     # do "left-insert"-centric processing of difference based on l.c.s.:
                     if i == j and j != sel_start:         # match starts after start of selection
                         sel_to = sel_start + (j-sel_start)  # select to start of match
                     # do "left-insert"-centric processing of difference based on l.c.s.:
                     if i == j and j != sel_start:         # match starts after start of selection
                         sel_to = sel_start + (j-sel_start)  # select to start of match
@@ -5607,7 +5671,7 @@ class MaskedEditMixin:
             # To get all this right, we use the previous selection recorded to help us...
 
             if (sel_start, sel_to) != self._prevSelection:
             # To get all this right, we use the previous selection recorded to help us...
 
             if (sel_start, sel_to) != self._prevSelection:
-                dbg('calculated selection', (sel_start, sel_to), "doesn't match previous", self._prevSelection)
+##                dbg('calculated selection', (sel_start, sel_to), "doesn't match previous", self._prevSelection)
 
                 prev_sel_start, prev_sel_to = self._prevSelection
                 field = self._FindField(sel_start)
 
                 prev_sel_start, prev_sel_to = self._prevSelection
                 field = self._FindField(sel_start)
@@ -5626,8 +5690,8 @@ class MaskedEditMixin:
                     calc_select_len = sel_to - sel_start
                     prev_select_len = prev_sel_to - prev_sel_start
 
                     calc_select_len = sel_to - sel_start
                     prev_select_len = prev_sel_to - prev_sel_start
 
-                    dbg('sel_start == prev_sel_start', sel_start == prev_sel_start)
-                    dbg('sel_to > prev_sel_to', sel_to > prev_sel_to)
+##                    dbg('sel_start == prev_sel_start', sel_start == prev_sel_start)
+##                    dbg('sel_to > prev_sel_to', sel_to > prev_sel_to)
 
                     if prev_select_len >= calc_select_len:
                         # old selection was bigger; trust it:
 
                     if prev_select_len >= calc_select_len:
                         # old selection was bigger; trust it:
@@ -5638,7 +5702,7 @@ class MaskedEditMixin:
                           and sel_to == len(self._template) ):  # and calculated selection goes to end of control
 
                         i, j, k = sm.find_longest_match(prev_sel_to, length, prev_sel_to, length)
                           and sel_to == len(self._template) ):  # and calculated selection goes to end of control
 
                         i, j, k = sm.find_longest_match(prev_sel_to, length, prev_sel_to, length)
-                        dbg('i,j,k = ', (i,j,k), 'value[i:i+k] = "%s"' % value[i:i+k], 'prev[j:j+k] = "%s"' % prev[j:j+k] )
+##                        dbg('i,j,k = ', (i,j,k), 'value[i:i+k] = "%s"' % value[i:i+k], 'prev[j:j+k] = "%s"' % prev[j:j+k] )
                         if k > 0:
                             # difflib must not have optimized opcodes properly;
                             sel_to = j
                         if k > 0:
                             # difflib must not have optimized opcodes properly;
                             sel_to = j
@@ -5661,9 +5725,9 @@ class MaskedEditMixin:
                         else:
                             test_sel_start, test_sel_to = prev_sel_start, prev_sel_to
 
                         else:
                             test_sel_start, test_sel_to = prev_sel_start, prev_sel_to
 
-                        dbg('test selection:', (test_sel_start, test_sel_to))
-                        dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to])
-                        dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to])
+##                        dbg('test selection:', (test_sel_start, test_sel_to))
+##                        dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to])
+##                        dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to])
 
                         # if calculated selection spans characters, and same characters
                         # "before" the previous insertion point are present there as well,
 
                         # if calculated selection spans characters, and same characters
                         # "before" the previous insertion point are present there as well,
@@ -5674,14 +5738,15 @@ class MaskedEditMixin:
 
                             sel_start, sel_to = test_sel_start, test_sel_to
 
 
                             sel_start, sel_to = test_sel_start, test_sel_to
 
-            dbg('sel_start, sel_to:', sel_start, sel_to)
-            dbg('previous value: "%s"' % self._prevValue)
+##            dbg('sel_start, sel_to:', sel_start, sel_to)
+##            dbg('previous value: "%s"' % self._prevValue)
             self._SetValue(self._prevValue)
             self._SetInsertionPoint(sel_start)
             self._SetSelection(sel_start, sel_to)
         else:
             self._SetValue(self._prevValue)
             self._SetInsertionPoint(sel_start)
             self._SetSelection(sel_start, sel_to)
         else:
-            dbg('no difference between previous value')
-        dbg(indent=0)
+##            dbg('no difference between previous value')
+            pass
+##        dbg(indent=0)
 
 
     def _OnClear(self, event):
 
 
     def _OnClear(self, event):
@@ -5690,7 +5755,7 @@ class MaskedEditMixin:
 
 
     def _OnContextMenu(self, event):
 
 
     def _OnContextMenu(self, event):
-        dbg('MaskedEditMixin::OnContextMenu()', indent=1)
+##        dbg('MaskedEditMixin::OnContextMenu()', indent=1)
         menu = wxMenu()
         menu.Append(wxID_UNDO, "Undo", "")
         menu.AppendSeparator()
         menu = wxMenu()
         menu.Append(wxID_UNDO, "Undo", "")
         menu.AppendSeparator()
@@ -5721,7 +5786,7 @@ class MaskedEditMixin:
         self.PopupMenu(menu, event.GetPosition())
         menu.Destroy()
         self._contextMenu = None
         self.PopupMenu(menu, event.GetPosition())
         menu.Destroy()
         self._contextMenu = None
-        dbg(indent=0)
+##        dbg(indent=0)
 
     def _UndoUpdateUI(self, event):
         if self._prevValue is None or self._prevValue == self._curValue:
 
     def _UndoUpdateUI(self, event):
         if self._prevValue is None or self._prevValue == self._curValue:
@@ -5730,13 +5795,73 @@ class MaskedEditMixin:
             self._contextMenu.Enable(wxID_UNDO, True)
 
 
             self._contextMenu.Enable(wxID_UNDO, True)
 
 
-## ---------- ---------- ---------- ---------- ---------- ---------- ----------
+    def _OnCtrlParametersChanged(self):
+        """
+        Overridable function to allow derived classes to take action as a
+        result of parameter changes prior to possibly changing the value
+        of the control.
+        """
+        pass
+
+ ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
+# ## TRICKY BIT: to avoid a ton of boiler-plate, and to
+# ## automate the getter/setter generation for each valid
+# ## control parameter so we never forget to add the
+# ## functions when adding parameters, this loop
+# ## programmatically adds them to the class:
+# ## (This makes it easier for Designers like Boa to
+# ## deal with masked controls.)
+#
+# ## To further complicate matters, this is done with an
+# ## extra level of inheritance, so that "general" classes like
+# ## MaskedTextCtrl can have all possible attributes,
+# ## while derived classes, like TimeCtrl and MaskedNumCtrl
+# ## can prevent exposure of those optional attributes of their base
+# ## class that do not make sense for their derivation.  Therefore,
+# ## we define
+# ##    BaseMaskedTextCtrl(TextCtrl, MaskedEditMixin)
+# ## and
+# ##    MaskedTextCtrl(BaseMaskedTextCtrl, MaskedEditAccessorsMixin).
+# ##
+# ## This allows us to then derive:
+# ##    MaskedNumCtrl( BaseMaskedTextCtrl )
+# ##
+# ## and not have to expose all the same accessor functions for the
+# ## derived control when they don't all make sense for it.
+# ##
+class MaskedEditAccessorsMixin:
+
+    # Define the default set of attributes exposed by the most generic masked controls:
+    exposed_basectrl_params = MaskedEditMixin.valid_ctrl_params.keys() + Field.valid_params.keys()
+    exposed_basectrl_params.remove('index')
+    exposed_basectrl_params.remove('extent')
+    exposed_basectrl_params.remove('foregroundColour')   # (base class already has this)
+
+    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 MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
+
+
+class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
     """
     This is the primary derivation from MaskedEditMixin.  It provides
     a general masked text control that can be configured with different
     """
     This is the primary derivation from MaskedEditMixin.  It provides
     a general masked text control that can be configured with different
-    masks.
+    masks.  It's actually a "base masked textCtrl", so that the
+    MaskedTextCtrl class can be derived from it, and add those
+    accessor functions to it that are appropriate to the general class,
+    whilst other classes can derive from BaseMaskedTextCtrl, and
+    only define those accessor functions that are appropriate for
+    those derivations.
     """
 
     def __init__( self, parent, id=-1, value = '',
     """
 
     def __init__( self, parent, id=-1, value = '',
@@ -5755,6 +5880,7 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
 
         self.controlInitialized = True
         MaskedEditMixin.__init__( self, name, **kwargs )
 
         self.controlInitialized = True
         MaskedEditMixin.__init__( self, name, **kwargs )
+
         self._SetInitialValue(value)
 
         if setupEventHandling:
         self._SetInitialValue(value)
 
         if setupEventHandling:
@@ -5770,7 +5896,7 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
 
 
     def __repr__(self):
 
 
     def __repr__(self):
-        return "<MaskedTextCtrl: %s>" % self.GetValue()
+        return "<BaseMaskedTextCtrl: %s>" % self.GetValue()
 
 
     def _GetSelection(self):
 
 
     def _GetSelection(self):
@@ -5785,14 +5911,14 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         Allow mixin to set the text selection of this control.
         REQUIRED by any class derived from MaskedEditMixin.
         """
         Allow mixin to set the text selection of this control.
         REQUIRED by any class derived from MaskedEditMixin.
         """
-##        dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
+####        dbg("MaskedTextCtrl::_SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
         return self.SetSelection( sel_start, sel_to )
 
     def SetSelection(self, sel_start, sel_to):
         """
         This is just for debugging...
         """
         return self.SetSelection( sel_start, sel_to )
 
     def SetSelection(self, sel_start, sel_to):
         """
         This is just for debugging...
         """
-        dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
+##        dbg("MaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)" % locals())
         wx.TextCtrl.SetSelection(self, sel_start, sel_to)
 
 
         wx.TextCtrl.SetSelection(self, sel_start, sel_to)
 
 
@@ -5800,14 +5926,14 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         return self.GetInsertionPoint()
 
     def _SetInsertionPoint(self, pos):
         return self.GetInsertionPoint()
 
     def _SetInsertionPoint(self, pos):
-##        dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
+####        dbg("MaskedTextCtrl::_SetInsertionPoint(%(pos)d)" % locals())
         self.SetInsertionPoint(pos)
 
     def SetInsertionPoint(self, pos):
         """
         This is just for debugging...
         """
         self.SetInsertionPoint(pos)
 
     def SetInsertionPoint(self, pos):
         """
         This is just for debugging...
         """
-        dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
+##        dbg("MaskedTextCtrl::SetInsertionPoint(%(pos)d)" % locals())
         wx.TextCtrl.SetInsertionPoint(self, pos)
 
 
         wx.TextCtrl.SetInsertionPoint(self, pos)
 
 
@@ -5823,12 +5949,12 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         Allow mixin to set the raw value of the control with this function.
         REQUIRED by any class derived from MaskedEditMixin.
         """
         Allow mixin to set the raw value of the control with this function.
         REQUIRED by any class derived from MaskedEditMixin.
         """
-        dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
+##        dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
         # Record current selection and insertion point, for undo
         self._prevSelection = self._GetSelection()
         self._prevInsertionPoint = self._GetInsertionPoint()
         wx.TextCtrl.SetValue(self, value)
         # Record current selection and insertion point, for undo
         self._prevSelection = self._GetSelection()
         self._prevInsertionPoint = self._GetInsertionPoint()
         wx.TextCtrl.SetValue(self, value)
-        dbg(indent=0)
+##        dbg(indent=0)
 
     def SetValue(self, value):
         """
 
     def SetValue(self, value):
         """
@@ -5837,7 +5963,7 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         masked control.  NOTE: this must be done in the class derived
         from the base wx control.
         """
         masked control.  NOTE: this must be done in the class derived
         from the base wx control.
         """
-        dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
+##        dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
 
         if not self._mask:
             wx.TextCtrl.SetValue(self, value)    # revert to base control behavior
 
         if not self._mask:
             wx.TextCtrl.SetValue(self, value)    # revert to base control behavior
@@ -5857,12 +5983,12 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
             and (self._isFloat or self._isInt)            # and it's a numeric control
             and self._ctrl_constraints._alignRight ):   # and it's a right-aligned control
 
             and (self._isFloat or self._isInt)            # and it's a numeric control
             and self._ctrl_constraints._alignRight ):   # and it's a right-aligned control
 
-            dbg('len(value)', len(value), ' < self._masklength', self._masklength)
+##            dbg('len(value)', len(value), ' < self._masklength', self._masklength)
             # try to intelligently "pad out" the value to the right size:
             value = self._template[0:self._masklength - len(value)] + value
             if self._isFloat and value.find('.') == -1:
                 value = value[1:]
             # try to intelligently "pad out" the value to the right size:
             value = self._template[0:self._masklength - len(value)] + value
             if self._isFloat and value.find('.') == -1:
                 value = value[1:]
-            dbg('padded value = "%s"' % value)
+##            dbg('padded value = "%s"' % value)
 
         # make SetValue behave the same as if you had typed the value in:
         try:
 
         # make SetValue behave the same as if you had typed the value in:
         try:
@@ -5881,22 +6007,23 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
                 dateparts = value.split(' ')
                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                 value = string.join(dateparts, ' ')
                 dateparts = value.split(' ')
                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                 value = string.join(dateparts, ' ')
-                dbg('adjusted value: "%s"' % value)
+##                dbg('adjusted value: "%s"' % value)
                 value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             else:
                 value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             else:
-                dbg('exception thrown', indent=0)
+##                dbg('exception thrown', indent=0)
                 raise
 
                 raise
 
-        self._SetValue(value)
-##        dbg('queuing insertion after .SetValue', self._masklength)
+        self._SetValue(value)   # note: to preserve similar capability, .SetValue()
+                                # does not change IsModified()
+####        dbg('queuing insertion after .SetValue', self._masklength)
         wx.CallAfter(self._SetInsertionPoint, self._masklength)
         wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
         wx.CallAfter(self._SetInsertionPoint, self._masklength)
         wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def Clear(self):
         """ Blanks the current control value by replacing it with the default value."""
 
 
     def Clear(self):
         """ Blanks the current control value by replacing it with the default value."""
-        dbg("MaskedTextCtrl::Clear - value reset to default value (template)")
+##        dbg("MaskedTextCtrl::Clear - value reset to default value (template)")
         if self._mask:
             self.ClearValue()
         else:
         if self._mask:
             self.ClearValue()
         else:
@@ -5908,9 +6035,9 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         Allow mixin to refresh the base control with this function.
         REQUIRED by any class derived from MaskedEditMixin.
         """
         Allow mixin to refresh the base control with this function.
         REQUIRED by any class derived from MaskedEditMixin.
         """
-        dbg('MaskedTextCtrl::_Refresh', indent=1)
+##        dbg('MaskedTextCtrl::_Refresh', indent=1)
         wx.TextCtrl.Refresh(self)
         wx.TextCtrl.Refresh(self)
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def Refresh(self):
 
 
     def Refresh(self):
@@ -5919,10 +6046,10 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         validate the contents of the masked control as it refreshes.
         NOTE: this must be done in the class derived from the base wx control.
         """
         validate the contents of the masked control as it refreshes.
         NOTE: this must be done in the class derived from the base wx control.
         """
-        dbg('MaskedTextCtrl::Refresh', indent=1)
+##        dbg('MaskedTextCtrl::Refresh', indent=1)
         self._CheckValid()
         self._Refresh()
         self._CheckValid()
         self._Refresh()
-        dbg(indent=0)
+##        dbg(indent=0)
 
 
     def _IsEditable(self):
 
 
     def _IsEditable(self):
@@ -5988,12 +6115,22 @@ class MaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         return self._calcSize(size)
 
 
         return self._calcSize(size)
 
 
+class MaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
+    """
+    This extra level of inheritance allows us to add the generic set of
+    masked edit parameters only to this class while allowing other
+    classes to derive from the "base" masked text control, and provide
+    a smaller set of valid accessor functions.
+    """
+    pass
+
+
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 ## Because calling SetSelection programmatically does not fire EVT_COMBOBOX
 ## events, we have to do it ourselves when we auto-complete.
 class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
     def __init__(self, id, selection = 0, object=None):
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 ## Because calling SetSelection programmatically does not fire EVT_COMBOBOX
 ## events, we have to do it ourselves when we auto-complete.
 class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
     def __init__(self, id, selection = 0, object=None):
-        wx.PyCommandEvent.__init__(self, wx.EVT_COMMAND_COMBOBOX_SELECTED, id)
+        wx.PyCommandEvent.__init__(self, wx.wxEVT_COMMAND_COMBOBOX_SELECTED, id)
 
         self.__selection = selection
         self.SetEventObject(object)
 
         self.__selection = selection
         self.SetEventObject(object)
@@ -6004,7 +6141,7 @@ class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
         return self.__selection
 
 
         return self.__selection
 
 
-class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
+class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
     """
     This masked edit control adds the ability to use a masked input
     on a combobox, and do auto-complete of such values.
     """
     This masked edit control adds the ability to use a masked input
     on a combobox, and do auto-complete of such values.
@@ -6032,8 +6169,9 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
             kwargs['compareNoCase'] = True
 
         MaskedEditMixin.__init__( self, name, **kwargs )
             kwargs['compareNoCase'] = True
 
         MaskedEditMixin.__init__( self, name, **kwargs )
+
         self._choices = self._ctrl_constraints._choices
         self._choices = self._ctrl_constraints._choices
-        dbg('self._choices:', self._choices)
+##        dbg('self._choices:', self._choices)
 
         if self._ctrl_constraints._alignRight:
             choices = [choice.rjust(self._masklength) for choice in choices]
 
         if self._ctrl_constraints._alignRight:
             choices = [choice.rjust(self._masklength) for choice in choices]
@@ -6169,7 +6307,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
             and self._ctrl_constraints._alignRight ):   # and it's a right-aligned control
             # try to intelligently "pad out" the value to the right size:
             value = self._template[0:self._masklength - len(value)] + value
             and self._ctrl_constraints._alignRight ):   # and it's a right-aligned control
             # try to intelligently "pad out" the value to the right size:
             value = self._template[0:self._masklength - len(value)] + value
-            dbg('padded value = "%s"' % value)
+##            dbg('padded value = "%s"' % value)
 
         # For wxComboBox, ensure that values are properly padded so that
         # if varying length choices are supplied, they always show up
 
         # For wxComboBox, ensure that values are properly padded so that
         # if varying length choices are supplied, they always show up
@@ -6198,13 +6336,13 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                 dateparts = value.split(' ')
                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                 value = string.join(dateparts, ' ')
                 dateparts = value.split(' ')
                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                 value = string.join(dateparts, ' ')
-                dbg('adjusted value: "%s"' % value)
+##                dbg('adjusted value: "%s"' % value)
                 value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             else:
                 raise
 
         self._SetValue(value)
                 value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
             else:
                 raise
 
         self._SetValue(value)
-##        dbg('queuing insertion after .SetValue', self._masklength)
+####        dbg('queuing insertion after .SetValue', self._masklength)
         wx.CallAfter(self._SetInsertionPoint, self._masklength)
         wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
 
         wx.CallAfter(self._SetInsertionPoint, self._masklength)
         wx.CallAfter(self._SetSelection, self._masklength, self._masklength)
 
@@ -6301,7 +6439,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                 choice = choice.ljust(self._masklength)
             if self._ctrl_constraints._fillChar != ' ':
                 choice = choice.replace(' ', self._fillChar)
                 choice = choice.ljust(self._masklength)
             if self._ctrl_constraints._fillChar != ' ':
                 choice = choice.replace(' ', self._fillChar)
-            dbg('updated choice:', choice)
+##            dbg('updated choice:', choice)
 
 
             self._ctrl_constraints._compareChoices.append(compareChoice)
 
 
             self._ctrl_constraints._compareChoices.append(compareChoice)
@@ -6330,14 +6468,12 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         wx.ComboBox.Clear(self)
 
 
         wx.ComboBox.Clear(self)
 
 
-    def SetCtrlParameters( self, **kwargs ):
+    def _OnCtrlParametersChanged(self):
         """
         """
-        Override mixin's default SetCtrlParameters to detect changes in choice list, so
+        Override mixin's default OnCtrlParametersChanged to detect changes in choice list, so
         we can update the base control:
         """
         we can update the base control:
         """
-        MaskedEditMixin.SetCtrlParameters(self, **kwargs )
-        if( self.controlInitialized
-            and (kwargs.has_key('choices') or self._choices != self._ctrl_constraints._choices) ):
+        if self.controlInitialized and self._choices != self._ctrl_constraints._choices:
             wx.ComboBox.Clear(self)
             self._choices = self._ctrl_constraints._choices
             for choice in self._choices:
             wx.ComboBox.Clear(self)
             self._choices = self._ctrl_constraints._choices
             for choice in self._choices:
@@ -6351,25 +6487,25 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         works, but has the nasty side effect of generating lots of intermediate
         events.
         """
         works, but has the nasty side effect of generating lots of intermediate
         events.
         """
-        dbg(suspend=1)  # turn off debugging around this function
-        dbg('MaskedComboBox::GetMark', indent=1)
+##        dbg(suspend=1)  # turn off debugging around this function
+##        dbg('MaskedComboBox::GetMark', indent=1)
         if self.__readonly:
         if self.__readonly:
-            dbg(indent=0)
+##            dbg(indent=0)
             return 0, 0 # no selection possible for editing
 ##        sel_start, sel_to = wxComboBox.GetMark(self)        # what I'd *like* to have!
         sel_start = sel_to = self.GetInsertionPoint()
             return 0, 0 # no selection possible for editing
 ##        sel_start, sel_to = wxComboBox.GetMark(self)        # what I'd *like* to have!
         sel_start = sel_to = self.GetInsertionPoint()
-        dbg("current sel_start:", sel_start)
+##        dbg("current sel_start:", sel_start)
         value = self.GetValue()
         value = self.GetValue()
-        dbg('value: "%s"' % value)
+##        dbg('value: "%s"' % value)
 
         self._ignoreChange = True               # tell _OnTextChange() to ignore next event (if any)
 
         wx.ComboBox.Cut(self)
         newvalue = self.GetValue()
 
         self._ignoreChange = True               # tell _OnTextChange() to ignore next event (if any)
 
         wx.ComboBox.Cut(self)
         newvalue = self.GetValue()
-        dbg("value after Cut operation:", newvalue)
+##        dbg("value after Cut operation:", newvalue)
 
         if newvalue != value:                   # something was selected; calculate extent
 
         if newvalue != value:                   # something was selected; calculate extent
-            dbg("something selected")
+##            dbg("something selected")
             sel_to = sel_start + len(value) - len(newvalue)
             wx.ComboBox.SetValue(self, value)    # restore original value and selection (still ignoring change)
             wx.ComboBox.SetInsertionPoint(self, sel_start)
             sel_to = sel_start + len(value) - len(newvalue)
             wx.ComboBox.SetValue(self, value)    # restore original value and selection (still ignoring change)
             wx.ComboBox.SetInsertionPoint(self, sel_start)
@@ -6377,7 +6513,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
 
         self._ignoreChange = False              # tell _OnTextChange() to pay attn again
 
 
         self._ignoreChange = False              # tell _OnTextChange() to pay attn again
 
-        dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0)
+##        dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0)
         return sel_start, sel_to
 
 
         return sel_start, sel_to
 
 
@@ -6386,7 +6522,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         Necessary for bookkeeping on choice selection, to keep current value
         current.
         """
         Necessary for bookkeeping on choice selection, to keep current value
         current.
         """
-        dbg('MaskedComboBox::SetSelection(%d)' % index)
+##        dbg('MaskedComboBox::SetSelection(%d)' % index)
         if self._mask:
             self._prevValue = self._curValue
             self._curValue = self._choices[index]
         if self._mask:
             self._prevValue = self._curValue
             self._curValue = self._choices[index]
@@ -6414,7 +6550,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         on the text of the control somehow interferes with the combobox's
         selection mechanism for the arrow keys.
         """
         on the text of the control somehow interferes with the combobox's
         selection mechanism for the arrow keys.
         """
-        dbg('MaskedComboBox::OnSelectChoice', indent=1)
+##        dbg('MaskedComboBox::OnSelectChoice', indent=1)
 
         if not self._mask:
             event.Skip()
 
         if not self._mask:
             event.Skip()
@@ -6436,7 +6572,7 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                                                 self._ctrl_constraints._compareNoCase,
                                                 current_index = self._ctrl_constraints._autoCompleteIndex)
         if match_index is not None:
                                                 self._ctrl_constraints._compareNoCase,
                                                 current_index = self._ctrl_constraints._autoCompleteIndex)
         if match_index is not None:
-            dbg('setting selection to', match_index)
+##            dbg('setting selection to', match_index)
             # issue appropriate event to outside:
             self._OnAutoSelect(self._ctrl_constraints, match_index=match_index)
             self._CheckValid()
             # issue appropriate event to outside:
             self._OnAutoSelect(self._ctrl_constraints, match_index=match_index)
             self._CheckValid()
@@ -6445,15 +6581,15 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
             pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
             field = self._FindField(pos)
             if self.IsEmpty() or not field._hasList:
             pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
             field = self._FindField(pos)
             if self.IsEmpty() or not field._hasList:
-                dbg('selecting 1st value in list')
+##                dbg('selecting 1st value in list')
                 self._OnAutoSelect(self._ctrl_constraints, match_index=0)
                 self._CheckValid()
                 keep_processing = False
             else:
                 # attempt field-level auto-complete
                 self._OnAutoSelect(self._ctrl_constraints, match_index=0)
                 self._CheckValid()
                 keep_processing = False
             else:
                 # attempt field-level auto-complete
-                dbg(indent=0)
+##                dbg(indent=0)
                 keep_processing = self._OnAutoCompleteField(event)
                 keep_processing = self._OnAutoCompleteField(event)
-        dbg('keep processing?', keep_processing, indent=0)
+##        dbg('keep processing?', keep_processing, indent=0)
         return keep_processing
 
 
         return keep_processing
 
 
@@ -6462,17 +6598,17 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         Override mixin (empty) autocomplete handler, so that autocompletion causes
         combobox to update appropriately.
         """
         Override mixin (empty) autocomplete handler, so that autocompletion causes
         combobox to update appropriately.
         """
-        dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1)
+##        dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1)
 ##        field._autoCompleteIndex = match_index
         if field == self._ctrl_constraints:
             self.SetSelection(match_index)
 ##        field._autoCompleteIndex = match_index
         if field == self._ctrl_constraints:
             self.SetSelection(match_index)
-            dbg('issuing combo selection event')
+##            dbg('issuing combo selection event')
             self.GetEventHandler().ProcessEvent(
                 MaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
         self._CheckValid()
             self.GetEventHandler().ProcessEvent(
                 MaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
         self._CheckValid()
-        dbg('field._autoCompleteIndex:', match_index)
-        dbg('self.GetSelection():', self.GetSelection())
-        dbg(indent=0)
+##        dbg('field._autoCompleteIndex:', match_index)
+##        dbg('self.GetSelection():', self.GetSelection())
+##        dbg(indent=0)
 
 
     def _OnReturn(self, event):
 
 
     def _OnReturn(self, event):
@@ -6485,25 +6621,75 @@ class MaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         programmatic wxComboBox.SetSelection() call to pick the appropriate
         item in the list. (and then do the usual OnReturn bit.)
         """
         programmatic wxComboBox.SetSelection() call to pick the appropriate
         item in the list. (and then do the usual OnReturn bit.)
         """
-        dbg('MaskedComboBox::OnReturn', indent=1)
-        dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
+##        dbg('MaskedComboBox::OnReturn', indent=1)
+##        dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
         if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
             wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
 
         event.m_keyCode = wx.WXK_TAB
         event.Skip()
         if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
             wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
 
         event.m_keyCode = wx.WXK_TAB
         event.Skip()
-        dbg(indent=0)
+##        dbg(indent=0)
+
+
+class MaskedComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
+    """
+    This extra level of inheritance allows us to add the generic set of
+    masked edit parameters only to this class while allowing other
+    classes to derive from the "base" masked combobox control, and provide
+    a smaller set of valid accessor functions.
+    """
+    pass
 
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
 
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
-class IpAddrCtrl( MaskedTextCtrl ):
+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.
     """
     """
     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,
     def __init__( self, parent, id=-1, value = '',
                   pos = wx.DefaultPosition,
                   size = wx.DefaultSize,
@@ -6521,7 +6707,7 @@ class IpAddrCtrl( MaskedTextCtrl ):
             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}"
 
 
             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}"
 
 
-        MaskedTextCtrl.__init__(
+        BaseMaskedTextCtrl.__init__(
                 self, parent, id=id, value = value,
                 pos=pos, size=size,
                 style = style,
                 self, parent, id=id, value = value,
                 pos=pos, size=size,
                 style = style,
@@ -6530,6 +6716,7 @@ class IpAddrCtrl( MaskedTextCtrl ):
                 setupEventHandling = setupEventHandling,
                 **kwargs)
 
                 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]))"
         # 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]))"
@@ -6548,7 +6735,7 @@ class IpAddrCtrl( MaskedTextCtrl ):
 
 
     def OnDot(self, event):
 
 
     def OnDot(self, event):
-        dbg('IpAddrCtrl::OnDot', indent=1)
+##        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)
         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
         oldvalue = self.GetValue()
         edit_start, edit_end, slice = self._FindFieldExtent(pos, getslice=True)
@@ -6559,26 +6746,26 @@ class IpAddrCtrl( MaskedTextCtrl ):
                 newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
                 self._SetValue(newvalue)
                 self._SetInsertionPoint(pos)
                 newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
                 self._SetValue(newvalue)
                 self._SetInsertionPoint(pos)
-        dbg(indent=0)
+##        dbg(indent=0)
         return self._OnChangeField(event)
 
 
 
     def GetAddress(self):
         return self._OnChangeField(event)
 
 
 
     def GetAddress(self):
-        value = MaskedTextCtrl.GetValue(self)
+        value = BaseMaskedTextCtrl.GetValue(self)
         return value.replace(' ','')    # remove spaces from the value
 
 
     def _OnCtrl_S(self, event):
         return value.replace(' ','')    # remove spaces from the value
 
 
     def _OnCtrl_S(self, event):
-        dbg("IpAddrCtrl::_OnCtrl_S")
+##        dbg("IpAddrCtrl::_OnCtrl_S")
         if self._demo:
             print "value:", self.GetAddress()
         return False
 
     def SetValue(self, value):
         if self._demo:
             print "value:", self.GetAddress()
         return False
 
     def SetValue(self, value):
-        dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1)
+##        dbg('IpAddrCtrl::SetValue(%s)' % str(value), indent=1)
         if type(value) not in (types.StringType, types.UnicodeType):
         if type(value) not in (types.StringType, types.UnicodeType):
-            dbg(indent=0)
+##            dbg(indent=0)
             raise ValueError('%s must be a string', str(value))
 
         bValid = True   # assume True
             raise ValueError('%s must be a string', str(value))
 
         bValid = True   # assume True
@@ -6608,13 +6795,13 @@ class IpAddrCtrl( MaskedTextCtrl ):
                     parts[i] = '   '    # convert empty field to 3-char length
 
         if not bValid:
                     parts[i] = '   '    # convert empty field to 3-char length
 
         if not bValid:
-            dbg(indent=0)
+##            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:
             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)
+##            dbg('parts:', parts)
             value = string.join(parts, '.')
             value = string.join(parts, '.')
-            MaskedTextCtrl.SetValue(self, value)
-        dbg(indent=0)
+            BaseMaskedTextCtrl.SetValue(self, value)
+##        dbg(indent=0)
 
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
@@ -6928,7 +7115,7 @@ To see a great example of validations in action, try entering a bad email addres
            ("US Date + Time","USDATETIMEMMDDYYYY/HHMM"),
            ("US Date MMDDYYYY","USDATEMMDDYYYY/"),
            ("Time (with seconds)","TIMEHHMMSS"),
            ("US Date + Time","USDATETIMEMMDDYYYY/HHMM"),
            ("US Date MMDDYYYY","USDATEMMDDYYYY/"),
            ("Time (with seconds)","TIMEHHMMSS"),
-           ("Military Time\n(without seconds)","MILTIMEHHMM"),
+           ("Military Time\n(without seconds)","24HRTIMEHHMM"),
            ("Social Sec#","USSOCIALSEC"),
            ("Credit Card","CREDITCARD"),
            ("Expiration MM/YY","EXPDATEMMYY"),
            ("Social Sec#","USSOCIALSEC"),
            ("Credit Card","CREDITCARD"),
            ("Expiration MM/YY","EXPDATEMMYY"),
@@ -7012,7 +7199,7 @@ i=1
 ##
 ## 3. WS: Controls on wxPanels don't seem to pass Shift-WXK_TAB to their
 ##      EVT_KEY_DOWN or EVT_CHAR event handlers.  Until this is fixed in
 ##
 ## 3. WS: Controls on wxPanels don't seem to pass Shift-WXK_TAB to their
 ##      EVT_KEY_DOWN or EVT_CHAR event handlers.  Until this is fixed in
-##      wxWindows, shift-tab won't take you backwards through the fields of
+##      wxWidgets, shift-tab won't take you backwards through the fields of
 ##      a MaskedTextCtrl like it should.  Until then Shifted arrow keys will
 ##      work like shift-tab and tab ought to.
 ##
 ##      a MaskedTextCtrl like it should.  Until then Shifted arrow keys will
 ##      work like shift-tab and tab ought to.
 ##
@@ -7032,6 +7219,28 @@ i=1
 
 ## CHANGELOG:
 ## ====================
 
 ## CHANGELOG:
 ## ====================
+##  Version 1.5
+##  (Reported) bugs fixed:
+##   1. Crash ensues if you attempt to change the mask of a read-only
+##      MaskedComboBox after initial construction.
+##   2. Changed strategy of defining Get/Set property functions so that
+##      these are now generated dynamically at runtime, rather than as
+##      part of the class definition.  (This makes it possible to have
+##      more general base classes that have many more options for configuration
+##      without requiring that derivations support the same options.)
+##   3. Fixed IsModified for _Paste() and _OnErase().
+##
+##   Enhancements:
+##   1. Fixed "attribute function inheritance," since base control is more
+##      generic than subsequent derivations, not all property functions of a
+##      generic control should be exposed in those derivations.  New strategy
+##      uses base control classes (eg. BaseMaskedTextCtrl) that should be
+##      used to derive new class types, and mixed with their own mixins to
+##      only expose those attributes from the generic masked controls that
+##      make sense for the derivation.  (This makes Boa happier.)
+##   2. Renamed (with b-c) MILTIME autoformats to 24HRTIME, so as to be less
+##      "parochial."
+##
 ##  Version 1.4
 ##  (Reported) bugs fixed:
 ##   1. Right-click menu allowed "cut" operation that destroyed mask
 ##  Version 1.4
 ##  (Reported) bugs fixed:
 ##   1. Right-click menu allowed "cut" operation that destroyed mask
@@ -7311,7 +7520,7 @@ i=1
 ##      (i.e. disallow empty values if True).
 ##
 ##  Version 0.0.5
 ##      (i.e. disallow empty values if True).
 ##
 ##  Version 0.0.5
-##   1. get_plainValue method renamed to GetPlainValue following the wxWindows
+##   1. get_plainValue method renamed to GetPlainValue following the wxWidgets
 ##      StudlyCaps(tm) standard (thanks Paul Moore).  ;)
 ##   2. New format code 'F' causes the control to auto-fit (auto-size) itself
 ##      based on the length of the mask template.
 ##      StudlyCaps(tm) standard (thanks Paul Moore).  ;)
 ##   2. New format code 'F' causes the control to auto-fit (auto-size) itself
 ##      based on the length of the mask template.