1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxComboBox class 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  13 #pragma implementation "combobox.h" 
  16 #include "wx/wxprec.h" 
  20 #include "wx/combobox.h" 
  21 #include "wx/button.h" 
  23 #include "wx/mac/uma.h" 
  25 #if !USE_SHARED_LIBRARY 
  26 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
, wxControl
) 
  29 // composite combobox implementation by Dan "Bud" Keith bud@otsys.com 
  32 static int nextPopUpMenuId 
= 1000 ; 
  33 MenuHandle 
NewUniqueMenu() 
  35   MenuHandle handle 
= NewMenu( nextPopUpMenuId 
, "\pMenu" ) ; 
  41 // ---------------------------------------------------------------------------- 
  43 // ---------------------------------------------------------------------------- 
  45 // the margin between the text control and the choice 
  46 #if TARGET_API_MAC_OSX 
  47 // margin should be bigger on OS X due to blue highlight 
  48 // around text control. 
  49 static const wxCoord MARGIN 
= 4; 
  50 // this is the border a focus rect on OSX is needing 
  51 static const int    TEXTFOCUSBORDER 
= 3 ; 
  53 static const wxCoord MARGIN 
= 2; 
  54 static const int    TEXTFOCUSBORDER 
= 0 ; 
  56 static const int    POPUPHEIGHT 
= 23; 
  59 // ---------------------------------------------------------------------------- 
  60 // wxComboBoxText: text control forwards events to combobox 
  61 // ---------------------------------------------------------------------------- 
  63 class wxComboBoxText 
: public wxTextCtrl
 
  66     wxComboBoxText( wxComboBox 
* cb 
) 
  67         : wxTextCtrl( cb 
, 1 ) 
  73     void OnChar( wxKeyEvent
& event 
) 
  75         // Allows processing the tab key to go to the next control 
  76         if (event
.GetKeyCode() == WXK_TAB
) 
  78             wxNavigationKeyEvent NavEvent
; 
  79             NavEvent
.SetEventObject(this); 
  80             NavEvent
.SetDirection(true); 
  81             NavEvent
.SetWindowChange(false); 
  83             // Get the parent of the combo and have it process the navigation? 
  84             if (m_cb
->GetParent()->GetEventHandler()->ProcessEvent(NavEvent
)) 
  87         if ( event
.GetKeyCode() == WXK_RETURN 
) 
  89             wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_cb
->GetId()); 
  90             event
.SetString( GetValue() ); 
  91             event
.SetInt( m_cb
->GetSelection() ); 
  92             event
.SetEventObject( m_cb 
); 
  94             // This will invoke the dialog default action, such 
  95             // as the clicking the default button. 
  97             if (!m_cb
->GetEventHandler()->ProcessEvent( event 
)) 
  99                 wxWindow 
*parent 
= GetParent(); 
 100                 while( parent 
&& !parent
->IsTopLevel() && parent
->GetDefaultItem() == NULL 
) { 
 101                     parent 
= parent
->GetParent() ; 
 103                 if ( parent 
&& parent
->GetDefaultItem() ) 
 105                     wxButton 
*def 
= wxDynamicCast(parent
->GetDefaultItem(), 
 107                     if ( def 
&& def
->IsEnabled() ) 
 109                         wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, def
->GetId() ); 
 110                         event
.SetEventObject(def
); 
 122     // Use the KeyUp as a naive approximation for TEXT_UPDATED, even though it is somewhat delayed 
 123     // but this is less complicated than dealing with idle-ness, and is much better than nothing 
 124     void OnKeyUp( wxKeyEvent
& event 
) 
 126         if ( event
.GetKeyCode() != WXK_RETURN 
&& event
.GetKeyCode() != WXK_TAB 
) 
 128             wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, m_cb
->GetId()); 
 129             event
.SetString( GetValue() ); 
 130             event
.SetEventObject( m_cb 
); 
 131             m_cb
