]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/combobox.cpp
Add the unified style on OS X 10.4, and default to using it.
[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 233 // delete the controls now, don't leave them alive even though they would
12f31626
SC
234 // still be eventually deleted by our parent - but it will be too late, the
235 // user code expects them to be gone now
6eae1f7d
DS
236 if (m_text != NULL)
237 {
f5bb2251
GD
238 delete m_text;
239 m_text = NULL;
240 }
6eae1f7d
DS
241
242 if (m_choice != NULL)
243 {
f5bb2251
GD
244 delete m_choice;
245 m_choice = NULL;
246 }
12f31626
SC
247}
248
12f31626
SC
249// ----------------------------------------------------------------------------
250// geometry
251// ----------------------------------------------------------------------------
252
253wxSize wxComboBox::DoGetBestSize() const
254{
88db1d64 255 if (!m_choice && !m_text)
1deb64c0 256 return GetSize();
6eae1f7d 257
12f31626 258 wxSize size = m_choice->GetBestSize();
150e31d2 259
d99937cd 260 if ( m_text != NULL )
12f31626
SC
261 {
262 wxSize sizeText = m_text->GetBestSize();
f26ca7f8
KO
263 if (sizeText.y > size.y)
264 size.y = sizeText.y;
6eae1f7d 265
ff6871ef 266 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
788e118f
SC
267 size.x += TEXTFOCUSBORDER ;
268 size.y += 2 * TEXTFOCUSBORDER ;
12f31626 269 }
ff6871ef
SC
270 else
271 {
272 // clipping is too tight
273 size.y += 1 ;
274 }
6eae1f7d 275
12f31626
SC
276 return size;
277}
278
150e31d2 279void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
ff6871ef 280{
6eae1f7d 281 wxControl::DoMoveWindow( x, y, width , height );
150e31d2 282
d99937cd 283 if ( m_text == NULL )
12f31626 284 {
facd6764
SC
285 // we might not be fully constructed yet, therefore watch out...
286 if ( m_choice )
287 m_choice->SetSize(0, 0 , width, -1);
12f31626
SC
288 }
289 else
290 {
ff6871ef 291 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
6eae1f7d
DS
292 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
293
ff6871ef
SC
294 // put it at an inset of 1 to have outer area shadows drawn as well
295 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
150e31d2 296 }
12f31626
SC
297}
298
12f31626
SC
299// ----------------------------------------------------------------------------
300// operations forwarded to the subcontrols
301// ----------------------------------------------------------------------------
302
303bool wxComboBox::Enable(bool enable)
304{
305 if ( !wxControl::Enable(enable) )
7d8268a1 306 return false;
12f31626 307
228146b0
JS
308 if (m_text)
309 m_text->Enable(enable);
310
7d8268a1 311 return true;
12f31626
SC
312}
313
314bool wxComboBox::Show(bool show)
315{
316 if ( !wxControl::Show(show) )
7d8268a1 317 return false;
12f31626 318
7d8268a1 319 return true;
12f31626
SC
320}
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
8dc6614e
KH
332void wxComboBox::Init()
333{
de160b06 334 WX_INIT_CONTROL_CONTAINER();
8dc6614e
KH
335}
336
6eae1f7d
DS
337bool wxComboBox::Create(wxWindow *parent,
338 wxWindowID id,
339 const wxString& value,
340 const wxPoint& pos,
341 const wxSize& size,
342 const wxArrayString& choices,
343 long style,
344 const wxValidator& validator,
345 const wxString& name)
584ad2a3 346{
a236aa20
VZ
347 return Create( parent, id, value, pos, size, 0, NULL,
348 style, validator, name );
584ad2a3
MB
349}
350
6eae1f7d
DS
351bool wxComboBox::Create(wxWindow *parent,
352 wxWindowID id,
353 const wxString& value,
354 const wxPoint& pos,
355 const wxSize& size,
356 int n,
357 const wxString choices[],
358 long style,
359 const wxValidator& validator,
360 const wxString& name)
e9576ca5 361{
327788ac 362 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
29998406 363 validator, name) )
12f31626 364 {
7d8268a1 365 return false;
12f31626
SC
366 }
367
327788ac 368 m_choice = new wxComboBoxChoice(this, style );
12f31626
SC
369 wxSize csize = size;
370 if ( style & wxCB_READONLY )
371 {
d99937cd 372 m_text = NULL;
12f31626
SC
373 }
374 else
375 {
376 m_text = new wxComboBoxText(this);
150e31d2 377 if ( size.y == -1 )
788e118f
SC
378 {
379 csize.y = m_text->GetSize().y ;
380 csize.y += 2 * TEXTFOCUSBORDER ;
12f31626
SC
381 }
382 }
150e31d2 383
12f31626 384 DoSetSize(pos.x, pos.y, csize.x, csize.y);
150e31d2 385
a236aa20 386 Append( n, choices );
12f31626 387
6eae1f7d 388 // Needed because it is a wxControlWithItems
170acdc9 389 SetInitialSize(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
aa61d352 407unsigned int 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{
797f8d96
SC
463 if ( m_text )
464 m_text->SetInsertionPoint(pos);
e9576ca5
SC
465}
466
467void wxComboBox::SetInsertionPointEnd()
468{
797f8d96
SC
469 if ( m_text )
470 m_text->SetInsertionPointEnd();
e9576ca5
SC
471}
472
473long wxComboBox::GetInsertionPoint() const
474{
797f8d96
SC
475 if ( m_text )
476 return m_text->GetInsertionPoint();
e9576ca5
SC
477 return 0;
478}
479
7d8268a1 480wxTextPos wxComboBox::GetLastPosition() const
e9576ca5 481{
797f8d96
SC
482 if ( m_text )
483 return m_text->GetLastPosition();
e9576ca5
SC
484 return 0;
485}
486
487void wxComboBox::Replace(long from, long to, const wxString& value)
488{
797f8d96
SC
489 if ( m_text )
490 m_text->Replace(from,to,value);
e9576ca5
SC
491}
492
493void wxComboBox::Remove(long from, long to)
494{
797f8d96
SC
495 if ( m_text )
496 m_text->Remove(from,to);
e9576ca5
SC
497}
498
499void wxComboBox::SetSelection(long from, long to)
500{
797f8d96
SC
501 if ( m_text )
502 m_text->SetSelection(from,to);
e9576ca5
SC
503}
504
a236aa20
VZ
505int wxComboBox::DoInsertItems(const wxArrayStringsAdapter& items,
506 unsigned int pos,
507 void **clientData,
508 wxClientDataType type)
509{
510 // wxItemContainer should probably be doing it itself but usually this is
511 // not necessary as the derived class DoInsertItems() calls
512 // AssignNewItemClientData() which initializes m_clientDataItemsType
513 // correctly; however as we just forward everything to wxChoice, we need to
514 // do it ourselves
515 //
516 // also notice that we never use wxClientData_Object with wxChoice as we
517 // don't want it to delete the data -- we will
518 int rc = m_choice->DoInsertItems(items, pos, clientData,
519 clientData ? wxClientData_Void
520 : wxClientData_None) ;
521 if ( rc != wxNOT_FOUND )
522 {
523 if ( !HasClientData() && type != wxClientData_None )
524 m_clientDataItemsType = type;
525 }
e71a0aa9 526
a236aa20 527 return rc;
e71a0aa9
SC
528}
529
aa61d352 530void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
e71a0aa9 531{
a236aa20 532 return m_choice->SetClientData( n , clientData ) ;
e71a0aa9
SC
533}
534
aa61d352 535void* wxComboBox::DoGetItemClientData(unsigned int n) const
e71a0aa9 536{
a236aa20 537 return m_choice->GetClientData( n ) ;
e9576ca5
SC
538}
539
a236aa20 540void wxComboBox::DoDeleteOneItem(unsigned int n)
e9576ca5 541{
12f31626 542 m_choice->Delete( n );
e9576ca5
SC
543}
544
a236aa20 545void wxComboBox::DoClear()
e9576ca5 546{
12f31626 547 m_choice->Clear();
e9576ca5
SC
548}
549
550int wxComboBox::GetSelection() const
551{
12f31626 552 return m_choice->GetSelection();
e9576ca5
SC
553}
554
555void wxComboBox::SetSelection(int n)
556{
12f31626 557 m_choice->SetSelection( n );
150e31d2 558
d99937cd 559 if ( m_text != NULL )
4ff9366d 560 m_text->SetValue(n != wxNOT_FOUND ? GetString(n) : wxString(wxEmptyString));
e9576ca5
SC
561}
562
11e62fe6 563int wxComboBox::FindString(const wxString& s, bool bCase) const
e9576ca5 564{
11e62fe6 565 return m_choice->FindString( s, bCase );
e9576ca5
SC
566}
567
aa61d352 568wxString wxComboBox::GetString(unsigned int n) const
e9576ca5 569{
12f31626 570 return m_choice->GetString( n );
e9576ca5
SC
571}
572
573wxString wxComboBox::GetStringSelection() const
574{
6eae1f7d 575 int sel = GetSelection();
aa61d352
VZ
576 if (sel != wxNOT_FOUND)
577 return wxString(this->GetString((unsigned int)sel));
519cb848 578 else
427ff662 579 return wxEmptyString;
e9576ca5
SC
580}
581
aa61d352 582void wxComboBox::SetString(unsigned int n, const wxString& s)
e71a0aa9 583{
aa61d352 584 m_choice->SetString( n , s );
e71a0aa9
SC
585}
586
150e31d2
JS
587bool wxComboBox::IsEditable() const
588{
7d8268a1 589 return m_text != NULL && !HasFlag(wxCB_READONLY);
150e31d2
JS
590}
591
592void wxComboBox::Undo()
593{
594 if (m_text != NULL)
595 m_text->Undo();
596}
597
598void wxComboBox::Redo()
599{
600 if (m_text != NULL)
601 m_text->Redo();
602}
603
604void wxComboBox::SelectAll()
605{
606 if (m_text != NULL)
607 m_text->SelectAll();
608}
609
610bool wxComboBox::CanCopy() const
611{
612 if (m_text != NULL)
613 return m_text->CanCopy();
614 else
615 return false;
616}
617
618bool wxComboBox::CanCut() const
619{
620 if (m_text != NULL)
621 return m_text->CanCut();
622 else
623 return false;
624}
625
626bool wxComboBox::CanPaste() const
627{
628 if (m_text != NULL)
629 return m_text->CanPaste();
630 else
631 return false;
632}
633
634bool wxComboBox::CanUndo() const
635{
636 if (m_text != NULL)
637 return m_text->CanUndo();
638 else
639 return false;
640}
641
642bool wxComboBox::CanRedo() const
643{
644 if (m_text != NULL)
645 return m_text->CanRedo();
646 else
647 return false;
648}
e71a0aa9 649
6eae1f7d 650wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
519cb848 651{
6eae1f7d
DS
652/*
653 For consistency with other platforms, clicking in the text area does not constitute a selection
519cb848 654 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
465605e0 655 event.SetInt(GetSelection());
519cb848 656 event.SetEventObject(this);
0a67a93b 657 event.SetString(GetStringSelection());
6eae1f7d
DS
658 ProcessCommand(event);
659*/
660
12fce8fb 661 return noErr ;
e9576ca5 662}
519cb848 663
841f23e1 664#endif // wxUSE_COMBOBOX