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
.SetInitialSize((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
.SetInitialSize((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
) 
 227     def IsEmpty(*args
, **kw
): 
 228         return MaskedEditMixin
.IsEmpty(*args
, **kw
) 
 233         Allow mixin to get the raw value of the control with this function. 
 234         REQUIRED by any class derived from MaskedEditMixin. 
 236         return self
.GetValue() 
 238     def _SetValue(self
, value
): 
 240         Allow mixin to set the raw value of the control with this function. 
 241         REQUIRED by any class derived from MaskedEditMixin. 
 243         # For wxComboBox, ensure that values are properly padded so that 
 244         # if varying length choices are supplied, they always show up 
 245         # in the window properly, and will be the appropriate length 
 247         if self
._ctrl
_constraints
._alignRight
: 
 248             value 
= value
.rjust(self
._masklength
) 
 250             value 
= value
.ljust(self
._masklength
) 
 252         # Record current selection and insertion point, for undo 
 253         self
._prevSelection 
= self
._GetSelection
() 
 254         self
._prevInsertionPoint 
= self
._GetInsertionPoint
() 
 255         wx
.ComboBox
.SetValue(self
, value
) 
 256         # text change events don't always fire, so we check validity here 
 257         # to make certain formatting is applied: 
 260     def SetValue(self
, value
): 
 262         This function redefines the externally accessible .SetValue to be 
 263         a smart "paste" of the text in question, so as not to corrupt the 
 264         masked control.  NOTE: this must be done in the class derived 
 265         from the base wx control. 
 268             wx
.ComboBox
.SetValue(value
)   # revert to base control behavior 
 271         # empty previous contents, replacing entire value: 
 272         self
._SetInsertionPoint
(0) 
 273         self
._SetSelection
(0, self
._masklength
) 
 275         if( len(value
) < self
._masklength                
# value shorter than control 
 276             and (self
._isFloat 
or self
._isInt
)            # and it's a numeric control 
 277             and self
._ctrl
_constraints
._alignRight 
):   # and it's a right-aligned control 
 278             # try to intelligently "pad out" the value to the right size: 
 279             value 
= self
._template
[0:self
._masklength 
- len(value
)] + value
 
 280 ##            dbg('padded value = "%s"' % value) 
 282         # For wxComboBox, ensure that values are properly padded so that 
 283         # if varying length choices are supplied, they always show up 
 284         # in the window properly, and will be the appropriate length 
 286         elif self
._ctrl
_constraints
._alignRight
: 
 287             value 
= value
.rjust(self
._masklength
) 
 289             value 
= value
.ljust(self
._masklength
) 
 292         # make SetValue behave the same as if you had typed the value in: 
 294             value
, replace_to 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 296                 self
._isNeg 
= False     # (clear current assumptions) 
 297                 value 
= self
._adjustFloat
(value
) 
 299                 self
._isNeg 
= False     # (clear current assumptions) 
 300                 value 
= self
._adjustInt
(value
) 
 301             elif self
._isDate 
and not self
.IsValid(value
) and self
._4digityear
: 
 302                 value 
= self
._adjustDate
(value
, fixcentury
=True) 
 304             # If date, year might be 2 digits vs. 4; try adjusting it: 
 305             if self
._isDate 
and self
._4digityear
: 
 306                 dateparts 
= value
.split(' ') 
 307                 dateparts
[0] = self
._adjustDate
(dateparts
[0], fixcentury
=True) 
 308                 value 
= string
.join(dateparts
, ' ') 
 309 ##                dbg('adjusted value: "%s"' % value) 
 310                 value 
= self
._Paste
(value
, raise_on_invalid
=True, just_return_value
=True) 
 314         self
._SetValue
(value
) 
 315 ####        dbg('queuing insertion after .SetValue', replace_to) 
 316         wx
.CallAfter(self
._SetInsertionPoint
, replace_to
) 
 317         wx
.CallAfter(self
._SetSelection
, replace_to
, replace_to
) 
 322         Allow mixin to refresh the base control with this function. 
 323         REQUIRED by any class derived from MaskedEditMixin. 
 325         wx
.ComboBox
.Refresh(self
) 
 329         This function redefines the externally accessible .Refresh() to 
 330         validate the contents of the masked control as it refreshes. 
 331         NOTE: this must be done in the class derived from the base wx control. 
 337     def _IsEditable(self
): 
 339         Allow mixin to determine if the base control is editable with this function. 
 340         REQUIRED by any class derived from MaskedEditMixin. 
 342         return not self
.__readonly
 
 347         This function redefines the externally accessible .Cut to be 
 348         a smart "erase" of the text in question, so as not to corrupt the 
 349         masked control.  NOTE: this must be done in the class derived 
 350         from the base wx control. 
 353             self
._Cut
()             # call the mixin's Cut method 
 355             wx
.ComboBox
.Cut(self
)    # else revert to base control behavior 
 360         This function redefines the externally accessible .Paste to be 
 361         a smart "paste" of the text in question, so as not to corrupt the 
 362         masked control.  NOTE: this must be done in the class derived 
 363         from the base wx control. 
 366             self
._Paste
()           # call the mixin's Paste method 
 368             wx
.ComboBox
.Paste(self
)  # else revert to base control behavior 
 373         This function defines the undo operation for the control. (The default 
 379             wx
.ComboBox
.Undo()       # else revert to base control behavior 
 381     def Append( self
, choice
, clientData
=None ): 
 383         This base control function override is necessary so the control can keep track 
 384         of any additions to the list of choices, because wx.ComboBox doesn't have an 
 385         accessor for the choice list.  The code here is the same as in the 
 386         SetParameters() mixin function, but is done for the individual value 
 387         as appended, so the list can be built incrementally without speed penalty. 
 390             if type(choice
) not in (types
.StringType
, types
.UnicodeType
): 
 391                 raise TypeError('%s: choices must be a sequence of strings' % str(self
._index
)) 
 392             elif not self
.IsValid(choice
): 
 393                 raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self
._index
), choice
)) 
 395             if not self
._ctrl
_constraints
._choices
: 
 396                 self
._ctrl
_constraints
._compareChoices 
= [] 
 397                 self
._ctrl
_constraints
._choices 
= [] 
 400             compareChoice 
= choice
.strip() 
 402             if self
._ctrl
_constraints
._compareNoCase
: 
 403                 compareChoice 
= compareChoice
.lower() 
 405             if self
._ctrl
_constraints
._alignRight
: 
 406                 choice 
= choice
.rjust(self
._masklength
) 
 408                 choice 
= choice
.ljust(self
._masklength
) 
 409             if self
._ctrl
_constraints
._fillChar 
!= ' ': 
 410                 choice 
= choice
.replace(' ', self
._fillChar
) 
 411 ##            dbg('updated choice:', choice) 
 414             self
._ctrl
_constraints
._compareChoices
.append(compareChoice
) 
 415             self
._ctrl
_constraints
._choices
.append(choice
) 
 416             self
._choices 
= self
._ctrl
_constraints
._choices     
# (for shorthand) 
 418             if( not self
.IsValid(choice
) and 
 419                (not self
._ctrl
_constraints
.IsEmpty(choice
) or 
 420                 (self
._ctrl
_constraints
.IsEmpty(choice
) and self
._ctrl
_constraints
._validRequired
) ) ): 
 421                 raise ValueError('"%s" is not a valid value for the control "%s" as specified.' % (choice
, self
.name
)) 
 423         wx
.ComboBox
.Append(self
, choice
, clientData
) 
 426     def AppendItems( self
, choices 
): 
 428         AppendItems() is handled in terms of Append, to avoid code replication. 
 430         for choice 
in choices
: 
 436         This base control function override is necessary so the derived control can 
 437         keep track of any additions to the list of choices, because wx.ComboBox 
 438         doesn't have an accessor for the choice list. 
 442             self
._ctrl
_constraints
._autoCompleteIndex 
= -1 
 443             if self
._ctrl
_constraints
._choices
: 
 444                 self
.SetCtrlParameters(choices
=[]) 
 445         wx
.ComboBox
.Clear(self
) 
 448     def _OnCtrlParametersChanged(self
): 
 450         This overrides the mixin's default OnCtrlParametersChanged to detect 
 451         changes in choice list, so masked.Combobox can update the base control: 
 453         if self
.controlInitialized 
and self
._choices 
!= self
._ctrl
_constraints
._choices
: 
 454             wx
.ComboBox
.Clear(self
) 
 455             self
._choices 
= self
._ctrl
_constraints
._choices
 
 456             for choice 
in self
._choices
: 
 457                 wx
.ComboBox
.Append( self
, choice 
) 
 460     # Not all wx platform implementations have .GetMark, so we make the following test, 
 461     # and fall back to our old hack if they don't... 
 463     if not hasattr(wx
.ComboBox
, 'GetMark'): 
 466             This function is a hack to make up for the fact that wx.ComboBox has no 
 467             method for returning the selected portion of its edit control.  It 
 468             works, but has the nasty side effect of generating lots of intermediate 
 471 ##            dbg(suspend=1)  # turn off debugging around this function 
 472 ##            dbg('MaskedComboBox::GetMark', indent=1) 
 475                 return 0, 0 # no selection possible for editing 
 476 ##            sel_start, sel_to = wxComboBox.GetMark(self)        # what I'd *like* to have! 
 477             sel_start 
= sel_to 
= self
.GetInsertionPoint() 
 478 ##            dbg("current sel_start:", sel_start) 
 479             value 
= self
.GetValue() 
 480 ##            dbg('value: "%s"' % value) 
 482             self
._ignoreChange 
= True               # tell _OnTextChange() to ignore next event (if any) 
 484             wx
.ComboBox
.Cut(self
) 
 485             newvalue 
= self
.GetValue() 
 486 ##            dbg("value after Cut operation:", newvalue) 
 488             if newvalue 
!= value
:                   # something was selected; calculate extent 
 489 ##                dbg("something selected") 
 490                 sel_to 
= sel_start 
+ len(value
) - len(newvalue
) 
 491                 wx
.ComboBox
.SetValue(self
, value
)    # restore original value and selection (still ignoring change) 
 492                 wx
.ComboBox
.SetInsertionPoint(self
, sel_start
) 
 493                 wx
.ComboBox
.SetMark(self
, sel_start
, sel_to
) 
 495             self
._ignoreChange 
= False              # tell _OnTextChange() to pay attn again 
 497 ##            dbg('computed selection:', sel_start, sel_to, indent=0, suspend=0) 
 498             return sel_start
, sel_to
 
 501 ##            dbg('MaskedComboBox::GetMark()', indent = 1) 
 502             ret 
= wx
.ComboBox
.GetMark(self
) 
 503 ##            dbg('returned', ret, indent=0) 
 507     def SetSelection(self
, index
): 
 509         Necessary override for bookkeeping on choice selection, to keep current value 
 512 ##        dbg('MaskedComboBox::SetSelection(%d)' % index) 
 514             self
._prevValue 
= self
._curValue
 
 515             self
._curValue 
= self
._choices
[index
] 
 516             self
._ctrl
_constraints
._autoCompleteIndex 
= index
 
 517         wx
.ComboBox
.SetSelection(self
, index
) 
 520     def _OnKeyDownInComboBox(self
, event
): 
 522         This function is necessary because navigation and control key 
 523         events do not seem to normally be seen by the wxComboBox's 
 524         EVT_CHAR routine.  (Tabs don't seem to be visible no matter 
 527         if event
.GetKeyCode() in self
._nav 
+ self
._control
: 
 531             event
.Skip()    # let mixin default KeyDown behavior occur 
 534     def _OnSelectChoice(self
, event
): 
 536         This function appears to be necessary, because the processing done 
 537         on the text of the control somehow interferes with the combobox's 
 538         selection mechanism for the arrow keys. 
 540 ##        dbg('MaskedComboBox::OnSelectChoice', indent=1) 
 546         value 
= self
.GetValue().strip() 
 548         if self
._ctrl
_constraints
._compareNoCase
: 
 549             value 
= value
.lower() 
 551         if event
.GetKeyCode() == wx
.WXK_UP
: 
 555         match_index
, partial_match 
= self
._autoComplete
( 
 557                                                 self
._ctrl
_constraints
._compareChoices
, 
 559                                                 self
._ctrl
_constraints
._compareNoCase
, 
 560                                                 current_index 
= self
._ctrl
_constraints
._autoCompleteIndex
) 
 561         if match_index 
is not None: 
 562 ##            dbg('setting selection to', match_index) 
 563             # issue appropriate event to outside: 
 564             self
._OnAutoSelect
(self
._ctrl
_constraints
, match_index
=match_index
) 
 566             keep_processing 
= False 
 568             pos 
= self
._adjustPos
(self
._GetInsertionPoint
(), event
.GetKeyCode()) 
 569             field 
= self
._FindField
(pos
) 
 570             if self
.IsEmpty() or not field
._hasList
: 
 571 ##                dbg('selecting 1st value in list') 
 572                 self
._OnAutoSelect
(self
._ctrl
_constraints
, match_index
=0) 
 574                 keep_processing 
= False 
 576                 # attempt field-level auto-complete 
 578                 keep_processing 
= self
._OnAutoCompleteField
(event
) 
 579 ##        dbg('keep processing?', keep_processing, indent=0) 
 580         return keep_processing
 
 583     def _OnAutoSelect(self
, field
, match_index
): 
 585         Override mixin (empty) autocomplete handler, so that autocompletion causes 
 586         combobox to update appropriately. 
 588 ##        dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1) 
 589 ##        field._autoCompleteIndex = match_index 
 590         if field 
== self
._ctrl
_constraints
: 
 591             self
.SetSelection(match_index
) 
 592 ##            dbg('issuing combo selection event') 
 593             self
.GetEventHandler().ProcessEvent( 
 594                 MaskedComboBoxSelectEvent( self
.GetId(), match_index
, self 
) ) 
 596 ##        dbg('field._autoCompleteIndex:', match_index) 
 597 ##        dbg('self.GetSelection():', self.GetSelection()) 
 598         end 
= self
._goEnd
(getPosOnly
=True) 
 599 ##        dbg('scheduling set of end position to:', end) 
 600         # work around bug in wx 2.5 
 601         wx
.CallAfter(self
.SetInsertionPoint
, 0) 
 602         wx
.CallAfter(self
.SetInsertionPoint
, end
) 
 606     def _OnReturn(self
, event
): 
 608         For wx.ComboBox, it seems that if you hit return when the dropdown is 
 609         dropped, the event that dismisses the dropdown will also blank the 
 610         control, because of the implementation of wxComboBox.  So this function 
 611         examines the selection and if it is -1, and the value according to 
 612         (the base control!) is a value in the list, then it schedules a 
 613         programmatic wxComboBox.SetSelection() call to pick the appropriate 
 614         item in the list. (and then does the usual OnReturn bit.) 
 616 ##        dbg('MaskedComboBox::OnReturn', indent=1) 
 617 ##        dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection()) 
 618         if self
.GetSelection() == -1 and self
.GetValue().lower().strip() in self
._ctrl
_constraints
._compareChoices
: 
 619             wx
.CallAfter(self
.SetSelection
, self
._ctrl
_constraints
._autoCompleteIndex
) 
 621         event
.m_keyCode 
= wx
.WXK_TAB
 
 626 class ComboBox( BaseMaskedComboBox
, MaskedEditAccessorsMixin 
): 
 628     The "user-visible" masked combobox control, this class is 
 629     identical to the BaseMaskedComboBox class it's derived from. 
 630     (This extra level of inheritance allows us to add the generic 
 631     set of masked edit parameters only to this class while allowing 
 632     other classes to derive from the "base" masked combobox control, 
 633     and provide a smaller set of valid accessor functions.) 
 634     See BaseMaskedComboBox for available methods. 
 639 class PreMaskedComboBox( BaseMaskedComboBox
, MaskedEditAccessorsMixin 
): 
 641     This class exists to support the use of XRC subclassing. 
 643     # This should really be wx.EVT_WINDOW_CREATE but it is not 
 644     # currently delivered for native controls on all platforms, so 
 645     # we'll use EVT_SIZE instead.  It should happen shortly after the 
 646     # control is created as the control is set to its "best" size. 
 647     _firstEventType 
= wx
.EVT_SIZE
 
 650         pre 
= wx
.PreComboBox() 
 652         self
.Bind(self
._firstEventType
, self
.OnCreate
) 
 655     def OnCreate(self
, evt
): 
 656         self
.Unbind(self
._firstEventType
) 
 661 ## ==================== 
 663 ##  1. Made definition of "hack" GetMark conditional on base class not 
 664 ##     implementing it properly, to allow for migration in wx code base 
 665 ##     while taking advantage of improvements therein for some platforms. 
 668 ##  1. Converted docstrings to reST format, added doc for ePyDoc. 
 669 ##  2. Renamed helper functions, vars etc. not intended to be visible in public 
 670 ##     interface to code. 
 673 ##  1. Added .SetFont() method that properly resizes control 
 674 ##  2. Modified control to support construction via XRC mechanism. 
 675 ##  3. Added AppendItems() to conform with latest combobox.