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