1 #---------------------------------------------------------------------------- 
   2 # Name:         masked.combobox.py 
   4 # Email:        wsadkin@nameconnector.com 
   6 # Copyright:    (c) 2003 by Will Sadkin, 2003 
   8 # License:      wxWidgets license 
   9 #---------------------------------------------------------------------------- 
  11 # This masked edit class allows for the semantics of masked controls 
  12 # to be applied to combo boxes. 
  14 #---------------------------------------------------------------------------- 
  17 Provides masked edit capabilities within a ComboBox format, as well as 
  18 a base class from which you can derive masked comboboxes tailored to a specific 
  19 function.  See maskededit module overview for how to configure the control. 
  22 import  wx
, types
, string
 
  23 from wx
.lib
.masked 
import * 
  25 # jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would 
  26 # be a good place to implement the 2.3 logger class 
  27 from wx
.tools
.dbg 
import Logger
 
  31 ## ---------- ---------- ---------- ---------- ---------- ---------- ---------- 
  32 ## Because calling SetSelection programmatically does not fire EVT_COMBOBOX 
  33 ## events, we have to do it ourselves when we auto-complete. 
  34 class MaskedComboBoxSelectEvent(wx
.PyCommandEvent
): 
  36     Because calling SetSelection programmatically does not fire EVT_COMBOBOX 
  37     events, the derived control has to do it itself when it auto-completes. 
  39     def __init__(self
, id, selection 
= 0, object=None): 
  40         wx
.PyCommandEvent
.__init
__(self
, wx
.wxEVT_COMMAND_COMBOBOX_SELECTED
, id) 
  42         self
.__selection 
= selection
 
  43         self
.SetEventObject(object) 
  45     def GetSelection(self
): 
  46         """Retrieve the value of the control at the time 
  47         this event was generated.""" 
  48         return self
