1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/combobxc.cpp 
   3 // Purpose:     wxComboBox class using HIView ComboBox 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  14 #include "wx/combobox.h" 
  17     #include "wx/button.h" 
  21 #include "wx/mac/uma.h" 
  22 #if TARGET_API_MAC_OSX 
  24     #include <HIToolbox/HIView.h> 
  28 IMPLEMENT_DYNAMIC_CLASS(wxComboBox
, wxControl
) 
  30 #if TARGET_API_MAC_OSX 
  31 #define USE_HICOMBOBOX 1 //use hi combobox define 
  33 #define USE_HICOMBOBOX 0 
  36 static int nextPopUpMenuId 
= 1000; 
  37 MenuHandle 
NewUniqueMenu() 
  39   MenuHandle handle 
= NewMenu( nextPopUpMenuId 
, "\pMenu" ); 
  45 static const EventTypeSpec eventList
[] = 
  47     { kEventClassTextField 
, kEventTextAccepted 
} , 
  50 static pascal OSStatus 
wxMacComboBoxEventHandler( EventHandlerCallRef handler 
, EventRef event 
, void *data 
) 
  52     OSStatus result 
= eventNotHandledErr
; 
  53     wxComboBox
* cb 
= (wxComboBox
*) data
; 
  55     wxMacCarbonEvent 
cEvent( event 
); 
  57     switch( cEvent
.GetClass() ) 
  59         case kEventClassTextField 
: 
  60             switch( cEvent
.GetKind() ) 
  62                 case kEventTextAccepted 
: 
  64                         wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, cb
->GetId() ); 
  65                         event
.SetInt( cb
->GetSelection() ); 
  66                         event
.SetString( cb
->GetStringSelection() ); 
  67                         event
.SetEventObject( cb 
); 
  68                         cb
->GetEventHandler()->ProcessEvent( event 
); 
  83 DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacComboBoxEventHandler 
) 
  87 // ---------------------------------------------------------------------------- 
  89 // ---------------------------------------------------------------------------- 
  91 // the margin between the text control and the choice 
  92 static const wxCoord MARGIN 
= 2; 
  93 #if TARGET_API_MAC_OSX 
  94 static const int    POPUPWIDTH 
= 24; 
  96 static const int    POPUPWIDTH 
= 18; 
  98 static const int    POPUPHEIGHT 
= 23; 
 100 // ---------------------------------------------------------------------------- 
 101 // wxComboBoxText: text control forwards events to combobox 
 102 // ---------------------------------------------------------------------------- 
 104 class wxComboBoxText 
: public wxTextCtrl
 
 107     wxComboBoxText( wxComboBox 
* cb 
) 
 108         : wxTextCtrl( cb 
, 1 ) 
 114     void OnChar( wxKeyEvent
& event 
) 
 116         if ( event
.GetKeyCode() == WXK_RETURN 
) 
 118             wxString value 
= GetValue(); 
 120             if ( m_cb
->GetCount() == 0 ) 
 122                 // make Enter generate "selected" event if there is only one item 
 123                 // in the combobox - without it, it's impossible to select it at 
 125                 wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, m_cb
->GetId() ); 
 127                 event
.SetString( value 
); 
 128                 event
.SetEventObject( m_cb 
); 
 129                 m_cb
->GetEventHandler()->ProcessEvent( event 
); 
 133                 // add the item to the list if it's not there yet 
 134                 if ( m_cb
->FindString(value
) == wxNOT_FOUND 
) 
 137                     m_cb
->SetStringSelection(value
); 
 139                     // and generate the selected event for it 
 140                     wxCommandEvent 
event( wxEVT_COMMAND_COMBOBOX_SELECTED
, m_cb
->GetId() ); 
 141                     event
.SetInt( m_cb
->GetCount() - 1 ); 
 142                     event
.SetString( value 
); 
 143                     event
.SetEventObject( m_cb 
); 
 144                     m_cb
->GetEventHandler()->ProcessEvent( event 
); 
 147                 // This will invoke the dialog default action, such 
 148                 // as the clicking the default button. 
 150                 wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 151                 if ( tlw 
&& tlw
->GetDefaultItem() ) 
 153                     wxButton 
