]> git.saurik.com Git - wxWidgets.git/blob - 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
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/combobox.cpp
3 // Purpose: wxComboBox class
4 // Author: Stefan Csomor, Dan "Bud" Keith (composite combobox)
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_COMBOBOX
15
16 #include "wx/combobox.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/button.h"
20 #include "wx/menu.h"
21 #include "wx/containr.h"
22 #include "wx/toplevel.h"
23 #endif
24
25 #include "wx/mac/uma.h"
26
27 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
28
29 WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox, wxControl)
30
31 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
32 WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox)
33 END_EVENT_TABLE()
34
35
36 static int nextPopUpMenuId = 1000 ;
37
38 MenuHandle NewUniqueMenu()
39 {
40 MenuHandle handle = UMANewMenu(nextPopUpMenuId, wxString(wxT("Menu")), wxFont::GetDefaultEncoding() );
41 nextPopUpMenuId++ ;
42
43 return handle ;
44 }
45
46
47 // ----------------------------------------------------------------------------
48 // constants
49 // ----------------------------------------------------------------------------
50
51 // the margin between the text control and the choice
52 #if TARGET_API_MAC_OSX
53 // margin should be bigger on OS X due to blue highlight
54 // around text control.
55 static const wxCoord MARGIN = 4;
56 // this is the border a focus rect on OSX is needing
57 static const int TEXTFOCUSBORDER = 3 ;
58 #else
59 static const wxCoord MARGIN = 2;
60 static const int TEXTFOCUSBORDER = 0 ;
61 #endif
62
63
64 // ----------------------------------------------------------------------------
65 // wxComboBoxText: text control forwards events to combobox
66 // ----------------------------------------------------------------------------
67
68 class wxComboBoxText : public wxTextCtrl
69 {
70 public:
71 wxComboBoxText( wxComboBox * cb )
72 : wxTextCtrl( cb , 1 )
73 {
74 m_cb = cb;
75 SetTriggerOnSetValue( false );
76 }
77
78 protected:
79 void OnChar( wxKeyEvent& event )
80 {
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))
91 return;
92 }
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;
100
101 if ( event.GetKeyCode() == WXK_RETURN )
102 {
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 );
107
108 // This will invoke the dialog default action,
109 // such as the clicking the default button.
110 if (!m_cb->GetEventHandler()->ProcessEvent( event ))
111 {
112 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
113 if ( tlw && tlw->GetDefaultItem() )
114 {
115 wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
116 if ( def && def->IsEnabled() )
117 {
118 wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
119 event.SetEventObject(def);
120 def->Command(event);
121 }
122 }
123
124 return;
125 }
126 }
127
128 event.Skip();
129 }
130
131 void OnKeyUp( wxKeyEvent& event )
132 {
133 event.SetEventObject(m_cb);
134 event.SetId(m_cb->GetId());
135 if (! m_cb->GetEventHandler()->ProcessEvent(event))
136 event.Skip();
137 }
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 }
146
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();
153 }
154
155 private:
156 wxComboBox *m_cb;
157
158 DECLARE_EVENT_TABLE()
159 };
160
161 BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl)
162 EVT_KEY_DOWN(wxComboBoxText::OnKeyDown)
163 EVT_CHAR(wxComboBoxText::OnChar)
164 EVT_KEY_UP(wxComboBoxText::OnKeyUp)
165 EVT_TEXT(wxID_ANY, wxComboBoxText::OnText)
166 END_EVENT_TABLE()
167
168 class wxComboBoxChoice : public wxChoice
169 {
170 public:
171 wxComboBoxChoice( wxComboBox *cb, int style )
172 : wxChoice( cb , 1 , wxDefaultPosition , wxDefaultSize , 0 , NULL , style & (wxCB_SORT) )
173 {
174 m_cb = cb;
175 }
176
177 int GetPopupWidth() const
178 {
179 switch ( GetWindowVariant() )
180 {
181 case wxWINDOW_VARIANT_NORMAL :
182 case wxWINDOW_VARIANT_LARGE :
183 return 24 ;
184
185 default :
186 return 21 ;
187 }
188 }
189
190 protected:
191 void OnChoice( wxCommandEvent& e )
192 {
193 wxString s = e.GetString();
194
195 m_cb->DelegateChoice( s );
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);
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 );
208 }
209
210 virtual wxSize DoGetBestSize() const
211 {
212 wxSize sz = wxChoice::DoGetBestSize() ;
213 if (! m_cb->HasFlag(wxCB_READONLY) )
214 sz.x = GetPopupWidth() ;
215
216 return sz ;
217 }
218
219 private:
220 wxComboBox *m_cb;
221
222 friend class wxComboBox;
223
224 DECLARE_EVENT_TABLE()
225 };
226
227 BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
228 EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice)
229 END_EVENT_TABLE()
230
231 wxComboBox::~wxComboBox()
232 {
233 // delete the controls now, don't leave them alive even though they would
234 // still be eventually deleted by our parent - but it will be too late, the
235 // user code expects them to be gone now
236 if (m_text != NULL)
237 {
238 delete m_text;
239 m_text = NULL;
240 }
241
242 if (m_choice != NULL)
243 {
244 delete m_choice;
245 m_choice = NULL;
246 }
247 }
248
249 // ----------------------------------------------------------------------------
250 // geometry
251 // ----------------------------------------------------------------------------
252
253 wxSize wxComboBox::DoGetBestSize() const
254 {
255 if (!m_choice && !m_text)
256 return GetSize();
257
258 wxSize size = m_choice->GetBestSize();
259
260 if ( m_text != NULL )
261 {
262 wxSize sizeText = m_text->GetBestSize();
263 if (sizeText.y > size.y)
264 size.y = sizeText.y;
265
266 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
267 size.x += TEXTFOCUSBORDER ;
268 size.y += 2 * TEXTFOCUSBORDER ;
269 }
270 else
271 {
272 // clipping is too tight
273 size.y += 1 ;
274 }
275
276 return size;
277 }
278
279 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
280 {
281 wxControl::DoMoveWindow( x, y, width , height );
282
283 if ( m_text == NULL )
284 {
285 // we might not be fully constructed yet, therefore watch out...
286 if ( m_choice )
287 m_choice->SetSize(0, 0 , width, -1);
288 }
289 else
290 {
291 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
292 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
293
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);
296 }
297 }
298
299 // ----------------------------------------------------------------------------
300 // operations forwarded to the subcontrols
301 // ----------------------------------------------------------------------------
302
303 bool wxComboBox::Enable(bool enable)
304 {
305 if ( !wxControl::Enable(enable) )
306 return false;
307
308 if (m_text)
309 m_text->Enable(enable);
310
311 return true;
312 }
313
314 bool wxComboBox::Show(bool show)
315 {
316 if ( !wxControl::Show(show) )
317 return false;
318
319 return true;
320 }
321
322 void wxComboBox::DelegateTextChanged( const wxString& value )
323 {
324 SetStringSelection( value );
325 }
326
327 void wxComboBox::DelegateChoice( const wxString& value )
328 {
329 SetStringSelection( value );
330 }
331
332 void wxComboBox::Init()
333 {
334 WX_INIT_CONTROL_CONTAINER();
335 }
336
337 bool 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)
346 {
347 return Create( parent, id, value, pos, size, 0, NULL,
348 style, validator, name );
349 }
350
351 bool 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)
361 {
362 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
363 validator, name) )
364 {
365 return false;
366 }
367
368 m_choice = new wxComboBoxChoice(this, style );
369 wxSize csize = size;
370 if ( style & wxCB_READONLY )
371 {
372 m_text = NULL;
373 }
374 else
375 {
376 m_text = new wxComboBoxText(this);
377 if ( size.y == -1 )
378 {
379 csize.y = m_text->GetSize().y ;
380 csize.y += 2 * TEXTFOCUSBORDER ;
381 }
382 }
383
384 DoSetSize(pos.x, pos.y, csize.x, csize.y);
385
386 Append( n, choices );
387
388 // Needed because it is a wxControlWithItems
389 SetInitialSize(size);
390 SetStringSelection(value);
391
392 return true;
393 }
394
395 wxString wxComboBox::GetValue() const
396 {
397 wxString result;
398
399 if ( m_text == NULL )
400 result = m_choice->GetString( m_choice->GetSelection() );
401 else
402 result = m_text->GetValue();
403
404 return result;
405 }
406
407 unsigned int wxComboBox::GetCount() const
408 {
409 return m_choice->GetCount() ;
410 }
411
412 void wxComboBox::SetValue(const wxString& value)
413 {
414 if ( HasFlag(wxCB_READONLY) )
415 SetStringSelection( value ) ;
416 else
417 m_text->SetValue( value );
418 }
419
420 // Clipboard operations
421
422 void wxComboBox::Copy()
423 {
424 if ( m_text != NULL )
425 m_text->Copy();
426 }
427
428 void wxComboBox::Cut()
429 {
430 if ( m_text != NULL )
431 m_text->Cut();
432 }
433
434 void wxComboBox::Paste()
435 {
436 if ( m_text != NULL )
437 m_text->Paste();
438 }
439
440 void wxComboBox::SetEditable(bool editable)
441 {
442 if ( ( m_text == NULL ) && editable )
443 {
444 m_text = new wxComboBoxText( this );
445 }
446 else if ( ( m_text != NULL ) && !editable )
447 {
448 delete m_text;
449 m_text = NULL;
450 }
451
452 int currentX, currentY;
453 GetPosition( &currentX, &currentY );
454
455 int currentW, currentH;
456 GetSize( &currentW, &currentH );
457
458 DoMoveWindow( currentX, currentY, currentW, currentH );
459 }
460
461 void wxComboBox::SetInsertionPoint(long pos)
462 {
463 if ( m_text )
464 m_text->SetInsertionPoint(pos);
465 }
466
467 void wxComboBox::SetInsertionPointEnd()
468 {
469 if ( m_text )
470 m_text->SetInsertionPointEnd();
471 }
472
473 long wxComboBox::GetInsertionPoint() const
474 {
475 if ( m_text )
476 return m_text->GetInsertionPoint();
477 return 0;
478 }
479
480 wxTextPos wxComboBox::GetLastPosition() const
481 {
482 if ( m_text )
483 return m_text->GetLastPosition();
484 return 0;
485 }
486
487 void wxComboBox::Replace(long from, long to, const wxString& value)
488 {
489 if ( m_text )
490 m_text->Replace(from,to,value);
491 }
492
493 void wxComboBox::Remove(long from, long to)
494 {
495 if ( m_text )
496 m_text->Remove(from,to);
497 }
498
499 void wxComboBox::SetSelection(long from, long to)
500 {
501 if ( m_text )
502 m_text->SetSelection(from,to);
503 }
504
505 int 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 }
526
527 return rc;
528 }
529
530 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
531 {
532 return m_choice->SetClientData( n , clientData ) ;
533 }
534
535 void* wxComboBox::DoGetItemClientData(unsigned int n) const
536 {
537 return m_choice->GetClientData( n ) ;
538 }
539
540 void wxComboBox::DoDeleteOneItem(unsigned int n)
541 {
542 m_choice->Delete( n );
543 }
544
545 void wxComboBox::DoClear()
546 {
547 m_choice->Clear();
548 }
549
550 int wxComboBox::GetSelection() const
551 {
552 return m_choice->GetSelection();
553 }
554
555 void wxComboBox::SetSelection(int n)
556 {
557 m_choice->SetSelection( n );
558
559 if ( m_text != NULL )
560 m_text->SetValue(n != wxNOT_FOUND ? GetString(n) : wxString(wxEmptyString));
561 }
562
563 int wxComboBox::FindString(const wxString& s, bool bCase) const
564 {
565 return m_choice->FindString( s, bCase );
566 }
567
568 wxString wxComboBox::GetString(unsigned int n) const
569 {
570 return m_choice->GetString( n );
571 }
572
573 wxString wxComboBox::GetStringSelection() const
574 {
575 int sel = GetSelection();
576 if (sel != wxNOT_FOUND)
577 return wxString(this->GetString((unsigned int)sel));
578 else
579 return wxEmptyString;
580 }
581
582 void wxComboBox::SetString(unsigned int n, const wxString& s)
583 {
584 m_choice->SetString( n , s );
585 }
586
587 bool wxComboBox::IsEditable() const
588 {
589 return m_text != NULL && !HasFlag(wxCB_READONLY);
590 }
591
592 void wxComboBox::Undo()
593 {
594 if (m_text != NULL)
595 m_text->Undo();
596 }
597
598 void wxComboBox::Redo()
599 {
600 if (m_text != NULL)
601 m_text->Redo();
602 }
603
604 void wxComboBox::SelectAll()
605 {
606 if (m_text != NULL)
607 m_text->SelectAll();
608 }
609
610 bool wxComboBox::CanCopy() const
611 {
612 if (m_text != NULL)
613 return m_text->CanCopy();
614 else
615 return false;
616 }
617
618 bool wxComboBox::CanCut() const
619 {
620 if (m_text != NULL)
621 return m_text->CanCut();
622 else
623 return false;
624 }
625
626 bool wxComboBox::CanPaste() const
627 {
628 if (m_text != NULL)
629 return m_text->CanPaste();
630 else
631 return false;
632 }
633
634 bool wxComboBox::CanUndo() const
635 {
636 if (m_text != NULL)
637 return m_text->CanUndo();
638 else
639 return false;
640 }
641
642 bool wxComboBox::CanRedo() const
643 {
644 if (m_text != NULL)
645 return m_text->CanRedo();
646 else
647 return false;
648 }
649
650 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
651 {
652 /*
653 For consistency with other platforms, clicking in the text area does not constitute a selection
654 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
655 event.SetInt(GetSelection());
656 event.SetEventObject(this);
657 event.SetString(GetStringSelection());
658 ProcessCommand(event);
659 */
660
661 return noErr ;
662 }
663
664 #endif // wxUSE_COMBOBOX