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