]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wxPython/lib/timectrl.py
Clear possible exception from failed wxPyConvertSwigPtr
[wxWidgets.git] / wxPython / wxPython / lib / timectrl.py
index 52d9c4b4fc00566c2b2226369d9e05ff18d1cd86..d71c4cebd66839185097deba5d8fb2fbd130ec68 100644 (file)
-#----------------------------------------------------------------------------
-# Name:         wxTimeCtrl.py
-# Author:       Will Sadkin
-# Created:      09/19/2002
-# Copyright:    (c) 2002 by Will Sadkin, 2002
-# RCS-ID:       $Id$
-# License:      wxWindows license
-#----------------------------------------------------------------------------
-# NOTE:
-#   This was written way it is because of the lack of masked edit controls
-#   in wxWindows/wxPython.  I would also have preferred to derive this
-#   control from a wxSpinCtrl rather than wxTextCtrl, but the wxTextCtrl
-#   component of that control is inaccessible through the interface exposed in
-#   wxPython.
-#
-#   wxTimeCtrl does not use validators, because it does careful manipulation
-#   of the cursor in the text window on each keystroke, and validation is
-#   cursor-position specific, so the control intercepts the key codes before the
-#   validator would fire.
-#
+## This file imports items from the wx package into the wxPython package for
+## backwards compatibility.  Some names will also have a 'wx' added on if
+## that is how they used to be named in the old wxPython package.
 
-from wxPython.wx import *
-import string
+import wx.lib.masked.timectrl
 
-# wxWindows' wxTextCtrl translates Composite "control key"
-# events into single events before returning them to its OnChar
-# routine.  The doc says that this results in 1 for Ctrl-A, 2 for
-# Ctrl-B, etc. However, there are no wxPython or wxWindows
-# symbols for them, so I'm defining codes for Ctrl-X (cut) and
-# Ctrl-V (paste) here for readability:
-WXK_CTRL_X = (ord('X')+1) - ord('A')
-WXK_CTRL_V = (ord('V')+1) - ord('A')
+__doc__ =  wx.lib.masked.timectrl.__doc__
 
