1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/combobox.cpp 
   3 // Purpose:     wxComboBox class 
   4 // Author:      Stefan Csomor, Dan "Bud" Keith (composite combobox) 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  16 #include "wx/combobox.h" 
  19     #include "wx/button.h" 
  23 #include "wx/containr.h" 
  24 #include "wx/mac/uma.h" 
  26 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
, wxControl
) 
  28 WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox
) 
  30 BEGIN_EVENT_TABLE(wxComboBox
, wxControl
) 
  31     WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox
) 
  35 static int nextPopUpMenuId 
= 1000 ; 
  37 MenuHandle 
NewUniqueMenu() 
  39     MenuHandle handle 
= NewMenu( nextPopUpMenuId 
, "\pMenu" ) ; 
  46 // ---------------------------------------------------------------------------- 
  48 // ---------------------------------------------------------------------------- 
  50 // the margin between the text control and the choice 
  51 #if TARGET_API_MAC_OSX 
  52 // margin should be bigger on OS X due to blue highlight 
  53 // around text control. 
  54 static const wxCoord MARGIN 
= 4; 
  55 // this is the border a focus rect on OSX is needing 
  56 static const int    TEXTFOCUSBORDER 
= 3 ; 
  58 static const wxCoord MARGIN 
= 2; 
  59 static const int    TEXTFOCUSBORDER 
= 0 ; 
  61 static const int    POPUPHEIGHT 
= 23; 
  64 // ---------------------------------------------------------------------------- 
  65 // wxComboBoxText: text control forwards events to combobox 
  66 // ---------------------------------------------------------------------------- 
  68 class wxComboBoxText 
: public wxTextCtrl
 
  71     wxComboBoxText( wxComboBox 
* cb 
) 
  72         : wxTextCtrl( cb 
, 1 ) 
  75         SetTriggerOnSetValue( false ); 
  79     void OnChar( wxKeyEvent
& event 
) 
  81         // Allows processing the tab key to go to the next control 
  82         if (event
.GetKeyCode() == WXK_TAB
) 
  84             wxNavigationKeyEvent NavEvent
; 
  85             NavEvent
.SetEventObject(this); 
  86             NavEvent
.SetDirection(true); 
  87             NavEvent
.SetWindowChange(false); 
  89             // Get the parent of the combo and have it process the navigation? 
  90             if (m_cb
->GetParent()->GetEventHandler()->ProcessEvent(NavEvent
)) 
  94         // send the event to the combobox class in case the user has bound EVT_CHAR 
  95         wxKeyEvent 
kevt(event
); 
  96         kevt
.SetEventObject(m_cb
); 
  97         if (m_cb
->GetEventHandler()->ProcessEvent(kevt
)) 
  98             // If the event was handled and not skipped then we're done 
 101         if ( event
.GetKeyCode() == WXK_RETURN 
) 
 103             wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_cb
->GetId()); 
 104             event
.SetString( GetValue() ); 
 105             event
.SetInt( m_cb
->GetSelection() ); 
 106             event
.SetEventObject( m_cb 
); 
 108             // This will invoke the dialog default action, 
 109             // such as the clicking the default button. 
 110             if (!m_cb
->GetEventHandler()->ProcessEvent( event 
)) 
 112                 wxWindow 
*parent 
= GetParent(); 
 113                 while ( parent 
&& !parent
->IsTopLevel() && parent
->GetDefaultItem() == NULL 
) 
 114                     parent 
= parent
->GetParent() ; 
 116                 if ( parent 
&& parent
->GetDefaultItem() ) 
 118                     wxButton 
*def 
= wxDynamicCast(parent
->GetDefaultItem(), wxButton
); 
 119                     if ( def 
&& def
->IsEnabled() ) 
 121                         wxCommandEvent 
event( wxEVT_COMMAND_BUTTON_CLICKED
, def
->GetId() ); 
 122                         event
.SetEventObject(def
); 
 134     void OnKeyUp( wxKeyEvent
& event 
) 
 136         event
.SetEventObject(m_cb
); 
 137         event
.SetId(m_cb
->GetId()); 
 138         if (! m_cb
->GetEventHandler()->ProcessEvent(event
)) 
 142     void OnKeyDown( wxKeyEvent
& event 
) 
 144         event