.__selection
 
  51 class BaseMaskedComboBox( wx
.ComboBox
, MaskedEditMixin 
): 
  53     Base class for generic masked edit comboboxes; allows auto-complete of values. 
  54     It is not meant to be instantiated directly, but rather serves as a base class 
  55     for any subsequent refinements. 
  57     def __init__( self
, parent
, id=-1, value 
= '', 
  58                   pos 
= wx
.DefaultPosition
, 
  59                   size 
= wx
.DefaultSize
, 
  61                   style 
= wx
.CB_DROPDOWN
, 
  62                   validator 
= wx
.DefaultValidator
, 
  63                   name 
= "maskedComboBox", 
  64                   setupEventHandling 
= True,        ## setup event handling by default): 
  68         kwargs
['choices'] = choices                 
## set up maskededit to work with choice list too 
  70         ## Since combobox completion is case-insensitive, always validate same way 
  71         if not kwargs
.has_key('compareNoCase'): 
  72             kwargs
['compareNoCase'] = True 
  74         MaskedEditMixin
.__init
__( self
, name
, **kwargs 
) 
  76         self
._choices 
= self
._ctrl
_constraints
._choices
 
  77 ##        dbg('self._choices:', self._choices) 
  79         if self
._ctrl
_constraints
._alignRight
: 
  80             choices 
= [choice
.rjust(self
._masklength
) for choice 
in choices
] 
  82             choices 
= [choice
.ljust(self
._masklength
) for choice 
in choices
] 
  84         wx
.ComboBox
.__init
__(self
, parent
, id, value
='', 
  86                             choices
=choices
, style
=style|wx
.WANTS_CHARS
, 
  89         self
.controlInitialized 
= True 
  91         self
._PostInit
(style
=style
, setupEventHandling
=setupEventHandling
, 
  92                        name
=name
, value
=value
, **kwargs
) 
  95     def _PostInit(self
, style
=wx
.CB_DROPDOWN
, 
  96                   setupEventHandling 
= True,        ## setup event handling by default): 
  97                   name 
= "maskedComboBox", value
='', **kwargs
): 
  99         # This is necessary, because wxComboBox currently provides no 
 100         # method for determining later if this was specified in the 
 101         # constructor for the control... 
 102         self
.__readonly 
= style 
& wx
.CB_READONLY 
== wx
.CB_READONLY
 
 104         if not hasattr(self
, 'controlInitialized'): 
 106             self
.controlInitialized 
= True          ## must have been called via XRC, therefore base class is constructed 
 107             if not kwargs
.has_key('choices'): 
 109                 kwargs
['choices'] = choices         
## set up maskededit to work with choice list too 
 112             ## Since combobox completion is case-insensitive, always validate same way 
 113             if not kwargs
.has_key('compareNoCase'): 
 114                 kwargs
['compareNoCase'] = True 
 116             MaskedEditMixin
.__init
__( self
, name
, **kwargs 
) 
 118             self
._choices 
= self
._ctrl
_constraints
._choices
 
 119 ##        dbg('self._choices:', self._choices) 
 121             if self
._ctrl
_constraints
._alignRight
: 
 122                 choices 
= [choice
.rjust(self
._masklength
) for choice 
in choices
] 
 124                 choices 
= [choice
.ljust(self
._masklength
) for choice 
in choices
] 
 125             wx
.ComboBox
.Clear(self
) 
 126             wx
.ComboBox
.AppendItems(self
, choices
) 
 129         # Set control font - fixed width by default 
 133             self
.SetClientSize(self
._CalcSize
()) 
 134             width 
= self
.GetSize().width
 
 135             height 
= self
.GetBestSize().height
 
 136             self
.SetBestFittingSize((width
, height
)) 
 140             # ensure value is width of the mask of the control: 
 141             if self
._ctrl
_constraints
._alignRight
: 
 142                 value 
= value
.rjust(self
._masklength
) 
 144                 value 
= value
.ljust(self
._masklength
) 
 147             self
.SetStringSelection(value
) 
 149             self
._SetInitialValue
(value
) 
 152         self
._SetKeycodeHandler
(wx
.WXK_UP
, self
._OnSelectChoice
) 
 153         self
._SetKeycodeHandler
(wx
.WXK_DOWN
, self
._OnSelectChoice
) 
 155         if setupEventHandling
: 
 156             ## Setup event handlers 
 157             self
.Bind(wx
.EVT_SET_FOCUS
, self
._OnFocus 
)         ## defeat automatic full selection 
 158             self
.Bind(wx
.EVT_KILL_FOCUS
, self
._OnKillFocus 
)    ## run internal validator 
 159             self
.Bind(wx
.EVT_LEFT_DCLICK
, self
._OnDoubleClick
)  ## select field under cursor on dclick 
 160             self
.Bind(wx
.EVT_RIGHT_UP
, self
._OnContextMenu 
)    ## bring up an appropriate context menu 
 161             self
.Bind(wx
.EVT_CHAR
, self
._OnChar 
)               ## handle each keypress 
 162             self
.Bind(wx
.EVT_KEY_DOWN
, self
._OnKeyDownInComboBox 
) ## for special processing of up/down keys 
 163             self
.Bind(wx
.EVT_KEY_DOWN
, self
._OnKeyDown 
)        ## for processing the rest of the control keys 
 164                                                                 ## (next in evt chain) 
 165             self
.Bind(wx
.EVT_TEXT
, self
._OnTextChange 
)         ## color control appropriately & keep 
 166                                                                 ## track of previous value for undo 
 171         return "<MaskedComboBox: %s>" % self
.GetValue() 
 174     def _CalcSize(self
, size
=None): 
 176         Calculate automatic size if allowed; augment base mixin function 
 177         to account for the selector button. 
 179         size 
= self
._calcSize
(size
) 
 180         return (size
[0]+20, size
[1]) 
 183     def SetFont(self
, *args
, **kwargs
): 
 184         """ Set the font, then recalculate control size, if appropriate. """ 
 185         wx
.ComboBox
.SetFont(self
, *args
, **kwargs
) 
 187 ##            dbg('calculated size:', self._CalcSize())             
 188             self
.SetClientSize(self
._CalcSize
()) 
 189             width 
= self
.GetSize().width
 
 190             height 
= self
.GetBestSize().height
 
 191 ##            dbg('setting client size to:', (width, height)) 
 192             self
.SetBestFittingSize((width
, height
)) 
 195     def _GetSelection(self
): 
 197         Allow mixin to get the text selection of this control. 
 198         REQUIRED by any class derived from MaskedEditMixin. 
 200 ##        dbg('MaskedComboBox::_GetSelection()') 
 201         return self
.GetMark() 
 203     def _SetSelection(self
, sel_start
, sel_to
): 
 205         Allow mixin to set the text selection of this control. 
 206         REQUIRED by any class derived from MaskedEditMixin. 
 208 ##        dbg('MaskedComboBox::_SetSelection: setting mark to (%d, %d)' % (sel_start, sel_to)) 
 209         return self
.SetMark( sel_start
, sel_to 
) 
 212     def _GetInsertionPoint(self
): 
 213 ##        dbg('MaskedComboBox::_GetInsertionPoint()', indent=1) 
 214 ##        ret = self.GetInsertionPoint() 
 215         # work around new bug in 2.5, in which the insertion point 
 216         # returned is always at the right side of the selection, 
 217         # rather than the start, as is the case with TextCtrl. 
 218         ret 
= self
.GetMark()[0] 
 219 ##        dbg('returned', ret, indent=0) 
 222     def _SetInsertionPoint(self
, pos
): 
 223 ##        dbg('MaskedComboBox::_SetInsertionPoint(%d)' % pos) 
 224         self
.SetInsertionPoint(pos
) 
 229         Allow mixin to get the raw value of the control with this function. 
 230         REQUIRED by any class derived from MaskedEditMixin. 
 232         return self
.GetValue() 
 234     def _SetValue(self
, value
): 
 236         Allow mixin to set the raw value of the control with this function. 
 237         REQUIRED by any class derived from MaskedEditMixin. 
 239         # For wxComboBox, ensure that values are properly padded so that 
 240         # if varying length choices are supplied, they always show up 
 241         # in the window properly, and will be the appropriate length 
 243         if self
._ctrl
_constraints
._alignRight
: 
 244             value 
= value
.rjust(self
._masklength
) 
 246             value 
= value
.ljust(self
._masklength
) 
 248         # Record current selection and insertion point, for undo 
 249         self
._prevSelection 
= self
._GetSelection
() 
 250         self
._prevInsertionPoint 
= self
._GetInsertionPoint
() 
 251         wx
.ComboBox
.SetValue(self
, value
) 
 252         # text change events don't always fire, so we check validity here 
 253         # to make certain formatting is applied: 
 256     def SetValue(self
, value
): 
 258         This function redefines the externally accessible .SetValue to be 
 259         a smart "paste" of the text in question, so as not to corrupt the 
 260         masked control.  NOTE: this must be done in the class derived 
 261         from the base wx control. 
 264             wx
.ComboBox
.SetValue(value
)   # revert to base control behavior 
 267         # empty previous contents, replacing entire value: 
 268         self
._SetInsertionPoint
(0) 
 269         self
._SetSelection
(0, self
._masklength
) 
 271         if( len(value
) < self
._masklength                
# value shorter than control 
 272             and (self
._isFloat 
or self
._isInt
)            # and it's a numeric control 
 273             and self
._ctrl
_constraints
._alignRight 
):   # and it's a right-aligned control 
 274             # try to intelligently "pad out" the value to the right size: 
 275             value 
= self
._template
[0:self
._masklength 
- len(value
)] + value
 
 276 ##            dbg('padded value = "%s"' % value) 
 278         # For wxComboBox, ensure that values are properly padded so that 
 279         # if varying length choices are supplied, they always show up 
 280         # in the window properly, and will be the appropriate length 
 282         elif self
._ctrl
_constraints
._alignRight
: 
 283             value 
= value
.rjust(self
._masklength
) 
 285             value 
= value
.ljust(self
._masklength
) 
 288         # make SetValue behave the same as if you had typed the value in: 
 290             value
, replace_to 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 292                 self
._isNeg 
= False     # (clear current assumptions) 
 293                 value 
= self
._adjustFloat
(value
) 
 295                 self
._isNeg 
= False     # (clear current assumptions) 
 296                 value 
= self
._adjustInt
(value
) 
 297             elif self
._isDate 
and not self
.IsValid(value
) and self
._4digityear
: 
 298                 value 
= self
._adjustDate
(value
, fixcentury
=True) 
 300             # If date, year might be 2 digits vs. 4; try adjusting it: 
 301             if self
._isDate 
and self
._4digityear
: 
 302                 dateparts 
= value
.split(' ') 
 303                 dateparts
[0] = self
._adjustDate
(dateparts
[0], fixcentury
=True) 
 304                 value 
= string
.join(dateparts
, ' ') 
 305 ##                dbg('adjusted value: "%s"' % value) 
 306                 value 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 310         self
._SetValue
(value
) 
 311 ####        dbg('queuing insertion after .SetValue', replace_to) 
 312         wx
.CallAfter(self
._SetInsertionPoint
, replace_to
) 
 313         wx
.CallAfter(self
._SetSelection
, replace_to
, replace_to
) 
 318         Allow mixin to refresh the base control with this function. 
 319         REQUIRED by any class derived from MaskedEditMixin. 
 321         wx
.ComboBox
.Refresh(self
) 
 325         This function redefines the externally accessible .Refresh() to 
 326         validate the contents of the masked control as it refreshes. 
 327         NOTE: this must be done in the class derived from the base wx control. 
 333     def _IsEditable(self
): 
 335         Allow mixin to determine if the base control is editable with this function. 
 336         REQUIRED by any class derived from MaskedEditMixin. 
 338         return not self
.__readonly
 
 343         This function redefines the externally accessible .Cut to be 
 344         a smart "erase" of the text in question, so as not to corrupt the 
 345         masked control.  NOTE: this must be done in the class derived 
 346         from the base wx control. 
 349             self
._Cut
()             # call the mixin's Cut method 
 351             wx
.ComboBox
.Cut(self
)    # else revert to base control behavior 
 356         This function redefines the externally accessible .Paste to be 
 357         a smart "paste" of the text in question, so as not to corrupt the 
 358         masked control.  NOTE: this must be done in the class derived 
 359         from the base wx control. 
 362             self
._Paste
()           # call the mixin's Paste method 
 364             wx
.ComboBox
.Paste(self
)  # else revert to base control behavior 
 369         This function defines the undo operation for the control. (The default 
 375             wx
.ComboBox
.Undo()       # else revert to base control behavior 
 377     def Append( self
, choice
, clientData
=None ): 
 379         This base control function override is necessary so the control can keep track 
 380         of any additions to the list of choices, because wx.ComboBox doesn't have an 
 381         accessor for the choice list.  The code here is the same as in the 
 382         SetParameters() mixin function, but is done for the individual value 
 383         as appended, so the list can be built incrementally without speed penalty. 
 386             if type(choice
) not in (types
.StringType
, types
.UnicodeType
): 
 387                 raise TypeError('%s: choices must be a sequence of strings' % str(self
._index
)) 
 388             elif not self
.IsValid(choice
): 
 389                 raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self
._index
), choice
)) 
 391             if not self
