]> git.saurik.com Git - wxWidgets.git/commitdiff
Patches from Will Sadkin:
authorRobin Dunn <robin@alldunn.com>
Thu, 22 Jul 2004 18:38:34 +0000 (18:38 +0000)
committerRobin Dunn <robin@alldunn.com>
Thu, 22 Jul 2004 18:38:34 +0000 (18:38 +0000)
MaskedEditMixin:
- fixed size calculations on changing fonts
- fixed tabbing logic now that tab events are entered into the control
  by default (ie event.Skip()) if wx.TE_PROCESS_TAB is set
- fixed code attempting to fix the selection after focus events
  generated on control destruction, to prevent tracebacks

TextCtrl, ComboBox
- Added support for XRC
- Fixed sizing calculation code
- Added SetFont() override method that will recalculate the size if this
  is called.
- Added AppendItems() for ComboBox

NumCtrl:
- prevented ctrl from accepting same grouping and decimal character,
- fixed issue preventing paste from working if decimal char was different
  than '.'

TimeCtrl:
- Fixed default value to use 24hour time (which will be converted
  appropriately if format supports it, and added code to check if
  local timezone uses "AM/PM" for this concept; if not, control now
  defaults to 24hour format, and disallows the am/pm form.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@28400 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

wxPython/wx/lib/masked/__init__.py
wxPython/wx/lib/masked/combobox.py
wxPython/wx/lib/masked/maskededit.py
wxPython/wx/lib/masked/numctrl.py
wxPython/wx/lib/masked/textctrl.py
wxPython/wx/lib/masked/timectrl.py

index 1a5a59ea70c55ebce5171c065f641f11d575e798..8e2c018745d7c005e9f242207e4b4b715ae89272 100644 (file)
@@ -12,8 +12,8 @@
 
 # import relevant external symbols into package namespace:
 from maskededit import *
-from textctrl   import BaseMaskedTextCtrl, TextCtrl
-from combobox   import BaseMaskedComboBox, ComboBox, MaskedComboBoxSelectEvent
+from textctrl   import BaseMaskedTextCtrl, PreMaskedTextCtrl, TextCtrl
+from combobox   import BaseMaskedComboBox, PreMaskedComboBox, ComboBox, MaskedComboBoxSelectEvent
 from numctrl    import NumCtrl, wxEVT_COMMAND_MASKED_NUMBER_UPDATED, EVT_NUM, NumberUpdatedEvent
 from timectrl   import TimeCtrl, wxEVT_TIMEVAL_UPDATED, EVT_TIMEUPDATE, TimeUpdatedEvent
 from ipaddrctrl import IpAddrCtrl
index c42db0c9459505383d243da6b5453c4f1f0a95cb..7e1ec0d0d5562a10cdacd171993354e522bfec23 100644 (file)
@@ -54,11 +54,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                   **kwargs):
 
 
-        # This is necessary, because wxComboBox currently provides no
-        # method for determining later if this was specified in the
-        # constructor for the control...
-        self.__readonly = style & wx.CB_READONLY == wx.CB_READONLY
-
         kwargs['choices'] = choices                 ## set up maskededit to work with choice list too
 
         ## Since combobox completion is case-insensitive, always validate same way
@@ -80,15 +75,56 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
                             choices=choices, style=style|wx.WANTS_CHARS,
                             validator=validator,
                             name=name)
-
         self.controlInitialized = True
 
+        self._PostInit(style=style, setupEventHandling=setupEventHandling,
+                       name=name, value=value, **kwargs)
+        
+
+    def _PostInit(self, style=wx.CB_DROPDOWN,
+                  setupEventHandling = True,        ## setup event handling by default):
+                  name = "maskedComboBox", value='', **kwargs):
+
+        # This is necessary, because wxComboBox currently provides no
+        # method for determining later if this was specified in the
+        # constructor for the control...
+        self.__readonly = style & wx.CB_READONLY == wx.CB_READONLY
+
+        if not hasattr(self, 'controlInitialized'):
+            
+            self.controlInitialized = True          ## must have been called via XRC, therefore base class is constructed
+            if not kwargs.has_key('choices'):
+                choices=[]
+                kwargs['choices'] = choices         ## set up maskededit to work with choice list too
+            self._choices = []
+
+            ## Since combobox completion is case-insensitive, always validate same way
+            if not kwargs.has_key('compareNoCase'):
+                kwargs['compareNoCase'] = True
+
+            MaskedEditMixin.__init__( self, name, **kwargs )
+
+            self._choices = self._ctrl_constraints._choices
+##        dbg('self._choices:', self._choices)
+
+            if self._ctrl_constraints._alignRight:
+                choices = [choice.rjust(self._masklength) for choice in choices]
+            else:
+                choices = [choice.ljust(self._masklength) for choice in choices]
+            wx.ComboBox.Clear(self)
+            wx.ComboBox.AppendItems(self, choices)
+
+
         # Set control font - fixed width by default
         self._setFont()
 
         if self._autofit:
             self.SetClientSize(self._CalcSize())