.SetEventObject(m_cb
); 
 145         event
.SetId(m_cb
->GetId()); 
 146         if (! m_cb
->GetEventHandler()->ProcessEvent(event
)) 
 150     void OnText( wxCommandEvent
& event 
) 
 152         event
.SetEventObject(m_cb
); 
 153         event
.SetId(m_cb
->GetId()); 
 154         if (! m_cb
->GetEventHandler()->ProcessEvent(event
)) 
 161     DECLARE_EVENT_TABLE() 
 164 BEGIN_EVENT_TABLE(wxComboBoxText
, wxTextCtrl
) 
 165     EVT_KEY_DOWN(wxComboBoxText::OnKeyDown
) 
 166     EVT_CHAR(wxComboBoxText::OnChar
) 
 167     EVT_KEY_UP(wxComboBoxText::OnKeyUp
) 
 168     EVT_TEXT(-1, wxComboBoxText::OnText
) 
 171 class wxComboBoxChoice 
: public wxChoice
 
 174     wxComboBoxChoice( wxComboBox 
*cb
, int style 
) 
 175         : wxChoice( cb 
, 1 , wxDefaultPosition 
, wxDefaultSize 
, 0 , NULL 
, style 
& (wxCB_SORT
) ) 
 180     int GetPopupWidth() const 
 182         switch ( GetWindowVariant() ) 
 184             case wxWINDOW_VARIANT_NORMAL 
: 
 185             case wxWINDOW_VARIANT_LARGE 
: 
 194     void OnChoice( wxCommandEvent
& e 
) 
 196         wxString    s 
= e
.GetString(); 
 198         m_cb
->DelegateChoice( s 
); 
 199         wxCommandEvent 
event2(wxEVT_COMMAND_COMBOBOX_SELECTED
, m_cb
->GetId() ); 
 200         event2
.SetInt(m_cb
->GetSelection()); 
 201         event2
.SetEventObject(m_cb
); 
 202         event2
.SetString(m_cb
->GetStringSelection()); 
 203         m_cb
->ProcessCommand(event2
); 
 205         // For consistency with MSW and GTK, also send a text updated event 
 206         // After all, the text is updated when a selection is made 
 207         wxCommandEvent 
TextEvent( wxEVT_COMMAND_TEXT_UPDATED
, m_cb
->GetId() ); 
 208         TextEvent
.SetString( m_cb
->GetStringSelection() ); 
 209         TextEvent
.SetEventObject( m_cb 
); 
 210         m_cb
->ProcessCommand( TextEvent 
); 
 213     virtual wxSize 
DoGetBestSize() const 
 215         wxSize sz 
= wxChoice::DoGetBestSize() ; 
 216         if (! m_cb
->HasFlag(wxCB_READONLY
) ) 
 217             sz
.x 
= GetPopupWidth() ; 
 225     friend class wxComboBox
; 
 227     DECLARE_EVENT_TABLE() 
 230 BEGIN_EVENT_TABLE(wxComboBoxChoice
, wxChoice
) 
 231     EVT_CHOICE(-1, wxComboBoxChoice::OnChoice
) 
 234 wxComboBox::~wxComboBox() 
 236     // delete client objects 
 239     // delete the controls now, don't leave them alive even though they would 
 240     // still be eventually deleted by our parent - but it will be too late, the 
 241     // user code expects them to be gone now 
 248     if (m_choice 
!= NULL
) 
 255 // ---------------------------------------------------------------------------- 
 257 // ---------------------------------------------------------------------------- 
 259 wxSize 
wxComboBox::DoGetBestSize() const 
 261     if (!m_choice 
&& !m_text
) 
 264     wxSize size 
= m_choice
->GetBestSize(); 
 266     if ( m_text 
!= NULL 
) 
 268         wxSize  sizeText 
= m_text
->GetBestSize(); 
 269         if (sizeText
.y 
> size
.y
) 
 272         size
.x 
= m_choice
->GetPopupWidth() + sizeText
.x 
+ MARGIN
; 
 273         size
.x 
+= TEXTFOCUSBORDER 
; 
 274         size
