1///////////////////////////////////////////////////////////////////////////// 2// Name: src/osx/cocoa/combobox.mm 3// Purpose: wxChoice 4// Author: Stefan Csomor 5// Modified by: 6// Created: 1998-01-01 7// Copyright: (c) Stefan Csomor 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12 13#if wxUSE_COMBOBOX 14 15#include "wx/combobox.h" 16#include "wx/evtloop.h" 17 18#ifndef WX_PRECOMP 19 #include "wx/menu.h" 20 #include "wx/dcclient.h" 21#endif 22 23#include "wx/osx/cocoa/private/textimpl.h" 24 25// work in progress 26 27@interface wxNSTableDataSource : NSObject wxOSX_10_6_AND_LATER(<NSComboBoxDataSource>) 28{ 29 wxNSComboBoxControl* impl; 30} 31 32- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox; 33- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index; 34 35@end 36 37 38@implementation wxNSComboBox 39 40+ (void)initialize 41{ 42 static BOOL initialized = NO; 43 if (!initialized) 44 { 45 initialized = YES; 46 wxOSXCocoaClassAddWXMethods( self ); 47 } 48} 49 50- (void) dealloc 51{ 52 [fieldEditor release]; 53 [super dealloc]; 54} 55 56// Over-riding NSComboBox onKeyDown method doesn't work for key events. 57// Ensure that we can use our own wxNSTextFieldEditor to catch key events. 58// See windowWillReturnFieldEditor in nonownedwnd.mm. 59// Key events will be caught and handled via wxNSTextFieldEditor onkey... 60// methods in textctrl.mm. 61 62- (void) setFieldEditor:(wxNSTextFieldEditor*) editor 63{ 64 if ( editor != fieldEditor ) 65 { 66 [editor retain]; 67 [fieldEditor release]; 68 fieldEditor = editor; 69 } 70} 71 72- (wxNSTextFieldEditor*) fieldEditor 73{ 74 return fieldEditor; 75} 76 77- (void)controlTextDidChange:(NSNotification *)aNotification 78{ 79 wxUnusedVar(aNotification); 80 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 81 if ( impl && impl->ShouldSendEvents() ) 82 { 83 wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); 84 if ( wxpeer ) { 85 wxCommandEvent event(wxEVT_TEXT, wxpeer->GetId()); 86 event.SetEventObject( wxpeer ); 87 event.SetString( static_cast<wxComboBox*>(wxpeer)->GetValue() ); 88 wxpeer->HandleWindowEvent( event ); 89 } 90 } 91} 92 93- (void)controlTextDidEndEditing:(NSNotification *) aNotification 94{ 95 wxUnusedVar(aNotification); 96 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 97 if ( impl ) 98 { 99 wxNSTextFieldControl* timpl = dynamic_cast<wxNSTextFieldControl*>(impl); 100 if ( timpl ) 101 timpl->UpdateInternalSelectionFromEditor(fieldEditor); 102 impl->DoNotifyFocusLost(); 103 } 104} 105 106- (void)comboBoxWillPopUp:(NSNotification *)notification 107{ 108 wxUnusedVar(notification); 109 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 110 if( impl && impl->ShouldSendEvents() ) 111 { 112 wxComboBox* wxpeer = static_cast<wxComboBox*>(impl->GetWXPeer()); 113 if( wxpeer ) 114 { 115 wxCommandEvent event(wxEVT_COMBOBOX_DROPDOWN, wxpeer->GetId()); 116 event.SetEventObject( wxpeer ); 117 wxpeer->GetEventHandler()->ProcessEvent( event ); 118 } 119 } 120} 121 122- (void)comboBoxWillDismiss:(NSNotification *)notification 123{ 124 wxUnusedVar(notification); 125 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 126 if( impl && impl->ShouldSendEvents() ) 127 { 128 wxComboBox* wxpeer = static_cast<wxComboBox*>(impl->GetWXPeer()); 129 if( wxpeer ) 130 { 131 wxCommandEvent event(wxEVT_COMBOBOX_CLOSEUP, wxpeer->GetId()); 132 event.SetEventObject( wxpeer ); 133 wxpeer->GetEventHandler()->ProcessEvent( event ); 134 } 135 } 136} 137 138- (void)comboBoxSelectionDidChange:(NSNotification *)notification 139{ 140 wxUnusedVar(notification); 141 wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); 142 if ( impl && impl->ShouldSendEvents()) 143 { 144 wxComboBox* wxpeer = static_cast<wxComboBox*>(impl->GetWXPeer()); 145 if ( wxpeer ) { 146 const int sel = wxpeer->GetSelection(); 147 148 wxCommandEvent event(wxEVT_COMBOBOX, wxpeer->GetId()); 149 event.SetEventObject( wxpeer ); 150 event.SetInt( sel ); 151 event.SetString( wxpeer->GetString(sel) ); 152 // For some reason, wxComboBox::GetValue will not return the newly selected item 153 // while we're inside this callback, so use AddPendingEvent to make sure 154 // GetValue() returns the right value. 155 156 wxpeer->GetEventHandler()->AddPendingEvent( event ); 157 158 } 159 } 160} 161@end 162 163wxNSComboBoxControl::wxNSComboBoxControl( wxComboBox *wxPeer, WXWidget w ) 164 : wxNSTextFieldControl(wxPeer, wxPeer, w) 165{ 166 m_comboBox = (NSComboBox*)w; 167} 168 169wxNSComboBoxControl::~wxNSComboBoxControl() 170{ 171} 172 173void wxNSComboBoxControl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd) 174{ 175 // NSComboBox has its own event loop, which reacts very badly to our synthetic 176 // events used to signal when a wxEvent is posted, so during that time we switch 177 // the wxEventLoop::WakeUp implementation to a lower-level version 178 179 bool reset = false; 180 wxEventLoop* const loop = (wxEventLoop*) wxEventLoopBase::GetActive(); 181 182 if ( loop != NULL && [event type] == NSLeftMouseDown ) 183 { 184 reset = true; 185 loop->OSXUseLowLevelWakeup(true); 186 } 187 188 wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd]; 189 superimpl(slf, (SEL)_cmd, event); 190 191 if ( reset ) 192 { 193 loop->OSXUseLowLevelWakeup(false); 194 } 195} 196 197int wxNSComboBoxControl::GetSelectedItem() const 198{ 199 return [m_comboBox indexOfSelectedItem]; 200} 201 202void wxNSComboBoxControl::SetSelectedItem(int item) 203{ 204 SendEvents(false); 205 206 if ( item != wxNOT_FOUND ) 207 { 208 wxASSERT_MSG( item >= 0 && item < [m_comboBox numberOfItems], 209 "Inavlid item index." ); 210 [m_comboBox selectItemAtIndex: item]; 211 } 212 else // remove current selection (if we have any) 213 { 214 const int sel = GetSelectedItem(); 215 if ( sel != wxNOT_FOUND ) 216 [m_comboBox deselectItemAtIndex:sel]; 217 } 218 219 SendEvents(true); 220} 221 222int wxNSComboBoxControl::GetNumberOfItems() const 223{ 224 return [m_comboBox numberOfItems]; 225} 226 227void wxNSComboBoxControl::InsertItem(int pos, const wxString& item) 228{ 229 [m_comboBox insertItemWithObjectValue:wxCFStringRef( item , m_wxPeer->GetFont().GetEncoding() ).AsNSString() atIndex:pos]; 230} 231 232void wxNSComboBoxControl::RemoveItem(int pos) 233{ 234 SendEvents(false); 235 [m_comboBox removeItemAtIndex:pos]; 236 SendEvents(true); 237} 238 239void wxNSComboBoxControl::Clear() 240{ 241 SendEvents(false); 242 [m_comboBox removeAllItems]; 243 [m_comboBox setStringValue:@""]; 244 SendEvents(true); 245} 246 247wxString wxNSComboBoxControl::GetStringAtIndex(int pos) const 248{ 249 return wxCFStringRef::AsString([m_comboBox itemObjectValueAtIndex:pos], m_wxPeer->GetFont().GetEncoding()); 250} 251 252int wxNSComboBoxControl::FindString(const wxString& text) const 253{ 254 NSInteger nsresult = [m_comboBox indexOfItemWithObjectValue:wxCFStringRef( text , m_wxPeer->GetFont().GetEncoding() ).AsNSString()]; 255 256 int result; 257 if (nsresult == NSNotFound) 258 result = wxNOT_FOUND; 259 else 260 result = (int) nsresult; 261 return result; 262} 263 264void wxNSComboBoxControl::Popup() 265{ 266 id ax = NSAccessibilityUnignoredDescendant(m_comboBox); 267 [ax accessibilitySetValue: [NSNumber numberWithBool: YES] forAttribute: NSAccessibilityExpandedAttribute]; 268} 269 270void wxNSComboBoxControl::Dismiss() 271{ 272 id ax = NSAccessibilityUnignoredDescendant(m_comboBox); 273 [ax accessibilitySetValue: [NSNumber numberWithBool: NO] forAttribute: NSAccessibilityExpandedAttribute]; 274} 275 276void wxNSComboBoxControl::SetEditable(bool editable) 277{ 278 // TODO: unfortunately this does not work, setEditable just means the same as CB_READONLY 279 // I don't see a way to access the text field directly 280 281 // Behavior NONE <- SELECTECTABLE 282 [m_comboBox setEditable:editable]; 283} 284 285wxWidgetImplType* wxWidgetImpl::CreateComboBox( wxComboBox* wxpeer, 286 wxWindowMac* WXUNUSED(parent), 287 wxWindowID WXUNUSED(id), 288 wxMenu* WXUNUSED(menu), 289 const wxPoint& pos, 290 const wxSize& size, 291 long style, 292 long WXUNUSED(extraStyle)) 293{ 294 NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ; 295 wxNSComboBox* v = [[wxNSComboBox alloc] initWithFrame:r]; 296 [v setNumberOfVisibleItems:13]; 297 if (style & wxCB_READONLY) 298 [v setEditable:NO]; 299 wxNSComboBoxControl* c = new wxNSComboBoxControl( wxpeer, v ); 300 return c; 301} 302 303wxSize wxComboBox::DoGetBestSize() const 304{ 305 int lbWidth = GetCount() > 0 ? 20 : 100; // some defaults 306 wxSize baseSize = wxWindow::DoGetBestSize(); 307 int lbHeight = baseSize.y; 308 int wLine; 309 310 { 311 wxClientDC dc(const_cast<wxComboBox*>(this)); 312 313 // Find the widest line 314 for(unsigned int i = 0; i < GetCount(); i++) 315 { 316 wxString str(GetString(i)); 317 318 wxCoord width, height ; 319 dc.GetTextExtent( str , &width, &height); 320 wLine = width ; 321 322 lbWidth = wxMax( lbWidth, wLine ) ; 323 } 324 325 // Add room for the popup arrow 326 lbWidth += 2 * lbHeight ; 327 } 328 329 return wxSize( lbWidth, lbHeight ); 330} 331 332#endif // wxUSE_COMBOBOX 333