->GetEventHandler()->ProcessEvent(event
); 
 137     DECLARE_EVENT_TABLE() 
 140 BEGIN_EVENT_TABLE(wxComboBoxText
, wxTextCtrl
) 
 141     EVT_CHAR( wxComboBoxText::OnChar
) 
 142     EVT_KEY_UP( wxComboBoxText::OnKeyUp
) 
 145 class wxComboBoxChoice 
: public wxChoice
 
 148     wxComboBoxChoice(wxComboBox 
*cb
, int style
) 
 153     int GetPopupWidth() const 
 155         switch ( GetWindowVariant() ) 
 157             case wxWINDOW_VARIANT_NORMAL 
: 
 158             case wxWINDOW_VARIANT_LARGE 
: 
 166     void OnChoice( wxCommandEvent
& e 
) 
 168         wxString    s 
= e
.GetString(); 
 170         m_cb
->DelegateChoice( s 
); 
 171         wxCommandEvent 
event2(wxEVT_COMMAND_COMBOBOX_SELECTED
, m_cb
->GetId() ); 
 172         event2
.SetInt(m_cb
->GetSelection()); 
 173         event2
.SetEventObject(m_cb
); 
 174         event2
.SetString(m_cb
->GetStringSelection()); 
 175         m_cb
->ProcessCommand(event2
); 
 177         // For consistency with MSW and GTK, also send a text updated event 
 178         // After all, the text is updated when a selection is made 
 179         wxCommandEvent 
TextEvent( wxEVT_COMMAND_TEXT_UPDATED
, m_cb
->GetId() ); 
 180         TextEvent
.SetString( m_cb
->GetStringSelection() ); 
 181         TextEvent
.SetEventObject( m_cb 
); 
 182         m_cb
->ProcessCommand( TextEvent 
); 
 184     virtual wxSize 
DoGetBestSize() const 
 186         wxSize sz 
= wxChoice::DoGetBestSize() ; 
 187         if (! m_cb
->HasFlag(wxCB_READONLY
) ) 
 188             sz