-            self.SetSizeHints(self.GetSize())
+            width = self.GetSize().width
+            height = self.GetBestSize().height
+            self.SetSize((width, height))
+            self.SetSizeHints((width, height))
+
 
         if value:
             # ensure value is width of the mask of the control:
@@ -134,6 +170,19 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         return (size[0]+20, size[1])
 
 
+    def SetFont(self, *args, **kwargs):
+        """ Set the font, then recalculate control size, if appropriate. """
+        wx.ComboBox.SetFont(self, *args, **kwargs)
+        if self._autofit:
+            dbg('calculated size:', self._CalcSize())            
+            self.SetClientSize(self._CalcSize())
+            width = self.GetSize().width
+            height = self.GetBestSize().height
+            dbg('setting client size to:', (width, height))
+            self.SetSize((width, height))
+            self.SetSizeHints((width, height))
+
+
     def _GetSelection(self):
         """
         Allow mixin to get the text selection of this control.
@@ -306,7 +355,6 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         else:
             wx.ComboBox.Undo()       # else revert to base control behavior
 
-
     def Append( self, choice, clientData=None ):
         """
         This function override is necessary so we can keep track of any additions to the list
@@ -352,6 +400,13 @@ class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
         wx.ComboBox.Append(self, choice, clientData)
 
 
