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