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