PageRenderTime 31ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/ExtLibs/wxWidgets/src/osx/cocoa/window.mm

https://bitbucket.org/lennonchan/cafu
Objective C++ | 1609 lines | 1233 code | 263 blank | 113 comment | 198 complexity | 35e4693d257625b10977c4eab211d79f MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/osx/cocoa/window.mm
  3. // Purpose: widgets (non tlw) for cocoa
  4. // Author: Stefan Csomor
  5. // Modified by:
  6. // Created: 2008-06-20
  7. // RCS-ID: $Id$
  8. // Copyright: (c) Stefan Csomor
  9. // Licence: wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11. #include "wx/wxprec.h"
  12. #ifndef WX_PRECOMP
  13. #include "wx/dcclient.h"
  14. #include "wx/frame.h"
  15. #include "wx/log.h"
  16. #include "wx/textctrl.h"
  17. #include "wx/combobox.h"
  18. #endif
  19. #ifdef __WXMAC__
  20. #include "wx/osx/private.h"
  21. #endif
  22. #include "wx/evtloop.h"
  23. #if wxUSE_CARET
  24. #include "wx/caret.h"
  25. #endif
  26. #if wxUSE_DRAG_AND_DROP
  27. #include "wx/dnd.h"
  28. #endif
  29. #if wxUSE_TOOLTIPS
  30. #include "wx/tooltip.h"
  31. #endif
  32. #include <objc/objc-runtime.h>
  33. // Get the window with the focus
  34. NSView* GetViewFromResponder( NSResponder* responder )
  35. {
  36. NSView* view = nil;
  37. if ( [responder isKindOfClass:[NSTextView class]] )
  38. {
  39. NSView* delegate = (NSView*) [(NSTextView*)responder delegate];
  40. if ( [delegate isKindOfClass:[NSTextField class] ] )
  41. view = delegate;
  42. else
  43. view = (NSView*) responder;
  44. }
  45. else
  46. {
  47. if ( [responder isKindOfClass:[NSView class]] )
  48. view = (NSView*) responder;
  49. }
  50. return view;
  51. }
  52. NSView* GetFocusedViewInWindow( NSWindow* keyWindow )
  53. {
  54. NSView* focusedView = nil;
  55. if ( keyWindow != nil )
  56. focusedView = GetViewFromResponder([keyWindow firstResponder]);
  57. return focusedView;
  58. }
  59. WXWidget wxWidgetImpl::FindFocus()
  60. {
  61. return GetFocusedViewInWindow( [NSApp keyWindow] );
  62. }
  63. NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const wxSize &size , bool adjustForOrigin )
  64. {
  65. int x, y, w, h ;
  66. window->MacGetBoundsForControl( pos , size , x , y, w, h , adjustForOrigin ) ;
  67. wxRect bounds(x,y,w,h);
  68. NSView* sv = (window->GetParent()->GetHandle() );
  69. return wxToNSRect( sv, bounds );
  70. }
  71. @interface wxNSView : NSView
  72. {
  73. NSTrackingRectTag rectTag;
  74. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  75. NSTrackingArea* _trackingArea;
  76. #endif
  77. }
  78. // the tracking tag is needed to track mouse enter / exit events
  79. - (void) setTrackingTag: (NSTrackingRectTag)tag;
  80. - (NSTrackingRectTag) trackingTag;
  81. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  82. // under 10.5 we can also track mouse moved events on non-focused windows if
  83. // we use the new NSTrackingArea APIs.
  84. - (void) updateTrackingArea;
  85. - (NSTrackingArea*) trackingArea;
  86. #endif
  87. @end // wxNSView
  88. @interface NSView(PossibleMethods)
  89. - (void)setTitle:(NSString *)aString;
  90. - (void)setStringValue:(NSString *)aString;
  91. - (void)setIntValue:(int)anInt;
  92. - (void)setFloatValue:(float)aFloat;
  93. - (void)setDoubleValue:(double)aDouble;
  94. - (double)minValue;
  95. - (double)maxValue;
  96. - (void)setMinValue:(double)aDouble;
  97. - (void)setMaxValue:(double)aDouble;
  98. - (void)sizeToFit;
  99. - (BOOL)isEnabled;
  100. - (void)setEnabled:(BOOL)flag;
  101. - (void)setImage:(NSImage *)image;
  102. - (void)setControlSize:(NSControlSize)size;
  103. - (void)setFont:(NSFont *)fontObject;
  104. - (id)contentView;
  105. - (void)setTarget:(id)anObject;
  106. - (void)setAction:(SEL)aSelector;
  107. - (void)setDoubleAction:(SEL)aSelector;
  108. - (void)setBackgroundColor:(NSColor*)aColor;
  109. - (void)setOpaque:(BOOL)opaque;
  110. - (void)setTextColor:(NSColor *)color;
  111. - (void)setImagePosition:(NSCellImagePosition)aPosition;
  112. @end
  113. long wxOSXTranslateCocoaKey( NSEvent* event )
  114. {
  115. long retval = 0;
  116. if ([event type] != NSFlagsChanged)
  117. {
  118. NSString* s = [event charactersIgnoringModifiers];
  119. // backspace char reports as delete w/modifiers for some reason
  120. if ([s length] == 1)
  121. {
  122. switch ( [s characterAtIndex:0] )
  123. {
  124. // backspace key
  125. case 0x7F :
  126. case 8 :
  127. retval = WXK_BACK;
  128. break;
  129. case NSUpArrowFunctionKey :
  130. retval = WXK_UP;
  131. break;
  132. case NSDownArrowFunctionKey :
  133. retval = WXK_DOWN;
  134. break;
  135. case NSLeftArrowFunctionKey :
  136. retval = WXK_LEFT;
  137. break;
  138. case NSRightArrowFunctionKey :
  139. retval = WXK_RIGHT;
  140. break;
  141. case NSInsertFunctionKey :
  142. retval = WXK_INSERT;
  143. break;
  144. case NSDeleteFunctionKey :
  145. retval = WXK_DELETE;
  146. break;
  147. case NSHomeFunctionKey :
  148. retval = WXK_HOME;
  149. break;
  150. // case NSBeginFunctionKey :
  151. // retval = WXK_BEGIN;
  152. // break;
  153. case NSEndFunctionKey :
  154. retval = WXK_END;
  155. break;
  156. case NSPageUpFunctionKey :
  157. retval = WXK_PAGEUP;
  158. break;
  159. case NSPageDownFunctionKey :
  160. retval = WXK_PAGEDOWN;
  161. break;
  162. case NSHelpFunctionKey :
  163. retval = WXK_HELP;
  164. break;
  165. default:
  166. int intchar = [s characterAtIndex: 0];
  167. if ( intchar >= NSF1FunctionKey && intchar <= NSF24FunctionKey )
  168. retval = WXK_F1 + (intchar - NSF1FunctionKey );
  169. break;
  170. }
  171. }
  172. }
  173. // Some keys don't seem to have constants. The code mimics the approach
  174. // taken by WebKit. See:
  175. // http://trac.webkit.org/browser/trunk/WebCore/platform/mac/KeyEventMac.mm
  176. switch( [event keyCode] )
  177. {
  178. // command key
  179. case 54:
  180. case 55:
  181. retval = WXK_COMMAND;
  182. break;
  183. // caps locks key
  184. case 57: // Capslock
  185. retval = WXK_CAPITAL;
  186. break;
  187. // shift key
  188. case 56: // Left Shift
  189. case 60: // Right Shift
  190. retval = WXK_SHIFT;
  191. break;
  192. // alt key
  193. case 58: // Left Alt
  194. case 61: // Right Alt
  195. retval = WXK_ALT;
  196. break;
  197. // ctrl key
  198. case 59: // Left Ctrl
  199. case 62: // Right Ctrl
  200. retval = WXK_CONTROL;
  201. break;
  202. // clear key
  203. case 71:
  204. retval = WXK_CLEAR;
  205. break;
  206. // tab key
  207. case 48:
  208. retval = WXK_TAB;
  209. break;
  210. case 75: // /
  211. retval = WXK_NUMPAD_DIVIDE;
  212. break;
  213. case 67: // *
  214. retval = WXK_NUMPAD_MULTIPLY;
  215. break;
  216. case 78: // -
  217. retval = WXK_NUMPAD_SUBTRACT;
  218. break;
  219. case 69: // +
  220. retval = WXK_NUMPAD_ADD;
  221. break;
  222. case 76: // Enter
  223. retval = WXK_NUMPAD_ENTER;
  224. break;
  225. case 65: // .
  226. retval = WXK_NUMPAD_DECIMAL;
  227. break;
  228. case 82: // 0
  229. retval = WXK_NUMPAD0;
  230. break;
  231. case 83: // 1
  232. retval = WXK_NUMPAD1;
  233. break;
  234. case 84: // 2
  235. retval = WXK_NUMPAD2;
  236. break;
  237. case 85: // 3
  238. retval = WXK_NUMPAD3;
  239. break;
  240. case 86: // 4
  241. retval = WXK_NUMPAD4;
  242. break;
  243. case 87: // 5
  244. retval = WXK_NUMPAD5;
  245. break;
  246. case 88: // 6
  247. retval = WXK_NUMPAD6;
  248. break;
  249. case 89: // 7
  250. retval = WXK_NUMPAD7;
  251. break;
  252. case 91: // 8
  253. retval = WXK_NUMPAD8;
  254. break;
  255. case 92: // 9
  256. retval = WXK_NUMPAD9;
  257. break;
  258. default:
  259. //retval = [event keyCode];
  260. break;
  261. }
  262. return retval;
  263. }
  264. void wxWidgetCocoaImpl::SetupKeyEvent(wxKeyEvent &wxevent , NSEvent * nsEvent, NSString* charString)
  265. {
  266. UInt32 modifiers = [nsEvent modifierFlags] ;
  267. int eventType = [nsEvent type];
  268. wxevent.m_shiftDown = modifiers & NSShiftKeyMask;
  269. wxevent.m_controlDown = modifiers & NSControlKeyMask;
  270. wxevent.m_altDown = modifiers & NSAlternateKeyMask;
  271. wxevent.m_metaDown = modifiers & NSCommandKeyMask;
  272. wxevent.m_rawCode = [nsEvent keyCode];
  273. wxevent.m_rawFlags = modifiers;
  274. wxevent.SetTimestamp( (int)([nsEvent timestamp] * 1000) ) ;
  275. wxString chars;
  276. if ( eventType != NSFlagsChanged )
  277. {
  278. NSString* nschars = [nsEvent charactersIgnoringModifiers];
  279. if ( charString )
  280. {
  281. // if charString is set, it did not come from key up / key down
  282. wxevent.SetEventType( wxEVT_CHAR );
  283. chars = wxCFStringRef::AsString(charString);
  284. }
  285. else if ( nschars )
  286. {
  287. chars = wxCFStringRef::AsString(nschars);
  288. }
  289. }
  290. int aunichar = chars.Length() > 0 ? chars[0] : 0;
  291. long keyval = 0;
  292. if (wxevent.GetEventType() != wxEVT_CHAR)
  293. {
  294. keyval = wxOSXTranslateCocoaKey(nsEvent) ;
  295. switch (eventType)
  296. {
  297. case NSKeyDown :
  298. wxevent.SetEventType( wxEVT_KEY_DOWN ) ;
  299. break;
  300. case NSKeyUp :
  301. wxevent.SetEventType( wxEVT_KEY_UP ) ;
  302. break;
  303. case NSFlagsChanged :
  304. switch (keyval)
  305. {
  306. case WXK_CONTROL:
  307. wxevent.SetEventType( wxevent.m_controlDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
  308. break;
  309. case WXK_SHIFT:
  310. wxevent.SetEventType( wxevent.m_shiftDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
  311. break;
  312. case WXK_ALT:
  313. wxevent.SetEventType( wxevent.m_altDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
  314. break;
  315. case WXK_COMMAND:
  316. wxevent.SetEventType( wxevent.m_metaDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
  317. break;
  318. }
  319. break;
  320. default :
  321. break ;
  322. }
  323. }
  324. if ( !keyval )
  325. {
  326. if ( wxevent.GetEventType() == wxEVT_KEY_UP || wxevent.GetEventType() == wxEVT_KEY_DOWN )
  327. keyval = wxToupper( aunichar ) ;
  328. else
  329. keyval = aunichar;
  330. }
  331. #if wxUSE_UNICODE
  332. wxevent.m_uniChar = aunichar;
  333. #endif
  334. wxevent.m_keyCode = keyval;
  335. wxWindowMac* peer = GetWXPeer();
  336. if ( peer )
  337. {
  338. wxevent.SetEventObject(peer);
  339. wxevent.SetId(peer->GetId()) ;
  340. }
  341. }
  342. UInt32 g_lastButton = 0 ;
  343. bool g_lastButtonWasFakeRight = false ;
  344. // better scroll wheel support
  345. // see http://lists.apple.com/archives/cocoa-dev/2007/Feb/msg00050.html
  346. @interface NSEvent (DeviceDelta)
  347. - (CGFloat)deviceDeltaX;
  348. - (CGFloat)deviceDeltaY;
  349. @end
  350. void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent )
  351. {
  352. int eventType = [nsEvent type];
  353. UInt32 modifiers = [nsEvent modifierFlags] ;
  354. NSPoint locationInWindow = [nsEvent locationInWindow];
  355. // adjust coordinates for the window of the target view
  356. if ( [nsEvent window] != [m_osxView window] )
  357. {
  358. if ( [nsEvent window] != nil )
  359. locationInWindow = [[nsEvent window] convertBaseToScreen:locationInWindow];
  360. if ( [m_osxView window] != nil )
  361. locationInWindow = [[m_osxView window] convertScreenToBase:locationInWindow];
  362. }
  363. NSPoint locationInView = [m_osxView convertPoint:locationInWindow fromView:nil];
  364. wxPoint locationInViewWX = wxFromNSPoint( m_osxView, locationInView );
  365. // these parameters are not given for all events
  366. UInt32 button = [nsEvent buttonNumber];
  367. UInt32 clickCount = 0;
  368. wxevent.m_x = locationInViewWX.x;
  369. wxevent.m_y = locationInViewWX.y;
  370. wxevent.m_shiftDown = modifiers & NSShiftKeyMask;
  371. wxevent.m_controlDown = modifiers & NSControlKeyMask;
  372. wxevent.m_altDown = modifiers & NSAlternateKeyMask;
  373. wxevent.m_metaDown = modifiers & NSCommandKeyMask;
  374. wxevent.SetTimestamp( (int)([nsEvent timestamp] * 1000) ) ;
  375. UInt32 mouseChord = 0;
  376. switch (eventType)
  377. {
  378. case NSLeftMouseDown :
  379. case NSLeftMouseDragged :
  380. mouseChord = 1U;
  381. break;
  382. case NSRightMouseDown :
  383. case NSRightMouseDragged :
  384. mouseChord = 2U;
  385. break;
  386. case NSOtherMouseDown :
  387. case NSOtherMouseDragged :
  388. mouseChord = 4U;
  389. break;
  390. }
  391. // a control click is interpreted as a right click
  392. bool thisButtonIsFakeRight = false ;
  393. if ( button == 0 && (modifiers & NSControlKeyMask) )
  394. {
  395. button = 1 ;
  396. thisButtonIsFakeRight = true ;
  397. }
  398. // otherwise we report double clicks by connecting a left click with a ctrl-left click
  399. if ( clickCount > 1 && button != g_lastButton )
  400. clickCount = 1 ;
  401. // we must make sure that our synthetic 'right' button corresponds in
  402. // mouse down, moved and mouse up, and does not deliver a right down and left up
  403. switch (eventType)
  404. {
  405. case NSLeftMouseDown :
  406. case NSRightMouseDown :
  407. case NSOtherMouseDown :
  408. g_lastButton = button ;
  409. g_lastButtonWasFakeRight = thisButtonIsFakeRight ;
  410. break;
  411. }
  412. if ( button == 0 )
  413. {
  414. g_lastButton = 0 ;
  415. g_lastButtonWasFakeRight = false ;
  416. }
  417. else if ( g_lastButton == 1 && g_lastButtonWasFakeRight )
  418. button = g_lastButton ;
  419. // Adjust the chord mask to remove the primary button and add the
  420. // secondary button. It is possible that the secondary button is
  421. // already pressed, e.g. on a mouse connected to a laptop, but this
  422. // possibility is ignored here:
  423. if( thisButtonIsFakeRight && ( mouseChord & 1U ) )
  424. mouseChord = ((mouseChord & ~1U) | 2U);
  425. if(mouseChord & 1U)
  426. wxevent.m_leftDown = true ;
  427. if(mouseChord & 2U)
  428. wxevent.m_rightDown = true ;
  429. if(mouseChord & 4U)
  430. wxevent.m_middleDown = true ;
  431. // translate into wx types
  432. switch (eventType)
  433. {
  434. case NSLeftMouseDown :
  435. case NSRightMouseDown :
  436. case NSOtherMouseDown :
  437. clickCount = [nsEvent clickCount];
  438. switch ( button )
  439. {
  440. case 0 :
  441. wxevent.SetEventType( clickCount > 1 ? wxEVT_LEFT_DCLICK : wxEVT_LEFT_DOWN ) ;
  442. break ;
  443. case 1 :
  444. wxevent.SetEventType( clickCount > 1 ? wxEVT_RIGHT_DCLICK : wxEVT_RIGHT_DOWN ) ;
  445. break ;
  446. case 2 :
  447. wxevent.SetEventType( clickCount > 1 ? wxEVT_MIDDLE_DCLICK : wxEVT_MIDDLE_DOWN ) ;
  448. break ;
  449. default:
  450. break ;
  451. }
  452. break ;
  453. case NSLeftMouseUp :
  454. case NSRightMouseUp :
  455. case NSOtherMouseUp :
  456. clickCount = [nsEvent clickCount];
  457. switch ( button )
  458. {
  459. case 0 :
  460. wxevent.SetEventType( wxEVT_LEFT_UP ) ;
  461. break ;
  462. case 1 :
  463. wxevent.SetEventType( wxEVT_RIGHT_UP ) ;
  464. break ;
  465. case 2 :
  466. wxevent.SetEventType( wxEVT_MIDDLE_UP ) ;
  467. break ;
  468. default:
  469. break ;
  470. }
  471. break ;
  472. case NSScrollWheel :
  473. {
  474. float deltaX = 0.0;
  475. float deltaY = 0.0;
  476. wxevent.SetEventType( wxEVT_MOUSEWHEEL ) ;
  477. // see http://developer.apple.com/qa/qa2005/qa1453.html
  478. // for more details on why we have to look for the exact type
  479. const EventRef cEvent = (EventRef) [nsEvent eventRef];
  480. bool isMouseScrollEvent = false;
  481. if ( cEvent )
  482. isMouseScrollEvent = ::GetEventKind(cEvent) == kEventMouseScroll;
  483. if ( isMouseScrollEvent )
  484. {
  485. deltaX = [nsEvent deviceDeltaX];
  486. deltaY = [nsEvent deviceDeltaY];
  487. }
  488. else
  489. {
  490. deltaX = ([nsEvent deltaX] * 10);
  491. deltaY = ([nsEvent deltaY] * 10);
  492. }
  493. wxevent.m_wheelDelta = 10;
  494. wxevent.m_linesPerAction = 1;
  495. if ( fabs(deltaX) > fabs(deltaY) )
  496. {
  497. wxevent.m_wheelAxis = 1;
  498. wxevent.m_wheelRotation = (int)deltaX;
  499. }
  500. else
  501. {
  502. wxevent.m_wheelRotation = (int)deltaY;
  503. }
  504. }
  505. break ;
  506. case NSMouseEntered :
  507. wxevent.SetEventType( wxEVT_ENTER_WINDOW ) ;
  508. break;
  509. case NSMouseExited :
  510. wxevent.SetEventType( wxEVT_LEAVE_WINDOW ) ;
  511. break;
  512. case NSLeftMouseDragged :
  513. case NSRightMouseDragged :
  514. case NSOtherMouseDragged :
  515. case NSMouseMoved :
  516. wxevent.SetEventType( wxEVT_MOTION ) ;
  517. break;
  518. default :
  519. break ;
  520. }
  521. wxevent.m_clickCount = clickCount;
  522. wxWindowMac* peer = GetWXPeer();
  523. if ( peer )
  524. {
  525. wxevent.SetEventObject(peer);
  526. wxevent.SetId(peer->GetId()) ;
  527. }
  528. }
  529. @implementation wxNSView
  530. + (void)initialize
  531. {
  532. static BOOL initialized = NO;
  533. if (!initialized)
  534. {
  535. initialized = YES;
  536. wxOSXCocoaClassAddWXMethods( self );
  537. }
  538. }
  539. - (void) setTrackingTag: (NSTrackingRectTag)tag
  540. {
  541. rectTag = tag;
  542. }
  543. - (NSTrackingRectTag) trackingTag
  544. {
  545. return rectTag;
  546. }
  547. #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
  548. - (void) updateTrackingArea
  549. {
  550. if (_trackingArea)
  551. {
  552. [self removeTrackingArea: _trackingArea];
  553. [_trackingArea release];
  554. }
  555. NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveAlways;
  556. NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: [self bounds] options: options owner: self userInfo: nil];
  557. [self addTrackingArea: area];
  558. _trackingArea = area;
  559. }
  560. - (NSTrackingArea*) trackingArea
  561. {
  562. return _trackingArea;
  563. }
  564. #endif
  565. @end // wxNSView
  566. //
  567. // event handlers
  568. //
  569. #if wxUSE_DRAG_AND_DROP
  570. // see http://lists.apple.com/archives/Cocoa-dev/2005/Jul/msg01244.html
  571. // for details on the NSPasteboard -> PasteboardRef conversion
  572. NSDragOperation wxOSX_draggingEntered( id self, SEL _cmd, id <NSDraggingInfo>sender )
  573. {
  574. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  575. if (impl == NULL)
  576. return NSDragOperationNone;
  577. return impl->draggingEntered(sender, self, _cmd);
  578. }
  579. void wxOSX_draggingExited( id self, SEL _cmd, id <NSDraggingInfo> sender )
  580. {
  581. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  582. if (impl == NULL)
  583. return ;
  584. return impl->draggingExited(sender, self, _cmd);
  585. }
  586. NSDragOperation wxOSX_draggingUpdated( id self, SEL _cmd, id <NSDraggingInfo>sender )
  587. {
  588. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  589. if (impl == NULL)
  590. return NSDragOperationNone;
  591. return impl->draggingUpdated(sender, self, _cmd);
  592. }
  593. BOOL wxOSX_performDragOperation( id self, SEL _cmd, id <NSDraggingInfo> sender )
  594. {
  595. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  596. if (impl == NULL)
  597. return NSDragOperationNone;
  598. return impl->performDragOperation(sender, self, _cmd) ? YES:NO ;
  599. }
  600. #endif
  601. void wxOSX_mouseEvent(NSView* self, SEL _cmd, NSEvent *event)
  602. {
  603. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  604. if (impl == NULL)
  605. return;
  606. impl->mouseEvent(event, self, _cmd);
  607. }
  608. BOOL wxOSX_acceptsFirstMouse(NSView* WXUNUSED(self), SEL WXUNUSED(_cmd), NSEvent *WXUNUSED(event))
  609. {
  610. // This is needed to support click through, otherwise the first click on a window
  611. // will not do anything unless it is the active window already.
  612. return YES;
  613. }
  614. void wxOSX_keyEvent(NSView* self, SEL _cmd, NSEvent *event)
  615. {
  616. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  617. if (impl == NULL)
  618. return;
  619. impl->keyEvent(event, self, _cmd);
  620. }
  621. void wxOSX_insertText(NSView* self, SEL _cmd, NSString* text)
  622. {
  623. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  624. if (impl == NULL)
  625. return;
  626. impl->insertText(text, self, _cmd);
  627. }
  628. BOOL wxOSX_performKeyEquivalent(NSView* self, SEL _cmd, NSEvent *event)
  629. {
  630. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  631. if (impl == NULL)
  632. return NO;
  633. return impl->performKeyEquivalent(event, self, _cmd);
  634. }
  635. BOOL wxOSX_acceptsFirstResponder(NSView* self, SEL _cmd)
  636. {
  637. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  638. if (impl == NULL)
  639. return NO;
  640. return impl->acceptsFirstResponder(self, _cmd);
  641. }
  642. BOOL wxOSX_becomeFirstResponder(NSView* self, SEL _cmd)
  643. {
  644. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  645. if (impl == NULL)
  646. return NO;
  647. return impl->becomeFirstResponder(self, _cmd);
  648. }
  649. BOOL wxOSX_resignFirstResponder(NSView* self, SEL _cmd)
  650. {
  651. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  652. if (impl == NULL)
  653. return NO;
  654. return impl->resignFirstResponder(self, _cmd);
  655. }
  656. void wxOSX_resetCursorRects(NSView* self, SEL _cmd)
  657. {
  658. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  659. if (impl == NULL)
  660. return;
  661. impl->resetCursorRects(self, _cmd);
  662. }
  663. BOOL wxOSX_isFlipped(NSView* self, SEL _cmd)
  664. {
  665. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  666. if (impl == NULL)
  667. return NO;
  668. return impl->isFlipped(self, _cmd) ? YES:NO;
  669. }
  670. typedef void (*wxOSX_DrawRectHandlerPtr)(NSView* self, SEL _cmd, NSRect rect);
  671. void wxOSX_drawRect(NSView* self, SEL _cmd, NSRect rect)
  672. {
  673. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  674. if (impl == NULL)
  675. return;
  676. #ifdef wxUSE_THREADS
  677. // OS X starts a NSUIHeartBeatThread for animating the default button in a
  678. // dialog. This causes a drawRect of the active dialog from outside the
  679. // main UI thread. This causes an occasional crash since the wx drawing
  680. // objects (like wxPen) are not thread safe.
  681. //
  682. // Notice that NSUIHeartBeatThread seems to be undocumented and doing
  683. // [NSWindow setAllowsConcurrentViewDrawing:NO] does not affect it.
  684. if ( !wxThread::IsMain() )
  685. {
  686. if ( impl->IsUserPane() )
  687. {
  688. wxWindow* win = impl->GetWXPeer();
  689. if ( win->UseBgCol() )
  690. {
  691. CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
  692. CGContextSaveGState( context );
  693. CGContextSetFillColorWithColor( context, win->GetBackgroundColour().GetCGColor());
  694. CGRect r = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
  695. CGContextFillRect( context, r );
  696. CGContextRestoreGState( context );
  697. }
  698. }
  699. else
  700. {
  701. // just call the superclass handler, we don't need any custom wx drawing
  702. // here and it seems to work fine:
  703. wxOSX_DrawRectHandlerPtr
  704. superimpl = (wxOSX_DrawRectHandlerPtr)
  705. [[self superclass] instanceMethodForSelector:_cmd];
  706. superimpl(self, _cmd, rect);
  707. }
  708. return;
  709. }
  710. #endif // wxUSE_THREADS
  711. return impl->drawRect(&rect, self, _cmd);
  712. }
  713. void wxOSX_controlAction(NSView* self, SEL _cmd, id sender)
  714. {
  715. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  716. if (impl == NULL)
  717. return;
  718. impl->controlAction(self, _cmd, sender);
  719. }
  720. void wxOSX_controlDoubleAction(NSView* self, SEL _cmd, id sender)
  721. {
  722. wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
  723. if (impl == NULL)
  724. return;
  725. impl->controlDoubleAction(self, _cmd, sender);
  726. }
  727. unsigned int wxWidgetCocoaImpl::draggingEntered(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
  728. {
  729. id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
  730. NSPasteboard *pboard = [sender draggingPasteboard];
  731. NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
  732. wxWindow* wxpeer = GetWXPeer();
  733. if ( wxpeer == NULL )
  734. return NSDragOperationNone;
  735. wxDropTarget* target = wxpeer->GetDropTarget();
  736. if ( target == NULL )
  737. return NSDragOperationNone;
  738. wxDragResult result = wxDragNone;
  739. NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
  740. wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
  741. if ( sourceDragMask & NSDragOperationLink )
  742. result = wxDragLink;
  743. else if ( sourceDragMask & NSDragOperationCopy )
  744. result = wxDragCopy;
  745. else if ( sourceDragMask & NSDragOperationMove )
  746. result = wxDragMove;
  747. PasteboardRef pboardRef;
  748. PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
  749. target->SetCurrentDragPasteboard(pboardRef);
  750. result = target->OnEnter(pt.x, pt.y, result);
  751. CFRelease(pboardRef);
  752. NSDragOperation nsresult = NSDragOperationNone;
  753. switch (result )
  754. {
  755. case wxDragLink:
  756. nsresult = NSDragOperationLink;
  757. case wxDragMove:
  758. nsresult = NSDragOperationMove;
  759. case wxDragCopy:
  760. nsresult = NSDragOperationCopy;
  761. default :
  762. break;
  763. }
  764. return nsresult;
  765. }
  766. void wxWidgetCocoaImpl::draggingExited(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
  767. {
  768. id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
  769. NSPasteboard *pboard = [sender draggingPasteboard];
  770. wxWindow* wxpeer = GetWXPeer();
  771. if ( wxpeer == NULL )
  772. return;
  773. wxDropTarget* target = wxpeer->GetDropTarget();
  774. if ( target == NULL )
  775. return;
  776. PasteboardRef pboardRef;
  777. PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
  778. target->SetCurrentDragPasteboard(pboardRef);
  779. target->OnLeave();
  780. CFRelease(pboardRef);
  781. }
  782. unsigned int wxWidgetCocoaImpl::draggingUpdated(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
  783. {
  784. id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
  785. NSPasteboard *pboard = [sender draggingPasteboard];
  786. NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
  787. wxWindow* wxpeer = GetWXPeer();
  788. if ( wxpeer == NULL )
  789. return NSDragOperationNone;
  790. wxDropTarget* target = wxpeer->GetDropTarget();
  791. if ( target == NULL )
  792. return NSDragOperationNone;
  793. wxDragResult result = wxDragNone;
  794. NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
  795. wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
  796. if ( sourceDragMask & NSDragOperationLink )
  797. result = wxDragLink;
  798. else if ( sourceDragMask & NSDragOperationCopy )
  799. result = wxDragCopy;
  800. else if ( sourceDragMask & NSDragOperationMove )
  801. result = wxDragMove;
  802. PasteboardRef pboardRef;
  803. PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
  804. target->SetCurrentDragPasteboard(pboardRef);
  805. result = target->OnDragOver(pt.x, pt.y, result);
  806. CFRelease(pboardRef);
  807. NSDragOperation nsresult = NSDragOperationNone;
  808. switch (result )
  809. {
  810. case wxDragLink:
  811. nsresult = NSDragOperationLink;
  812. case wxDragMove:
  813. nsresult = NSDragOperationMove;
  814. case wxDragCopy:
  815. nsresult = NSDragOperationCopy;
  816. default :
  817. break;
  818. }
  819. return nsresult;
  820. }
  821. bool wxWidgetCocoaImpl::performDragOperation(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
  822. {
  823. id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
  824. NSPasteboard *pboard = [sender draggingPasteboard];
  825. NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
  826. wxWindow* wxpeer = GetWXPeer();
  827. wxDropTarget* target = wxpeer->GetDropTarget();
  828. wxDragResult result = wxDragNone;
  829. NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
  830. wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
  831. if ( sourceDragMask & NSDragOperationLink )
  832. result = wxDragLink;
  833. else if ( sourceDragMask & NSDragOperationCopy )
  834. result = wxDragCopy;
  835. else if ( sourceDragMask & NSDragOperationMove )
  836. result = wxDragMove;
  837. PasteboardRef pboardRef;
  838. PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
  839. target->SetCurrentDragPasteboard(pboardRef);
  840. if (target->OnDrop(pt.x, pt.y))
  841. result = target->OnData(pt.x, pt.y, result);
  842. CFRelease(pboardRef);
  843. return result != wxDragNone;
  844. }
  845. typedef void (*wxOSX_TextEventHandlerPtr)(NSView* self, SEL _cmd, NSString *event);
  846. typedef void (*wxOSX_EventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
  847. typedef BOOL (*wxOSX_PerformKeyEventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
  848. typedef BOOL (*wxOSX_FocusHandlerPtr)(NSView* self, SEL _cmd);
  849. typedef BOOL (*wxOSX_ResetCursorRectsHandlerPtr)(NSView* self, SEL _cmd);
  850. void wxWidgetCocoaImpl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
  851. {
  852. if ( !DoHandleMouseEvent(event) )
  853. {
  854. // for plain NSView mouse events would propagate to parents otherwise
  855. if (!IsUserPane())
  856. {
  857. wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  858. superimpl(slf, (SEL)_cmd, event);
  859. }
  860. }
  861. }
  862. void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
  863. {
  864. if ( [event type] == NSKeyDown )
  865. {
  866. // there are key equivalents that are not command-combos and therefore not handled by cocoa automatically,
  867. // therefore we call the menubar directly here, exit if the menu is handling the shortcut
  868. if ( [[[NSApplication sharedApplication] mainMenu] performKeyEquivalent:event] )
  869. return;
  870. m_lastKeyDownEvent = event;
  871. }
  872. if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
  873. {
  874. wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  875. superimpl(slf, (SEL)_cmd, event);
  876. }
  877. m_lastKeyDownEvent = NULL;
  878. }
  879. void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
  880. {
  881. if ( m_lastKeyDownEvent==NULL || m_hasEditor || !DoHandleCharEvent(m_lastKeyDownEvent, text) )
  882. {
  883. wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  884. superimpl(slf, (SEL)_cmd, text);
  885. }
  886. }
  887. bool wxWidgetCocoaImpl::performKeyEquivalent(WX_NSEvent event, WXWidget slf, void *_cmd)
  888. {
  889. bool handled = false;
  890. wxKeyEvent wxevent(wxEVT_KEY_DOWN);
  891. SetupKeyEvent( wxevent, event );
  892. // because performKeyEquivalent is going up the entire view hierarchy, we don't have to
  893. // walk up the ancestors ourselves but let cocoa do it
  894. int command = m_wxPeer->GetAcceleratorTable()->GetCommand( wxevent );
  895. if (command != -1)
  896. {
  897. wxEvtHandler * const handler = m_wxPeer->GetEventHandler();
  898. wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
  899. handled = handler->ProcessEvent( command_event );
  900. if ( !handled )
  901. {
  902. // accelerators can also be used with buttons, try them too
  903. command_event.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED);
  904. handled = handler->ProcessEvent( command_event );
  905. }
  906. }
  907. if ( !handled )
  908. {
  909. wxOSX_PerformKeyEventHandlerPtr superimpl = (wxOSX_PerformKeyEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  910. return superimpl(slf, (SEL)_cmd, event);
  911. }
  912. return YES;
  913. }
  914. bool wxWidgetCocoaImpl::acceptsFirstResponder(WXWidget slf, void *_cmd)
  915. {
  916. if ( IsUserPane() )
  917. return m_wxPeer->AcceptsFocus();
  918. else
  919. {
  920. wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  921. return superimpl(slf, (SEL)_cmd);
  922. }
  923. }
  924. bool wxWidgetCocoaImpl::becomeFirstResponder(WXWidget slf, void *_cmd)
  925. {
  926. wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  927. // get the current focus before running becomeFirstResponder
  928. NSView* otherView = FindFocus();
  929. wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
  930. BOOL r = superimpl(slf, (SEL)_cmd);
  931. if ( r )
  932. {
  933. DoNotifyFocusEvent( true, otherWindow );
  934. }
  935. return r;
  936. }
  937. bool wxWidgetCocoaImpl::resignFirstResponder(WXWidget slf, void *_cmd)
  938. {
  939. wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  940. BOOL r = superimpl(slf, (SEL)_cmd);
  941. // get the current focus after running resignFirstResponder
  942. // note that this value isn't reliable, it might return the same view that
  943. // is resigning
  944. NSView* otherView = FindFocus();
  945. wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
  946. // It doesn't make sense to notify about the loss of focus if we're not
  947. // really losing it and the window which has just gained focus is the same
  948. // one as this window itself. Of course, this should never happen in the
  949. // first place but somehow it does in wxGrid code and without this check we
  950. // enter into an infinite recursion, see #12267.
  951. if ( otherWindow == this )
  952. return r;
  953. // NSTextViews have an editor as true responder, therefore the might get the
  954. // resign notification if their editor takes over, don't trigger any event then
  955. if ( r && !m_hasEditor)
  956. {
  957. DoNotifyFocusEvent( false, otherWindow );
  958. }
  959. return r;
  960. }
  961. void wxWidgetCocoaImpl::resetCursorRects(WXWidget slf, void *_cmd)
  962. {
  963. wxWindow* wxpeer = GetWXPeer();
  964. if ( wxpeer )
  965. {
  966. NSCursor *cursor = (NSCursor*)wxpeer->GetCursor().GetHCURSOR();
  967. if (cursor == NULL)
  968. {
  969. wxOSX_ResetCursorRectsHandlerPtr superimpl = (wxOSX_ResetCursorRectsHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
  970. superimpl(slf, (SEL)_cmd);
  971. }
  972. else
  973. {
  974. [slf addCursorRect: [slf bounds]
  975. cursor: cursor];
  976. }
  977. }
  978. }
  979. bool wxWidgetCocoaImpl::isFlipped(WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
  980. {
  981. return m_isFlipped;
  982. }
  983. #define OSX_DEBUG_DRAWING 0
  984. void wxWidgetCocoaImpl::drawRect(void* rect, WXWidget slf, void *WXUNUSED(_cmd))
  985. {
  986. // preparing the update region
  987. wxRegion updateRgn;
  988. const NSRect *rects;
  989. NSInteger count;
  990. [slf getRectsBeingDrawn:&rects count:&count];
  991. for ( int i = 0 ; i < count ; ++i )
  992. {
  993. updateRgn.Union(wxFromNSRect(slf, rects[i]));
  994. }
  995. wxWindow* wxpeer = GetWXPeer();
  996. if ( wxpeer->MacGetLeftBorderSize() != 0 || wxpeer->MacGetTopBorderSize() != 0 )
  997. {
  998. // as this update region is in native window locals we must adapt it to wx window local
  999. updateRgn.Offset( wxpeer->MacGetLeftBorderSize() , wxpeer->MacGetTopBorderSize() );
  1000. }
  1001. if ( wxpeer->MacGetTopLevelWindow()->GetWindowStyle() & wxFRAME_SHAPED )
  1002. {
  1003. int xoffset = 0, yoffset = 0;
  1004. wxRegion rgn = wxpeer->MacGetTopLevelWindow()->GetShape();
  1005. wxpeer->MacRootWindowToWindow( &xoffset, &yoffset );
  1006. rgn.Offset( xoffset, yoffset );
  1007. updateRgn.Intersect(rgn);
  1008. }
  1009. wxpeer->GetUpdateRegion() = updateRgn;
  1010. // setting up the drawing context
  1011. CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
  1012. CGContextSaveGState( context );
  1013. #if OSX_DEBUG_DRAWING
  1014. CGContextBeginPath( context );
  1015. CGContextMoveToPoint(context, 0, 0);
  1016. NSRect bounds = [slf bounds];
  1017. CGContextAddLineToPoint(context, 10, 0);
  1018. CGContextMoveToPoint(context, 0, 0);
  1019. CGContextAddLineToPoint(context, 0, 10);
  1020. CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
  1021. CGContextAddLineToPoint(context, bounds.size.width, bounds.size.height-10);
  1022. CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
  1023. CGContextAddLineToPoint(context, bounds.size.width-10, bounds.size.height);
  1024. CGContextClosePath( context );
  1025. CGContextStrokePath(context);
  1026. #endif
  1027. if ( !m_isFlipped )
  1028. {
  1029. CGContextTranslateCTM( context, 0, [m_osxView bounds].size.height );
  1030. CGContextScaleCTM( context, 1, -1 );
  1031. }
  1032. wxpeer->MacSetCGContextRef( context );
  1033. bool handled = wxpeer->MacDoRedraw( 0 );
  1034. CGContextRestoreGState( context );
  1035. CGContextSaveGState( context );
  1036. if ( !handled )
  1037. {
  1038. // call super
  1039. SEL _cmd = @selector(drawRect:);
  1040. wxOSX_DrawRectHandlerPtr superimpl = (wxOSX_DrawRectHandlerPtr) [[slf superclass] instanceMethodForSelector:_cmd];
  1041. superimpl(slf, _cmd, *(NSRect*)rect);
  1042. CGContextRestoreGState( context );
  1043. CGContextSaveGState( context );
  1044. }
  1045. // as we called restore above, we have to flip again if necessary
  1046. if ( !m_isFlipped )
  1047. {
  1048. CGContextTranslateCTM( context, 0, [m_osxView bounds].size.height );
  1049. CGContextScaleCTM( context, 1, -1 );
  1050. }
  1051. wxpeer->MacPaintChildrenBorders();
  1052. wxpeer->MacSetCGContextRef( NULL );
  1053. CGContextRestoreGState( context );
  1054. }
  1055. void wxWidgetCocoaImpl::controlAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
  1056. {
  1057. wxWindow* wxpeer = (wxWindow*) GetWXPeer();
  1058. if ( wxpeer )
  1059. wxpeer->OSXHandleClicked(0);
  1060. }
  1061. void wxWidgetCocoaImpl::controlDoubleAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
  1062. {
  1063. }
  1064. void wxWidgetCocoaImpl::controlTextDidChange()
  1065. {
  1066. wxWindow* wxpeer = (wxWindow*)GetWXPeer();
  1067. if ( wxpeer )
  1068. {
  1069. // since native rtti doesn't have to be enabled and wx' rtti is not aware of the mixin wxTextEntry, workaround is needed
  1070. wxTextCtrl *tc = wxDynamicCast( wxpeer , wxTextCtrl );
  1071. wxComboBox *cb = wxDynamicCast( wxpeer , wxComboBox );
  1072. if ( tc )
  1073. tc->SendTextUpdatedEventIfAllowed();
  1074. else if ( cb )
  1075. cb->SendTextUpdatedEventIfAllowed();
  1076. else
  1077. {
  1078. wxFAIL_MSG("Unexpected class for controlTextDidChange event");
  1079. }
  1080. }
  1081. }
  1082. //
  1083. #if OBJC_API_VERSION >= 2
  1084. #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
  1085. class_addMethod(c, s, i, t );
  1086. #else
  1087. #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
  1088. { s, (char*) t, i },
  1089. #endif
  1090. void wxOSXCocoaClassAddWXMethods(Class c)
  1091. {
  1092. #if OBJC_API_VERSION < 2
  1093. static objc_method wxmethods[] =
  1094. {
  1095. #endif
  1096. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1097. wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1098. wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1099. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1100. wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1101. wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1102. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseMoved:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1103. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1104. wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1105. wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1106. wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstMouse:), (IMP) wxOSX_acceptsFirstMouse, "v@:@" )
  1107. wxOSX_CLASS_ADD_METHOD(c, @selector(scrollWheel:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1108. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseEntered:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1109. wxOSX_CLASS_ADD_METHOD(c, @selector(mouseExited:), (IMP) wxOSX_mouseEvent, "v@:@" )
  1110. wxOSX_CLASS_ADD_METHOD(c, @selector(keyDown:), (IMP) wxOSX_keyEvent, "v@:@" )
  1111. wxOSX_CLASS_ADD_METHOD(c, @selector(keyUp:), (IMP) wxOSX_keyEvent, "v@:@" )
  1112. wxOSX_CLASS_ADD_METHOD(c, @selector(flagsChanged:), (IMP) wxOSX_keyEvent, "v@:@" )
  1113. wxOSX_CLASS_ADD_METHOD(c, @selector(insertText:), (IMP) wxOSX_insertText, "v@:@" )
  1114. wxOSX_CLASS_ADD_METHOD(c, @selector(performKeyEquivalent:), (IMP) wxOSX_performKeyEquivalent, "c@:@" )
  1115. wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstResponder), (IMP) wxOSX_acceptsFirstResponder, "c@:" )
  1116. wxOSX_CLASS_ADD_METHOD(c, @selector(becomeFirstResponder), (IMP) wxOSX_becomeFirstResponder, "c@:" )
  1117. wxOSX_CLASS_ADD_METHOD(c, @selector(resignFirstResponder), (IMP) wxOSX_resignFirstResponder, "c@:" )
  1118. wxOSX_CLASS_ADD_METHOD(c, @selector(resetCursorRects), (IMP) wxOSX_resetCursorRects, "v@:" )
  1119. wxOSX_CLASS_ADD_METHOD(c, @selector(isFlipped), (IMP) wxOSX_isFlipped, "c@:" )
  1120. wxOSX_CLASS_ADD_METHOD(c, @selector(drawRect:), (IMP) wxOSX_drawRect, "v@:{_NSRect={_NSPoint=ff}{_NSSize=ff}}" )
  1121. wxOSX_CLASS_ADD_METHOD(c, @selector(controlAction:), (IMP) wxOSX_controlAction, "v@:@" )
  1122. wxOSX_CLASS_ADD_METHOD(c, @selector(controlDoubleAction:), (IMP) wxOSX_controlDoubleAction, "v@:@" )
  1123. #if wxUSE_DRAG_AND_DROP
  1124. wxOSX_CLASS_ADD_METHOD(c, @selector(draggingEntered:), (IMP) wxOSX_draggingEntered, "I@:@" )
  1125. wxOSX_CLASS_ADD_METHOD(c, @selector(draggingUpdated:), (IMP) wxOSX_draggingUpdated, "I@:@" )
  1126. wxOSX_CLASS_ADD_METHOD(c, @selector(draggingExited:), (IMP) wxOSX_draggingExited, "v@:@" )
  1127. wxOSX_CLASS_ADD_METHOD(c, @selector(performDragOperation:), (IMP) wxOSX_performDragOperation, "c@:@" )
  1128. #endif
  1129. #if OBJC_API_VERSION < 2
  1130. } ;
  1131. static int method_count = WXSIZEOF( wxmethods );
  1132. static objc_method_list *wxmethodlist = NULL;
  1133. if ( wxmethodlist == NULL )
  1134. {
  1135. wxmethodlist = (objc_method_list*) malloc(sizeof(objc_method_list) + sizeof(wxmethods) );
  1136. memcpy( &wxmethodlist->method_list[0], &wxmethods[0], sizeof(wxmethods) );
  1137. wxmethodlist->method_count = method_count;
  1138. wxmethodlist->obsolete = 0;
  1139. }
  1140. class_addMethods( c, wxmethodlist );
  1141. #endif
  1142. }
  1143. //
  1144. // C++ implementation class
  1145. //
  1146. IMPLEMENT_DYNAMIC_CLASS( wxWidgetCocoaImpl , wxWidgetImpl )
  1147. wxWidgetCocoaImpl::wxWidgetCocoaImpl( wxWindowMac* peer , WXWidget w, bool isRootControl, bool isUserPane ) :
  1148. wxWidgetImpl( peer, isRootControl, isUserPane )
  1149. {
  1150. Init();
  1151. m_osxView = w;
  1152. // check if the user wants to create the control initially hidden
  1153. if ( !peer->IsShown() )
  1154. SetVisibility(false);
  1155. // gc aware handling
  1156. if ( m_osxView )
  1157. CFRetain(m_osxView);
  1158. [m_osxView release];
  1159. }
  1160. wxWidgetCocoaImpl::wxWidgetCocoaImpl()
  1161. {
  1162. Init();
  1163. }
  1164. void wxWidgetCocoaImpl::Init()
  1165. {
  1166. m_osxView = NULL;
  1167. m_isFlipped = true;
  1168. m_lastKeyDownEvent = NULL;
  1169. m_hasEditor = false;
  1170. }
  1171. wxWidgetCocoaImpl::~wxWidgetCocoaImpl()
  1172. {
  1173. RemoveAssociations( this );
  1174. if ( !IsRootControl() )
  1175. {
  1176. NSView *sv = [m_osxView superview];
  1177. if ( sv != nil )
  1178. [m_osxView removeFromSuperview];
  1179. }
  1180. // gc aware handling
  1181. if ( m_osxView )
  1182. CFRelease(m_osxView);
  1183. }
  1184. bool wxWidgetCocoaImpl::IsVisible() const
  1185. {
  1186. return [m_osxView isHiddenOrHasHiddenAncestor] == NO;
  1187. }
  1188. void wxWidgetCocoaImpl::SetVisibility( bool visible )
  1189. {
  1190. [m_osxView setHidden:(visible ? NO:YES)];
  1191. }
  1192. // ----------------------------------------------------------------------------
  1193. // window animation stuff
  1194. // ----------------------------------------------------------------------------
  1195. // define a delegate used to refresh the window during animation
  1196. @interface wxNSAnimationDelegate : NSObject wxOSX_10_6_AND_LATER(<NSAnimationDelegate>)
  1197. {
  1198. wxWindow *m_win;
  1199. bool m_isDone;
  1200. }
  1201. - (id)init:(wxWindow *)win;
  1202. - (bool)isDone;
  1203. // NSAnimationDelegate methods
  1204. - (void)animationDidEnd:(NSAnimation*)animation;
  1205. - (void)animation:(NSAnimation*)animation
  1206. didReachProgressMark:(NSAnimationProgress)progress;
  1207. @end
  1208. @implementation wxNSAnimationDelegate
  1209. - (id)init:(wxWindow *)win
  1210. {
  1211. self = [super init];
  1212. m_win = win;
  1213. m_isDone = false;
  1214. return self;
  1215. }
  1216. - (bool)isDone
  1217. {
  1218. return m_isDone;
  1219. }
  1220. - (void)animation:(NSAnimation*)animation
  1221. didReachProgressMark:(NSAnimationProgress)progress
  1222. {
  1223. wxUnusedVar(animation);
  1224. wxUnusedVar(progress);
  1225. m_win->SendSizeEvent();
  1226. }
  1227. - (void)animationDidEnd:(NSAnimation*)animation
  1228. {
  1229. wxUnusedVar(animation);
  1230. m_isDone = true;
  1231. }
  1232. @end
  1233. /* static */
  1234. bool
  1235. wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
  1236. bool show,
  1237. wxShowEffect effect,
  1238. unsigned timeout)
  1239. {
  1240. // create the dictionary describing the animation to perform on this view
  1241. NSObject * const
  1242. viewOrWin = static_cast<NSObject *>(win->OSXGetViewOrWindow());
  1243. NSMutableDictionary * const
  1244. dict = [NSMutableDictionary dictionaryWithCapacity:4];
  1245. [dict setObject:viewOrWin forKey:NSViewAnimationTargetKey];
  1246. // determine the start and end rectangles assuming we're hiding the window
  1247. const wxRect rectOrig = win->GetRect();
  1248. wxRect rectStart,
  1249. rectEnd;
  1250. rectStart =
  1251. rectEnd = rectOrig;
  1252. if ( show )
  1253. {
  1254. if ( effect == wxSHOW_EFFECT_ROLL_TO_LEFT ||
  1255. effect == wxSHOW_EFFECT_SLIDE_TO_LEFT )
  1256. effect = wxSHOW_EFFECT_ROLL_TO_RIGHT;
  1257. else if ( effect == wxSHOW_EFFECT_ROLL_TO_RIGHT ||
  1258. effect == wxSHOW_EFFECT_SLIDE_TO_RIGHT )
  1259. effect = wxSHOW_EFFECT_ROLL_TO_LEFT;
  1260. else if ( effect == wxSHOW_EFFECT_ROLL_TO_TOP ||
  1261. effect == wxSHOW_EFFECT_SLIDE_TO_TOP )
  1262. effect = wxSHOW_EFFECT_ROLL_TO_BOTTOM;
  1263. else if ( effect == wxSHOW_EFFECT_ROLL_TO_BOTTOM ||
  1264. effect == wxSHOW_EFFECT_SLIDE_TO_BOTTOM )
  1265. effect = wxSHOW_EFFECT_ROLL_TO_TOP;
  1266. }
  1267. switch ( effect )
  1268. {
  1269. case wxSHOW_EFFECT_ROLL_TO_LEFT:
  1270. case wxSHOW_EFFECT_SLIDE_TO_LEFT:
  1271. rectEnd.width = 0;
  1272. break;
  1273. case wxSHOW_EFFECT_ROLL_TO_RIGHT:
  1274. case wxSHOW_EFFECT_SLIDE_TO_RIGHT:
  1275. rectEnd.x = rectStart.GetRight();
  1276. rectEnd.width = 0;
  1277. break;
  1278. case wxSHOW_EFFECT_ROLL_TO_TOP:
  1279. case wxSHOW_EFFECT_SLIDE_TO_TOP:
  1280. rectEnd.height = 0;
  1281. break;
  1282. case wxSHOW_EFFECT_ROLL_TO_BOTTOM:
  1283. case wxSHOW_EFFECT_SLIDE_TO_BOTTOM:
  1284. rectEnd.y = rectStart.GetBottom();
  1285. rectEnd.height = 0;
  1286. break;
  1287. case wxSHOW_EFFECT_EXPAND:
  1288. rectEnd.x = rectStart.x + rectStart.width / 2;
  1289. rectEnd.y = rectStart.y + rectStart.height / 2;
  1290. rectEnd.width =
  1291. rectEnd.height = 0;
  1292. break;
  1293. case wxSHOW_EFFECT_BLEND:
  1294. [dict setObject:(show ? NSViewAnimationFadeInEffect
  1295. : NSViewAnimationFadeOutEffect)
  1296. forKey:NSViewAnimationEffectKey];
  1297. break;
  1298. case wxSHOW_EFFECT_NONE:
  1299. case wxSHOW_EFFECT_MAX:
  1300. wxFAIL_MSG( "unexpected animation effect" );
  1301. return false;
  1302. default:
  1303. wxFAIL_MSG( "unknown animation effect" );
  1304. return false;
  1305. };
  1306. if ( show )
  1307. {
  1308. // we need to restore it to the original rectangle instead of making it
  1309. // disappear
  1310. wxSwap(rectStart, rectEnd);
  1311. // and as the window is currently hidden, we need to show it for the
  1312. // animation to be visible at all (but don't restore it at its full
  1313. // rectangle as it shouldn't appear immediately)
  1314. win->SetSize(rectStart);
  1315. win->Show();
  1316. }
  1317. NSView * const parentView = [viewOrWin isKindOfClass:[NSView class]]
  1318. ? [(NSView *)viewOrWin superview]
  1319. : nil;
  1320. const NSRect rStart = wxToNSRect(parentView, rectStart);
  1321. const NSRect rEnd = wxToNSRect(parentView, rectEnd);
  1322. [dict setObject:[NSValue valueWithRect:rStart]
  1323. forKey:NSViewAnimationStartFrameKey];
  1324. [dict setObject:[NSValue valueWithRect:rEnd]
  1325. forKey:NSViewAnimationEndFrameKey];
  1326. // create an animation using the values in the above dictionary
  1327. NSViewAnimation * const
  1328. anim = [[NSViewAnimation alloc]
  1329. initWithViewAnimations:[NSArray arrayWithObject:dict]];
  1330. if ( !timeout )
  1331. {
  1332. // what is a good default duration? Windows uses 200ms, Web frameworks
  1333. // use anything from 250ms to 1s... choose something in the middle
  1334. timeout = 500;
  1335. }
  1336. [anim setDuration:timeout/1000.]; // duration is in seconds here
  1337. // if the window being animated changes its layout depending on its size
  1338. // (which is almost always the case) we need to redo it during animation
  1339. //
  1340. // the number of layouts here is arbitrary, but 10 seems like too few (e.g.
  1341. // controls in wxInfoBar visibly jump around)
  1342. const int NUM_LAYOUTS = 20;
  1343. for ( float f = 1./NUM_LAYOUTS; f < 1.; f += 1./NUM_LAYOUTS )
  1344. [anim addProgressMark:f];
  1345. wxNSAnimationDelegate * const
  1346. animDelegate = [[wx