._ctrl
_constraints
._choices
: 
 392                 self
._ctrl
_constraints
._compareChoices 
= [] 
 393                 self
._ctrl
_constraints
._choices 
= [] 
 396             compareChoice 
= choice
.strip() 
 398             if self
._ctrl
_constraints
._compareNoCase
: 
 399                 compareChoice 
= compareChoice
.lower() 
 401             if self
._ctrl
_constraints
._alignRight
: 
 402                 choice 
= choice
.rjust(self
._masklength
) 
 404                 choice 
= choice
.ljust(self
._masklength
) 
 405             if self
._ctrl
_constraints
._fillChar 
!= ' ': 
 406                 choice 
= choice
.replace(' ', self
._fillChar
) 
 407 ##            dbg('updated choice:', choice) 
 410             self
._ctrl
_constraints
._compareChoices
.append(compareChoice
) 
 411             self
._ctrl
_constraints
._choices
.append(choice
) 
 412             self
._choices 
= self
._ctrl
_constraints
._choices     
# (for shorthand) 
 414             if( not self
.IsValid(choice
) and 
 415                (not self
._ctrl
_constraints
.IsEmpty(choice
) or 
 416                 (self
._ctrl
_constraints
.IsEmpty(choice
) and self
._ctrl
_constraints
._validRequired
) ) ): 
 417                 raise ValueError('"%s" is not a valid value for the control "%s" as specified.' % (choice
, self
.name
)) 
 419         wx
