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