+    def AppendItems( self, choices ):
+        """
+        AppendItems() is handled in terms of Append, to avoid code replication.
+        """
+        for choice in choices:
+            self.Append(choice)
+
 
     def Clear( self ):
         """
@@ -544,3 +599,30 @@ class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
     pass
 
 
+class PreMaskedComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
+    """
+    This allows us to use XRC subclassing.
+    """
+    # This should really be wx.EVT_WINDOW_CREATE but it is not
+    # currently delivered for native controls on all platforms, so
+    # we'll use EVT_SIZE instead.  It should happen shortly after the
+    # control is created as the control is set to its "best" size.
+    _firstEventType = wx.EVT_SIZE
+
+    def __init__(self):
+        pre = wx.PreComboBox()
+        self.PostCreate(pre)
+        self.Bind(self._firstEventType, self.OnCreate)
+
+
+    def OnCreate(self, evt):
+        self.Unbind(self._firstEventType)
+        self._PostInit()
+
+i=0
+## CHANGELOG:
+## ====================
+##  Version 1.1
+##  1. Added .SetFont() method that properly resizes control
+##  2. Modified control to support construction via XRC mechanism.
+##  3. Added AppendItems() to conform with latest combobox.
index a271fc4a7bd328693d73a7f98cb4b36d986ace75..4bafe637120778a19b93991ef0b6fe9bd71713e1 100644 (file)
@@ -757,7 +757,7 @@ import  wx
 from wx.tools.dbg import Logger
 
 dbg = Logger()
-##dbg(enable=0)
+##dbg(enable=1)
 
 ## ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
@@ -1914,9 +1914,13 @@ class MaskedEditMixin:
                         self._prevValue = newvalue  # disallow undo of sign type
 
             if self._autofit:
-##                dbg('setting client size to:', self._CalcSize())
+##                dbg('calculated size:', self._CalcSize())
                 self.SetClientSize(self._CalcSize())
-                self.SetSizeHints(self.GetSize())
+                width = self.GetSize().width
+                height = self.GetBestSize().height
+##                dbg('setting client size to:', (width, height))
+                self.SetSize((width, height))
+                self.SetSizeHints((width, height))
 
             # Set value/type-specific formatting
             self._applyFormatting()
@@ -1991,8 +1995,24 @@ class MaskedEditMixin:
                 self._SetInitialValue()
 
                 if self._autofit:
+                    # this is tricky, because, as Robin explains:
+                    # "Basically there are two sizes to deal with, that are potentially 
+                    #  different.  The client size is the inside size and may, depending
+                    #  on platform, exclude the borders and such.  The normal size is
+                    #  the outside size that does include the borders.  What you are
+                    #  calculating (in _CalcSize) is the client size, but the sizers
+                    #  deal with the full size and so that is the minimum size that
+                    #  we need to set with SetSizeHints.  The root of the problem is
+                    #  that in _calcSize the current client size height is returned,
+                    #  instead of a height based on the current font.  So I suggest using
+                    #  _calcSize to just get the width, and then use GetBestSize to
+                    #  get the height."
                     self.SetClientSize(self._CalcSize())
-                    self.SetSizeHints(self.GetSize())
+                    width = self.GetSize().width
+                    height = self.GetBestSize().height
+                    self.SetSize((width, height))
+                    self.SetSizeHints((width, height))
+
 
             # Set value/type-specific formatting
             self._applyFormatting()
@@ -2642,7 +2662,7 @@ class MaskedEditMixin:
                 sizing_text += 'M'
 ####            dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text)
             w, h = self.GetTextExtent(sizing_text)
-            size = (w+4, self.GetClientSize().height)
+            size = (w+4, self.GetSize().height)
 ####            dbg('size:', size, indent=0)
         return size
 
@@ -2690,7 +2710,7 @@ class MaskedEditMixin:
 ##            dbg('ignoring bogus text change event', indent=0)
             pass
         else:
-##            dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
+##            dbg('curvalue: "%s", newvalue: "%s", len(newvalue): %d' % (self._curValue, newvalue, len(newvalue)))
             if self._Change():
                 if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
 ##                    dbg('clearing self._isNeg')
@@ -2864,6 +2884,8 @@ class MaskedEditMixin:
                         if newfield != field and newfield._selectOnFieldEntry:
 ##                            dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
                             wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
+                        else:
+                            wx.CallAfter(self._SetSelection, newpos, new_select_to)
                     keep_processing = False
 
             elif keep_processing:
@@ -3400,12 +3422,12 @@ class MaskedEditMixin:
 
     def _OnReturn(self, event):
          """
-         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."
+         Swallows the return, issues a Navigate event instead, since
+         masked controls are "single line" by defn.
          """
 ##         dbg('MaskedEditMixin::OnReturn')
-         event.m_keyCode = wx.WXK_TAB
-         event.Skip()
+         self.Navigate(True)
+         return False
 
 
     def _OnHome(self,event):
@@ -3486,9 +3508,6 @@ class MaskedEditMixin:
         """
         Primarily handles TAB events, but can be used for any key that
         designer wants to change fields within a masked edit control.
