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