]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/combobox.cpp
remove the drag-and-drop compatibility hack for extracting the embedded size of the...
[wxWidgets.git] / src / mac / carbon / combobox.cpp
... / ...
CommitLineData
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
27IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
28
29WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox, wxControl)
30
31BEGIN_EVENT_TABLE(wxComboBox, wxControl)
32 WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox)
33END_EVENT_TABLE()
34
35
36static int nextPopUpMenuId = 1000 ;
37
38MenuHandle 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.
55static const wxCoord MARGIN = 4;
56// this is the border a focus rect on OSX is needing
57static const int TEXTFOCUSBORDER = 3 ;
58#else
59static const wxCoord MARGIN = 2;
60static const int TEXTFOCUSBORDER = 0 ;
61#endif
62
63
64// ----------------------------------------------------------------------------
65// wxComboBoxText: text control forwards events to combobox
66// ----------------------------------------------------------------------------
67
68class wxComboBoxText : public wxTextCtrl
69{
70public:
71 wxComboBoxText( wxComboBox * cb )
72 : wxTextCtrl( cb , 1 )
73 {
74 m_cb = cb;
75 SetTriggerOnSetValue( false );
76 }
77
78protected:
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
155private:
156 wxComboBox *m_cb;
157
158 DECLARE_EVENT_TABLE()
159};
160
161BEGIN_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)
166END_EVENT_TABLE()
167
168class wxComboBoxChoice : public wxChoice
169{
170public:
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
190protected:
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
219private:
220 wxComboBox *m_cb;
221
222 friend class wxComboBox;
223
224 DECLARE_EVENT_TABLE()
225};
226
227BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
228 EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice)
229END_EVENT_TABLE()
230
231wxComboBox::~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
253wxSize 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
279void 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
303bool 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
314bool wxComboBox::Show(bool show)
315{
316 if ( !wxControl::Show(show) )
317 return false;
318
319 return true;
320}
321
322void wxComboBox::DelegateTextChanged( const wxString& value )
323{
324 SetStringSelection( value );
325}
326
327void wxComboBox::DelegateChoice( const wxString& value )
328{
329 SetStringSelection( value );
330}
331
332void wxComboBox::Init()
333{
334 WX_INIT_CONTROL_CONTAINER();
335}
336
337bool 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
351bool 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
395wxString 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
407unsigned int wxComboBox::GetCount() const
408{
409 return m_choice->GetCount() ;
410}
411
412void 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
422void wxComboBox::Copy()
423{
424 if ( m_text != NULL )
425 m_text->Copy();
426}
427
428void wxComboBox::Cut()
429{
430 if ( m_text != NULL )
431 m_text->Cut();
432}
433
434void wxComboBox::Paste()
435{
436 if ( m_text != NULL )
437 m_text->Paste();
438}
439
440void 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
461void wxComboBox::SetInsertionPoint(long pos)
462{
463 if ( m_text )
464 m_text->SetInsertionPoint(pos);
465}
466
467void wxComboBox::SetInsertionPointEnd()
468{
469 if ( m_text )
470 m_text->SetInsertionPointEnd();
471}
472
473long wxComboBox::GetInsertionPoint() const
474{
475 if ( m_text )
476 return m_text->GetInsertionPoint();
477 return 0;
478}
479
480wxTextPos wxComboBox::GetLastPosition() const
481{
482 if ( m_text )
483 return m_text->GetLastPosition();
484 return 0;
485}
486
487void wxComboBox::Replace(long from, long to, const wxString& value)
488{
489 if ( m_text )
490 m_text->Replace(from,to,value);
491}
492
493void wxComboBox::Remove(long from, long to)
494{
495 if ( m_text )
496 m_text->Remove(from,to);
497}
498
499void wxComboBox::SetSelection(long from, long to)
500{
501 if ( m_text )
502 m_text->SetSelection(from,to);
503}
504
505int wxComboBox::DoInsertItems(const wxArrayStringsAdapter& items,
506 unsigned int pos,
507 void **clientData,
508 wxClientDataType type)
509{
510 // wxItemContainer should probably be doing it itself but usually this is
511 // not necessary as the derived class DoInsertItems() calls
512 // AssignNewItemClientData() which initializes m_clientDataItemsType
513 // correctly; however as we just forward everything to wxChoice, we need to
514 // do it ourselves
515 //
516 // also notice that we never use wxClientData_Object with wxChoice as we
517 // don't want it to delete the data -- we will
518 int rc = m_choice->DoInsertItems(items, pos, clientData,
519 clientData ? wxClientData_Void
520 : wxClientData_None) ;
521 if ( rc != wxNOT_FOUND )
522 {
523 if ( !HasClientData() && type != wxClientData_None )
524 m_clientDataItemsType = type;
525 }
526
527 return rc;
528}
529
530void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
531{
532 return m_choice->SetClientData( n , clientData ) ;
533}
534
535void* wxComboBox::DoGetItemClientData(unsigned int n) const
536{
537 return m_choice->GetClientData( n ) ;
538}
539
540void wxComboBox::DoDeleteOneItem(unsigned int n)
541{
542 m_choice->Delete( n );
543}
544
545void wxComboBox::DoClear()
546{
547 m_choice->Clear();
548}
549
550int wxComboBox::GetSelection() const
551{
552 return m_choice->GetSelection();
553}
554
555void wxComboBox::SetSelection(int n)
556{
557 m_choice->SetSelection( n );
558
559 if ( m_text != NULL )
560 m_text->SetValue(n != wxNOT_FOUND ? GetString(n) : wxString(wxEmptyString));
561}
562
563int wxComboBox::FindString(const wxString& s, bool bCase) const
564{
565 return m_choice->FindString( s, bCase );
566}
567
568wxString wxComboBox::GetString(unsigned int n) const
569{
570 return m_choice->GetString( n );
571}
572
573wxString wxComboBox::GetStringSelection() const
574{
575 int sel = GetSelection();
576 if (sel != wxNOT_FOUND)
577 return wxString(this->GetString((unsigned int)sel));
578 else
579 return wxEmptyString;
580}
581
582void wxComboBox::SetString(unsigned int n, const wxString& s)
583{
584 m_choice->SetString( n , s );
585}
586
587bool wxComboBox::IsEditable() const
588{
589 return m_text != NULL && !HasFlag(wxCB_READONLY);
590}
591
592void wxComboBox::Undo()
593{
594 if (m_text != NULL)
595 m_text->Undo();
596}
597
598void wxComboBox::Redo()
599{
600 if (m_text != NULL)
601 m_text->Redo();
602}
603
604void wxComboBox::SelectAll()
605{
606 if (m_text != NULL)
607 m_text->SelectAll();
608}
609
610bool wxComboBox::CanCopy() const
611{
612 if (m_text != NULL)
613 return m_text->CanCopy();
614 else
615 return false;
616}
617
618bool wxComboBox::CanCut() const
619{
620 if (m_text != NULL)
621 return m_text->CanCut();
622 else
623 return false;
624}
625
626bool wxComboBox::CanPaste() const
627{
628 if (m_text != NULL)
629 return m_text->CanPaste();
630 else
631 return false;
632}
633
634bool wxComboBox::CanUndo() const
635{
636 if (m_text != NULL)
637 return m_text->CanUndo();
638 else
639 return false;
640}
641
642bool wxComboBox::CanRedo() const
643{
644 if (m_text != NULL)
645 return m_text->CanRedo();
646 else
647 return false;
648}
649
650wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
651{
652/*
653 For consistency with other platforms, clicking in the text area does not constitute a selection
654 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
655 event.SetInt(GetSelection());
656 event.SetEventObject(this);
657 event.SetString(GetStringSelection());
658 ProcessCommand(event);
659*/
660
661 return noErr ;
662}
663
664#endif // wxUSE_COMBOBOX