-        NOTE: at the moment, although coded to handle shift-TAB and
-        control-shift-TAB, these events are not sent to the controls
-        by the framework.
         """
 ##        dbg('MaskedEditMixin::_OnChangeField', indent = 1)
         # determine end of current field:
@@ -3500,7 +3519,10 @@ class MaskedEditMixin:
             self._AdjustField(pos)
             if event.GetKeyCode() == wx.WXK_TAB:
 ##                dbg('tab to next ctrl')
-                event.Skip()
+                # As of 2.5.2, you don't call event.Skip() to do
+                # this, but instead force explicit navigation, if
+                # wx.TE_PROCESS_TAB is used (like in the masked edits)
+                self.Navigate(True)
             #else: do nothing
 ##            dbg(indent=0)
             return False
@@ -3534,7 +3556,10 @@ class MaskedEditMixin:
                 self._AdjustField(pos)
                 if event.GetKeyCode() == wx.WXK_TAB:
 ##                    dbg('tab to previous ctrl')
-                    event.Skip()
+                    # As of 2.5.2, you don't call event.Skip() to do
+                    # this, but instead force explicit navigation, if
+                    # wx.TE_PROCESS_TAB is used (like in the masked edits)
+                    self.Navigate(False)
                 else:
 ##                    dbg('position at beginning')
                     wx.CallAfter(self._SetInsertionPoint, field_start)
@@ -3580,7 +3605,10 @@ class MaskedEditMixin:
                     self._AdjustField(pos)
                     if event.GetKeyCode() == wx.WXK_TAB:
 ##                        dbg('tab to next ctrl')
-                        event.Skip()
+                        # As of 2.5.2, you don't call event.Skip() to do
+                        # this, but instead force explicit navigation, if
+                        # wx.TE_PROCESS_TAB is used (like in the masked edits)
+                        self.Navigate(True)
                     else:
 ##                        dbg('position at end')
                         wx.CallAfter(self._SetInsertionPoint, field_end)
@@ -3594,7 +3622,10 @@ class MaskedEditMixin:
                         self._AdjustField(pos)
                         if event.GetKeyCode() == wx.WXK_TAB:
 ##                            dbg('tab to next ctrl')
-                            event.Skip()
+                            # As of 2.5.2, you don't call event.Skip() to do
+                            # this, but instead force explicit navigation, if
+                            # wx.TE_PROCESS_TAB is used (like in the masked edits)
+                            self.Navigate(True)
                         #else: do nothing
 ##                        dbg(indent=0)
                         return False
@@ -3640,6 +3671,8 @@ class MaskedEditMixin:
             if fraction._selectOnFieldEntry:
 ##                dbg('queuing selection after decimal point to:', (start, end))
                 wx.CallAfter(self._SetSelection, start, end)
+            else:
+                wx.CallAfter(self._SetSelection, start, start)
             keep_processing = False
 
         if self._isInt:      ## handle integer value, truncate from current position
@@ -3654,6 +3687,7 @@ class MaskedEditMixin:
             if newstr.find(')') != -1:
                 newpos -= 1     # (don't move past right paren)
             wx.CallAfter(self._SetInsertionPoint, newpos)
+            wx.CallAfter(self._SetSelection, newpos, newpos)
             keep_processing = False
 ##        dbg(indent=0)
 
@@ -3949,6 +3983,7 @@ class MaskedEditMixin:
                     pos = pos+2
 
         if newvalue != value:
+##            dbg('old value: "%s"\nnew value: "%s"' % (value, newvalue))
             self._SetValue(newvalue)
             self._SetInsertionPoint(pos)
 
@@ -4042,6 +4077,8 @@ class MaskedEditMixin:
                     self._SetInsertionPoint(pos)
                     if pos < sel_to:    # restore selection
                         self._SetSelection(pos, sel_to)
+                    else:
+                        self._SetSelection(pos, pos)
 ##        dbg('adjusted pos:', pos, indent=0)
         return pos
 
@@ -5182,7 +5219,10 @@ class MaskedEditMixin:
         the control, and deselect.
         """
 ##        dbg('MaskedEditMixin::_fixSelection', indent=1)
-        if not self._mask or not self._IsEditable():
+        # can get here if called with wx.CallAfter after underlying 
+        # control has been destroyed on close, but after focus
+        # events
+        if not self or not self._mask or not self._IsEditable():
 ##            dbg(indent=0)
             return
 
@@ -6396,6 +6436,8 @@ i=1
 ##     chars properly.)
 ##  4. Fixed autoselect behavior to work similarly to (2) above, so that combobox
 ##     selection will only select the non-empty text, as per request.
+##  5. Fixed tabbing to work with 2.5.2 semantics.
+##  6. Fixed size calculation to handle changing fonts
 ##
 ##  Version 1.6
 ##  1. Reorganized masked controls into separate package, renamed things accordingly
index f8f1934e1a26fb571dd68fb0f77be05d2d5fbc93..cfb64b22a22222f905d14066b847e7b2c5dc103f 100644 (file)
@@ -386,7 +386,7 @@ MININT = -maxint-1
 from wx.tools.dbg import Logger
 from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
 dbg = Logger()
-##dbg(enable=0)
+##dbg(enable=1)
 
 #----------------------------------------------------------------------------
 
@@ -654,14 +654,26 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
 
             maskededit_kwargs['mask'] = intmask+fracmask
 
-        if kwargs.has_key('groupChar'):
+        if kwargs.has_key('groupChar') or kwargs.has_key('decimalChar'):
             old_groupchar = self._groupChar     # save so we can reformat properly