-# The following bit of function is for debugging the subsequent code.
-# To turn on debugging output, set _debug to 1
-_debug = 0
-_indent = 0
-
-if _debug:
-    def _dbg(*args, **kwargs):
-        global _indent
-
-        if len(args):
-            if _indent:      print ' ' * 3 * _indent,
-            for arg in args: print arg,
-            print
-        # else do nothing
-
-        # post process args:
-        for kwarg, value in kwargs.items():
-            if kwarg == 'indent' and value:         _indent = _indent + 1
-            elif kwarg == 'indent' and value == 0:  _indent = _indent - 1
-            if _indent < 0: _indent = 0
-else:
-    def _dbg(*args, **kwargs):
-        pass
-
-
-# This class of event fires whenever the value of the time changes in the control:
-wxEVT_TIMEVAL_UPDATED = wxNewId()
-def EVT_TIMEUPDATE(win, id, func):
-    """Used to trap events indicating that the current time has been changed."""
-    win.Connect(id, -1, wxEVT_TIMEVAL_UPDATED, func)
-
-class TimeUpdatedEvent(wxPyCommandEvent):
-    def __init__(self, id, value ='12:00:00 AM'):
-        wxPyCommandEvent.__init__(self, wxEVT_TIMEVAL_UPDATED, id)
-        self.value = value
-    def GetValue(self):
-        """Retrieve the value of the time control at the time this event was generated"""
-        return self.value
-
-
-# Set up all the positions of the cells in the wxTimeCtrl (once at module import):
-# Format of control is:
-#               hh:mm:ss xM
-#                         1
-# positions:    01234567890
-_listCells = ['hour', 'minute', 'second', 'am_pm']
-_listCellRange =   [(0,1,2), (3,4,5), (6,7,8), (9,10,11)]
-_listDelimPos =  [2,5,8]
-
-# Create dictionary of cell ranges, indexed by name or position in the range:
-_dictCellRange = {}
-for i in range(4):
-    _dictCellRange[_listCells[i]] = _listCellRange[i]
-for cell in _listCells:
-    for i in _dictCellRange[cell]:
-        _dictCellRange[i] = _dictCellRange[cell]
-
-
-# Create lists of starting and ending positions for each range, and a dictionary of starting
-# positions indexed by name
-_listStartCellPos = []
-_listEndCellPos = []
-for tup in _listCellRange:
-    _listStartCellPos.append(tup[0])  # 1st char of cell
-    _listEndCellPos.append(tup[1])    # last char of cell (not including delimiter)
-
-_dictStartCellPos = {}
-for i in range(4):
-    _dictStartCellPos[_listCells[i]] = _listStartCellPos[i]
-
-
-class wxTimeCtrl(wxTextCtrl):
-    def __init__ (
-                self, parent, id=-1, value = '12:00:00 AM',
-                pos = wxDefaultPosition, size = wxDefaultSize,
-                fmt24hr=0,
-                spinButton = None,
-                style = wxTE_PROCESS_TAB, name = "time"
-        ):
-        wxTextCtrl.__init__(self, parent, id, value='',
-                            pos=pos, size=size, style=style, name=name)
-
-        self.__fmt24hr = fmt24hr
-
-        if size == wxDefaultSize:
-            # set appropriate default sizes depending on format:
-            if self.__fmt24hr:
-                testText = '00:00:00'
-            else:
-                testText = '00:00:00 MM'
-            _dbg(wxPlatform)
-
-            if wxPlatform != "__WXMSW__":   # give it a little extra space
-                testText += 'M'
-            if wxPlatform == "__WXMAC__":   # give it even a little more...
-                testText += 'M'
-
-            w, h = self.GetTextExtent(testText)
-            self.SetClientSize( (w+4, self.GetClientSize().height) )
-
-
-        if self.__fmt24hr:  self.__lastCell = 'second'
-        else:               self.__lastCell = 'am_pm'
-
-        # Validate initial value and set if appropriate
-        try:
-            self.SetValue(value)
-        except:
-            self.SetValue('12:00:00 AM')
-
-        # set initial position and selection state
-        self.__SetCurrentCell(_dictStartCellPos['hour'])
-        self.__OnChangePos(None)
-
-        # Set up internal event handlers to change the event reaction behavior of
-        # the base wxTextCtrl:
-        EVT_TEXT(self, self.GetId(), self.__OnTextChange)
-        EVT_SET_FOCUS(self, self.__OnFocus)
-        EVT_LEFT_UP(self, self.__OnChangePos)
-        EVT_LEFT_DCLICK(self, self.__OnDoubleClick)
-        EVT_CHAR(self, self.__OnChar)
-
-        if spinButton:
-            self.BindSpinButton(spinButton)     # bind spin button up/down events to this control
-
-
-    def BindSpinButton(self, sb):
-        """
-        This function binds an externally created spin button to the control, so that
-        up/down events from the button automatically change the control.
-        """
-        _dbg('wxTimeCtrl::BindSpinButton')
-        self.__spinButton = sb
-        if self.__spinButton:
-            # bind event handlers to spin ctrl
-            EVT_SPIN_UP(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinUp)
-            EVT_SPIN_DOWN(self.__spinButton, self.__spinButton.GetId(), self.__OnSpinDown)
-
-
-
-    def __repr__(self):
-        return "<wxTimeCtrl: %s>" % self.GetValue()
-
-
-
-    def SetValue(self, value):
-        """
-        Validating SetValue function for time strings, doing 12/24 format conversion as appropriate.
-        """
-        _dbg('wxTimeCtrl::SetValue', indent=1)
-        dict_range = _dictCellRange             # (for brevity)
-        dict_start = _dictStartCellPos
-
-        fmt12len = dict_range['am_pm'][-1]
-        fmt24len = dict_range['second'][-1]
-        try:
-            separators_correct = value[2] == ':' and value[5] == ':'
-            len_ok = len(value) in (fmt12len, fmt24len)
-
-            if len(value) > fmt24len:
-                separators_correct = separators_correct and value[8] == ' '
-            hour = int(value[dict_range['hour'][0]:dict_range['hour'][-1]])
-            hour_ok = ((hour in range(0,24) and len(value) == fmt24len)
-                       or (hour in range(1,13) and len(value) == fmt12len
-                           and value[dict_start['am_pm']:] in ('AM', 'PM')))
-
-            minute = int(value[dict_range['minute'][0]:dict_range['minute'][-1]])
-            min_ok  = minute in range(60)
-            second  = int(value[dict_range['second'][0]:dict_range['second'][-1]])
-            sec_ok  = second in range(60)
-
-            _dbg('len_ok =', len_ok, 'separators_correct =', separators_correct)
-            _dbg('hour =', hour, 'hour_ok =', hour_ok, 'min_ok =', min_ok, 'sec_ok =', sec_ok)
-
-            if len_ok and hour_ok and min_ok and sec_ok and separators_correct:
-                _dbg('valid time string')
-
-                self.__hour = hour
-                if len(value) == fmt12len:                      # handle 12 hour format conversion for actual hour:
-                    am = value[dict_start['am_pm']:] == 'AM'
-                    if hour != 12 and not am:
-                        self.__hour = hour = (hour+12) % 24
-                    elif hour == 12:
-                        if am: self.__hour = hour = 0
-
-                self.__minute = minute
-                self.__second = second
-
-                # valid time
-                need_to_convert = ((self.__fmt24hr and len(value) == fmt12len)
-                                   or (not self.__fmt24hr and len(value) == fmt24len))
-                _dbg('need_to_convert =', need_to_convert)
-
-                if need_to_convert:     #convert to 12/24 hour format as specified:
-                    if self.__fmt24hr and len(value) == fmt12len:
-                        text = '%.2d:%.2d:%.2d' % (hour, minute, second)
-                    else:
-                        if hour > 12:
-                            hour = hour - 12
-                            am_pm = 'PM'
-                        elif hour == 12:
-                            am_pm = 'PM'
-                        else:
-                            if hour == 0: hour = 12
-                            am_pm = 'AM'
-                        text = '%2d:%.2d:%.2d %s' % (hour, minute, second, am_pm)
-                else:
-                    text = value
-                _dbg('text=', text)
-                wxTextCtrl.SetValue(self, text)
-                _dbg('firing TimeUpdatedEvent...')
-                evt = TimeUpdatedEvent(self.GetId(), text)
-                evt.SetEventObject(self)
-                self.GetEventHandler().ProcessEvent(evt)
-            else:
-                _dbg('len_ok:', len_ok, 'separators_correct =', separators_correct)
-                _dbg('hour_ok:', hour_ok, 'min_ok:', min_ok, 'sec_ok:', sec_ok, indent=0)
-                raise ValueError, 'value is not a valid time string'
-
-        except (TypeError, ValueError):
-            _dbg(indent=0)
-            raise ValueError, 'value is not a valid time string'
-        _dbg(indent=0)
-
-    def SetWxDateTime(self, wxdt):
-        value = '%2d:%.2d:%.2d' % (wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
-        self.SetValue(value)
-
-    def GetWxDateTime(self):
-        t = wxDateTimeFromHMS(self.__hour, self.__minute, self.__second)
-        return t
-
-    def SetMxDateTime(self, mxdt):
-        from mx import DateTime
-        value = '%2d:%.2d:%.2d' % (mxdt.hour, mxdt.minute, mxdt.second)
-        self.SetValue(value)
-
-    def GetMxDateTime(self):
-        from mx import DateTime
-        t = DateTime.Time(self.__hour, self.__minute, self.__second)
-        return t
-
-#-------------------------------------------------------------------------------------------------------------
-# these are private functions and overrides:
-
-    def __SetCurrentCell(self, pos):
-        """
-        Sets state variables that indicate the current cell and position within the control.
-        """
-        self.__posCurrent = pos
-        self.__cellStart, self.__cellEnd = _dictCellRange[pos][0], _dictCellRange[pos][-1]
-
-
-    def SetInsertionPoint(self, pos):
-        """
-        Records the specified position and associated cell before calling base class' function.
-        """
-        _dbg('wxTimeCtrl::SetInsertionPoint', pos, indent=1)
-
-        # Adjust pos to legal value if not already
-        if pos < 0: pos = 0
-        elif pos in _listDelimPos + [_dictCellRange[self.__lastCell]]:
-            pos = pos - 1
-            if self.__lastCell == 'am_pm' and pos in _dictCellRange[self.__lastCell]:
-                pos = _dictStartCellPos[self.__lastCell]
-
-        self.__SetCurrentCell(pos)
-        wxTextCtrl.SetInsertionPoint(self, pos)                 # (causes EVT_TEXT event to fire)
-        _dbg(indent=0)
-
-
-    def SetSelection(self, sel_start, sel_to):
-        _dbg('wxTimeCtrl::SetSelection', sel_start, sel_to, indent=1)
-
-        # Adjust selection range to legal extent if not already
-        if sel_start < 0:
-            self.SetInsertionPoint(0)
-            sel_start = self.__posCurrent
-
-        elif sel_start in _listDelimPos + [_dictCellRange[self.__lastCell]]:
-            self.SetInsertionPoint(sel_start - 1)
-            sel_start = self.__posCurrent
-
-        if self.__posCurrent != sel_start:                      # force selection and insertion point to match
-            self.SetInsertionPoint(sel_start)
-
-        if sel_to not in _dictCellRange[sel_start]:
-            sel_to = _dictCellRange[sel_start][-1]              # limit selection to end of current cell
-
-        self.__bSelection = sel_start != sel_to
-        self.__posSelectTo = sel_to
-        wxTextCtrl.SetSelection(self, sel_start, sel_to)
-        _dbg(indent=0)
-
-
-    def __OnFocus(self,event):
-        """
-        This event handler is currently necessary to work around new default
-        behavior as of wxPython2.3.3;
-        The TAB key auto selects the entire contents of the wxTextCtrl *after*
-        the EVT_SET_FOCUS event occurs; therefore we can't query/adjust the selection
-        *here*, because it hasn't happened yet.  So to prevent this behavior, and
-        preserve the correct selection when the focus event is not due to tab,
-        we need to pull the following trick:
-        """
-        _dbg('wxTimeCtrl::OnFocus')
-        wxCallAfter(self.__FixSelection)
-        event.Skip()
-
-
-    def __FixSelection(self):
-        """
-        This gets called after the TAB traversal selection is made, if the
-        focus event was due to this, but before the EVT_LEFT_* events if
-        the focus shift was due to a mouse event.
-
-        The trouble is that, a priori, there's no explicit notification of
-        why the focus event we received.  However, the whole reason we need to
-        do this is because the default behavior on TAB traveral in a wxTextCtrl is
-        now to select the entire contents of the window, something we don't want.
-        So we can *now* test the selection range, and if it's "the whole text"
-        we can assume the cause, change the insertion point to the start of
-        the control, and deselect.
-        """
-        _dbg('wxTimeCtrl::FixSelection', indent=1)
-        sel_start, sel_to = self.GetSelection()
-        if sel_start == 0 and sel_to in _dictCellRange[self.__lastCell]:
-            # 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')
-            self.SetInsertionPoint(0)
-            self.SetSelection(self.__cellStart, self.__cellEnd)
-        _dbg(indent=0)
-
-
-    def __OnTextChange(self, event):
-        """
-        This private event handler is required to retain the current position information of the cursor
-        after update to the underlying text control is done.
-        """
-        _dbg('wxTimeCtrl::OnTextChange', indent=1)
-        self.__SetCurrentCell(self.__posCurrent)                # ensure cell range vars are set
-
-        # Note: must call self.SetSelection here to preserve insertion point cursor after update!
-        # (I don't know why, but this does the trick!)
-        if self.__bSelection:
-            _dbg('reselecting from ', self.__posCurrent, 'to', self.__posSelectTo)
-            self.SetSelection(self.__posCurrent, self.__posSelectTo)
-        else:
-            self.SetSelection(self.__posCurrent, self.__posCurrent)
-        event.Skip()
-        _dbg(indent=0)
-
-    def __OnSpin(self, key):
-        self.__IncrementValue(key, self.__posCurrent)
-        self.SetFocus()
-        self.SetInsertionPoint(self.__posCurrent)
-        self.SetSelection(self.__posCurrent, self.__posSelectTo)
-
-    def __OnSpinUp(self, event):
-        """
-        Event handler for any bound spin button on EVT_SPIN_UP;
-        causes control to behave as if up arrow was pressed.
-        """
-        _dbg('wxTimeCtrl::OnSpinUp', indent=1)
-        self.__OnSpin(WXK_UP)
-        _dbg(indent=0)
-
-
-    def __OnSpinDown(self, event):
-        """
-        Event handler for any bound spin button on EVT_SPIN_DOWN;
-        causes control to behave as if down arrow was pressed.
-        """
-        _dbg('wxTimeCtrl::OnSpinDown', indent=1)
-        self.__OnSpin(WXK_DOWN)
-        _dbg(indent=0)
-
-
-    def __OnChangePos(self, event):
-        """
-        Event handler for motion events; this handler
-        changes limits the selection to the new cell boundaries.
-        """
-        _dbg('wxTimeCtrl::OnChangePos', indent=1)
-        pos = self.GetInsertionPoint()
-        self.__SetCurrentCell(pos)
-        sel_start, sel_to = self.GetSelection()
-        selection = sel_start != sel_to
-        if not selection:
-            # disallow position at end of field:
-            if pos in _listDelimPos + [_dictCellRange[self.__lastCell][-1]]:
-                self.SetInsertionPoint(pos-1)
-            self.__posSelectTo = self.__cellEnd
-        else:
-            # only allow selection to end of current cell:
-            if sel_to < pos:   self.__posSelectTo = self.__cellStart
-            elif sel_to > pos: self.__posSelectTo = self.__cellEnd
-
-        _dbg('new pos =', self.__posCurrent, 'select to ', self.__posSelectTo)
-        self.SetSelection(self.__posCurrent, self.__posSelectTo)
-        if event: event.Skip()
-        _dbg(indent=0)
-
-
-    def __OnDoubleClick(self, event):
-        """
-        Event handler for left double-click mouse events; this handler
-        causes the cell at the double-click point to be selected.
-        """
-        _dbg('wxTimeCtrl::OnDoubleClick', indent=1)
-        pos = self.GetInsertionPoint()
-        self.__SetCurrentCell(pos)
-        if self.__posCurrent != self.__cellStart:
-            self.SetInsertionPoint(self.__cellStart)
-        self.SetSelection(self.__cellStart, self.__cellEnd)
-        _dbg(indent=0)
-
-
-    def __OnChar(self, event):
-        """
-        This private event handler is the main control point for the wxTimeCtrl.
-        It governs whether input characters are accepted and if so, handles them
-        so as to provide appropriate cursor and selection behavior for the control.
-        """
-        _dbg('wxTimeCtrl::OnChar', indent=1)
-
-        # NOTE: Returning without calling event.Skip() eats the event before it
-        # gets to the text control...
-
-        key = event.GetKeyCode()
-        text = self.GetValue()
-        pos = self.GetInsertionPoint()
-        if pos != self.__posCurrent:
-            _dbg("insertion point has moved; resetting current cell")
-            self.__SetCurrentCell(pos)
-            self.SetSelection(self.__posCurrent, self.__posCurrent)
-
-        sel_start, sel_to = self.GetSelection()
-        selection = sel_start != sel_to
-        _dbg('sel_start=', sel_start, 'sel_to =', sel_to)
-        if not selection:
-            self.__bSelection = False                       # predict unselection of entire region
-
-        _dbg('keycode = ', key)
-        _dbg('pos = ', pos)
-
-        # don't allow deletion, cut or paste:
-        if key in (WXK_DELETE, WXK_BACK, WXK_CTRL_X, WXK_CTRL_V):
-            pass
-
-        elif key == WXK_TAB:                                # skip to next field if applicable:
-            _dbg('key == WXK_TAB')
-            dict_range = _dictCellRange                     # (for brevity)
-            dict_start = _dictStartCellPos
-            if event.ShiftDown():                           # tabbing backwords
-
-                ###(NOTE: doesn't work; wxTE_PROCESS_TAB doesn't appear to send us this event!)
-
-                _dbg('event.ShiftDown()')
-                if pos in dict_range['hour']:               # already in 1st field
-                    self.__SetCurrentCell(dict_start['hour']) # ensure we have our member vars set
-                    event.Skip()                            #then do normal tab processing for the form
-                    _dbg(indent=0)
-                    return
-
-                elif pos in dict_range['minute']:           # skip to hours field
-                    new_pos = dict_start['hour']
-                elif pos in dict_range['second']:           # skip to minutes field
-                    new_pos = dict_start['minute']
-                elif pos in dict_range['am_pm']:            # skip to seconds field
-                    new_pos = dict_start['second']
-
-                self.SetInsertionPoint(new_pos)             # force insert point to jump to next cell (swallowing TAB)
-                self.__OnChangePos(None)                    # update selection accordingly
-
-            else:
-                # Tabbing forwards through control...
-
-                if pos in dict_range[self.__lastCell]:      # already in last field; ensure we have our members set
-                    self.__SetCurrentCell(dict_start[self.__lastCell])
-                    _dbg('tab in last cell')
-                    event.Skip()                            # then do normal tab processing for the form
-                    _dbg(indent=0)
-                    return
-
-                if pos in dict_range['second']:             # skip to AM/PM field (if not last cell)
-                    new_pos = dict_start['am_pm']
-                elif pos in dict_range['minute']:           # skip to seconds field
-                    new_pos = dict_start['second']
-                elif pos in dict_range['hour']:             # skip to minutes field
-                    new_pos = dict_start['minute']
-
-                self.SetInsertionPoint(new_pos)             # force insert point to jump to next cell (swallowing TAB)
-                self.__OnChangePos(None)                    # update selection accordingly
-
-        elif key == WXK_LEFT:                               # move left; set insertion point as appropriate:
-            _dbg('key == WXK_LEFT')
-            if event.ShiftDown():                           # selecting a range...
-                _dbg('event.ShiftDown()')
-                if pos in _listStartCellPos:                # can't select pass delimiters
-                    if( sel_to == pos+2 and sel_to != _dictCellRange['am_pm'][-1]):
-                        self.SetSelection(pos, pos+1)       # allow deselection of 2nd char in cell if not am/pm
-                    # else ignore event
-
-                elif pos in _listEndCellPos:                # can't use normal selection, because position ends up
-                                                            # at delimeter
-                    _dbg('set selection from', pos-1, 'to', self.__posCurrent)
-                    self.SetInsertionPoint(pos-1)           # this selects the previous position
-                    self.SetSelection(self.__posCurrent, pos)
-                else:
-                    self.SetInsertionPoint(sel_to - 1)      # this unselects the last digit
-                    self.SetSelection(self.__posCurrent, pos)
-
-            else:   # ... not selecting
-                if pos == 0:                                # can't position before position 0
-                    pass
-                elif pos in _listStartCellPos:              # skip (left) OVER the colon/space:
-                    self.SetInsertionPoint(pos-2)
-                    self.__OnChangePos(None)                # set the selection appropriately
-                else:
-                    self.SetInsertionPoint(pos-1)           # reposition the cursor and
-                    self.__OnChangePos(None)                # set the selection appropriately
-
-
-        elif key == WXK_RIGHT:                              # move right
-            _dbg('key == WXK_RIGHT')
-            if event.ShiftDown():
-                _dbg('event.ShiftDown()')
-                if sel_to in _listDelimPos:                 # can't select pass delimiters
-                    pass
-                else:
-                    self.SetSelection(self.__posCurrent, sel_to+1)
-            else:
-                if( (self.__lastCell == 'second'
-                     and pos == _dictStartCellPos['second']+1)
-                    or (self.__lastCell == 'am_pm'
-                        and pos == _dictStartCellPos['am_pm']) ):
-                    pass                                    # don't allow cursor past last cell
-                elif pos in _listEndCellPos:                # skip (right) OVER the colon/space:
-                    self.SetInsertionPoint(pos+2)
-                    self.__OnChangePos(None)                # set the selection appropriately
-                else:
-                    self.SetInsertionPoint(pos+1)           # reposition the cursor and
-                    self.__OnChangePos(None)                # set the selection appropriately
-
-        elif key in (WXK_UP, WXK_DOWN):
-            _dbg('key in (WXK_UP, WXK_DOWN)')
-            self.__IncrementValue(key, pos)                   # increment/decrement as appropriate
-
-
-        elif key < WXK_SPACE or key == WXK_DELETE or key > 255:
-            event.Skip()                                    # non alphanumeric; process normally (Right thing to do?)
-
-        elif chr(key) in ['!', 'c', 'C']:                   # Special character; sets the value of the control to "now"
-            _dbg("key == '!'; setting time to 'now'")
-            now = wxDateTime_Now()
-            self.SetWxDateTime(now)
-
-        elif chr(key) in string.digits:                     # let ChangeValue validate and update current position
-            self.__ChangeValue(chr(key), pos)               # handle event (and swallow it)
-
-        elif chr(key) in ('a', 'A', 'p', 'P', ' '):         # let ChangeValue validate and update current position
-            self.__ChangeValue(chr(key), pos)               # handle event (and swallow it)
-
-        else:                                               # disallowed char; swallow event
-            pass
-        _dbg(indent=0)
-
-
-    def __IncrementValue(self, key, pos):
-        _dbg('wxTimeCtrl::IncrementValue', key, pos, indent=1)
-        text = self.GetValue()
-
-        sel_start, sel_to = self.GetSelection()
-        selection = sel_start != sel_to
-        cell_selected = selection and sel_to -1 != pos
-
-        dict_start = _dictStartCellPos                      # (for brevity)
-
-        # Determine whether we should change the entire cell or just a portion of it:
-        if( cell_selected
-            or (pos in _listStartCellPos and not selection)
-            or (text[pos] == ' ' and text[pos+1] not in ('1', '2'))
-            or (text[pos] == '9' and text[pos-1] == ' ' and key == WXK_UP)
-            or (text[pos] == '1' and text[pos-1] == ' ' and key == WXK_DOWN)
-            or pos >= dict_start['am_pm']):
-
-            self.__IncrementCell(key, pos)
-        else:
-            if key == WXK_UP:   inc = 1
-            else:               inc = -1
-
-            if pos == dict_start['hour'] and not self.__fmt24hr:
-                if text[pos] == ' ': digit = '1'                    # allow ' ' or 1 for 1st digit in 12hr format
-                else:                digit = ' '
-            else:
-                if pos == dict_start['hour']:
-                    if int(text[pos + 1]) >3:   mod = 2             # allow for 20-23
-                    else:                       mod = 3             # allow 00-19
-                elif pos == dict_start['hour'] + 1:
-                    if self.__fmt24hr:
-                        if text[pos - 1] == '2': mod = 4            # allow hours 20-23
-                        else:                    mod = 10           # allow hours 00-19
-                    else:
-                        if text[pos - 1] == '1': mod = 3            # allow hours 10-12
-                        else:                    mod = 10           # allow 0-9
-
-                elif pos in (dict_start['minute'],
-                             dict_start['second']): mod = 6         # allow minutes/seconds 00-59
-                else:                               mod = 10
-
-                digit = '%d' % ((int(text[pos]) + inc) % mod)
-
-            _dbg("new digit = \'%s\'" % digit)
-            self.__ChangeValue(digit, pos)
-        _dbg(indent=0)
-
-
-    def __IncrementCell(self, key, pos):
-        _dbg('wxTimeCtrl::IncrementCell', key, pos, indent=1)
-        self.__SetCurrentCell(pos)                                  # determine current cell
-        hour, minute, second = self.__hour, self.__minute, self.__second
-        text = self.GetValue()
-        dict_start = _dictStartCellPos                              # (for brevity)
-        if key == WXK_UP:   inc = 1
-        else:               inc = -1
-
-        if self.__cellStart == dict_start['am_pm']:
-            am = text[dict_start['am_pm']:] == 'AM'
-            if am: hour = hour + 12
-            else:  hour = hour - 12
-        else:
-            if self.__cellStart == dict_start['hour']:
-                hour = (hour + inc) % 24
-            elif self.__cellStart == dict_start['minute']:
-                minute = (minute + inc) % 60
-            elif self.__cellStart == dict_start['second']:
-                second = (second + inc) % 60
-
-        newvalue = '%.2d:%.2d:%.2d' % (hour, minute, second)
-
-        self.SetValue(newvalue)
-        self.SetInsertionPoint(self.__cellStart)
-        self.SetSelection(self.__cellStart, self.__cellEnd)
-        _dbg(indent=0)
-
-
-    def __ChangeValue(self, char, pos):
-        _dbg('wxTimeCtrl::ChangeValue', "\'" + char + "\'", pos, indent=1)
-        text = self.GetValue()
-
-        self.__SetCurrentCell(pos)
-        sel_start, sel_to = self.GetSelection()
-        self.__posSelectTo = sel_to
-        self.__bSelection = selection = sel_start != sel_to
-        cell_selected = selection and sel_to -1 != pos
-        _dbg('cell_selected =', cell_selected, indent=0)
-
-        dict_start = _dictStartCellPos                          # (for brevity)
-
-        if pos in _listDelimPos: return                         # don't allow change of punctuation
-
-        elif( 0 < pos < dict_start['am_pm'] and char not in string.digits):
-            return                                              # AM/PM not allowed in this position
-
-        # See if we're changing the hour cell, and validate/update appropriately:
-        #
-        hour_start = dict_start['hour']                         # (ie. 0)
-
-        if pos == hour_start:                                   # if at 1st position,
-            if self.__fmt24hr:                                  # and using 24 hour format
-                if cell_selected:                               # replace cell contents with hour represented by digit
-                    newtext = '%.2d' % int(char) + text[hour_start+2:]
-                elif char not in ('0', '1', '2'):               # return if digit not 0,1, or 2
-                    return
-                else:                                           # relace current position
-                    newtext = char + text[pos+1:]
-            else:                                               # (12 hour format)
-                if cell_selected:
-                    if char == ' ': return                      # can't erase entire cell
-                    elif char == '0':                           # treat 0 as '12'
-                        newtext = '12' + text[hour_start+2:]
-                    else:                                       # replace cell contents with hour represented by digit
-                        newtext = '%2d' % int(char) + text[hour_start+2:]
-                else:
-                    if char not in ('1', ' '):                  # can only type a 1 or space
-                        return
-                    if text[pos+1] not in ('0', '1', '2'):      # and then, only if other column is 0,1, or 2
-                        return
-                    if char == ' ' and text[pos+1] == '0':      # and char isn't space if 2nd column is 0
-                        return
-                    else:                                       # ok; replace current position
-                        newtext = char + text[pos+1:]
-                if char == ' ': self.SetInsertionPoint(pos+1)   # move insert point to legal position
-
-        elif pos == hour_start+1:                               # if editing 2nd position of hour
-            if( not self.__fmt24hr                              # and using 12 hour format
-                and text[hour_start] == '1'                     # if 1st char is 1,
-                and char not in ('0', '1', '2')):               # disallow anything bug 0,1, or 2
-                return
-            newtext = text[hour_start] + char + text[hour_start+2:]  # else any digit ok
-
-        # Do the same sort of validation for minute and second cells
-        elif pos in (dict_start['minute'], dict_start['second']):
-            if cell_selected:                                   # if cell selected, replace value
-                newtext = text[:pos] + '%.2d' % int(char) + text[pos+2:]
-            elif int(char) > 5: return                          # else disallow > 59 for minute and second fields
-            else:
-                newtext = text[:pos] + char + text[pos+1:]      # else ok
-
-        elif pos in (dict_start['minute']+1, dict_start['second']+1):
-            newtext = text[:pos] + char + text[pos+1:]          # all digits ok for 2nd digit of minute/second
-
-        # Process AM/PM cell
-        elif pos == dict_start['am_pm']:
-            char = char.upper()
-            if char not in ('A','P'): return                    # disallow all but A or P as 1st char of column
-            newtext = text[:pos] + char + text[pos+1:]
-        else: return    # not a valid position
-
-        _dbg(indent=1)
-        # update member position vars and set selection to character changed
-        if not cell_selected:
-            _dbg('reselecting current digit')
-            self.__posSelectTo = pos+1
-
-        _dbg('newtext=', newtext)
-        self.SetValue(newtext)
-        self.SetInsertionPoint(self.__posCurrent)
-        self.SetSelection(self.__posCurrent, self.__posSelectTo)
-        _dbg(indent=0)
-
-
-    def Cut(self):
-        """
-        Override wxTextCtrl::Cut() method, as this operation should not
-        be allowed for wxTimeCtrls.
-        """
-        return
-
-
-    def Paste(self):
-        """
-        Override wxTextCtrl::Paste() method, as this operation should not
-        be allowed for wxTimeCtrls.
-        """
-        return
-
-
-#----------------------------------------------------------------------------
-# Test jig for wxTimeCtrl:
-
-if __name__ == '__main__':
-    import traceback
-
-    class TestPanel(wxPanel):
-        def __init__(self, parent, id,
-                     pos = wxPyDefaultPosition, size = wxPyDefaultSize,
-                     fmt24hr = 0, test_mx = 0,
-                     style = wxTAB_TRAVERSAL ):
-
-            wxPanel.__init__(self, parent, id, pos, size, style)
-
-            self.test_mx = test_mx
-
-            self.tc = wxTimeCtrl(self, 10, fmt24hr = fmt24hr)
-            sb = wxSpinButton( self, 20, wxDefaultPosition, wxSize(-1,20), 0 )
-            self.tc.BindSpinButton(sb)
-
-            sizer = wxBoxSizer( wxHORIZONTAL )
-            sizer.AddWindow( self.tc, 0, wxALIGN_CENTRE|wxLEFT|wxTOP|wxBOTTOM, 5 )
-            sizer.AddWindow( sb, 0, wxALIGN_CENTRE|wxRIGHT|wxTOP|wxBOTTOM, 5 )
-
-            self.SetAutoLayout( True )
-            self.SetSizer( sizer )
-            sizer.Fit( self )
-            sizer.SetSizeHints( self )
-
-            EVT_TIMEUPDATE(self, self.tc.GetId(), self.OnTimeChange)
-
-        def OnTimeChange(self, event):
-            _dbg('OnTimeChange: value = ', event.GetValue())
-            wxdt = self.tc.GetWxDateTime()
-            _dbg('wxdt =', wxdt.GetHour(), wxdt.GetMinute(), wxdt.GetSecond())
-            if self.test_mx:
-                mxdt = self.tc.GetMxDateTime()
-                _dbg('mxdt =', mxdt.hour, mxdt.minute, mxdt.second)
-
-
-    class MyApp(wxApp):
-        def OnInit(self):
-            import sys
-            fmt24hr = '24' in sys.argv
-            test_mx = 'mx' in sys.argv
-            try:
-                frame = wxFrame(NULL, -1, "wxTimeCtrl Test", wxPoint(20,20), wxSize(100,100) )
-                panel = TestPanel(frame, -1, wxPoint(-1,-1), fmt24hr=fmt24hr, test_mx = test_mx)
-                frame.Show(True)
-            except:
-                traceback.print_exc()
-                return False
-            return True
-
-    try:
-        app = MyApp(0)
-        app.MainLoop()
-    except:
-        traceback.print_exc()
+EVT_TIMEUPDATE = wx.lib.masked.timectrl.EVT_TIMEUPDATE
+TimeUpdatedEvent = wx.lib.masked.timectrl.TimeUpdatedEvent
+wxTimeCtrl = wx.lib.masked.timectrl.TimeCtrl