]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/combobox.cpp
A more complete fix for the generic control border issue, a fix for getting/setting...
[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 client objects
234 FreeData();
235
236 // delete the controls now, don't leave them alive even though they would
237 // still be eventually deleted by our parent - but it will be too late, the
238 // user code expects them to be gone now
239 if (m_text != NULL)
240 {
241 delete m_text;
242 m_text = NULL;
243 }
244
245 if (m_choice != NULL)
246 {
247 delete m_choice;
248 m_choice = NULL;
249 }
250 }
251
252 // ----------------------------------------------------------------------------
253 // geometry
254 // ----------------------------------------------------------------------------
255
256 wxSize wxComboBox::DoGetBestSize() const
257 {
258 if (!m_choice && !m_text)
259 return GetSize();
260
261 wxSize size = m_choice->GetBestSize();
262
263 if ( m_text != NULL )
264 {
265 wxSize sizeText = m_text->GetBestSize();
266 if (sizeText.y > size.y)
267 size.y = sizeText.y;
268
269 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
270 size.x += TEXTFOCUSBORDER ;
271 size.y += 2 * TEXTFOCUSBORDER ;
272 }
273 else
274 {
275 // clipping is too tight
276 size.y += 1 ;
277 }
278
279 return size;
280 }
281
282 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
283 {
284 wxControl::DoMoveWindow( x, y, width , height );
285
286 if ( m_text == NULL )
287 {
288 // we might not be fully constructed yet, therefore watch out...
289 if ( m_choice )
290 m_choice->SetSize(0, 0 , width, -1);
291 }
292 else
293 {
294 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
295 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
296
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);
299 }
300 }
301
302 // ----------------------------------------------------------------------------
303 // operations forwarded to the subcontrols
304 // ----------------------------------------------------------------------------
305
306 bool wxComboBox::Enable(bool enable)
307 {
308 if ( !wxControl::Enable(enable) )
309 return false;
310
311 if (m_text)
312 m_text->Enable(enable);
313
314 return true;
315 }
316
317 bool wxComboBox::Show(bool show)
318 {
319 if ( !wxControl::Show(show) )
320 return false;
321
322 return true;
323 }
324
325 void wxComboBox::DelegateTextChanged( const wxString& value )
326 {
327 SetStringSelection( value );
328 }
329
330 void wxComboBox::DelegateChoice( const wxString& value )
331 {
332 SetStringSelection( value );
333 }
334
335 void wxComboBox::Init()
336 {
337 m_container.SetContainerWindow(this);
338 }
339
340 bool 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)
349 {
350 wxCArrayString chs( choices );
351
352 return Create( parent, id, value, pos, size, chs.GetCount(),
353 chs.GetStrings(), style, validator, name );
354 }
355
356 bool 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)
366 {
367 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
368 validator, name) )
369 {
370 return false;
371 }
372
373 m_choice = new wxComboBoxChoice(this, style );
374 wxSize csize = size;
375 if ( style & wxCB_READONLY )
376 {
377 m_text = NULL;
378 }
379 else
380 {
381 m_text = new wxComboBoxText(this);
382 if ( size.y == -1 )
383 {
384 csize.y = m_text->GetSize().y ;
385 csize.y += 2 * TEXTFOCUSBORDER ;
386 }
387 }
388
389 DoSetSize(pos.x, pos.y, csize.x, csize.y);
390
391 for ( int i = 0 ; i < n ; i++ )
392 {
393 m_choice->DoAppend( choices[ i ] );
394 }
395
396 // Needed because it is a wxControlWithItems
397 SetInitialSize(size);
398 SetStringSelection(value);
399
400 return true;
401 }
402
403 wxString wxComboBox::GetValue() const
404 {
405 wxString result;
406
407 if ( m_text == NULL )
408 result = m_choice->GetString( m_choice->GetSelection() );
409 else
410 result = m_text->GetValue();
411
412 return result;
413 }
414
415 unsigned int wxComboBox::GetCount() const
416 {
417 return m_choice->GetCount() ;
418 }
419
420 void wxComboBox::SetValue(const wxString& value)
421 {
422 if ( HasFlag(wxCB_READONLY) )
423 SetStringSelection( value ) ;
424 else
425 m_text->SetValue( value );
426 }
427
428 // Clipboard operations
429
430 void wxComboBox::Copy()
431 {
432 if ( m_text != NULL )
433 m_text->Copy();
434 }
435
436 void wxComboBox::Cut()
437 {
438 if ( m_text != NULL )
439 m_text->Cut();
440 }
441
442 void wxComboBox::Paste()
443 {
444 if ( m_text != NULL )
445 m_text->Paste();
446 }
447
448 void wxComboBox::SetEditable(bool editable)
449 {
450 if ( ( m_text == NULL ) && editable )
451 {
452 m_text = new wxComboBoxText( this );
453 }
454 else if ( ( m_text != NULL ) && !editable )
455 {
456 delete m_text;
457 m_text = NULL;
458 }
459
460 int currentX, currentY;
461 GetPosition( &currentX, &currentY );
462
463 int currentW, currentH;
464 GetSize( &currentW, &currentH );
465
466 DoMoveWindow( currentX, currentY, currentW, currentH );
467 }
468
469 void wxComboBox::SetInsertionPoint(long pos)
470 {
471 if ( m_text )
472 m_text->SetInsertionPoint(pos);
473 }
474
475 void wxComboBox::SetInsertionPointEnd()
476 {
477 if ( m_text )
478 m_text->SetInsertionPointEnd();
479 }
480
481 long wxComboBox::GetInsertionPoint() const
482 {
483 if ( m_text )
484 return m_text->GetInsertionPoint();
485 return 0;
486 }
487
488 wxTextPos wxComboBox::GetLastPosition() const
489 {
490 if ( m_text )
491 return m_text->GetLastPosition();
492 return 0;
493 }
494
495 void wxComboBox::Replace(long from, long to, const wxString& value)
496 {
497 if ( m_text )
498 m_text->Replace(from,to,value);
499 }
500
501 void wxComboBox::Remove(long from, long to)
502 {
503 if ( m_text )
504 m_text->Remove(from,to);
505 }
506
507 void wxComboBox::SetSelection(long from, long to)
508 {
509 if ( m_text )
510 m_text->SetSelection(from,to);
511 }
512
513 int wxComboBox::DoAppend(const wxString& item)
514 {
515 return m_choice->DoAppend( item ) ;
516 }
517
518 int wxComboBox::DoInsert(const wxString& item, unsigned int pos)
519 {
520 return m_choice->DoInsert( item , pos ) ;
521 }
522
523 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
524 {
525 return m_choice->DoSetItemClientData( n , clientData ) ;
526 }
527
528 void* wxComboBox::DoGetItemClientData(unsigned int n) const
529 {
530 return m_choice->DoGetItemClientData( n ) ;
531 }
532
533 void wxComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
534 {
535 return m_choice->DoSetItemClientObject(n, clientData);
536 }
537
538 wxClientData* wxComboBox::DoGetItemClientObject(unsigned int n) const
539 {
540 return m_choice->DoGetItemClientObject( n ) ;
541 }
542
543 void wxComboBox::FreeData()
544 {
545 if ( HasClientObjectData() )
546 {
547 unsigned int count = GetCount();
548 for ( unsigned int n = 0; n < count; n++ )
549 {
550 SetClientObject( n, NULL );
551 }
552 }
553 }
554
555 void wxComboBox::Delete(unsigned int n)
556 {
557 // force client object deletion
558 if( HasClientObjectData() )
559 SetClientObject( n, NULL );
560 m_choice->Delete( n );
561 }
562
563 void wxComboBox::Clear()
564 {
565 FreeData();
566 m_choice->Clear();
567 }
568
569 int wxComboBox::GetSelection() const
570 {
571 return m_choice->GetSelection();
572 }
573
574 void wxComboBox::SetSelection(int n)
575 {
576 m_choice->SetSelection( n );
577
578 if ( m_text != NULL )
579 m_text->SetValue(GetString(n));
580 }
581
582 int wxComboBox::FindString(const wxString& s, bool bCase) const
583 {
584 return m_choice->FindString( s, bCase );
585 }
586
587 wxString wxComboBox::GetString(unsigned int n) const
588 {
589 return m_choice->GetString( n );
590 }
591
592 wxString wxComboBox::GetStringSelection() const
593 {
594 int sel = GetSelection();
595 if (sel != wxNOT_FOUND)
596 return wxString(this->GetString((unsigned int)sel));
597 else
598 return wxEmptyString;
599 }
600
601 void wxComboBox::SetString(unsigned int n, const wxString& s)
602 {
603 m_choice->SetString( n , s );
604 }
605
606 bool wxComboBox::IsEditable() const
607 {
608 return m_text != NULL && !HasFlag(wxCB_READONLY);
609 }
610
611 void wxComboBox::Undo()
612 {
613 if (m_text != NULL)
614 m_text->Undo();
615 }
616
617 void wxComboBox::Redo()
618 {
619 if (m_text != NULL)
620 m_text->Redo();
621 }
622
623 void wxComboBox::SelectAll()
624 {
625 if (m_text != NULL)
626 m_text->SelectAll();
627 }
628
629 bool wxComboBox::CanCopy() const
630 {
631 if (m_text != NULL)
632 return m_text->CanCopy();
633 else
634 return false;
635 }
636
637 bool wxComboBox::CanCut() const
638 {
639 if (m_text != NULL)
640 return m_text->CanCut();
641 else
642 return false;
643 }
644
645 bool wxComboBox::CanPaste() const
646 {
647 if (m_text != NULL)
648 return m_text->CanPaste();
649 else
650 return false;
651 }
652
653 bool wxComboBox::CanUndo() const
654 {
655 if (m_text != NULL)
656 return m_text->CanUndo();
657 else
658 return false;
659 }
660
661 bool wxComboBox::CanRedo() const
662 {
663 if (m_text != NULL)
664 return m_text->CanRedo();
665 else
666 return false;
667 }
668
669 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
670 {
671 /*
672 For consistency with other platforms, clicking in the text area does not constitute a selection
673 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
674 event.SetInt(GetSelection());
675 event.SetEventObject(this);
676 event.SetString(GetStringSelection());
677 ProcessCommand(event);
678 */
679
680 return noErr ;
681 }
682
683 #endif // wxUSE_COMBOBOX