]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/combobox.cpp
make access specifiers for the virtual functions match their access in the base class...
[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 #include "wx/button.h"
18 #include "wx/menu.h"
19 #include "wx/mac/uma.h"
20
21 IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
22
23
24 static int nextPopUpMenuId = 1000 ;
25
26 MenuHandle NewUniqueMenu()
27 {
28 MenuHandle handle = NewMenu( nextPopUpMenuId , "\pMenu" ) ;
29 nextPopUpMenuId++ ;
30
31 return handle ;
32 }
33
34
35 // ----------------------------------------------------------------------------
36 // constants
37 // ----------------------------------------------------------------------------
38
39 // the margin between the text control and the choice
40 #if TARGET_API_MAC_OSX
41 // margin should be bigger on OS X due to blue highlight
42 // around text control.
43 static const wxCoord MARGIN = 4;
44 // this is the border a focus rect on OSX is needing
45 static const int TEXTFOCUSBORDER = 3 ;
46 #else
47 static const wxCoord MARGIN = 2;
48 static const int TEXTFOCUSBORDER = 0 ;
49 #endif
50 static const int POPUPHEIGHT = 23;
51
52
53 // ----------------------------------------------------------------------------
54 // wxComboBoxText: text control forwards events to combobox
55 // ----------------------------------------------------------------------------
56
57 class wxComboBoxText : public wxTextCtrl
58 {
59 public:
60 wxComboBoxText( wxComboBox * cb )
61 : wxTextCtrl( cb , 1 )
62 {
63 m_cb = cb;
64 }
65
66 protected:
67 void OnChar( wxKeyEvent& event )
68 {
69 // Allows processing the tab key to go to the next control
70 if (event.GetKeyCode() == WXK_TAB)
71 {
72 wxNavigationKeyEvent NavEvent;
73 NavEvent.SetEventObject(this);
74 NavEvent.SetDirection(true);
75 NavEvent.SetWindowChange(false);
76
77 // Get the parent of the combo and have it process the navigation?
78 if (m_cb->GetParent()->GetEventHandler()->ProcessEvent(NavEvent))
79 return;
80 }
81
82 // send the event to the combobox class in case the user has bound EVT_CHAR
83 wxKeyEvent kevt(event);
84 kevt.SetEventObject(m_cb);
85 if (m_cb->GetEventHandler()->ProcessEvent(kevt))
86 // If the event was handled and not skipped then we're done
87 return;
88
89 if ( event.GetKeyCode() == WXK_RETURN )
90 {
91 wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_cb->GetId());
92 event.SetString( GetValue() );
93 event.SetInt( m_cb->GetSelection() );
94 event.SetEventObject( m_cb );
95
96 // This will invoke the dialog default action,
97 // such as the clicking the default button.
98 if (!m_cb->GetEventHandler()->ProcessEvent( event ))
99 {
100 wxWindow *parent = GetParent();
101 while ( parent && !parent->IsTopLevel() && parent->GetDefaultItem() == NULL )
102 parent = parent->GetParent() ;
103
104 if ( parent && parent->GetDefaultItem() )
105 {
106 wxButton *def = wxDynamicCast(parent->GetDefaultItem(), wxButton);
107 if ( def && def->IsEnabled() )
108 {
109 wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
110 event.SetEventObject(def);
111 def->Command(event);
112 }
113 }
114
115 return;
116 }
117 }
118
119 event.Skip();
120 }
121
122 void OnKeyUp( wxKeyEvent& event )
123 {
124 event.SetEventObject(m_cb);
125 event.SetId(m_cb->GetId());
126 if (! m_cb->GetEventHandler()->ProcessEvent(event))
127 event.Skip();
128 }
129
130 void OnKeyDown( wxKeyEvent& event )
131 {
132 event.SetEventObject(m_cb);
133 event.SetId(m_cb->GetId());
134 if (! m_cb->GetEventHandler()->ProcessEvent(event))
135 event.Skip();
136 }
137
138 void OnText( wxCommandEvent& event )
139 {
140 event.SetEventObject(m_cb);
141 event.SetId(m_cb->GetId());
142 if (! m_cb->GetEventHandler()->ProcessEvent(event))
143 event.Skip();
144 }
145
146 private:
147 wxComboBox *m_cb;
148
149 DECLARE_EVENT_TABLE()
150 };
151
152 BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl)
153 EVT_KEY_DOWN(wxComboBoxText::OnKeyDown)
154 EVT_CHAR(wxComboBoxText::OnChar)
155 EVT_KEY_UP(wxComboBoxText::OnKeyUp)
156 EVT_TEXT(-1, wxComboBoxText::OnText)
157 END_EVENT_TABLE()
158
159 class wxComboBoxChoice : public wxChoice
160 {
161 public:
162 wxComboBoxChoice( wxComboBox *cb, int style )
163 : wxChoice( cb , 1 , wxDefaultPosition , wxDefaultSize , 0 , NULL , style & (wxCB_SORT) )
164 {
165 m_cb = cb;
166 }
167
168 int GetPopupWidth() const
169 {
170 switch ( GetWindowVariant() )
171 {
172 case wxWINDOW_VARIANT_NORMAL :
173 case wxWINDOW_VARIANT_LARGE :
174 return 24 ;
175
176 default :
177 return 21 ;
178 }
179 }
180
181 protected:
182 void OnChoice( wxCommandEvent& e )
183 {
184 wxString s = e.GetString();
185
186 m_cb->DelegateChoice( s );
187 wxCommandEvent event2(wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() );
188 event2.SetInt(m_cb->GetSelection());
189 event2.SetEventObject(m_cb);
190 event2.SetString(m_cb->GetStringSelection());
191 m_cb->ProcessCommand(event2);
192
193 // For consistency with MSW and GTK, also send a text updated event
194 // After all, the text is updated when a selection is made
195 wxCommandEvent TextEvent( wxEVT_COMMAND_TEXT_UPDATED, m_cb->GetId() );
196 TextEvent.SetString( m_cb->GetStringSelection() );
197 TextEvent.SetEventObject( m_cb );
198 m_cb->ProcessCommand( TextEvent );
199 }
200
201 virtual wxSize DoGetBestSize() const
202 {
203 wxSize sz = wxChoice::DoGetBestSize() ;
204 if (! m_cb->HasFlag(wxCB_READONLY) )
205 sz.x = GetPopupWidth() ;
206
207 return sz ;
208 }
209
210 private:
211 wxComboBox *m_cb;
212
213 friend class wxComboBox;
214
215 DECLARE_EVENT_TABLE()
216 };
217
218 BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
219 EVT_CHOICE(-1, wxComboBoxChoice::OnChoice)
220 END_EVENT_TABLE()
221
222 wxComboBox::~wxComboBox()
223 {
224 // delete client objects
225 FreeData();
226
227 // delete the controls now, don't leave them alive even though they would
228 // still be eventually deleted by our parent - but it will be too late, the
229 // user code expects them to be gone now
230 if (m_text != NULL)
231 {
232 delete m_text;
233 m_text = NULL;
234 }
235
236 if (m_choice != NULL)
237 {
238 delete m_choice;
239 m_choice = NULL;
240 }
241 }
242
243 // ----------------------------------------------------------------------------
244 // geometry
245 // ----------------------------------------------------------------------------
246
247 wxSize wxComboBox::DoGetBestSize() const
248 {
249 if (!m_choice && !m_text)
250 return GetSize();
251
252 wxSize size = m_choice->GetBestSize();
253
254 if ( m_text != NULL )
255 {
256 wxSize sizeText = m_text->GetBestSize();
257 if (sizeText.y > size.y)
258 size.y = sizeText.y;
259
260 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
261 size.x += TEXTFOCUSBORDER ;
262 size.y += 2 * TEXTFOCUSBORDER ;
263 }
264 else
265 {
266 // clipping is too tight
267 size.y += 1 ;
268 }
269
270 return size;
271 }
272
273 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
274 {
275 wxControl::DoMoveWindow( x, y, width , height );
276
277 if ( m_text == NULL )
278 {
279 // we might not be fully constructed yet, therefore watch out...
280 if ( m_choice )
281 m_choice->SetSize(0, 0 , width, -1);
282 }
283 else
284 {
285 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
286 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
287
288 // put it at an inset of 1 to have outer area shadows drawn as well
289 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
290 }
291 }
292
293 // ----------------------------------------------------------------------------
294 // operations forwarded to the subcontrols
295 // ----------------------------------------------------------------------------
296
297 bool wxComboBox::Enable(bool enable)
298 {
299 if ( !wxControl::Enable(enable) )
300 return false;
301
302 if (m_text)
303 m_text->Enable(enable);
304
305 return true;
306 }
307
308 bool wxComboBox::Show(bool show)
309 {
310 if ( !wxControl::Show(show) )
311 return false;
312
313 return true;
314 }
315
316 void wxComboBox::SetFocus()
317 {
318 if ( m_text != NULL)
319 m_text->SetFocus();
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 bool wxComboBox::Create(wxWindow *parent,
333 wxWindowID id,
334 const wxString& value,
335 const wxPoint& pos,
336 const wxSize& size,
337 const wxArrayString& choices,
338 long style,
339 const wxValidator& validator,
340 const wxString& name)
341 {
342 wxCArrayString chs( choices );
343
344 return Create( parent, id, value, pos, size, chs.GetCount(),
345 chs.GetStrings(), style, validator, name );
346 }
347
348 bool wxComboBox::Create(wxWindow *parent,
349 wxWindowID id,
350 const wxString& value,
351 const wxPoint& pos,
352 const wxSize& size,
353 int n,
354 const wxString choices[],
355 long style,
356 const wxValidator& validator,
357 const wxString& name)
358 {
359 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
360 validator, name) )
361 {
362 return false;
363 }
364
365 m_choice = new wxComboBoxChoice(this, style );
366 wxSize csize = size;
367 if ( style & wxCB_READONLY )
368 {
369 m_text = NULL;
370 }
371 else
372 {
373 m_text = new wxComboBoxText(this);
374 if ( size.y == -1 )
375 {
376 csize.y = m_text->GetSize().y ;
377 csize.y += 2 * TEXTFOCUSBORDER ;
378 }
379 }
380
381 DoSetSize(pos.x, pos.y, csize.x, csize.y);
382
383 for ( int i = 0 ; i < n ; i++ )
384 {
385 m_choice->DoAppend( choices[ i ] );
386 }
387
388 // Needed because it is a wxControlWithItems
389 SetBestSize(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 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 // TODO
464 }
465
466 void wxComboBox::SetInsertionPointEnd()
467 {
468 // TODO
469 }
470
471 long wxComboBox::GetInsertionPoint() const
472 {
473 // TODO
474 return 0;
475 }
476
477 wxTextPos wxComboBox::GetLastPosition() const
478 {
479 // TODO
480 return 0;
481 }
482
483 void wxComboBox::Replace(long from, long to, const wxString& value)
484 {
485 // TODO
486 }
487
488 void wxComboBox::Remove(long from, long to)
489 {
490 // TODO
491 }
492
493 void wxComboBox::SetSelection(long from, long to)
494 {
495 // TODO
496 }
497
498 int wxComboBox::DoAppend(const wxString& item)
499 {
500 return m_choice->DoAppend( item ) ;
501 }
502
503 int wxComboBox::DoInsert(const wxString& item, int pos)
504 {
505 return m_choice->DoInsert( item , pos ) ;
506 }
507
508 void wxComboBox::DoSetItemClientData(int n, void* clientData)
509 {
510 return m_choice->DoSetItemClientData( n , clientData ) ;
511 }
512
513 void* wxComboBox::DoGetItemClientData(int n) const
514 {
515 return m_choice->DoGetItemClientData( n ) ;
516 }
517
518 void wxComboBox::DoSetItemClientObject(int n, wxClientData* clientData)
519 {
520 return m_choice->DoSetItemClientObject( n , clientData ) ;
521 }
522
523 wxClientData* wxComboBox::DoGetItemClientObject(int n) const
524 {
525 return m_choice->DoGetItemClientObject( n ) ;
526 }
527
528 void wxComboBox::FreeData()
529 {
530 if ( HasClientObjectData() )
531 {
532 size_t count = GetCount();
533 for ( size_t n = 0; n < count; n++ )
534 {
535 SetClientObject( n, NULL );
536 }
537 }
538 }
539
540 void wxComboBox::Delete(int n)
541 {
542 // force client object deletion
543 if( HasClientObjectData() )
544 SetClientObject( n, NULL );
545 m_choice->Delete( n );
546 }
547
548 void wxComboBox::Clear()
549 {
550 FreeData();
551 m_choice->Clear();
552 }
553
554 int wxComboBox::GetSelection() const
555 {
556 return m_choice->GetSelection();
557 }
558
559 void wxComboBox::SetSelection(int n)
560 {
561 m_choice->SetSelection( n );
562
563 if ( m_text != NULL )
564 m_text->SetValue( GetString( n ) );
565 }
566
567 int wxComboBox::FindString(const wxString& s, bool bCase) const
568 {
569 return m_choice->FindString( s, bCase );
570 }
571
572 wxString wxComboBox::GetString(int n) const
573 {
574 return m_choice->GetString( n );
575 }
576
577 wxString wxComboBox::GetStringSelection() const
578 {
579 int sel = GetSelection();
580 if (sel > -1)
581 return wxString(this->GetString (sel));
582 else
583 return wxEmptyString;
584 }
585
586 void wxComboBox::SetString(int n, const wxString& s)
587 {
588 m_choice->SetString( n , s ) ;
589 }
590
591 bool wxComboBox::IsEditable() const
592 {
593 return m_text != NULL && !HasFlag(wxCB_READONLY);
594 }
595
596 void wxComboBox::Undo()
597 {
598 if (m_text != NULL)
599 m_text->Undo();
600 }
601
602 void wxComboBox::Redo()
603 {
604 if (m_text != NULL)
605 m_text->Redo();
606 }
607
608 void wxComboBox::SelectAll()
609 {
610 if (m_text != NULL)
611 m_text->SelectAll();
612 }
613
614 bool wxComboBox::CanCopy() const
615 {
616 if (m_text != NULL)
617 return m_text->CanCopy();
618 else
619 return false;
620 }
621
622 bool wxComboBox::CanCut() const
623 {
624 if (m_text != NULL)
625 return m_text->CanCut();
626 else
627 return false;
628 }
629
630 bool wxComboBox::CanPaste() const
631 {
632 if (m_text != NULL)
633 return m_text->CanPaste();
634 else
635 return false;
636 }
637
638 bool wxComboBox::CanUndo() const
639 {
640 if (m_text != NULL)
641 return m_text->CanUndo();
642 else
643 return false;
644 }
645
646 bool wxComboBox::CanRedo() const
647 {
648 if (m_text != NULL)
649 return m_text->CanRedo();
650 else
651 return false;
652 }
653
654 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
655 {
656 /*
657 For consistency with other platforms, clicking in the text area does not constitute a selection
658 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
659 event.SetInt(GetSelection());
660 event.SetEventObject(this);
661 event.SetString(GetStringSelection());
662 ProcessCommand(event);
663 */
664
665 return noErr ;
666 }
667
668 #endif