.y 
+= 2 * TEXTFOCUSBORDER 
; 
 278         // clipping is too tight 
 285 void wxComboBox::DoMoveWindow(int x
, int y
, int width
, int height
) 
 287     wxControl::DoMoveWindow( x
, y
, width 
, height 
); 
 289     if ( m_text 
== NULL 
) 
 291         // we might not be fully constructed yet, therefore watch out... 
 293             m_choice
->SetSize(0, 0 , width
, -1); 
 297         wxCoord wText 
= width 
- m_choice
->GetPopupWidth() - MARGIN
; 
 298         m_text
->SetSize(TEXTFOCUSBORDER
, TEXTFOCUSBORDER
, wText
, -1); 
 300         // put it at an inset of 1 to have outer area shadows drawn as well 
 301         m_choice
->SetSize(TEXTFOCUSBORDER 
+ wText 
+ MARGIN 
- 1 , TEXTFOCUSBORDER
, m_choice
->GetPopupWidth() , -1); 
 305 // ---------------------------------------------------------------------------- 
 306 // operations forwarded to the subcontrols 
 307 // ---------------------------------------------------------------------------- 
 309 bool wxComboBox::Enable(bool enable
) 
 311     if ( !wxControl::Enable(enable
) ) 
 315         m_text
->Enable(enable
); 
 320 bool wxComboBox::Show(bool show
) 
 322     if ( !wxControl::Show(show
) ) 
 328 void wxComboBox::DelegateTextChanged( const wxString
& value 
) 
 330     SetStringSelection( value 
); 
 333 void wxComboBox::DelegateChoice( const wxString
& value 
) 
 335     SetStringSelection( value 
); 
 338 void wxComboBox::Init() 
 340     m_container
.SetContainerWindow(this); 
 343 bool wxComboBox::Create(wxWindow 
*parent
, 
 345     const wxString
& value
, 
 348     const wxArrayString
& choices
, 
 350     const wxValidator
& validator
, 
 351     const wxString
& name
) 
 353     wxCArrayString 
