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