.x 
= GetPopupWidth() ; 
 195     DECLARE_EVENT_TABLE() 
 198 BEGIN_EVENT_TABLE(wxComboBoxChoice
, wxChoice
) 
 199     EVT_CHOICE(-1, wxComboBoxChoice::OnChoice
) 
 202 wxComboBox::~wxComboBox() 
 204     // delete client objects 
 207     // delete the controls now, don't leave them alive even though they would 
 208     // still be eventually deleted by our parent - but it will be too late, the 
 209     // user code expects them to be gone now 
 210     if (m_text 
!= NULL
) { 
 214     if (m_choice 
!= NULL
) { 
 221 // ---------------------------------------------------------------------------- 
 223 // ---------------------------------------------------------------------------- 
 225 wxSize 
wxComboBox::DoGetBestSize() const 
 227     if (!m_choice 
&& !m_text
) 
 229     wxSize size 
= m_choice
->GetBestSize(); 
 231     if ( m_text 
!= NULL 
) 
 233         wxSize  sizeText 
= m_text
->GetBestSize(); 
 234         if (sizeText
.y 
> size
.y
) 
 236         size
.x 
= m_choice
->GetPopupWidth() + sizeText
.x 
+ MARGIN
; 
 237         size
.x 
+= TEXTFOCUSBORDER 
; 
 238         size
.y 
+= 2 * TEXTFOCUSBORDER 
; 
 242         // clipping is too tight 
 248 void wxComboBox::DoMoveWindow(int x
, int y
, int width
, int height
) 
 250     wxControl::DoMoveWindow(x
, y
, width 
, height 
); 
 252     if ( m_text 
== NULL 
) 
 254         // we might not be fully constructed yet, therefore watch out... 
 256             m_choice
->SetSize(0, 0 , width
, -1); 
 260         wxCoord wText 
= width 
- m_choice
->GetPopupWidth() - MARGIN
; 
 261         m_text
->SetSize(TEXTFOCUSBORDER
, TEXTFOCUSBORDER
, wText
, -1 ); 
 262         // put it at an inset of 1 to have outer area shadows drawn as well 
 263         m_choice
->SetSize(TEXTFOCUSBORDER 
+ wText 
+ MARGIN 
- 1 , TEXTFOCUSBORDER
, m_choice
->GetPopupWidth() , -1); 
 269 // ---------------------------------------------------------------------------- 
 270 // operations forwarded to the subcontrols 
 271 // ---------------------------------------------------------------------------- 
 273 bool wxComboBox::Enable(bool enable
) 
 275     if ( !wxControl::Enable(enable
) ) 
 281 bool wxComboBox::Show(bool show
) 
 283     if ( !wxControl::Show(show
) ) 
 289 void wxComboBox::SetFocus() 
 291     if ( m_text 
!= NULL
) { 
 297 void wxComboBox::DelegateTextChanged( const wxString
& value 
) 
 299     SetStringSelection( value 
); 
 303 void wxComboBox::DelegateChoice( const wxString
& value 
) 
 305     SetStringSelection( value 
); 
 309 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 310            const wxString
& value
, 
 313            const wxArrayString
& choices
, 
 315            const wxValidator
& validator
, 
 316            const wxString
& name
) 
 318     wxCArrayString 
chs( choices 
); 
 320     return Create( parent
, id
, value
, pos
, size
, chs
.GetCount(), 
 321                    chs
.GetStrings(), style
, validator
, name 
); 
 325 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 326            const wxString
& value
, 
 329            int n
, const wxString choices
[], 
 331            const wxValidator
& validator
, 
 332            const wxString
& name
) 
 334     if ( !wxControl::Create(parent
, id
, wxDefaultPosition
, wxDefaultSize
, style 
, 
 335                             wxDefaultValidator
, name
) ) 
 340     m_choice 
= new wxComboBoxChoice(this, style 
); 
 342     if ( style 
& wxCB_READONLY 
) 
 348         m_text 
= new wxComboBoxText(this); 
 351             csize
.y 
= m_text
->GetSize().y 
; 
 352             csize
.y 
+= 2 * TEXTFOCUSBORDER 
; 
 356     DoSetSize(pos
.x
, pos
.y
, csize
.x
, csize
.y
); 
 358     for ( int i 
= 0 ; i 
< n 
; i
++ ) 
 360         m_choice
->DoAppend( choices
[ i 
] ); 
 363     SetBestSize(size
);   // Needed because it is a wxControlWithItems 
 364     SetStringSelection(value
); 
 369 wxString 
wxComboBox::GetValue() const 
 373     if ( m_text 
== NULL 
) 
 375         result 
= m_choice
->GetString( m_choice
->GetSelection() ); 
 379         result 
= m_text
->GetValue(); 
 385 int wxComboBox::GetCount() const 
 387     return m_choice
->GetCount() ; 
 390 void wxComboBox::SetValue(const wxString
& value
) 
 392     if ( HasFlag(wxCB_READONLY
) ) 
 393         SetStringSelection( value 
) ; 
 395         m_text
->SetValue( value 
); 
 398 // Clipboard operations 
 399 void wxComboBox::Copy() 
 401     if ( m_text 
!= NULL 
) 
 407 void wxComboBox::Cut() 
 409     if ( m_text 
!= NULL 
) 
 415 void wxComboBox::Paste() 
 417     if ( m_text 
!= NULL 
) 
 423 void wxComboBox::SetEditable(bool editable
) 
 425     if ( ( m_text 
== NULL 
) && editable 
) 
 427         m_text 
= new wxComboBoxText( this ); 
 429     else if ( ( m_text 
!= NULL 
) && !editable 
) 
 435     int currentX
, currentY
; 
 436     GetPosition( ¤tX
, ¤tY 
); 
 438     int currentW
, currentH
; 
 439     GetSize( ¤tW
, ¤tH 
); 
 441     DoMoveWindow( currentX
, currentY
, currentW
, currentH 
); 
 444 void wxComboBox::SetInsertionPoint(long pos
) 
 449 void wxComboBox::SetInsertionPointEnd() 
 454 long wxComboBox::GetInsertionPoint() const 
 460 wxTextPos 
wxComboBox::GetLastPosition() const 
 466 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 471 void wxComboBox::Remove(long from
, long to
) 
 476 void wxComboBox::SetSelection(long from
, long to
) 
 481 int wxComboBox::DoAppend(const wxString
& item
) 
 483     return m_choice
->DoAppend( item 
) ; 
 486 int wxComboBox::DoInsert(const wxString
& item
, int pos
) 
 488     return m_choice
->DoInsert( item 
, pos 
) ; 
 491 void wxComboBox::DoSetItemClientData(int n
, void* clientData
) 
 493     return m_choice
->DoSetItemClientData( n 
, clientData 
) ; 
 496 void* wxComboBox::DoGetItemClientData(int n
) const 
 498     return m_choice
->DoGetItemClientData( n 
) ; 
 501 void wxComboBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 503     return m_choice
->DoSetItemClientObject( n 
, clientData 
) ; 
 506 wxClientData
* wxComboBox::DoGetItemClientObject(int n
) const 
 508     return m_choice
->DoGetItemClientObject( n 
) ; 
 511 void wxComboBox::FreeData() 
 513     if ( HasClientObjectData() ) 
 515         size_t count 
= GetCount(); 
 516         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 518             SetClientObject( n
, NULL 
); 
 523 void wxComboBox::Delete(int n
) 
 525     // force client object deletion 
 526     if( HasClientObjectData() ) 
 527         SetClientObject( n
, NULL 
); 
 528     m_choice
->Delete( n 
); 
 531 void wxComboBox::Clear() 
 537 int wxComboBox::GetSelection() const 
 539     return m_choice
->GetSelection(); 
 542 void wxComboBox::SetSelection(int n
) 
 544     m_choice
->SetSelection( n 
); 
 546     if ( m_text 
!= NULL 
) 
 548         m_text
->SetValue( GetString( n 
) ); 
 552 int wxComboBox::FindString(const wxString
& s
) const 
 554     return m_choice
->FindString( s 
); 
 557 wxString 
wxComboBox::GetString(int n
) const 
 559     return m_choice
->GetString( n 
); 
 562 wxString 
wxComboBox::GetStringSelection() const 
 564     int sel 
= GetSelection (); 
 566         return wxString(this->GetString (sel
)); 
 568         return wxEmptyString
; 
 571 void wxComboBox::SetString(int n
, const wxString
& s
) 
 573     m_choice
->SetString( n 
, s 
) ; 
 576 bool wxComboBox::IsEditable() const 
 578     return m_text 
!= NULL 
&& !HasFlag(wxCB_READONLY
); 
 581 void wxComboBox::Undo() 
 587 void wxComboBox::Redo() 
 593 void wxComboBox::SelectAll() 
 599 bool wxComboBox::CanCopy() const 
 602         return m_text
->CanCopy(); 
 607 bool wxComboBox::CanCut() const 
 610         return m_text
->CanCut(); 
 615 bool wxComboBox::CanPaste() const 
 618         return m_text
->CanPaste(); 
 623 bool wxComboBox::CanUndo() const 
 626         return m_text
->CanUndo(); 
 631 bool wxComboBox::CanRedo() const 
 634         return m_text
->CanRedo(); 
 639 wxInt32 
wxComboBox::MacControlHit(WXEVENTHANDLERREF 
WXUNUSED(handler
) , WXEVENTREF 
WXUNUSED(event
) ) 
 641     /* For consistency with other platforms, clicking in the text area does not constitute a selection 
 642     wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId ); 
 643     event.SetInt(GetSelection()); 
 644     event.SetEventObject(this); 
 645     event.SetString(GetStringSelection()); 
 646     ProcessCommand(event);*/