]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/combobox.cpp
minor reformat
[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 DECLARE_EVENT_TABLE()
214 };
215
216 BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice)
217 EVT_CHOICE(-1, wxComboBoxChoice::OnChoice)
218 END_EVENT_TABLE()
219
220 wxComboBox::~wxComboBox()
221 {
222 // delete client objects
223 FreeData();
224
225 // delete the controls now, don't leave them alive even though they would
226 // still be eventually deleted by our parent - but it will be too late, the
227 // user code expects them to be gone now
228 if (m_text != NULL)
229 {
230 delete m_text;
231 m_text = NULL;
232 }
233
234 if (m_choice != NULL)
235 {
236 delete m_choice;
237 m_choice = NULL;
238 }
239 }
240
241 // ----------------------------------------------------------------------------
242 // geometry
243 // ----------------------------------------------------------------------------
244
245 wxSize wxComboBox::DoGetBestSize() const
246 {
247 if (!m_choice && !m_text)
248 return GetSize();
249
250 wxSize size = m_choice->GetBestSize();
251
252 if ( m_text != NULL )
253 {
254 wxSize sizeText = m_text->GetBestSize();
255 if (sizeText.y > size.y)
256 size.y = sizeText.y;
257
258 size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN;
259 size.x += TEXTFOCUSBORDER ;
260 size.y += 2 * TEXTFOCUSBORDER ;
261 }
262 else
263 {
264 // clipping is too tight
265 size.y += 1 ;
266 }
267
268 return size;
269 }
270
271 void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
272 {
273 wxControl::DoMoveWindow( x, y, width , height );
274
275 if ( m_text == NULL )
276 {
277 // we might not be fully constructed yet, therefore watch out...
278 if ( m_choice )
279 m_choice->SetSize(0, 0 , width, -1);
280 }
281 else
282 {
283 wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN;
284 m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1);
285
286 // put it at an inset of 1 to have outer area shadows drawn as well
287 m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1);
288 }
289 }
290
291 // ----------------------------------------------------------------------------
292 // operations forwarded to the subcontrols
293 // ----------------------------------------------------------------------------
294
295 bool wxComboBox::Enable(bool enable)
296 {
297 if ( !wxControl::Enable(enable) )
298 return false;
299
300 if (m_text)
301 m_text->Enable(enable);
302
303 return true;
304 }
305
306 bool wxComboBox::Show(bool show)
307 {
308 if ( !wxControl::Show(show) )
309 return false;
310
311 return true;
312 }
313
314 void wxComboBox::SetFocus()
315 {
316 if ( m_text != NULL)
317 m_text->SetFocus();
318 }
319
320 void wxComboBox::DelegateTextChanged( const wxString& value )
321 {
322 SetStringSelection( value );
323 }
324
325 void wxComboBox::DelegateChoice( const wxString& value )
326 {
327 SetStringSelection( value );
328 }
329
330 bool wxComboBox::Create(wxWindow *parent,
331 wxWindowID id,
332 const wxString& value,
333 const wxPoint& pos,
334 const wxSize& size,
335 const wxArrayString& choices,
336 long style,
337 const wxValidator& validator,
338 const wxString& name)
339 {
340 wxCArrayString chs( choices );
341
342 return Create( parent, id, value, pos, size, chs.GetCount(),
343 chs.GetStrings(), style, validator, name );
344 }
345
346 bool wxComboBox::Create(wxWindow *parent,
347 wxWindowID id,
348 const wxString& value,
349 const wxPoint& pos,
350 const wxSize& size,
351 int n,
352 const wxString choices[],
353 long style,
354 const wxValidator& validator,
355 const wxString& name)
356 {
357 if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style ,
358 validator, name) )
359 {
360 return false;
361 }
362
363 m_choice = new wxComboBoxChoice(this, style );
364 wxSize csize = size;
365 if ( style & wxCB_READONLY )
366 {
367 m_text = NULL;
368 }
369 else
370 {
371 m_text = new wxComboBoxText(this);
372 if ( size.y == -1 )
373 {
374 csize.y = m_text->GetSize().y ;
375 csize.y += 2 * TEXTFOCUSBORDER ;
376 }
377 }
378
379 DoSetSize(pos.x, pos.y, csize.x, csize.y);
380
381 for ( int i = 0 ; i < n ; i++ )
382 {
383 m_choice->DoAppend( choices[ i ] );
384 }
385
386 // Needed because it is a wxControlWithItems
387 SetBestSize(size);
388 SetStringSelection(value);
389
390 return true;
391 }
392
393 wxString wxComboBox::GetValue() const
394 {
395 wxString result;
396
397 if ( m_text == NULL )
398 result = m_choice->GetString( m_choice->GetSelection() );
399 else
400 result = m_text->GetValue();
401
402 return result;
403 }
404
405 int wxComboBox::GetCount() const
406 {
407 return m_choice->GetCount() ;
408 }
409
410 void wxComboBox::SetValue(const wxString& value)
411 {
412 if ( HasFlag(wxCB_READONLY) )
413 SetStringSelection( value ) ;
414 else
415 m_text->SetValue( value );
416 }
417
418 // Clipboard operations
419
420 void wxComboBox::Copy()
421 {
422 if ( m_text != NULL )
423 m_text->Copy();
424 }
425
426 void wxComboBox::Cut()
427 {
428 if ( m_text != NULL )
429 m_text->Cut();
430 }
431
432 void wxComboBox::Paste()
433 {
434 if ( m_text != NULL )
435 m_text->Paste();
436 }
437
438 void wxComboBox::SetEditable(bool editable)
439 {
440 if ( ( m_text == NULL ) && editable )
441 {
442 m_text = new wxComboBoxText( this );
443 }
444 else if ( ( m_text != NULL ) && !editable )
445 {
446 delete m_text;
447 m_text = NULL;
448 }
449
450 int currentX, currentY;
451 GetPosition( &currentX, &currentY );
452
453 int currentW, currentH;
454 GetSize( &currentW, &currentH );
455
456 DoMoveWindow( currentX, currentY, currentW, currentH );
457 }
458
459 void wxComboBox::SetInsertionPoint(long pos)
460 {
461 // TODO
462 }
463
464 void wxComboBox::SetInsertionPointEnd()
465 {
466 // TODO
467 }
468
469 long wxComboBox::GetInsertionPoint() const
470 {
471 // TODO
472 return 0;
473 }
474
475 wxTextPos wxComboBox::GetLastPosition() const
476 {
477 // TODO
478 return 0;
479 }
480
481 void wxComboBox::Replace(long from, long to, const wxString& value)
482 {
483 // TODO
484 }
485
486 void wxComboBox::Remove(long from, long to)
487 {
488 // TODO
489 }
490
491 void wxComboBox::SetSelection(long from, long to)
492 {
493 // TODO
494 }
495
496 int wxComboBox::DoAppend(const wxString& item)
497 {
498 return m_choice->DoAppend( item ) ;
499 }
500
501 int wxComboBox::DoInsert(const wxString& item, int pos)
502 {
503 return m_choice->DoInsert( item , pos ) ;
504 }
505
506 void wxComboBox::DoSetItemClientData(int n, void* clientData)
507 {
508 return m_choice->DoSetItemClientData( n , clientData ) ;
509 }
510
511 void* wxComboBox::DoGetItemClientData(int n) const
512 {
513 return m_choice->DoGetItemClientData( n ) ;
514 }
515
516 void wxComboBox::DoSetItemClientObject(int n, wxClientData* clientData)
517 {
518 return m_choice->DoSetItemClientObject( n , clientData ) ;
519 }
520
521 wxClientData* wxComboBox::DoGetItemClientObject(int n) const
522 {
523 return m_choice->DoGetItemClientObject( n ) ;
524 }
525
526 void wxComboBox::FreeData()
527 {
528 if ( HasClientObjectData() )
529 {
530 size_t count = GetCount();
531 for ( size_t n = 0; n < count; n++ )
532 {
533 SetClientObject( n, NULL );
534 }
535 }
536 }
537
538 void wxComboBox::Delete(int n)
539 {
540 // force client object deletion
541 if( HasClientObjectData() )
542 SetClientObject( n, NULL );
543 m_choice->Delete( n );
544 }
545
546 void wxComboBox::Clear()
547 {
548 FreeData();
549 m_choice->Clear();
550 }
551
552 int wxComboBox::GetSelection() const
553 {
554 return m_choice->GetSelection();
555 }
556
557 void wxComboBox::SetSelection(int n)
558 {
559 m_choice->SetSelection( n );
560
561 if ( m_text != NULL )
562 m_text->SetValue( GetString( n ) );
563 }
564
565 int wxComboBox::FindString(const wxString& s, bool bCase) const
566 {
567 return m_choice->FindString( s, bCase );
568 }
569
570 wxString wxComboBox::GetString(int n) const
571 {
572 return m_choice->GetString( n );
573 }
574
575 wxString wxComboBox::GetStringSelection() const
576 {
577 int sel = GetSelection();
578 if (sel > -1)
579 return wxString(this->GetString (sel));
580 else
581 return wxEmptyString;
582 }
583
584 void wxComboBox::SetString(int n, const wxString& s)
585 {
586 m_choice->SetString( n , s ) ;
587 }
588
589 bool wxComboBox::IsEditable() const
590 {
591 return m_text != NULL && !HasFlag(wxCB_READONLY);
592 }
593
594 void wxComboBox::Undo()
595 {
596 if (m_text != NULL)
597 m_text->Undo();
598 }
599
600 void wxComboBox::Redo()
601 {
602 if (m_text != NULL)
603 m_text->Redo();
604 }
605
606 void wxComboBox::SelectAll()
607 {
608 if (m_text != NULL)
609 m_text->SelectAll();
610 }
611
612 bool wxComboBox::CanCopy() const
613 {
614 if (m_text != NULL)
615 return m_text->CanCopy();
616 else
617 return false;
618 }
619
620 bool wxComboBox::CanCut() const
621 {
622 if (m_text != NULL)
623 return m_text->CanCut();
624 else
625 return false;
626 }
627
628 bool wxComboBox::CanPaste() const
629 {
630 if (m_text != NULL)
631 return m_text->CanPaste();
632 else
633 return false;
634 }
635
636 bool wxComboBox::CanUndo() const
637 {
638 if (m_text != NULL)
639 return m_text->CanUndo();
640 else
641 return false;
642 }
643
644 bool wxComboBox::CanRedo() const
645 {
646 if (m_text != NULL)
647 return m_text->CanRedo();
648 else
649 return false;
650 }
651
652 wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) )
653 {
654 /*
655 For consistency with other platforms, clicking in the text area does not constitute a selection
656 wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId );
657 event.SetInt(GetSelection());
658 event.SetEventObject(this);
659 event.SetString(GetStringSelection());
660 ProcessCommand(event);
661 */
662
663 return noErr ;
664 }
665
666 #endif