]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/combobox.cpp
Include wx/stopwatch.h according to precompiled headers of wx/wx.h (with other minor...
[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, wxControl)
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 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 client objects
234 FreeData();
235
236 // delete the controls now, don't leave them alive even though they would
237 // still be eventually deleted by our parent - but it will be too late, the
238 // user code expects them to be gone now
239 if (m_text != NULL)
240 {
241 delete m_text;
242 m_text = NULL;
243 }
244
245 if (m_choice != NULL)
246 {
247 delete m_choice;
248 m_choice = NULL;
249 }
250 }
251
252 // ----------------------------------------------------------------------------
253 // geometry
254 // ----------------------------------------------------------------------------
255
256 wxSize wxComboBox::DoGetBestSize() const
257 {
258 if (!m_choice && !m_text)
259 return GetSize();
260
261 wxSize size = m_choice->GetBestSize();
262
263 if ( m_text != NULL )
264 {
265 wxSize sizeText = m_text->GetBestSize();
266 if (sizeText.y > size.y)
267 size.y = sizeText.y;
268
269 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
270 size.x += TEXTFOCUSBORDER ;
271 size.y += 2 * TEXTFOCUSBORDER ;
272 }
273 else
274 {
275 // clipping is too tight
276 size.y += 1 ;
277 }
278
279 return size;
280 }
281
282 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
283 {
284 wxControl::DoMoveWindow( x, y, width , height );
285
286 if ( m_text == NULL )
287 {
288 // we might not be fully constructed yet, therefore watch out...
289 if ( m_choice )
290 m_choice->SetSize(0, 0 , width, -1);
291 }
292 else
293 {
294 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
295 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
296
297 // put it at an inset of 1 to have outer area shadows drawn as well
298 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
299 }
300 }
301
302 // ----------------------------------------------------------------------------
303 // operations forwarded to the subcontrols
304 // ----------------------------------------------------------------------------
305
306 bool wxComboBox::Enable(bool enable)
307 {
308 if ( !wxControl::Enable(enable) )
309 return false;
310
311 if (m_text)
312 m_text->Enable(enable);
313
314 return true;
315 }
316
317 bool wxComboBox::Show(bool show)
318 {
319 if ( !wxControl::Show(show) )
320 return false;
321
322 return true;
323 }
324
325 void wxComboBox::DelegateTextChanged( const wxString& value )
326 {
327 SetStringSelection( value );
328 }
329
330 void wxComboBox::DelegateChoice( const wxString& value )
331 {
332 SetStringSelection( value );
333 }
334
335 void wxComboBox::Init()
336 {
337 m_container.SetContainerWindow(this);
338 }
339
340 bool wxComboBox::Create(wxWindow *parent,
341 wxWindowID id,
342 const wxString& value,
343 const wxPoint& pos,
344 const wxSize& size,
345 const wxArrayString& choices,
346 long style,
347 const wxValidator& validator,
348 const wxString& name)
349 {
350 wxCArrayString chs( choices );
351
352 return Create( parent, id, value, pos, size, chs.GetCount(),
353 chs.GetStrings(), style, validator, name );
354 }
355
356 bool wxComboBox::Create(wxWindow *parent,
357 wxWindowID id,
358 const wxString& value,
359 const wxPoint& pos,
360 const wxSize& size,
361 int n,
362 const wxString choices[],
363 long style,
364 const wxValidator& validator,
365 const wxString& name)
366 {
367 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
368 validator, name) )
369 {
370 return false;
371 }
372
373 m_choice = new wxComboBoxChoice(this, style );
374 wxSize csize = size;
375 if ( style & wxCB_READONLY )
376 {
377 m_text = NULL;
378 }
379 else
380 {
381 m_text = new wxComboBoxText(this);
382 if ( size.y == -1 )
383 {
384 csize.y = m_text->GetSize().y ;
385 csize.y += 2 * TEXTFOCUSBORDER ;
386 }
387 }
388
389 DoSetSize(pos.x, pos.y, csize.x, csize.y);
390
391 for ( int i = 0 ; i < n ; i++ )
392 {
393 m_choice->DoAppend( choices[ i ] );
394 }
395
396 // Needed because it is a wxControlWithItems
397 SetBestSize(size);
398 SetStringSelection(value);
399
400 return true;
401 }
402
403 wxString wxComboBox::GetValue() const
404 {
405 wxString result;
406
407 if ( m_text == NULL )
408 result = m_choice->GetString( m_choice->GetSelection() );
409 else
410 result = m_text->GetValue();
411
412 return result;
413 }
414
415 unsigned int wxComboBox::GetCount() const
416 {
417 return m_choice->GetCount() ;
418 }
419
420 void wxComboBox::SetValue(const wxString& value)
421 {
422 if ( HasFlag(wxCB_READONLY) )
423 SetStringSelection( value ) ;
424 else
425 m_text->SetValue( value );
426 }
427
428 // Clipboard operations
429
430 void wxComboBox::Copy()
431 {
432 if ( m_text != NULL )
433 m_text->Copy();
434 }
435
436 void wxComboBox::Cut()
437 {
438 if ( m_text != NULL )
439 m_text->Cut();
440 }
441
442 void wxComboBox::Paste()
443 {
444 if ( m_text != NULL )
445 m_text->Paste();
446 }
447
448 void wxComboBox::SetEditable(bool editable)
449 {
450 if ( ( m_text == NULL ) && editable )
451 {
452 m_text = new wxComboBoxText( this );
453 }
454 else if ( ( m_text != NULL ) && !editable )
455 {
456 delete m_text;
457 m_text = NULL;
458 }
459
460 int currentX, currentY;
461 GetPosition( &currentX, &currentY );
462
463 int currentW, currentH;
464 GetSize( &currentW, &currentH );
465
466 DoMoveWindow( currentX, currentY, currentW, currentH );
467 }
468
469 void wxComboBox::SetInsertionPoint(long pos)
470 {
471 // TODO
472 }
473
474 void wxComboBox::SetInsertionPointEnd()
475 {
476 // TODO
477 }
478
479 long wxComboBox::GetInsertionPoint() const
480 {
481 // TODO
482 return 0;
483 }
484
485 wxTextPos wxComboBox::GetLastPosition() const
486 {
487 // TODO
488 return 0;
489 }
490
491 void wxComboBox::Replace(long from, long to, const wxString& value)
492 {
493 // TODO
494 }
495
496 void wxComboBox::Remove(long from, long to)
497 {
498 // TODO
499 }
500
501 void wxComboBox::SetSelection(long from, long to)
502 {
503 // TODO
504 }
505
506 int wxComboBox::DoAppend(const wxString& item)
507 {
508 return m_choice->DoAppend( item ) ;
509 }
510
511 int wxComboBox::DoInsert(const wxString& item, unsigned int pos)
512 {
513 return m_choice->DoInsert( item , pos ) ;
514 }
515
516 void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
517 {
518 return m_choice->DoSetItemClientData( n , clientData ) ;
519 }
520
521 void* wxComboBox::DoGetItemClientData(unsigned int n) const
522 {
523 return m_choice->DoGetItemClientData( n ) ;
524 }
525
526 void wxComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
527 {
528 return m_choice->DoSetItemClientObject(n, clientData);
529 }
530
531 wxClientData* wxComboBox::DoGetItemClientObject(unsigned int n) const
532 {
533 return m_choice->DoGetItemClientObject( n ) ;
534 }
535
536 void wxComboBox::FreeData()
537 {
538 if ( HasClientObjectData() )
539 {
540 unsigned int count = GetCount();
541 for ( unsigned int n = 0; n < count; n++ )
542 {
543 SetClientObject( n, NULL );
544 }
545 }
546 }
547
548 void wxComboBox::Delete(unsigned int n)
549 {
550 // force client object deletion
551 if( HasClientObjectData() )
552 SetClientObject( n, NULL );
553 m_choice->Delete( n );
554 }
555
556 void wxComboBox::Clear()
557 {
558 FreeData();
559 m_choice->Clear();
560 }
561
562 int wxComboBox::GetSelection() const
563 {
564 return m_choice->GetSelection();
565 }
566
567 void wxComboBox::SetSelection(int n)
568 {
569 m_choice->SetSelection( n );
570
571 if ( m_text != NULL )
572 m_text->SetValue(GetString(n));
573 }
574
575 int wxComboBox::FindString(const wxString& s, bool bCase) const
576 {
577 return m_choice->FindString( s, bCase );
578 }
579
580 wxString wxComboBox::GetString(unsigned int n) const
581 {
582 return m_choice->GetString( n );
583 }
584
585 wxString wxComboBox::GetStringSelection() const
586 {
587 int sel = GetSelection();
588 if (sel != wxNOT_FOUND)
589 return wxString(this->GetString((unsigned int)sel));
590 else
591 return wxEmptyString;
592 }
593
594 void wxComboBox::SetString(unsigned int n, const wxString& s)
595 {
596 m_choice->SetString( n , s );
597 }
598
599 bool wxComboBox::IsEditable() const
600 {
601 return m_text != NULL && !HasFlag(wxCB_READONLY);
602 }
603
604 void wxComboBox::Undo()
605 {
606 if (m_text != NULL)
607 m_text->Undo();
608 }
609
610 void wxComboBox::Redo()
611 {
612 if (m_text != NULL)
613 m_text->Redo();
614 }
615
616 void wxComboBox::SelectAll()
617 {
618 if (m_text != NULL)
619 m_text->SelectAll();
620 }
621
622 bool wxComboBox::CanCopy() const
623 {
624 if (m_text != NULL)
625 return m_text->CanCopy();
626 else
627 return false;
628 }
629
630 bool wxComboBox::CanCut() const
631 {
632 if (m_text != NULL)
633 return m_text->CanCut();
634 else
635 return false;
636 }
637
638 bool wxComboBox::CanPaste() const
639 {
640 if (m_text != NULL)
641 return m_text->CanPaste();
642 else
643 return false;
644 }
645
646 bool wxComboBox::CanUndo() const
647 {
648 if (m_text != NULL)
649 return m_text->CanUndo();
650 else
651 return false;
652 }
653
654 bool wxComboBox::CanRedo() const
655 {
656 if (m_text != NULL)
657 return m_text->CanRedo();
658 else
659 return false;
660 }
661
662 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
663 {
664 /*
665 For consistency with other platforms, clicking in the text area does not constitute a selection
666 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
667 event.SetInt(GetSelection());
668 event.SetEventObject(this);
669 event.SetString(GetStringSelection());
670 ProcessCommand(event);
671 */
672
673 return noErr ;
674 }
675
676 #endif // wxUSE_COMBOBOX