chs( choices 
); 
 355     return Create( parent
, id
, value
, pos
, size
, chs
.GetCount(), 
 356                    chs
.GetStrings(), style
, validator
, name 
); 
 359 bool wxComboBox::Create(wxWindow 
*parent
, 
 361     const wxString
& value
, 
 365     const wxString choices
[], 
 367     const wxValidator
& validator
, 
 368     const wxString
& name
) 
 370     if ( !wxControl::Create(parent
, id
, wxDefaultPosition
, wxDefaultSize
, style 
, 
 376     m_choice 
= new wxComboBoxChoice(this, style 
); 
 378     if ( style 
& wxCB_READONLY 
) 
 384         m_text 
= new wxComboBoxText(this); 
 387             csize
.y 
= m_text
->GetSize().y 
; 
 388             csize
.y 
+= 2 * TEXTFOCUSBORDER 
; 
 392     DoSetSize(pos
.x
, pos
.y
, csize
.x
, csize
.y
); 
 394     for ( int i 
= 0 ; i 
< n 
; i
++ ) 
 396         m_choice
->DoAppend( choices
[ i 
] ); 
 399     // Needed because it is a wxControlWithItems 
 401     SetStringSelection(value
); 
 406 wxString 
wxComboBox::GetValue() const 
 410     if ( m_text 
== NULL 
) 
 411         result 
= m_choice
->GetString( m_choice
->GetSelection() ); 
 413         result 
= m_text
->GetValue(); 
 418 unsigned int wxComboBox::GetCount() const 
 420     return m_choice
->GetCount() ; 
 423 void wxComboBox::SetValue(const wxString
& value
) 
 425     if ( HasFlag(wxCB_READONLY
) ) 
 426         SetStringSelection( value 
) ; 
 428         m_text
->SetValue( value 
); 
 431 // Clipboard operations 
 433 void wxComboBox::Copy() 
 435     if ( m_text 
!= NULL 
) 
 439 void wxComboBox::Cut() 
 441     if ( m_text 
!= NULL 
) 
 445 void wxComboBox::Paste() 
 447     if ( m_text 
!= NULL 
) 
 451 void wxComboBox::SetEditable(bool editable
) 
 453     if ( ( m_text 
== NULL 
) && editable 
) 
 455         m_text 
= new wxComboBoxText( this ); 
 457     else if ( ( m_text 
!= NULL 
) && !editable 
) 
 463     int currentX
, currentY
; 
 464     GetPosition( ¤tX
, ¤tY 
); 
 466     int currentW
, currentH
; 
 467     GetSize( ¤tW
, ¤tH 
); 
 469     DoMoveWindow( currentX
, currentY
, currentW
, currentH 
); 
 472 void wxComboBox::SetInsertionPoint(long pos
) 
 477 void wxComboBox::SetInsertionPointEnd() 
 482 long wxComboBox::GetInsertionPoint() const 
 488 wxTextPos 
wxComboBox::GetLastPosition() const 
 494 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 499 void wxComboBox::Remove(long from
, long to
) 
 504 void wxComboBox::SetSelection(long from
, long to
) 
 509 int wxComboBox::DoAppend(const wxString
& item
) 
 511     return m_choice
->DoAppend( item 
) ; 
 514 int wxComboBox::DoInsert(const wxString
& item
, unsigned int pos
) 
 516     return m_choice
->DoInsert( item 
, pos 
) ; 
 519 void wxComboBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 521     return m_choice
->DoSetItemClientData( n 
, clientData 
) ; 
 524 void* wxComboBox::DoGetItemClientData(unsigned int n
) const 
 526     return m_choice
->DoGetItemClientData( n 
) ; 
 529 void wxComboBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 531     return m_choice
->DoSetItemClientObject(n
, clientData
); 
 534 wxClientData
* wxComboBox::DoGetItemClientObject(unsigned int n
) const 
 536     return m_choice
->DoGetItemClientObject( n 
) ; 
 539 void wxComboBox::FreeData() 
 541     if ( HasClientObjectData() ) 
 543         unsigned int count 
= GetCount(); 
 544         for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 546             SetClientObject( n
, NULL 
); 
 551 void wxComboBox::Delete(unsigned int n
) 
 553     // force client object deletion 
 554     if( HasClientObjectData() ) 
 555         SetClientObject( n
, NULL 
); 
 556     m_choice
->Delete( n 
); 
 559 void wxComboBox::Clear() 
 565 int wxComboBox::GetSelection() const 
 567     return m_choice
->GetSelection(); 
 570 void wxComboBox::SetSelection(int n
) 
 572     m_choice
->SetSelection( n 
); 
 574     if ( m_text 
!= NULL 
) 
 575         m_text
->SetValue(GetString(n
)); 
 578 int wxComboBox::FindString(const wxString
& s
, bool bCase
) const 
 580     return m_choice
->FindString( s
, bCase 
); 
 583 wxString 
wxComboBox::GetString(unsigned int n
) const 
 585     return m_choice
->GetString( n 
); 
 588 wxString 
wxComboBox::GetStringSelection() const 
 590     int sel 
= GetSelection(); 
 591     if (sel 
!= wxNOT_FOUND
) 
 592         return wxString(this->GetString((unsigned int)sel
)); 
 594         return wxEmptyString
; 
 597 void wxComboBox::SetString(unsigned int n
, const wxString
& s
) 
 599     m_choice
->SetString( n 
, s 
); 
 602 bool wxComboBox::IsEditable() const 
 604     return m_text 
!= NULL 
&& !HasFlag(wxCB_READONLY
); 
 607 void wxComboBox::Undo() 
 613 void wxComboBox::Redo() 
 619 void wxComboBox::SelectAll() 
 625 bool wxComboBox::CanCopy() const 
 628         return m_text
->CanCopy(); 
 633 bool wxComboBox::CanCut() const 
 636         return m_text
->CanCut(); 
 641 bool wxComboBox::CanPaste() const 
 644         return m_text
->CanPaste(); 
 649 bool wxComboBox::CanUndo() const 
 652         return m_text
->CanUndo(); 
 657 bool wxComboBox::CanRedo() const 
 660         return m_text
->CanRedo(); 
 665 wxInt32 
wxComboBox::MacControlHit( WXEVENTHANDLERREF 
WXUNUSED(handler
) , WXEVENTREF 
WXUNUSED(event
) ) 
 668     For consistency with other platforms, clicking in the text area does not constitute a selection 
 669     wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId ); 
 670     event.SetInt(GetSelection()); 
 671     event.SetEventObject(this); 
 672     event.SetString(GetStringSelection()); 
 673     ProcessCommand(event);