-##            dbg("old_groupchar: '%s'" % old_groupchar)
-            maskededit_kwargs['groupChar'] = kwargs['groupChar']
-        if kwargs.has_key('decimalChar'):
             old_decimalchar = self._decimalChar
+##            dbg("old_groupchar: '%s'" % old_groupchar)
 ##            dbg("old_decimalchar: '%s'" % old_decimalchar)
-            maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
+            groupchar = old_groupchar
+            decimalchar = old_decimalchar
+
+            if kwargs.has_key('groupChar'):
+                maskededit_kwargs['groupChar'] = kwargs['groupChar']
+                groupchar = kwargs['groupChar']
+            if kwargs.has_key('decimalChar'):
+                maskededit_kwargs['decimalChar'] = kwargs['decimalChar']
+                decimalchar = kwargs['decimalChar']
+
+            # Add sanity check to make sure these are distinct, and if not,
+            # raise attribute error
+            if groupchar == decimalchar:
+                raise AttributeError('groupChar and decimalChar must be distinct')
+
 
         # for all other parameters, assign keyword args as appropriate:
         for key, param_value in kwargs.items():
@@ -1089,9 +1101,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
                            and value[sel_start:sel_to] == self._groupChar ):
                     self.SetInsertionPoint(sel_start)
                     self.SetSelection(sel_start, sel_to+1)
-
-        return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
 ##        dbg(indent=0)
+        return BaseMaskedTextCtrl._OnErase(self, event, just_return_value)
 
 
     def OnTextChange( self, event ):
@@ -1146,7 +1157,9 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         A ValueError exception will be raised if an invalid value
         is specified.
         """
+##        dbg('NumCtrl::SetValue(%s)' % value, indent=1)
         BaseMaskedTextCtrl.SetValue( self, self._toGUI(value) )
+##        dbg(indent=0)
 
 
     def SetIntegerWidth(self, value):
@@ -1519,7 +1532,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         Preprocessor for base control paste; if value needs to be right-justified
         to fit in control, do so prior to paste:
         """
-##        dbg('NumCtrl::_Paste (value = "%s")' % value)
+##        dbg('NumCtrl::_Paste (value = "%s")' % value, indent=1)
         if value is None:
             paste_text = self._getClipboardContents()
         else:
@@ -1533,7 +1546,7 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
         #
         field = self._FindField(sel_start)
         edit_start, edit_end = field._extent
-        paste_text = paste_text.replace(self._groupChar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')','')
+        paste_text = paste_text.replace(self._groupChar, '').replace('(', '-').replace(')','')
         if field._insertRight and self._groupDigits:
             # want to paste to the left; see if it will fit:
             left_text = old_value[edit_start:sel_start].lstrip()
@@ -1547,8 +1560,8 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
                 sel_start += sel_to - orig_sel_start    # decrease by amount selected
             else:
 ##                dbg("won't fit left;", 'paste text remains: "%s"' % paste_text)
-##            dbg('adjusted start before accounting for grouping:', sel_start)
-##            dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text)
+##                dbg('adjusted start before accounting for grouping:', sel_start)
+##                dbg('adjusted paste_text before accounting for grouping: "%s"' % paste_text)
                 pass
             if self._groupDigits and sel_start != orig_sel_start:
                 left_len = len(old_value[:sel_to].lstrip())
@@ -1564,12 +1577,6 @@ class NumCtrl(BaseMaskedTextCtrl, NumCtrlAccessorsMixin):
             self.SetInsertionPoint(sel_to)
             self.SetSelection(sel_start, sel_to)
 
-##        # treat paste as "replace number", if appropriate:
-##        sel_start, sel_to = self._GetSelection()
-##        if sel_start == sel_to or self._selectOnEntry and (sel_start, sel_to) == self._fields[0]._extent:
-##            paste_text = self._toGUI(paste_text)
-##            self._SetSelection(0, len(self._mask))
-
         new_text, replace_to = MaskedEditMixin._Paste(self,
                                         paste_text,
                                         raise_on_invalid=raise_on_invalid,
index 5f42128e813898605c278060d1faef2147d64f10..f232e2266ab91aa6feaf9fc0582b4bae71f379ce 100644 (file)
@@ -22,7 +22,7 @@ from wx.lib.masked import *
 # be a good place to implement the 2.3 logger class
 from wx.tools.dbg import Logger
 dbg = Logger()
-##dbg(enable=0)
+##dbg(enable=1)
 
 # ## TRICKY BIT: to avoid a ton of boiler-plate, and to
 # ## automate the getter/setter generation for each valid
@@ -76,6 +76,13 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
                             style=style, validator=validator,
                             name=name)
 
