]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/combobox.cpp
make sure we catch invalid control refs better
[wxWidgets.git] / src / mac / carbon / combobox.cpp
CommitLineData
e9576ca5 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/mac/carbon/combobox.cpp
e9576ca5 3// Purpose: wxComboBox class
6eae1f7d 4// Author: Stefan Csomor, Dan "Bud" Keith (composite combobox)
e9576ca5 5// Modified by:
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
a31a5f85 8// Copyright: (c) Stefan Csomor
65571936 9// Licence: wxWindows licence
e9576ca5
SC
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878
SC
12#include "wx/wxprec.h"
13
179e085f
RN
14#if wxUSE_COMBOBOX
15
e9576ca5 16#include "wx/combobox.h"
f1e01716
WS
17
18#ifndef WX_PRECOMP
19 #include "wx/button.h"
3b3dc801 20 #include "wx/menu.h"
841f23e1 21 #include "wx/containr.h"
0f7a4d1f 22 #include "wx/toplevel.h"
f1e01716
WS
23#endif
24
519cb848 25#include "wx/mac/uma.h"
e9576ca5 26
e9576ca5 27IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
e9576ca5 28
6c20e8f8 29WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox, wxControl)
7f10ed6e
SC
30
31BEGIN_EVENT_TABLE(wxComboBox, wxControl)
32 WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox)
33END_EVENT_TABLE()
34
519cb848 35
0e03d1fa 36static int nextPopUpMenuId = 1000 ;
6eae1f7d 37
150e31d2 38MenuHandle NewUniqueMenu()
892e461e 39{
9a62fa17 40 MenuHandle handle = UMANewMenu(nextPopUpMenuId, wxString(wxT("Menu")), wxFont::GetDefaultEncoding() );
6eae1f7d
DS
41 nextPopUpMenuId++ ;
42
43 return handle ;
892e461e 44}
519cb848 45
12f31626
SC
46
47// ----------------------------------------------------------------------------
48// constants
49// ----------------------------------------------------------------------------
50
51// the margin between the text control and the choice
6097743a 52#if TARGET_API_MAC_OSX
f26ca7f8
KO
53// margin should be bigger on OS X due to blue highlight
54// around text control.
b5267123 55static const wxCoord MARGIN = 4;
788e118f
SC
56// this is the border a focus rect on OSX is needing
57static const int TEXTFOCUSBORDER = 3 ;
6097743a 58#else
f26ca7f8 59static const wxCoord MARGIN = 2;
788e118f 60static const int TEXTFOCUSBORDER = 0 ;
6097743a 61#endif
12f31626
SC
62static const int POPUPHEIGHT = 23;
63
64
65// ----------------------------------------------------------------------------
66// wxComboBoxText: text control forwards events to combobox
67// ----------------------------------------------------------------------------
68
69class wxComboBoxText : public wxTextCtrl
70{
71public:
72 wxComboBoxText( wxComboBox * cb )
327788ac 73 : wxTextCtrl( cb , 1 )
12f31626
SC
74 {
75 m_cb = cb;
51478cd6 76 SetTriggerOnSetValue( false );
12f31626
SC
77 }
78
79protected:
8095ef23 80 void OnChar( wxKeyEvent& event )
12f31626 81 {
7d8268a1
WS
82 // Allows processing the tab key to go to the next control
83 if (event.GetKeyCode() == WXK_TAB)
84 {
85 wxNavigationKeyEvent NavEvent;
86 NavEvent.SetEventObject(this);
87 NavEvent.SetDirection(true);
88 NavEvent.SetWindowChange(false);
89
90 // Get the parent of the combo and have it process the navigation?
91 if (m_cb->GetParent()->GetEventHandler()->ProcessEvent(NavEvent))
645b5bd6 92 return;
7d8268a1 93 }
4a5d352f
RD
94
95 // send the event to the combobox class in case the user has bound EVT_CHAR
96 wxKeyEvent kevt(event);
97 kevt.SetEventObject(m_cb);
98 if (m_cb->GetEventHandler()->ProcessEvent(kevt))
99 // If the event was handled and not skipped then we're done
100 return;
11e62fe6 101
eb22f2a6 102 if ( event.GetKeyCode() == WXK_RETURN )
8095ef23 103 {
645b5bd6
JS
104 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_cb->GetId());
105 event.SetString( GetValue() );
106 event.SetInt( m_cb->GetSelection() );
107 event.SetEventObject( m_cb );
8095ef23 108
6eae1f7d
DS
109 // This will invoke the dialog default action,
110 // such as the clicking the default button.
645b5bd6
JS
111 if (!m_cb->GetEventHandler()->ProcessEvent( event ))
112 {
6c20e8f8
VZ
113 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
114 if ( tlw && tlw->GetDefaultItem() )
8095ef23 115 {
6c20e8f8 116 wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
8095ef23
SC
117 if ( def && def->IsEnabled() )
118 {
6eae1f7d 119 wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
8095ef23
SC
120 event.SetEventObject(def);
121 def->Command(event);
8095ef23
SC
122 }
123 }
124
125 return;
126 }
127 }
150e31d2 128
12f31626
SC
129 event.Skip();
130 }
131
645b5bd6
JS
132 void OnKeyUp( wxKeyEvent& event )
133 {
4a5d352f
RD
134 event.SetEventObject(m_cb);
135 event.SetId(m_cb->GetId());
136 if (! m_cb->GetEventHandler()->ProcessEvent(event))
137 event.Skip();
645b5bd6 138 }
4a5d352f
RD
139
140 void OnKeyDown( wxKeyEvent& event )
141 {
142 event.SetEventObject(m_cb);
143 event.SetId(m_cb->GetId());
144 if (! m_cb->GetEventHandler()->ProcessEvent(event))
145 event.Skip();
146 }
11e62fe6 147
4a5d352f
RD
148 void OnText( wxCommandEvent& event )
149 {
150 event.SetEventObject(m_cb);
151 event.SetId(m_cb->GetId());
152 if (! m_cb->GetEventHandler()->ProcessEvent(event))
153 event.Skip();
11e62fe6 154 }
4a5d352f 155
12f31626
SC
156private:
157 wxComboBox *m_cb;
158
159 DECLARE_EVENT_TABLE()
160};
161
162BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl)
6eae1f7d
DS
163 EVT_KEY_DOWN(wxComboBoxText::OnKeyDown)
164 EVT_CHAR(wxComboBoxText::OnChar)
165 EVT_KEY_UP(wxComboBoxText::OnKeyUp)
841f23e1 166 EVT_TEXT(wxID_ANY, wxComboBoxText::OnText)
12f31626
SC
167END_EVENT_TABLE()
168
169class wxComboBoxChoice : public wxChoice
170{
171public:
6eae1f7d 172 wxComboBoxChoice( wxComboBox *cb, int style )
768a4995 173 : wxChoice( cb , 1 , wxDefaultPosition , wxDefaultSize , 0 , NULL , style & (wxCB_SORT) )
12f31626
SC
174 {
175 m_cb = cb;
176 }
6eae1f7d 177
ff6871ef
SC
178 int GetPopupWidth() const
179 {
180 switch ( GetWindowVariant() )
181 {
182 case wxWINDOW_VARIANT_NORMAL :
183 case wxWINDOW_VARIANT_LARGE :
184 return 24 ;
6eae1f7d 185
ff6871ef
SC
186 default :
187 return 21 ;
188 }
189 }
12f31626
SC
190
191protected:
192 void OnChoice( wxCommandEvent& e )
193 {
194 wxString s = e.GetString();
195
196 m_cb->DelegateChoice( s );
8095ef23
SC
197 wxCommandEvent event2(wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() );
198 event2.SetInt(m_cb->GetSelection());
199 event2.SetEventObject(m_cb);
200 event2.SetString(m_cb->GetStringSelection());
201 m_cb->ProcessCommand(event2);
645b5bd6
JS
202
203 // For consistency with MSW and GTK, also send a text updated event
204 // After all, the text is updated when a selection is made
205 wxCommandEvent TextEvent( wxEVT_COMMAND_TEXT_UPDATED, m_cb->GetId() );
206 TextEvent.SetString( m_cb->GetStringSelection() );
207 TextEvent.SetEventObject( m_cb );
208 m_cb->ProcessCommand( TextEvent );
12f31626 209 }
6eae1f7d 210
6097743a
SC
211 virtual wxSize DoGetBestSize() const
212 {
213 wxSize sz = wxChoice::DoGetBestSize() ;
d0770e4a 214 if (! m_cb->HasFlag(wxCB_READONLY) )
ff6871ef 215 sz.x = GetPopupWidth() ;
6eae1f7d 216
6097743a 217 return sz ;
150e31d2 218 }
12f31626
SC
219
220private:
221 wxComboBox *m_cb;
222
6f02a879
VZ
223 friend class wxComboBox;
224
12f31626
SC
225 DECLARE_EVENT_TABLE()
226};
227
228BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
841f23e1 229 EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice)
12f31626
SC
230END_EVENT_TABLE()
231
12f31626
SC
232wxComboBox::~wxComboBox()
233{
e94e2e95
MB
234 // delete client objects
235 FreeData();
236
237 // delete the controls now, don't leave them alive even though they would
12f31626
SC
238 // still be eventually deleted by our parent - but it will be too late, the
239 // user code expects them to be gone now
6eae1f7d
DS
240 if (m_text != NULL)
241 {
f5bb2251
GD
242 delete m_text;
243 m_text = NULL;
244 }
6eae1f7d
DS
245
246 if (m_choice != NULL)
247 {
f5bb2251
GD
248 delete m_choice;
249 m_choice = NULL;
250 }
12f31626
SC
251}
252
12f31626
SC
253// ----------------------------------------------------------------------------
254// geometry
255// ----------------------------------------------------------------------------
256
257wxSize wxComboBox::DoGetBestSize() const
258{
88db1d64 259 if (!m_choice && !m_text)
1deb64c0 260 return GetSize();
6eae1f7d 261
12f31626 262 wxSize size = m_choice->GetBestSize();
150e31d2 263
d99937cd 264 if ( m_text != NULL )
12f31626
SC
265 {
266 wxSize sizeText = m_text->GetBestSize();
f26ca7f8
KO
267 if (sizeText.y > size.y)
268 size.y = sizeText.y;
6eae1f7d 269
ff6871ef 270 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
788e118f
SC
271 size.x += TEXTFOCUSBORDER ;
272 size.y += 2 * TEXTFOCUSBORDER ;
12f31626 273 }
ff6871ef
SC
274 else
275 {
276 // clipping is too tight
277 size.y += 1 ;
278 }
6eae1f7d 279
12f31626
SC
280 return size;
281}
282
150e31d2 283void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
ff6871ef 284{
6eae1f7d 285 wxControl::DoMoveWindow( x, y, width , height );
150e31d2 286
d99937cd 287 if ( m_text == NULL )
12f31626 288 {
facd6764
SC
289 // we might not be fully constructed yet, therefore watch out...
290 if ( m_choice )
291 m_choice->SetSize(0, 0 , width, -1);
12f31626
SC
292 }
293 else
294 {
ff6871ef 295 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
6eae1f7d
DS
296 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
297
ff6871ef
SC
298 // put it at an inset of 1 to have outer area shadows drawn as well
299 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
150e31d2 300 }
12f31626
SC
301}
302
12f31626
SC
303// ----------------------------------------------------------------------------
304// operations forwarded to the subcontrols
305// ----------------------------------------------------------------------------
306
307bool wxComboBox::Enable(bool enable)
308{
309 if ( !wxControl::Enable(enable) )
7d8268a1 310 return false;
12f31626 311
228146b0
JS
312 if (m_text)
313 m_text->Enable(enable);
314
7d8268a1 315 return true;
12f31626
SC
316}
317
318bool wxComboBox::Show(bool show)
319{
320 if ( !wxControl::Show(show) )
7d8268a1 321 return false;
12f31626 322
7d8268a1 323 return true;
12f31626
SC
324}
325
d99937cd
GD
326void wxComboBox::DelegateTextChanged( const wxString& value )
327{
8095ef23 328 SetStringSelection( value );
12f31626
SC
329}
330
12f31626
SC
331void wxComboBox::DelegateChoice( const wxString& value )
332{
333 SetStringSelection( value );
334}
335
8dc6614e
KH
336void wxComboBox::Init()
337{
338 m_container.SetContainerWindow(this);
339}
340
6eae1f7d
DS
341bool wxComboBox::Create(wxWindow *parent,
342 wxWindowID id,
343 const wxString& value,
344 const wxPoint& pos,
345 const wxSize& size,
346 const wxArrayString& choices,
347 long style,
348 const wxValidator& validator,
349 const wxString& name)
584ad2a3
MB
350{
351 wxCArrayString chs( choices );
352
353 return Create( parent, id, value, pos, size, chs.GetCount(),
354 chs.GetStrings(), style, validator, name );
355}
356
6eae1f7d
DS
357bool wxComboBox::Create(wxWindow *parent,
358 wxWindowID id,
359 const wxString& value,
360 const wxPoint& pos,
361 const wxSize& size,
362 int n,
363 const wxString choices[],
364 long style,
365 const wxValidator& validator,
366 const wxString& name)
e9576ca5 367{
327788ac 368 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
29998406 369 validator, name) )
12f31626 370 {
7d8268a1 371 return false;
12f31626
SC
372 }
373
327788ac 374 m_choice = new wxComboBoxChoice(this, style );
12f31626
SC
375 wxSize csize = size;
376 if ( style & wxCB_READONLY )
377 {
d99937cd 378 m_text = NULL;
12f31626
SC
379 }
380 else
381 {
382 m_text = new wxComboBoxText(this);
150e31d2 383 if ( size.y == -1 )
788e118f
SC
384 {
385 csize.y = m_text->GetSize().y ;
386 csize.y += 2 * TEXTFOCUSBORDER ;
12f31626
SC
387 }
388 }
150e31d2 389
12f31626 390 DoSetSize(pos.x, pos.y, csize.x, csize.y);
150e31d2 391
12f31626
SC
392 for ( int i = 0 ; i < n ; i++ )
393 {
465605e0 394 m_choice->DoAppend( choices[ i ] );
12f31626
SC
395 }
396
6eae1f7d
DS
397 // Needed because it is a wxControlWithItems
398 SetBestSize(size);
eba2f031 399 SetStringSelection(value);
11e62fe6 400
7d8268a1 401 return true;
e9576ca5
SC
402}
403
404wxString wxComboBox::GetValue() const
405{
12f31626 406 wxString result;
150e31d2 407
d99937cd 408 if ( m_text == NULL )
12f31626 409 result = m_choice->GetString( m_choice->GetSelection() );
12f31626 410 else
12f31626 411 result = m_text->GetValue();
12f31626
SC
412
413 return result;
e9576ca5
SC
414}
415
aa61d352 416unsigned int wxComboBox::GetCount() const
150e31d2
JS
417{
418 return m_choice->GetCount() ;
46cc7c4e
SC
419}
420
e9576ca5
SC
421void wxComboBox::SetValue(const wxString& value)
422{
30a936ee
SC
423 if ( HasFlag(wxCB_READONLY) )
424 SetStringSelection( value ) ;
425 else
426 m_text->SetValue( value );
e9576ca5
SC
427}
428
429// Clipboard operations
6eae1f7d 430
e9576ca5
SC
431void wxComboBox::Copy()
432{
d99937cd 433 if ( m_text != NULL )
12f31626 434 m_text->Copy();
e9576ca5
SC
435}
436
437void wxComboBox::Cut()
438{
d99937cd 439 if ( m_text != NULL )
12f31626 440 m_text->Cut();
e9576ca5
SC
441}
442
443void wxComboBox::Paste()
444{
d99937cd 445 if ( m_text != NULL )
12f31626 446 m_text->Paste();
e9576ca5
SC
447}
448
449void wxComboBox::SetEditable(bool editable)
450{
d99937cd 451 if ( ( m_text == NULL ) && editable )
12f31626
SC
452 {
453 m_text = new wxComboBoxText( this );
454 }
d99937cd 455 else if ( ( m_text != NULL ) && !editable )
12f31626
SC
456 {
457 delete m_text;
d99937cd 458 m_text = NULL;
12f31626
SC
459 }
460
461 int currentX, currentY;
462 GetPosition( &currentX, &currentY );
150e31d2 463
12f31626
SC
464 int currentW, currentH;
465 GetSize( &currentW, &currentH );
466
467 DoMoveWindow( currentX, currentY, currentW, currentH );
e9576ca5
SC
468}
469
470void wxComboBox::SetInsertionPoint(long pos)
471{
472 // TODO
473}
474
475void wxComboBox::SetInsertionPointEnd()
476{
477 // TODO
478}
479
480long wxComboBox::GetInsertionPoint() const
481{
482 // TODO
483 return 0;
484}
485
7d8268a1 486wxTextPos wxComboBox::GetLastPosition() const
e9576ca5
SC
487{
488 // TODO
489 return 0;
490}
491
492void wxComboBox::Replace(long from, long to, const wxString& value)
493{
494 // TODO
495}
496
497void wxComboBox::Remove(long from, long to)
498{
499 // TODO
500}
501
502void wxComboBox::SetSelection(long from, long to)
503{
504 // TODO
505}
506
150e31d2 507int wxComboBox::DoAppend(const wxString& item)
e9576ca5 508{
e71a0aa9
SC
509 return m_choice->DoAppend( item ) ;
510}
511
aa61d352 512int wxComboBox::DoInsert(const wxString& item, unsigned int pos)
e71a0aa9
SC
513{
514 return m_choice->DoInsert( item , pos ) ;
515}
516
aa61d352 517void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
e71a0aa9 518{
f148f2ba 519 return m_choice->DoSetItemClientData( n , clientData ) ;
e71a0aa9
SC
520}
521
aa61d352 522void* wxComboBox::DoGetItemClientData(unsigned int n) const
e71a0aa9 523{
f148f2ba 524 return m_choice->DoGetItemClientData( n ) ;
e71a0aa9 525}
22a70443 526
aa61d352 527void wxComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
e71a0aa9 528{
aa61d352 529 return m_choice->DoSetItemClientObject(n, clientData);
e71a0aa9
SC
530}
531
aa61d352 532wxClientData* wxComboBox::DoGetItemClientObject(unsigned int n) const
e71a0aa9 533{
f148f2ba
MB
534 return m_choice->DoGetItemClientObject( n ) ;
535}
536
537void wxComboBox::FreeData()
538{
539 if ( HasClientObjectData() )
540 {
aa61d352
VZ
541 unsigned int count = GetCount();
542 for ( unsigned int n = 0; n < count; n++ )
f148f2ba
MB
543 {
544 SetClientObject( n, NULL );
545 }
546 }
e9576ca5
SC
547}
548
aa61d352 549void wxComboBox::Delete(unsigned int n)
e9576ca5 550{
f148f2ba
MB
551 // force client object deletion
552 if( HasClientObjectData() )
553 SetClientObject( n, NULL );
12f31626 554 m_choice->Delete( n );
e9576ca5
SC
555}
556
557void wxComboBox::Clear()
558{
f148f2ba 559 FreeData();
12f31626 560 m_choice->Clear();
e9576ca5
SC
561}
562
563int wxComboBox::GetSelection() const
564{
12f31626 565 return m_choice->GetSelection();
e9576ca5
SC
566}
567
568void wxComboBox::SetSelection(int n)
569{
12f31626 570 m_choice->SetSelection( n );
150e31d2 571
d99937cd 572 if ( m_text != NULL )
aa61d352 573 m_text->SetValue(GetString(n));
e9576ca5
SC
574}
575
11e62fe6 576int wxComboBox::FindString(const wxString& s, bool bCase) const
e9576ca5 577{
11e62fe6 578 return m_choice->FindString( s, bCase );
e9576ca5
SC
579}
580
aa61d352 581wxString wxComboBox::GetString(unsigned int n) const
e9576ca5 582{
12f31626 583 return m_choice->GetString( n );
e9576ca5
SC
584}
585
586wxString wxComboBox::GetStringSelection() const
587{
6eae1f7d 588 int sel = GetSelection();
aa61d352
VZ
589 if (sel != wxNOT_FOUND)
590 return wxString(this->GetString((unsigned int)sel));
519cb848 591 else
427ff662 592 return wxEmptyString;
e9576ca5
SC
593}
594
aa61d352 595void wxComboBox::SetString(unsigned int n, const wxString& s)
e71a0aa9 596{
aa61d352 597 m_choice->SetString( n , s );
e71a0aa9
SC
598}
599
150e31d2
JS
600bool wxComboBox::IsEditable() const
601{
7d8268a1 602 return m_text != NULL && !HasFlag(wxCB_READONLY);
150e31d2
JS
603}
604
605void wxComboBox::Undo()
606{
607 if (m_text != NULL)
608 m_text->Undo();
609}
610
611void wxComboBox::Redo()
612{
613 if (m_text != NULL)
614 m_text->Redo();
615}
616
617void wxComboBox::SelectAll()
618{
619 if (m_text != NULL)
620 m_text->SelectAll();
621}
622
623bool wxComboBox::CanCopy() const
624{
625 if (m_text != NULL)
626 return m_text->CanCopy();
627 else
628 return false;
629}
630
631bool wxComboBox::CanCut() const
632{
633 if (m_text != NULL)
634 return m_text->CanCut();
635 else
636 return false;
637}
638
639bool wxComboBox::CanPaste() const
640{
641 if (m_text != NULL)
642 return m_text->CanPaste();
643 else
644 return false;
645}
646
647bool wxComboBox::CanUndo() const
648{
649 if (m_text != NULL)
650 return m_text->CanUndo();
651 else
652 return false;
653}
654
655bool wxComboBox::CanRedo() const
656{
657 if (m_text != NULL)
658 return m_text->CanRedo();
659 else
660 return false;
661}
e71a0aa9 662
6eae1f7d 663wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
519cb848 664{
6eae1f7d
DS
665/*
666 For consistency with other platforms, clicking in the text area does not constitute a selection
519cb848 667 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
465605e0 668 event.SetInt(GetSelection());
519cb848 669 event.SetEventObject(this);
0a67a93b 670 event.SetString(GetStringSelection());
6eae1f7d
DS
671 ProcessCommand(event);
672*/
673
12fce8fb 674 return noErr ;
e9576ca5 675}
519cb848 676
841f23e1 677#endif // wxUSE_COMBOBOX