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