+        self._PostInit(setupEventHandling = setupEventHandling,
+                      name=name, value=value,**kwargs )
+
+
+    def _PostInit(self,setupEventHandling=True,
+                 name='maskedTextCtrl' , value='', **kwargs):
+
         self.controlInitialized = True
         MaskedEditMixin.__init__( self, name, **kwargs )
 
@@ -219,6 +226,18 @@ class BaseMaskedTextCtrl( wx.TextCtrl, MaskedEditMixin ):
         wx.CallAfter(self._SetSelection, replace_to, replace_to)
 ##        dbg(indent=0)
 
+    def SetFont(self, *args, **kwargs):
+        """ Set the font, then recalculate control size, if appropriate. """
+        wx.TextCtrl.SetFont(self, *args, **kwargs)
+        if self._autofit:
+##            dbg('calculated size:', self._CalcSize())            
+            self.SetClientSize(self._CalcSize())
+            width = self.GetSize().width
+            height = self.GetBestSize().height
+##            dbg('setting client size to:', (width, height))
+            self.SetSize((width, height))
+            self.SetSizeHints((width, height))
+
 
     def Clear(self):
         """ Blanks the current control value by replacing it with the default value."""
@@ -324,3 +343,29 @@ class TextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
     pass
 
 
+class PreMaskedTextCtrl( BaseMaskedTextCtrl, MaskedEditAccessorsMixin ):
+    """
+    This allows us to use XRC subclassing.
+    """
+    # This should really be wx.EVT_WINDOW_CREATE but it is not
+    # currently delivered for native controls on all platforms, so
+    # we'll use EVT_SIZE instead.  It should happen shortly after the
+    # control is created as the control is set to its "best" size.
+    _firstEventType = wx.EVT_SIZE
+
+    def __init__(self):
+        pre = wx.PreTextCtrl()
+        self.PostCreate(pre)
+        self.Bind(self._firstEventType, self.OnCreate)
+
+
+    def OnCreate(self, evt):
+        self.Unbind(self._firstEventType)
+        self._PostInit()
+
+i=0
+## CHANGELOG:
+## ====================
+##  Version 1.1
+##  1. Added .SetFont() method that properly resizes control
+##  2. Modified control to support construction via XRC mechanism.
index 36fbbee97a68ae8e7d6eecb75b1f3cb51f826968..ababfc9185c8a693d705ab6e46bb7bfac0522e4b 100644 (file)
@@ -59,7 +59,7 @@ Here's the API for TimeCtrl:
 <DL><PRE>
     <B>TimeCtrl</B>(
          parent, id = -1,
-         <B>value</B> = '12:00:00 AM',
+         <B>value</B> = '00:00:00',
          pos = wx.DefaultPosition,
          size = wx.DefaultSize,
          <B>style</B> = wxTE_PROCESS_TAB,
@@ -82,7 +82,10 @@ Here's the API for TimeCtrl:
     with SetValue() after instantiation of the control.)
     <DL><B>size</B>
     <DD>The size of the control will be automatically adjusted for 12/24 hour format
-    if wx.DefaultSize is specified.
+    if wx.DefaultSize is specified.  NOTE: due to a problem with wx.DateTime, if the
+    locale does not use 'AM/PM' for its values, the default format will automatically
+    change to 24 hour format, and an AttributeError will be thrown if a non-24 format
+    is specified.
     <DT><B>style</B>
     <DD>By default, TimeCtrl will process TAB events, by allowing tab to the
     different cells within the control.
@@ -95,7 +98,7 @@ Here's the API for TimeCtrl:
     <DD>This parameter can be used instead of the fmt24hr and displaySeconds
     parameters, respectively; it provides a shorthand way to specify the time
     format you want.  Accepted values are 'HHMMSS', 'HHMM', '24HHMMSS', and