*def 
= wxDynamicCast(tlw
->GetDefaultItem(), wxButton
); 
 154                     if ( def 
&& def
->IsEnabled() ) 
 156                         wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, def
->GetId() ); 
 157                         event
.SetEventObject(def
); 
 172     DECLARE_EVENT_TABLE() 
 175 BEGIN_EVENT_TABLE(wxComboBoxText
, wxTextCtrl
) 
 176     EVT_CHAR( wxComboBoxText::OnChar
) 
 179 class wxComboBoxChoice 
: public wxChoice
 
 182     wxComboBoxChoice(wxComboBox 
*cb
, int style
) 
 189     void OnChoice( wxCommandEvent
& e 
) 
 191         wxString    s 
= e
.GetString(); 
 193         m_cb
->DelegateChoice( s 
); 
 194         wxCommandEvent 
event2(wxEVT_COMMAND_COMBOBOX_SELECTED
, m_cb
->GetId() ); 
 195         event2
.SetInt(m_cb
->GetSelection()); 
 196         event2
.SetEventObject(m_cb
); 
 197         event2
.SetString(m_cb
->GetStringSelection()); 
 198         m_cb
->ProcessCommand(event2
); 
 200     virtual wxSize 
DoGetBestSize() const 
 202         wxSize sz 
