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