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