= wxChoice::DoGetBestSize(); 
 210     DECLARE_EVENT_TABLE() 
 213 BEGIN_EVENT_TABLE(wxComboBoxChoice
, wxChoice
) 
 214     EVT_CHOICE(wxID_ANY
, wxComboBoxChoice::OnChoice
) 
 217 wxComboBox::~wxComboBox() 
 219     // delete client objects 
 222     // delete the controls now, don't leave them alive even though they would 
 223     // still be eventually deleted by our parent - but it will be too late, the 
 224     // user code expects them to be gone now 
 225     if (m_text 
!= NULL
) { 
 229     if (m_choice 
!= NULL
) { 
 236 // ---------------------------------------------------------------------------- 
 238 // ---------------------------------------------------------------------------- 
 240 wxSize 
wxComboBox::DoGetBestSize() const 
 243     return wxControl::DoGetBestSize(); 
 245     wxSize size 
= m_choice
->GetBestSize(); 
 247     if ( m_text 
!= NULL 
) 
 249         wxSize  sizeText 
= m_text
->GetBestSize(); 
 251         size
.x 
= POPUPWIDTH 
+ sizeText
.x 
+ MARGIN
; 
 258 void wxComboBox::DoMoveWindow(int x
, int y
, int width
, int height
) { 
 260     wxControl::DoMoveWindow(x
, y
, width
, height
); 
 262     height 
= POPUPHEIGHT
; 
 264     wxControl::DoMoveWindow(x
, y
, width
, height
); 
 266     if ( m_text 
== NULL 
) 
 268         // we might not be fully constructed yet, therefore watch out... 
 270             m_choice
->SetSize(0, 0 , width
, wxDefaultCoord
); 
 274         wxCoord wText 
= width 
- POPUPWIDTH 
- MARGIN
; 
 275         m_text
->SetSize(0, 0, wText
, height
); 
 276         m_choice
->SetSize(0 + wText 
+ MARGIN
, 0, POPUPWIDTH
, wxDefaultCoord
); 
 283 // ---------------------------------------------------------------------------- 
 284 // operations forwarded to the subcontrols 
 285 // ---------------------------------------------------------------------------- 
 287 bool wxComboBox::Enable(bool enable
) 
 289     if ( !wxControl::Enable(enable
) ) 
 295 bool wxComboBox::Show(bool show
) 
 297     if ( !wxControl::Show(show
) ) 
 303 void wxComboBox::SetFocus() 
 306     wxControl::SetFocus(); 
 308     if ( m_text 
!= NULL
) { 
 315 void wxComboBox::DelegateTextChanged( const wxString
& value 
) 
 317     SetStringSelection( value 
); 
 321 void wxComboBox::DelegateChoice( const wxString
& value 
) 
 323     SetStringSelection( value 
); 
 327 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 328            const wxString
& value
, 
 331            const wxArrayString
& choices
, 
 333            const wxValidator
& validator
, 
 334            const wxString
& name
) 
 336     wxCArrayString 
chs( choices 
); 
 338     return Create( parent
, id
, value
, pos
, size
, chs
.GetCount(), 
 339                    chs
.GetStrings(), style
, validator
, name 
); 
 343 bool wxComboBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 344            const wxString
& value
, 
 347            int n
, const wxString choices
[], 
 349            const wxValidator
& validator
, 
 350            const wxString
& name
) 
 355     m_macIsUserPane 
= false; 
 357     if ( !wxControl::Create(parent
, id
, wxDefaultPosition
, wxDefaultSize
, style 
, 
 358                             wxDefaultValidator
, name
) ) 
 363     Rect bounds 
= wxMacGetBoundsForControl( this , pos 
, size 
); 
 366     hiRect
.origin
.x 
= 20; //bounds.left; 
 367     hiRect
.origin
.y 
= 25; //bounds.top; 
 368     hiRect
.size
.width 
= 120;// bounds.right - bounds.left; 
 369     hiRect
.size
.height 
= 24; 
 371     //For some reason, this code causes the combo box not to be displayed at all. 
 372     //hiRect.origin.x = bounds.left; 
 373     //hiRect.origin.y = bounds.top; 
 374     //hiRect.size.width = bounds.right - bounds.left; 
 375     //hiRect.size.height = bounds.bottom - bounds.top; 
 376     //printf("left = %d, right = %d, top = %d, bottom = %d\n", bounds.left, bounds.right, bounds.top, bounds.bottom); 
 377     //printf("x = %d, y = %d, width = %d, height = %d\n", hibounds.origin.x, hibounds.origin.y, hibounds.size.width, hibounds.size.height); 
 378     m_peer 
= new wxMacControl(this); 
 379     verify_noerr( HIComboBoxCreate( &hiRect
, CFSTR(""), NULL
, NULL
, kHIComboBoxStandardAttributes
, m_peer
->GetControlRefAddr() ) ); 
 382     m_peer
->SetMinimum( 0 ); 
 383     m_peer
->SetMaximum( 100); 
 385         m_peer
->SetValue( 1 ); 
 387     MacPostControlCreate(pos
,size
); 
 389     for ( int i 
= 0; i 
< n
; i
++ ) 
 391         DoAppend( choices
[ i 
] ); 
 394     HIViewSetVisible( m_peer
->GetControlRef(), true ); 
 396     EventHandlerRef comboEventHandler
; 
 397     InstallControlEventHandler( m_peer
->GetControlRef(), GetwxMacComboBoxEventHandlerUPP(), 
 398         GetEventTypeCount(eventList
), eventList
, this, 
 399         (EventHandlerRef 
*)&comboEventHandler
); 
 401     m_choice 
= new wxComboBoxChoice(this, style 
); 
 402     m_choice
->SetMinSize( wxSize( POPUPWIDTH 
, POPUPHEIGHT 
) ); 
 405     if ( style 
& wxCB_READONLY 
) 
 411         m_text 
= new wxComboBoxText(this); 
 412         if ( size
.y 
== wxDefaultCoord 
) { 
 413           csize
.y 
= m_text
->GetSize().y
; 
 417     DoSetSize(pos
.x
, pos
.y
, csize
.x
, csize
.y
); 
 419     for ( int i 
= 0; i 
< n
; i
++ ) 
 421         m_choice
->DoAppend( choices
[ i 
] ); 
 423     SetInitialSize(csize
);   // Needed because it is a wxControlWithItems 
 429 wxString 
wxComboBox::GetValue() const 
 432     CFStringRef myString
; 
 433     HIComboBoxCopyTextItemAtIndex( m_peer
->GetControlRef(), (CFIndex
)GetSelection(), &myString 
); 
 434     return wxMacCFStringHolder( myString
, m_font
.GetEncoding() ).AsString(); 
 438     if ( m_text 
== NULL 
) 
 440         result 
= m_choice
->GetString( m_choice
->GetSelection() ); 
 444         result 
= m_text
->GetValue(); 
 451 void wxComboBox::SetValue(const wxString
& value
) 
 456     int s 
= FindString (value
); 
 457     if (s 
== wxNOT_FOUND 
&& !HasFlag(wxCB_READONLY
) ) 
 459         m_choice
->Append(value
); 
 461     SetStringSelection( value 
); 
 465 // Clipboard operations 
 466 void wxComboBox::Copy() 
 468     if ( m_text 
!= NULL 
) 
 474 void wxComboBox::Cut() 
 476     if ( m_text 
!= NULL 
) 
 482 void wxComboBox::Paste() 
 484     if ( m_text 
!= NULL 
) 
 490 void wxComboBox::SetEditable(bool editable
) 
 492     if ( ( m_text 
== NULL 
) && editable 
) 
 494         m_text 
= new wxComboBoxText( this ); 
 496     else if ( ( m_text 
!= NULL 
) && !editable 
) 
 502     int currentX
, currentY
; 
 503     GetPosition( ¤tX
, ¤tY 
); 
 505     int currentW
, currentH
; 
 506     GetSize( ¤tW
, ¤tH 
); 
 508     DoMoveWindow( currentX
, currentY
, currentW
, currentH 
); 
 511 void wxComboBox::SetInsertionPoint(long pos
) 
 516 void wxComboBox::SetInsertionPointEnd() 
 521 long wxComboBox::GetInsertionPoint() const 
 527 wxTextPos 
wxComboBox::GetLastPosition() const 
 533 void wxComboBox::Replace(long from
, long to
, const wxString
& value
) 
 538 void wxComboBox::Remove(long from
, long to
) 
 543 void wxComboBox::SetSelection(long from
, long to
) 
 548 int wxComboBox::DoAppend(const wxString
& item
) 
 552     HIComboBoxAppendTextItem( m_peer
->GetControlRef(), wxMacCFStringHolder( item
, m_font
.GetEncoding() ), &outIndex 
); 
 553     //SetControl32BitMaximum( m_peer->GetControlRef(), GetCount() ); 
 554     return (int) outIndex
; 
 556     return m_choice
->DoAppend( item 
); 
 560 int wxComboBox::DoInsert(const wxString
& item
, unsigned int pos
) 
 563     HIComboBoxInsertTextItemAtIndex( m_peer
->GetControlRef(), (CFIndex
)pos
, wxMacCFStringHolder(item
, m_font
.GetEncoding()) ); 
 565     //SetControl32BitMaximum( m_peer->GetControlRef(), GetCount() ); 
 569     return m_choice
->DoInsert( item 
, pos 
); 
 573 void wxComboBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 578     return m_choice
->DoSetItemClientData( n 
, clientData 
); 
 582 void* wxComboBox::DoGetItemClientData(unsigned int n
) const 
 587     return m_choice
->DoGetItemClientData( n 
); 
 591 void wxComboBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 596     return m_choice
->DoSetItemClientObject( n 
, clientData 
); 
 600 wxClientData
* wxComboBox::DoGetItemClientObject(unsigned int n
) const 
 605     return m_choice
->DoGetItemClientObject( n 
); 
 609 void wxComboBox::FreeData() 
 611     if (HasClientObjectData()) 
 613         unsigned int count 
= GetCount(); 
 614         for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 616             SetClientObject( n
, NULL 
); 
 621 unsigned int wxComboBox::GetCount() const { 
 623     return (unsigned int) HIComboBoxGetItemCount( m_peer
->GetControlRef() ); 
 625     return m_choice
->GetCount(); 
 629 void wxComboBox::Delete(unsigned int n
) 
 632     HIComboBoxRemoveItemAtIndex( m_peer
->GetControlRef(), (CFIndex
)n 
); 
 634     // force client object deletion 
 635     if( HasClientObjectData() ) 
 636         SetClientObject( n
, NULL 
); 
 637     m_choice
->Delete( n 
); 
 641 void wxComboBox::Clear() 
 645     for ( CFIndex i 
= GetCount() - 1; i 
>= 0; ++ i 
) 
 646         verify_noerr( HIComboBoxRemoveItemAtIndex( m_peer
->GetControlRef(), i 
) ); 
 647     m_peer
->SetData
<CFStringRef
>(kHIComboBoxEditTextPart
,kControlEditTextCFStringTag
,CFSTR("")); 
 653 int wxComboBox::GetSelection() const 
 656     return FindString( GetStringSelection() ); 
 658     return m_choice
->GetSelection(); 
 662 void wxComboBox::SetSelection(int n
) 
 665     SetControl32BitValue( m_peer
->GetControlRef() , n 
+ 1 ); 
 667     m_choice
->SetSelection( n 
); 
 669     if ( m_text 
!= NULL 
) 
 671         m_text
->SetValue(GetString(n
)); 
 676 int wxComboBox::FindString(const wxString
& s
, bool bCase
) const 
 679     for( unsigned int i 
= 0 ; i 
< GetCount() ; i
++ ) 
 681         if (GetString(i
).IsSameAs(s
, bCase
) ) 
 686     return m_choice
->FindString( s
, bCase 
); 
 690 wxString 
wxComboBox::GetString(unsigned int n
) const 
 693     CFStringRef itemText
; 
 694     HIComboBoxCopyTextItemAtIndex( m_peer
->GetControlRef(), (CFIndex
)n
, &itemText 
); 
 695     return wxMacCFStringHolder(itemText
).AsString(); 
 697     return m_choice
->GetString( n 
); 
 701 wxString 
wxComboBox::GetStringSelection() const 
 704     return wxMacCFStringHolder(m_peer
->GetData
<CFStringRef
>(kHIComboBoxEditTextPart
,kControlEditTextCFStringTag
)).AsString(); 
 706     int sel 
= GetSelection (); 
 707     if (sel 
!= wxNOT_FOUND
) 
 708         return wxString(this->GetString((unsigned int)sel
)); 
 710         return wxEmptyString
; 
 714 void wxComboBox::SetString(unsigned int n
, const wxString
& s
) 
 717     verify_noerr ( HIComboBoxInsertTextItemAtIndex( m_peer
->GetControlRef(), (CFIndex
) n
, 
 718         wxMacCFStringHolder(s
, m_font
.GetEncoding()) ) ); 
 719     verify_noerr ( HIComboBoxRemoveItemAtIndex( m_peer
->GetControlRef(), (CFIndex
) n 
+ 1 ) ); 
 721     m_choice
->SetString( n 
, s 
); 
 725 bool wxComboBox::IsEditable() const 
 729     return !HasFlag(wxCB_READONLY
); 
 731     return m_text 
!= NULL 
&& !HasFlag(wxCB_READONLY
); 
 735 void wxComboBox::Undo() 
 745 void wxComboBox::Redo() 
 755 void wxComboBox::SelectAll() 
 765 bool wxComboBox::CanCopy() const 
 772         return m_text
->CanCopy(); 
 778 bool wxComboBox::CanCut() const 
 785         return m_text
->CanCut(); 
 791 bool wxComboBox::CanPaste() const 
 798         return m_text
->CanPaste(); 
 804 bool wxComboBox::CanUndo() const 
 811         return m_text
->CanUndo(); 
 817 bool wxComboBox::CanRedo() const 
 824         return m_text
->CanRedo(); 
 830 wxInt32 
wxComboBox::MacControlHit(WXEVENTHANDLERREF 
WXUNUSED(handler
) , WXEVENTREF 
WXUNUSED(event
) ) 
 832     wxCommandEvent 
event(wxEVT_COMMAND_COMBOBOX_SELECTED
, m_windowId 
); 
 833     event
.SetInt(GetSelection()); 
 834     event
.SetEventObject(this); 
 835     event
.SetString(GetStringSelection()); 
 836     ProcessCommand(event
);