osx new layout
[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
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 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
29
30 WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox, wxControl)
31
32 BEGIN_EVENT_TABLE(wxComboBox, wxControl)
33 WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox)
34 END_EVENT_TABLE()
35
36
37 // ----------------------------------------------------------------------------
38 // constants
39 // ----------------------------------------------------------------------------
40
41 // the margin between the text control and the choice
42 // margin should be bigger on OS X due to blue highlight
43 // around text control.
44 static const wxCoord MARGIN = 4;
45 // this is the border a focus rect on OSX is needing
46 static const int TEXTFOCUSBORDER = 3 ;
47
48
49 // ----------------------------------------------------------------------------
50 // wxComboBoxText: text control forwards events to combobox
51 // ----------------------------------------------------------------------------
52
53 class wxComboBoxText : public wxTextCtrl
54 {
55 public:
56 wxComboBoxText( wxComboBox * cb )
57 : wxTextCtrl( cb , 1 )
58 {
59 m_cb = cb;
60 SetTriggerOnSetValue( false );
61 }
62
63 protected:
64 void OnChar( wxKeyEvent& event )
65 {
66 // Allows processing the tab key to go to the next control
67 if (event.GetKeyCode() == WXK_TAB)
68 {
69 wxNavigationKeyEvent NavEvent;
70 NavEvent.SetEventObject(this);
71 NavEvent.SetDirection(true);
72 NavEvent.SetWindowChange(false);
73
74 // Get the parent of the combo and have it process the navigation?
75 if (m_cb->GetParent()->HandleWindowEvent(NavEvent))
76 return;
77 }
78
79 // send the event to the combobox class in case the user has bound EVT_CHAR
80 wxKeyEvent kevt(event);
81 kevt.SetEventObject(m_cb);
82 if (m_cb->HandleWindowEvent(kevt))
83 // If the event was handled and not skipped then we're done
84 return;
85
86 if ( event.GetKeyCode() == WXK_RETURN )
87 {
88 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_cb->GetId());
89 event.SetString( GetValue() );
90 event.SetInt( m_cb->GetSelection() );
91 event.SetEventObject( m_cb );
92
93 // This will invoke the dialog default action,
94 // such as the clicking the default button.
95 if (!m_cb->HandleWindowEvent( event ))
96 {
97 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
98 if ( tlw && tlw->GetDefaultItem() )
99 {
100 wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
101 if ( def && def->IsEnabled() )
102 {
103 wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
104 event.SetEventObject(def);
105 def->Command(event);
106 }
107 }
108
109 return;
110 }
111 }
112
113 event.Skip();
114 }
115
116 void OnKeyUp( wxKeyEvent& event )
117 {
118 event.SetEventObject(m_cb);
119 event.SetId(m_cb->GetId());
120 if (! m_cb->HandleWindowEvent(event))
121 event.Skip();
122 }
123
124 void OnKeyDown( wxKeyEvent& event )
125 {
126 event.SetEventObject(m_cb);
127 event.SetId(m_cb->GetId());
128 if (! m_cb->HandleWindowEvent(event))
129 event.Skip();
130 }
131
132 void OnText( wxCommandEvent& event )
133 {
134 event.SetEventObject(m_cb);
135 event.SetId(m_cb->GetId());
136 if (! m_cb->HandleWindowEvent(event))
137 event.Skip();
138 }
139
140 private:
141 wxComboBox *m_cb;
142
143 DECLARE_EVENT_TABLE()
144 };
145
146 BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl)
147 EVT_KEY_DOWN(wxComboBoxText::OnKeyDown)
148 EVT_CHAR(wxComboBoxText::OnChar)
149 EVT_KEY_UP(wxComboBoxText::OnKeyUp)
150 EVT_TEXT(wxID_ANY, wxComboBoxText::OnText)
151 END_EVENT_TABLE()
152
153 class wxComboBoxChoice : public wxChoice
154 {
155 public:
156 wxComboBoxChoice( wxComboBox *cb, int style )
157 : wxChoice( cb , 1 , wxDefaultPosition , wxDefaultSize , 0 , NULL , style & (wxCB_SORT) )
158 {
159 m_cb = cb;
160 }
161
162 int GetPopupWidth() const
163 {
164 switch ( GetWindowVariant() )
165 {
166 case wxWINDOW_VARIANT_NORMAL :
167 case wxWINDOW_VARIANT_LARGE :
168 return 24 ;
169
170 default :
171 return 21 ;
172 }
173 }
174
175 protected:
176 void OnChoice( wxCommandEvent& e )
177 {
178 wxString s = e.GetString();
179
180 m_cb->DelegateChoice( s );
181 wxCommandEvent event2(wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() );
182 event2.SetInt(m_cb->GetSelection());
183 event2.SetEventObject(m_cb);
184 event2.SetString(m_cb->GetStringSelection());
185 m_cb->ProcessCommand(event2);
186
187 // For consistency with MSW and GTK, also send a text updated event
188 // After all, the text is updated when a selection is made
189 wxCommandEvent TextEvent( wxEVT_COMMAND_TEXT_UPDATED, m_cb->GetId() );
190 TextEvent.SetString( m_cb->GetStringSelection() );
191 TextEvent.SetEventObject( m_cb );
192 m_cb->ProcessCommand( TextEvent );
193 }
194
195 virtual wxSize DoGetBestSize() const
196 {
197 wxSize sz = wxChoice::DoGetBestSize() ;
198 if (! m_cb->HasFlag(wxCB_READONLY) )
199 sz.x = GetPopupWidth() ;
200
201 return sz ;
202 }
203
204 private:
205 wxComboBox *m_cb;
206
207 friend class wxComboBox;
208
209 DECLARE_EVENT_TABLE()
210 };
211
212 BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
213 EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice)
214 END_EVENT_TABLE()
215
216 wxComboBox::~wxComboBox()
217 {
218 // delete the controls now, don't leave them alive even though they would
219 // still be eventually deleted by our parent - but it will be too late, the
220 // user code expects them to be gone now
221 if (m_text != NULL)
222 {
223 delete m_text;
224 m_text = NULL;
225 }
226
227 if (m_choice != NULL)
228 {
229 delete m_choice;
230 m_choice = NULL;
231 }
232 }
233
234 // ----------------------------------------------------------------------------
235 // geometry
236 // ----------------------------------------------------------------------------
237
238 wxSize wxComboBox::DoGetBestSize() const
239 {
240 if (!m_choice && !m_text)
241 return GetSize();
242
243 wxSize size = m_choice->GetBestSize();
244
245 if ( m_text != NULL )
246 {
247 wxSize sizeText = m_text->GetBestSize();
248 if (sizeText.y > size.y)
249 size.y = sizeText.y;
250
251 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
252 size.x += TEXTFOCUSBORDER ;
253 size.y += 2 * TEXTFOCUSBORDER ;
254 }
255 else
256 {
257 // clipping is too tight
258 size.y += 1 ;
259 }
260
261 return size;
262 }
263
264 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
265 {
266 wxControl::DoMoveWindow( x, y, width , height );
267
268 if ( m_text == NULL )
269 {
270 // we might not be fully constructed yet, therefore watch out...
271 if ( m_choice )
272 m_choice->SetSize(0, 0 , width, -1);
273 }
274 else
275 {
276 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
277 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
278
279 // put it at an inset of 1 to have outer area shadows drawn as well
280 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
281 }
282 }
283
284 // ----------------------------------------------------------------------------
285 // operations forwarded to the subcontrols
286 // ----------------------------------------------------------------------------
287
288 bool wxComboBox::Enable(bool enable)
289 {
290 if ( !wxControl::Enable(enable) )
291 return false;
292
293 if (m_text)
294 m_text->Enable(enable);
295
296 return true;
297 }
298
299 bool wxComboBox::Show(bool show)
300 {
301 if ( !wxControl::Show(show) )
302 return false;
303
304 return true;
305 }
306
307 void wxComboBox::DelegateTextChanged( const wxString& value )
308 {
309 SetStringSelection( value );
310 }
311
312 void wxComboBox::DelegateChoice( const wxString& value )
313 {
314 SetStringSelection( value );
315 }
316
317 void wxComboBox::Init()
318 {
319 WX_INIT_CONTROL_CONTAINER();
320 }
321
322 bool wxComboBox::Create(wxWindow *parent,
323 wxWindowID id,
324 const wxString& value,
325 const wxPoint& pos,
326 const wxSize& size,
327 const wxArrayString& choices,
328 long style,
329 const wxValidator& validator,
330 const wxString& name)
331 {
332 if ( !Create( parent, id, value, pos, size, 0, NULL,
333 style, validator, name ) )
334 return false;
335
336 Append(choices);
337
338 return true;
339 }
340
341 bool wxComboBox::Create(wxWindow *parent,
342 wxWindowID id,
343 const wxString& value,
344 const wxPoint& pos,
345 const wxSize& size,
346 int n,
347 const wxString choices[],
348 long style,
349 const wxValidator& validator,
350 const wxString& name)
351 {
352 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
353 validator, name) )
354 {
355 return false;
356 }
357
358 wxSize csize = size;
359 if ( style & wxCB_READONLY )
360 {
361 m_text = NULL;
362 }
363 else
364 {
365 m_text = new wxComboBoxText(this);
366 if ( size.y == -1 )
367 {
368 csize.y = m_text->GetSize().y ;
369 csize.y += 2 * TEXTFOCUSBORDER ;
370 }
371 }
372 m_choice = new wxComboBoxChoice(this, style );
373
374 DoSetSize(pos.x, pos.y, csize.x, csize.y);
375
376 Append( n, choices );
377
378 // Needed because it is a wxControlWithItems
379 SetInitialSize(size);
380 SetStringSelection(value);
381
382 return true;
383 }
384
385 wxString wxComboBox::GetValue() const
386 {
387 wxString result;
388
389 if ( m_text == NULL )
390 result = m_choice->GetString( m_choice->GetSelection() );
391 else
392 result = m_text->GetValue();
393
394 return result;
395 }
396
397 unsigned int wxComboBox::GetCount() const
398 {
399 return m_choice->GetCount() ;
400 }
401
402 void wxComboBox::SetValue(const wxString& value)
403 {
404 if ( HasFlag(wxCB_READONLY) )
405 SetStringSelection( value ) ;
406 else
407 m_text->SetValue( value );
408 }
409
410 void wxComboBox::WriteText(const wxString& text)
411 {
412 m_text->WriteText(text);
413 }
414
415 void wxComboBox::GetSelection(long *from, long *to) const
416 {
417 m_text->GetSelection(from, to);
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 bool wxComboBox::HandleClicked( double timestampsec )
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 true ;
655 }
656
657 #endif // wxUSE_COMBOBOX