.ComboBox
.Append(self
, choice
, clientData
) 
 422     def AppendItems( self
, choices 
): 
 424         AppendItems() is handled in terms of Append, to avoid code replication. 
 426         for choice 
in choices
: 
 432         This base control function override is necessary so the derived control can 
 433         keep track of any additions to the list of choices, because wx.ComboBox 
 434         doesn't have an accessor for the choice list. 
 438             self
._ctrl
_constraints
._autoCompleteIndex 
= -1 
 439             if self
._ctrl
_constraints
._choices
: 
 440                 self
.SetCtrlParameters(choices
=[]) 
 441         wx
.ComboBox
.Clear(self
) 
 444     def _OnCtrlParametersChanged(self
): 
 446         This overrides the mixin's default OnCtrlParametersChanged to detect 
 447         changes in choice list, so masked.Combobox can update the base control: 
 449         if self
.controlInitialized 
and self
._choices 
!= self
._ctrl
_constraints
._choices
: 
 450             wx
.ComboBox
.Clear(self
) 
 451             self
._choices 
= self
._ctrl
_constraints
._choices
 
 452             for choice 
in self
._choices
: 
 453                 wx
.ComboBox
.Append( self
, choice 
) 
 456     # Not all wx platform implementations have .GetMark, so we make the following test, 
 457     # and fall back to our old hack if they don't... 
 459     if not hasattr(wx
.ComboBox
, 'GetMark'): 
 462             This function is a hack to make up for the fact that wx.ComboBox has no 
 463             method for returning the selected portion of its edit control.  It 
 464             works, but has the nasty side effect of generating lots of intermediate 
 467 ##            dbg(suspend=1)  # turn off debugging around this function 
 468 ##            dbg('MaskedComboBox::GetMark', indent=1) 
 471                 return 0, 0 # no selection possible for editing 
 472 ##            sel_start, sel_to = wxComboBox.GetMark(self)        # what I'd *like* to have! 
 473             sel_start 
