don't access m_clientDataItemsType directly
[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 return m_choice->DoInsertItems(items, pos, clientData, type);
511 }
512
513 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
514 {
515 return m_choice->DoSetItemClientData( n , clientData ) ;
516 }
517
518 void* wxComboBox::DoGetItemClientData(unsigned int n) const
519 {
520 return m_choice->DoGetItemClientData( n ) ;
521 }
522
523 wxClientDataType wxComboBox::GetClientDataType() const
524 {
525 return m_choice->GetClientDataType();
526 }
527
528 void wxComboBox::SetClientDataType(wxClientDataType clientDataItemsType)
529 {
530 m_choice->SetClientDataType(clientDataItemsType);
531 }
532
533 void wxComboBox::DoDeleteOneItem(unsigned int n)
534 {
535 m_choice->DoDeleteOneItem( n );
536 }
537
538 void wxComboBox::DoClear()
539 {
540 m_choice->DoClear();
541 }
542
543 int wxComboBox::GetSelection() const
544 {
545 return m_choice->GetSelection();
546 }
547
548 void wxComboBox::SetSelection(int n)
549 {
550 m_choice->SetSelection( n );
551
552 if ( m_text != NULL )
553 m_text->SetValue(n != wxNOT_FOUND ? GetString(n) : wxString(wxEmptyString));
554 }
555
556 int wxComboBox::FindString(const wxString& s, bool bCase) const
557 {
558 return m_choice->FindString( s, bCase );
559 }
560
561 wxString wxComboBox::GetString(unsigned int n) const
562 {
563 return m_choice->GetString( n );
564 }
565
566 wxString wxComboBox::GetStringSelection() const
567 {
568 int sel = GetSelection();
569 if (sel != wxNOT_FOUND)
570 return wxString(this->GetString((unsigned int)sel));
571 else
572 return wxEmptyString;
573 }
574
575 void wxComboBox::SetString(unsigned int n, const wxString& s)
576 {
577 m_choice->SetString( n , s );
578 }
579
580 bool wxComboBox::IsEditable() const
581 {
582 return m_text != NULL && !HasFlag(wxCB_READONLY);
583 }
584
585 void wxComboBox::Undo()
586 {
587 if (m_text != NULL)
588 m_text->Undo();
589 }
590
591 void wxComboBox::Redo()
592 {
593 if (m_text != NULL)
594 m_text->Redo();
595 }
596
597 void wxComboBox::SelectAll()
598 {
599 if (m_text != NULL)
600 m_text->SelectAll();
601 }
602
603 bool wxComboBox::CanCopy() const
604 {
605 if (m_text != NULL)
606 return m_text->CanCopy();
607 else
608 return false;
609 }
610
611 bool wxComboBox::CanCut() const
612 {
613 if (m_text != NULL)
614 return m_text->CanCut();
615 else
616 return false;
617 }
618
619 bool wxComboBox::CanPaste() const
620 {
621 if (m_text != NULL)
622 return m_text->CanPaste();
623 else
624 return false;
625 }
626
627 bool wxComboBox::CanUndo() const
628 {
629 if (m_text != NULL)
630 return m_text->CanUndo();
631 else
632 return false;
633 }
634
635 bool wxComboBox::CanRedo() const
636 {
637 if (m_text != NULL)
638 return m_text->CanRedo();
639 else
640 return false;
641 }
642
643 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
644 {
645 /*
646 For consistency with other platforms, clicking in the text area does not constitute a selection
647 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
648 event.SetInt(GetSelection());
649 event.SetEventObject(this);
650 event.SetString(GetStringSelection());
651 ProcessCommand(event);
652 */
653
654 return noErr ;
655 }
656
657 #endif // wxUSE_COMBOBOX