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