= sel_to 
= self
.GetInsertionPoint() 
 474 ##            dbg("current sel_start:", sel_start) 
 475             value 
= self
.GetValue() 
 476 ##            dbg('value: "%s"' % value) 
 478             self
._ignoreChange 
= True               # tell _OnTextChange() to ignore next event (if any) 
 480             wx
.ComboBox
.Cut(self
) 
 481             newvalue 
= self
.GetValue() 
 482 ##            dbg("value after Cut operation:", newvalue) 
 484             if newvalue 
!= value
:                   # something was selected; calculate extent 
 485 ##                dbg("something selected") 
 486                 sel_to 
= sel_start 
+ len(value
) - len(newvalue
) 
 487                 wx
.ComboBox
.SetValue(self
, value
)    # restore original value and selection (still ignoring change) 
 488                 wx
.ComboBox
.SetInsertionPoint(self
, sel_start
) 
 489                 wx
.ComboBox
.SetMark(self
, sel_start
, sel_to
) 
 491             self
._ignoreChange 
= False              # tell _OnTextChange() to pay attn again 
 493 ##            dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0) 
 494             return sel_start
, sel_to
 
 497 ##            dbg('MaskedComboBox::GetMark()', indent = 1) 
 498             ret 
= wx
.ComboBox
.GetMark(self
) 
 499 ##            dbg('returned', ret, indent=0) 
 503     def SetSelection(self
, index
): 
 505         Necessary override for bookkeeping on choice selection, to keep current value 
 508 ##        dbg('MaskedComboBox::SetSelection(%d)' % index) 
 510             self
._prevValue 
= self
._curValue
 
 511             self
._curValue 
= self
._choices
[index
] 
 512             self
._ctrl
_constraints
._autoCompleteIndex 
= index
 
 513         wx