-    '24HHMM'.  If the format is specified, the other two  arguments will be ignored.
+    '24HHMM'.  If the format is specified, the other two arguments will be ignored.
     <BR>
     <DT><B>fmt24hr</B>
     <DD>If True, control will display time in 24 hour time format; if False, it will
@@ -337,7 +340,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
         }
 
     def __init__ (
-                self, parent, id=-1, value = '12:00:00 AM',
+                self, parent, id=-1, value = '00:00:00',
                 pos = wx.DefaultPosition, size = wx.DefaultSize,
                 fmt24hr=False,
                 spinButton = None,
@@ -348,6 +351,15 @@ class TimeCtrl(BaseMaskedTextCtrl):
 
         # set defaults for control:
 ##        dbg('setting defaults:')
+
+        self.__fmt24hr = False
+        wxdt = wx.DateTimeFromDMY(1, 0, 1970)
+        if wxdt.Format('%p') != 'AM':
+            TimeCtrl.valid_ctrl_params['format'] = '24HHMMSS'
+            self.__fmt24hr = True
+            fmt24hr = True  # force/change default positional argument
+                            # (will countermand explicit set to False too.)
+
         for key, param_value in TimeCtrl.valid_ctrl_params.items():
             # This is done this way to make setattr behave consistently with
             # "private attribute" name mangling
@@ -367,7 +379,6 @@ class TimeCtrl(BaseMaskedTextCtrl):
             kwargs['displaySeconds'] = True
 
         # (handle positional arg (from original release) differently from rest of kwargs:)
-        self.__fmt24hr = False
         if not kwargs.has_key('format'):
             if fmt24hr:
                 if kwargs.has_key('displaySeconds') and kwargs['displaySeconds']:
@@ -449,7 +460,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
             self.SetLimited(limited)
             self.SetValue(value)
         except:
-            self.SetValue('12:00:00 AM')
+            self.SetValue('00:00:00')
 
         if spinButton:
             self.BindSpinButton(spinButton)     # bind spin button up/down events to this control
@@ -472,6 +483,12 @@ class TimeCtrl(BaseMaskedTextCtrl):
                 raise AttributeError('invalid keyword argument "%s"' % key)
 
             if key == 'format':
+                wxdt = wx.DateTimeFromDMY(1, 0, 1970)
+                if wxdt.Format('%p') != 'AM':
+                    require24hr = True
+                else:
+                    require24hr = False
+
                 # handle both local or generic 'maskededit' autoformat codes:
                 if param_value == 'HHMMSS' or param_value == 'TIMEHHMMSS':
                     self.__displaySeconds = True
@@ -487,6 +504,10 @@ class TimeCtrl(BaseMaskedTextCtrl):
                     self.__fmt24hr = True
                 else:
                     raise AttributeError('"%s" is not a valid format' % param_value)
+
+                if require24hr and not self.__fmt24hr:
+                    raise AttributeError('"%s" is an unsupported time format for the current locale' % param_value)
+
                 reset_format = True
 
             elif key in ("displaySeconds",  "display_seconds") and not kwargs.has_key('format'):
@@ -552,7 +573,7 @@ class TimeCtrl(BaseMaskedTextCtrl):
                 self.SetLimited(limited)
                 self.SetValue(value)
             except:
-                self.SetValue('12:00:00 AM')
+                self.SetValue('00:00:00')
 ##            dbg(indent=0)
             return {}   # no arguments to return
         else:
@@ -663,8 +684,13 @@ class TimeCtrl(BaseMaskedTextCtrl):
 ##            dbg('checkTime == len(value)?', valid)
 
             if not valid:
+                # deal with bug/deficiency in wx.DateTime:
+                if wxdt.Format('%p') not in ('AM', 'PM') and checkTime in (5,8):
+                    # couldn't parse the AM/PM field
+                    raise ValueError('cannot convert string "%s" to valid time for the current locale; please use 24hr time instead' % value)
+                else:
 ##                dbg(indent=0, suspend=0)
-                raise ValueError('cannot convert string "%s" to valid time' % value)
+                    raise ValueError('cannot convert string "%s" to valid time' % value)
 
         else:
             if isinstance(value, wx.DateTime):