.ComboBox
.SetSelection(self
, index
) 
 516     def _OnKeyDownInComboBox(self
, event
): 
 518         This function is necessary because navigation and control key 
 519         events do not seem to normally be seen by the wxComboBox's 
 520         EVT_CHAR routine.  (Tabs don't seem to be visible no matter 
 523         if event
.GetKeyCode() in self
._nav 
+ self
._control
: 
 527             event
.Skip()    # let mixin default KeyDown behavior occur 
 530     def _OnSelectChoice(self
, event
): 
 532         This function appears to be necessary, because the processing done 
 533         on the text of the control somehow interferes with the combobox's 
 534         selection mechanism for the arrow keys. 
 536 ##        dbg('MaskedComboBox::OnSelectChoice', indent=1) 
 542         value 
= self
.GetValue().strip() 
 544         if self
._ctrl
_constraints
._compareNoCase
: 
 545             value 
= value
.lower() 
 547         if event
.GetKeyCode() == wx
.WXK_UP
: 
 551         match_index
, partial_match 
= self
._autoComplete
( 
 553                                                 self
._ctrl
_constraints
._compareChoices
, 
 555                                                 self
._ctrl
_constraints
._compareNoCase
, 
 556                                                 current_index 
= self
._ctrl
_constraints
._autoCompleteIndex
) 
 557         if match_index 
is not None: 
 558 ##            dbg('setting selection to', match_index) 
 559             # issue appropriate event to outside: 
 560             self
._OnAutoSelect
(self
._ctrl
_constraints
, match_index
=match_index
) 
 562             keep_processing 
= False 
 564             pos 
= self
._adjustPos
(self
._GetInsertionPoint
(), event
.GetKeyCode()) 
 565             field 
= self
._FindField
(pos
) 
 566             if self
.IsEmpty() or not field
._hasList
: 
 567 ##                dbg('selecting 1st value in list') 
 568                 self
._OnAutoSelect
(self
._ctrl
_constraints
, match_index
=0) 
 570                 keep_processing 
= False 
 572                 # attempt field-level auto-complete 
 574                 keep_processing 
= self
._OnAutoCompleteField
(event
) 
 575 ##        dbg('keep processing?', keep_processing, indent=0) 
 576         return keep_processing
 
 579     def _OnAutoSelect(self
, field
, match_index
): 
 581         Override mixin (empty) autocomplete handler, so that autocompletion causes 
 582         combobox to update appropriately. 
 584 ##        dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1) 
 585 ##        field._autoCompleteIndex = match_index 
 586         if field 
== self
._ctrl
_constraints
: 
 587             self
.SetSelection(match_index
) 
 588 ##            dbg('issuing combo selection event') 
 589             self
.GetEventHandler().ProcessEvent( 
 590                 MaskedComboBoxSelectEvent( self
.GetId(), match_index
, self 
) ) 
 592 ##        dbg('field._autoCompleteIndex:', match_index) 
 593 ##        dbg('self.GetSelection():', self.GetSelection()) 
 594         end 
= self
._goEnd
(getPosOnly
=True) 
 595 ##        dbg('scheduling set of end position to:', end) 
 596         # work around bug in wx 2.5 
 597         wx
.CallAfter(self
.SetInsertionPoint
, 0) 
 598         wx
.CallAfter(self
.SetInsertionPoint
, end
) 
 602     def _OnReturn(self
, event
): 
 604         For wx.ComboBox, it seems that if you hit return when the dropdown is 
 605         dropped, the event that dismisses the dropdown will also blank the 
 606         control, because of the implementation of wxComboBox.  So this function 
 607         examines the selection and if it is -1, and the value according to 
 608         (the base control!) is a value in the list, then it schedules a 
 609         programmatic wxComboBox.SetSelection() call to pick the appropriate 
 610         item in the list. (and then does the usual OnReturn bit.) 
 612 ##        dbg('MaskedComboBox::OnReturn', indent=1) 
 613 ##        dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection()) 
 614         if self
.GetSelection() == -1 and self
.GetValue().lower().strip() in self
._ctrl
_constraints
._compareChoices
: 
 615             wx
.CallAfter(self
.SetSelection
, self
._ctrl
_constraints
._autoCompleteIndex
) 
 617         event
.m_keyCode 
= wx
.WXK_TAB
 
 622 class ComboBox( BaseMaskedComboBox
, MaskedEditAccessorsMixin 
): 
 624     The "user-visible" masked combobox control, this class is 
 625     identical to the BaseMaskedComboBox class it's derived from. 
 626     (This extra level of inheritance allows us to add the generic 
 627     set of masked edit parameters only to this class while allowing 
 628     other classes to derive from the "base" masked combobox control, 
 629     and provide a smaller set of valid accessor functions.) 
 630     See BaseMaskedComboBox for available methods. 
 635 class PreMaskedComboBox( BaseMaskedComboBox
, MaskedEditAccessorsMixin 
): 
 637     This class exists to support the use of XRC subclassing. 
 639     # This should really be wx.EVT_WINDOW_CREATE but it is not 
 640     # currently delivered for native controls on all platforms, so 
 641     # we'll use EVT_SIZE instead.  It should happen shortly after the 
 642     # control is created as the control is set to its "best" size. 
 643     _firstEventType 
= wx
.EVT_SIZE
 
 646         pre 
= wx
.PreComboBox() 
 648         self
.Bind(self
._firstEventType
, self
.OnCreate
) 
 651     def OnCreate(self
, evt
): 
 652         self
.Unbind(self
._firstEventType
) 
 657 ## ==================== 
 659 ##  1. Made definition of "hack" GetMark conditional on base class not 
 660 ##     implementing it properly, to allow for migration in wx code base 
 661 ##     while taking advantage of improvements therein for some platforms. 
 664 ##  1. Converted docstrings to reST format, added doc for ePyDoc. 
 665 ##  2. Renamed helper functions, vars etc. not intended to be visible in public 
 666 ##     interface to code. 
 669 ##  1. Added .SetFont() method that properly resizes control 
 670 ##  2. Modified control to support construction via XRC mechanism. 
 671 ##  3. Added AppendItems() to conform with latest combobox.