1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/window.mm
3 // Purpose: wxWindowCocoa
4 // Author: David Elliott
8 // Copyright: (c) 2002 David Elliott
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
16 #include "wx/window.h"
21 #include "wx/tooltip.h"
23 #include "wx/cocoa/autorelease.h"
24 #include "wx/cocoa/string.h"
25 #include "wx/cocoa/trackingrectmanager.h"
26 #include "wx/mac/corefoundation/cfref.h"
28 #import <Foundation/NSArray.h>
29 #import <Foundation/NSRunLoop.h>
30 #include "wx/cocoa/objc/NSView.h"
31 #import <AppKit/NSEvent.h>
32 #import <AppKit/NSScrollView.h>
33 #import <AppKit/NSColor.h>
34 #import <AppKit/NSClipView.h>
35 #import <Foundation/NSException.h>
36 #import <AppKit/NSApplication.h>
37 #import <AppKit/NSWindow.h>
38 #import <AppKit/NSScreen.h>
40 // Turn this on to paint green over the dummy views for debugging
41 #undef WXCOCOA_FILL_DUMMY_VIEW
43 #ifdef WXCOCOA_FILL_DUMMY_VIEW
44 #import <AppKit/NSBezierPath.h>
45 #endif //def WXCOCOA_FILL_DUMMY_VIEW
47 // STL list used by wxCocoaMouseMovedEventSynthesizer
50 /* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
51 * NSInteger post-Leopard. Pre-Leopard the Cocoa toolkit expects a function
52 * returning int and not NSComparisonResult. Post-Leopard the Cocoa toolkit
53 * expects a function returning the new non-enum NSComparsionResult.
54 * Hence we create a typedef named CocoaWindowCompareFunctionResult.
56 #if defined(NSINTEGER_DEFINED)
57 typedef NSComparisonResult CocoaWindowCompareFunctionResult;
59 typedef int CocoaWindowCompareFunctionResult;
62 // A category for methods that are only present in Panther's SDK
63 @interface NSView(wxNSViewPrePantherCompatibility)
64 - (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
67 // ========================================================================
68 // Helper functions for converting to/from wxWidgets coordinates and a
69 // specified NSView's coordinate system.
70 // ========================================================================
71 NSPoint CocoaTransformNSViewBoundsToWx(NSView *nsview, NSPoint pointBounds)
73 wxCHECK_MSG(nsview, pointBounds, wxT("Need to have a Cocoa view to do translation"));
74 if([nsview isFlipped])
76 NSRect ourBounds = [nsview bounds];
79 , ourBounds.size.height - pointBounds.y
83 NSRect CocoaTransformNSViewBoundsToWx(NSView *nsview, NSRect rectBounds)
85 wxCHECK_MSG(nsview, rectBounds, wxT("Need to have a Cocoa view to do translation"));
86 if([nsview isFlipped])
88 NSRect ourBounds = [nsview bounds];
91 , ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
92 , rectBounds.size.width
93 , rectBounds.size.height
97 NSPoint CocoaTransformNSViewWxToBounds(NSView *nsview, NSPoint pointWx)
99 wxCHECK_MSG(nsview, pointWx, wxT("Need to have a Cocoa view to do translation"));
100 if([nsview isFlipped])
102 NSRect ourBounds = [nsview bounds];
105 , ourBounds.size.height - pointWx.y
109 NSRect CocoaTransformNSViewWxToBounds(NSView *nsview, NSRect rectWx)
111 wxCHECK_MSG(nsview, rectWx, wxT("Need to have a Cocoa view to do translation"));
112 if([nsview isFlipped])
114 NSRect ourBounds = [nsview bounds];
117 , ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
123 // ============================================================================
124 // Screen coordinate helpers
125 // ============================================================================
128 General observation about Cocoa screen coordinates:
129 It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
131 It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
132 the BOTTOM-right corner of this screen. Recall that Cocoa uses cartesian coordinates so y-increase is up.
134 It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
135 relative to that screen's frame. The only real way to test this is to configure two screens one atop
136 the other such that the menubar screen is on top. The Dock at the bottom of the screen will then
137 eat into the visibleFrame of screen 1 by incrementing it's y-origin. Thus if you arrange two
138 1920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
139 visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
141 In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
142 However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
143 This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
146 namespace { // file namespace
148 class wxCocoaPrivateScreenCoordinateTransformer
150 DECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer)
152 wxCocoaPrivateScreenCoordinateTransformer();
153 ~wxCocoaPrivateScreenCoordinateTransformer();
154 wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
155 NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
158 NSScreen *m_screenZero;
159 NSRect m_screenZeroFrame;
162 // NOTE: This is intended to be a short-lived object. A future enhancment might
163 // make it a global and reconfigure it upon some notification that the screen layout
165 inline wxCocoaPrivateScreenCoordinateTransformer::wxCocoaPrivateScreenCoordinateTransformer()
167 NSArray *screens = [NSScreen screens];
172 if(screens != nil && [screens count] > 0)
173 m_screenZero = [[screens objectAtIndex:0] retain];
177 if(m_screenZero != nil)
178 m_screenZeroFrame = [m_screenZero frame];
181 wxLogWarning(wxT("Can't translate to/from wx screen coordinates and Cocoa screen coordinates"));
182 // Just blindly assume 1024x768 so that at least we can sort of flip things around into
183 // Cocoa coordinates.
184 // NOTE: Theoretically this case should never happen anyway.
185 m_screenZeroFrame = NSMakeRect(0,0,1024,768);
189 inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
191 [m_screenZero release];
195 inline wxPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
197 // x and y are in wx screen coordinates which we're going to arbitrarily define such that
198 // (0,0) is the TOP-left of screen 0 (the one with the menubar)
199 // NOTE WELL: This means that (0,0) is _NOT_ an appropriate position for a window.
203 // Working in Cocoa's screen coordinates we must realize that the x coordinate we want is
204 // the distance between the left side (origin.x) of the window's frame and the left side of
205 // screen zero's frame.
206 theWxOrigin.x = windowFrame.origin.x - m_screenZeroFrame.origin.x;
208 // Working in Cocoa's screen coordinates we must realize that the y coordinate we want is
209 // actually the distance between the top-left of the screen zero frame and the top-left
210 // of the window's frame.
212 theWxOrigin.y = (m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height) - (windowFrame.origin.y + windowFrame.size.height);
217 inline NSPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
219 NSPoint theCocoaOrigin;
221 // The position is in wx screen coordinates which we're going to arbitrarily define such that
222 // (0,0) is the TOP-left of screen 0 (the one with the menubar)
224 // NOTE: The usable rectangle is smaller and hence we have the keepOriginVisible flag
225 // which will move the origin downward and/or left as necessary if the origin is
226 // inside the screen0 rectangle (i.e. x/y >= 0 in wx coordinates) and outside the
227 // visible frame (i.e. x/y < the top/left of the screen0 visible frame in wx coordinates)
228 // We don't munge origin coordinates < 0 because it actually is possible that the menubar is on
229 // the top of the bottom screen and thus that origin is completely valid!
230 if(keepOriginVisible && (m_screenZero != nil))
232 // Do al of this in wx coordinates because it's far simpler since we're dealing with top/left points
233 wxPoint visibleOrigin = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates([m_screenZero visibleFrame]);
234 if(x >= 0 && x < visibleOrigin.x)
236 if(y >= 0 && y < visibleOrigin.y)
240 // The x coordinate is simple as it's just relative to screen zero's frame
241 theCocoaOrigin.x = m_screenZeroFrame.origin.x + x;
242 // Working in Cocoa's coordinates think to start at the bottom of screen zero's frame and add
243 // the height of that rect which gives us the coordinate for the top of the visible rect. Now realize that
244 // the wx coordinates are flipped so if y is say 10 then we want to be 10 pixels down from that and thus
245 // we subtract y. But then we still need to take into account the size of the window which is h and subtract
246 // that to get the bottom-left origin of the rectangle.
247 theCocoaOrigin.y = m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height - y - height;
249 return theCocoaOrigin;
254 wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
256 wxCocoaPrivateScreenCoordinateTransformer transformer;
257 return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
260 NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
262 wxCocoaPrivateScreenCoordinateTransformer transformer;
263 return transformer.OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,keepOriginVisible);
266 // ========================================================================
267 // wxWindowCocoaHider
268 // ========================================================================
269 class wxWindowCocoaHider: protected wxCocoaNSView
271 DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
273 wxWindowCocoaHider(wxWindow *owner);
274 virtual ~wxWindowCocoaHider();
275 inline WX_NSView GetNSView() { return m_dummyNSView; }
277 wxWindowCocoa *m_owner;
278 WX_NSView m_dummyNSView;
279 virtual void Cocoa_FrameChanged(void);
280 virtual void Cocoa_synthesizeMouseMoved(void) {}
281 #ifdef WXCOCOA_FILL_DUMMY_VIEW
282 virtual bool Cocoa_drawRect(const NSRect& rect);
283 #endif //def WXCOCOA_FILL_DUMMY_VIEW
285 wxWindowCocoaHider();
288 // ========================================================================
289 // wxWindowCocoaScrollView
290 // ========================================================================
291 class wxWindowCocoaScrollView: protected wxCocoaNSView
293 DECLARE_NO_COPY_CLASS(wxWindowCocoaScrollView)
295 wxWindowCocoaScrollView(wxWindow *owner);
296 virtual ~wxWindowCocoaScrollView();
297 inline WX_NSScrollView GetNSScrollView() { return m_cocoaNSScrollView; }
298 void ClientSizeToSize(int &width, int &height);
299 void DoGetClientSize(int *x, int *y) const;
301 void Unencapsulate();
303 wxWindowCocoa *m_owner;
304 WX_NSScrollView m_cocoaNSScrollView;
305 virtual void Cocoa_FrameChanged(void);
306 virtual void Cocoa_synthesizeMouseMoved(void) {}
308 wxWindowCocoaScrollView();
311 // ========================================================================
313 // ========================================================================
314 @interface wxDummyNSView : NSView
315 - (NSView *)hitTest:(NSPoint)aPoint;
317 WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
319 @implementation wxDummyNSView : NSView
320 - (NSView *)hitTest:(NSPoint)aPoint
326 WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
328 // ========================================================================
329 // wxWindowCocoaHider
330 // ========================================================================
331 wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
335 wxASSERT(owner->GetNSViewForHiding());
336 m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
337 initWithFrame:[owner->GetNSViewForHiding() frame]];
338 [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
339 AssociateNSView(m_dummyNSView);
342 wxWindowCocoaHider::~wxWindowCocoaHider()
344 DisassociateNSView(m_dummyNSView);
345 [m_dummyNSView release];
348 void wxWindowCocoaHider::Cocoa_FrameChanged(void)
350 // Keep the real window in synch with the dummy
351 wxASSERT(m_dummyNSView);
352 [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
356 #ifdef WXCOCOA_FILL_DUMMY_VIEW
357 bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
359 NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
360 [[NSColor greenColor] set];
365 #endif //def WXCOCOA_FILL_DUMMY_VIEW
367 // ========================================================================
368 // wxFlippedNSClipView
369 // ========================================================================
370 @interface wxFlippedNSClipView : NSClipView
373 WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
375 @implementation wxFlippedNSClipView : NSClipView
382 WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
384 // ========================================================================
385 // wxWindowCocoaScrollView
386 // ========================================================================
387 wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
390 wxAutoNSAutoreleasePool pool;
392 wxASSERT(owner->GetNSView());
393 m_cocoaNSScrollView = [[NSScrollView alloc]
394 initWithFrame:[owner->GetNSView() frame]];
395 AssociateNSView(m_cocoaNSScrollView);
397 /* Replace the default NSClipView with a flipped one. This ensures
398 scrolling is "pinned" to the top-left instead of bottom-right. */
399 NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
400 initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
401 [m_cocoaNSScrollView setContentView:flippedClip];
402 [flippedClip release];
404 [m_cocoaNSScrollView setBackgroundColor: [NSColor windowBackgroundColor]];
405 [m_cocoaNSScrollView setHasHorizontalScroller: YES];
406 [m_cocoaNSScrollView setHasVerticalScroller: YES];
410 void wxWindowCocoaScrollView::Encapsulate()
412 // Set the scroll view autoresizingMask to match the current NSView
413 [m_cocoaNSScrollView setAutoresizingMask: [m_owner->GetNSView() autoresizingMask]];
414 [m_owner->GetNSView() setAutoresizingMask: NSViewNotSizable];
415 // NOTE: replaceSubView will cause m_cocaNSView to be released
416 // except when it hasn't been added into an NSView hierarchy in which
417 // case it doesn't need to be and this should work out to a no-op
418 m_owner->CocoaReplaceView(m_owner->GetNSView(), m_cocoaNSScrollView);
419 // The NSView is still retained by owner
420 [m_cocoaNSScrollView setDocumentView: m_owner->GetNSView()];
421 // Now it's also retained by the NSScrollView
424 void wxWindowCocoaScrollView::Unencapsulate()
426 [m_cocoaNSScrollView setDocumentView: nil];
427 m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
428 if(![[m_owner->GetNSView() superview] isFlipped])
429 [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
432 wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
434 DisassociateNSView(m_cocoaNSScrollView);
435 [m_cocoaNSScrollView release];
438 void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
440 NSSize frameSize = [NSScrollView
441 frameSizeForContentSize: NSMakeSize(width,height)
442 hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
443 hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
444 borderType: [m_cocoaNSScrollView borderType]];
445 width = (int)frameSize.width;
446 height = (int)frameSize.height;
449 void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
451 NSSize nssize = [m_cocoaNSScrollView contentSize];
453 *x = (int)nssize.width;
455 *y = (int)nssize.height;
458 void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
460 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged"));
461 wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
462 event.SetEventObject(m_owner);
463 m_owner->GetEventHandler()->ProcessEvent(event);
466 // ========================================================================
468 // ========================================================================
469 // normally the base classes aren't included, but wxWindow is special
470 #ifdef __WXUNIVERSAL__
471 IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
473 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
476 BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
479 wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
482 void wxWindowCocoa::Init()
484 m_cocoaNSView = NULL;
486 m_wxCocoaScrollView = NULL;
487 m_isBeingDeleted = false;
489 m_visibleTrackingRectManager = NULL;
493 bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
497 const wxString& name)
499 if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
502 // TODO: create the window
503 m_cocoaNSView = NULL;
504 SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
505 [m_cocoaNSView release];
509 m_parent->AddChild(this);
510 m_parent->CocoaAddChild(this);
511 SetInitialFrameRect(pos,size);
518 wxWindow::~wxWindow()
520 wxAutoNSAutoreleasePool pool;
523 // Make sure our parent (in the wxWidgets sense) is our superview
524 // before we go removing from it.
525 if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
526 CocoaRemoveFromParent();
528 delete m_wxCocoaScrollView;
534 void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
536 // Pool here due to lack of one during wx init phase
537 wxAutoNSAutoreleasePool pool;
539 NSView *childView = child->GetNSViewForSuperview();
542 [m_cocoaNSView addSubview: childView];
543 child->m_isShown = !m_cocoaHider;
546 void wxWindowCocoa::CocoaRemoveFromParent(void)
548 [GetNSViewForSuperview() removeFromSuperview];
551 void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
553 // Clear the visible area tracking rect if we have one.
554 delete m_visibleTrackingRectManager;
555 m_visibleTrackingRectManager = NULL;
557 bool need_debug = cocoaNSView || m_cocoaNSView;
558 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
559 DisassociateNSView(m_cocoaNSView);
560 [cocoaNSView retain];
561 [m_cocoaNSView release];
562 m_cocoaNSView = cocoaNSView;
563 AssociateNSView(m_cocoaNSView);
564 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
567 WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
570 ? m_cocoaHider->GetNSView()
571 : m_wxCocoaScrollView
572 ? m_wxCocoaScrollView->GetNSScrollView()
576 WX_NSView wxWindowCocoa::GetNSViewForHiding() const
578 return m_wxCocoaScrollView
579 ? m_wxCocoaScrollView->GetNSScrollView()
583 NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
585 // TODO: Handle scrolling offset
586 return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
589 NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
591 // TODO: Handle scrolling offset
592 return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
595 NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
597 // TODO: Handle scrolling offset
598 return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
601 NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
603 // TODO: Handle scrolling offset
604 return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
607 WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
609 // TODO: Handle scrolling offset
610 NSAffineTransform *transform = wxDC::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
614 bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
616 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
617 // Recursion can happen if the event loop runs from within the paint
618 // handler. For instance, if an assertion dialog is shown.
619 // FIXME: This seems less than ideal.
622 wxLogDebug(wxT("Paint event recursion!"));
627 // Set m_updateRegion
628 const NSRect *rects = ▭ // The bounding box of the region
629 NSInteger countRects = 1;
630 // Try replacing the larger rectangle with a list of smaller ones:
631 if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
632 [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
634 NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
635 for(int i=0; i<countRects; i++)
637 transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
639 m_updateRegion = wxRegion(transformedRects,countRects);
640 free(transformedRects);
642 wxPaintEvent event(m_windowId);
643 event.SetEventObject(this);
644 bool ret = GetEventHandler()->ProcessEvent(event);
649 void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
651 wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
652 // Mouse events happen at the NSWindow level so we need to convert
653 // into our bounds coordinates then convert to wx coordinates.
654 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
655 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
656 // FIXME: Should we be adjusting for client area origin?
657 const wxPoint &clientorigin = GetClientAreaOrigin();
658 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
659 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
661 event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
662 event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
663 event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
664 event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
666 // TODO: set timestamp?
667 event.SetEventObject(this);
668 event.SetId(GetId());
671 bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
673 wxMouseEvent event(wxEVT_MOTION);
674 InitMouseEvent(event,theEvent);
675 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
676 return GetEventHandler()->ProcessEvent(event);
679 void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
681 wxMouseEvent event(wxEVT_MOTION);
682 NSWindow *window = [GetNSView() window];
683 NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
684 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
686 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
687 // FIXME: Should we be adjusting for client area origin?
688 const wxPoint &clientorigin = GetClientAreaOrigin();
689 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
690 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
692 // TODO: Handle shift, control, alt, meta flags
693 event.SetEventObject(this);
694 event.SetId(GetId());
696 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
697 GetEventHandler()->ProcessEvent(event);
700 bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
702 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
704 m_visibleTrackingRectManager->BeginSynthesizingEvents();
706 // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
707 // some other event comes in. That other event is (guess what) mouse moved events that will be sent
708 // to the NSWindow which will forward them on to the first responder. We are not likely to be the
709 // first responder, so the mouseMoved: events are effectively discarded.
710 [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
712 wxMouseEvent event(wxEVT_ENTER_WINDOW);
713 InitMouseEvent(event,theEvent);
714 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Mouse Entered @%d,%d"),this,event.m_x,event.m_y);
715 return GetEventHandler()->ProcessEvent(event);
721 bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
723 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
725 m_visibleTrackingRectManager->StopSynthesizingEvents();
727 wxMouseEvent event(wxEVT_LEAVE_WINDOW);
728 InitMouseEvent(event,theEvent);
729 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Mouse Exited @%d,%d"),this,event.m_x,event.m_y);
730 return GetEventHandler()->ProcessEvent(event);
736 bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
738 wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
739 InitMouseEvent(event,theEvent);
740 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
741 return GetEventHandler()->ProcessEvent(event);
744 bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
746 wxMouseEvent event(wxEVT_MOTION);
747 InitMouseEvent(event,theEvent);
748 event.m_leftDown = true;
749 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
750 return GetEventHandler()->ProcessEvent(event);
753 bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
755 wxMouseEvent event(wxEVT_LEFT_UP);
756 InitMouseEvent(event,theEvent);
757 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
758 return GetEventHandler()->ProcessEvent(event);
761 bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
763 wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
764 InitMouseEvent(event,theEvent);
765 wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
766 return GetEventHandler()->ProcessEvent(event);
769 bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
771 wxMouseEvent event(wxEVT_MOTION);
772 InitMouseEvent(event,theEvent);
773 event.m_rightDown = true;
774 wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
775 return GetEventHandler()->ProcessEvent(event);
778 bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
780 wxMouseEvent event(wxEVT_RIGHT_UP);
781 InitMouseEvent(event,theEvent);
782 wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
783 return GetEventHandler()->ProcessEvent(event);
786 bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
791 bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
796 bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
801 void wxWindowCocoa::Cocoa_FrameChanged(void)
803 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
804 if(m_visibleTrackingRectManager != NULL)
805 m_visibleTrackingRectManager->RebuildTrackingRect();
806 wxSizeEvent event(GetSize(), m_windowId);
807 event.SetEventObject(this);
808 GetEventHandler()->ProcessEvent(event);
811 bool wxWindowCocoa::Cocoa_resetCursorRects()
813 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
814 if(m_visibleTrackingRectManager != NULL)
815 m_visibleTrackingRectManager->RebuildTrackingRect();
817 if(!m_cursor.GetNSCursor())
820 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
825 bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
827 if(!wxWindowBase::SetCursor(cursor))
829 // Invalidate the cursor rects so the cursor will change
830 [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
834 bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
836 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
837 // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
838 if(m_visibleTrackingRectManager != NULL)
839 m_visibleTrackingRectManager->BuildTrackingRect();
843 bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
845 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
846 // Clear tracking rects. It is imperative this be done before the new window is set.
847 if(m_visibleTrackingRectManager != NULL)
848 m_visibleTrackingRectManager->ClearTrackingRect();
852 bool wxWindow::Close(bool force)
854 // The only reason this function exists is that it is virtual and
855 // wxTopLevelWindowCocoa will override it.
856 return wxWindowBase::Close(force);
859 void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
861 [[oldView superview] replaceSubview:oldView with:newView];
864 void wxWindow::DoEnable(bool enable)
866 CocoaSetEnabled(enable);
869 bool wxWindow::Show(bool show)
871 wxAutoNSAutoreleasePool pool;
872 // If the window is marked as visible, then it shouldn't have a dummy view
873 // If the window is marked hidden, then it should have a dummy view
874 // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
875 // wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
876 // Return false if there isn't a window to show or hide
877 NSView *cocoaView = GetNSViewForHiding();
882 // If state isn't changing, return false
885 CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
886 wxASSERT(![m_cocoaHider->GetNSView() superview]);
889 wxASSERT([cocoaView superview]);
893 // If state isn't changing, return false
896 m_cocoaHider = new wxWindowCocoaHider(this);
897 // NOTE: replaceSubview:with will cause m_cocaNSView to be
898 // (auto)released which balances out addSubview
899 CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
900 // m_coocaNSView is now only retained by us
901 wxASSERT([m_cocoaHider->GetNSView() superview]);
902 wxASSERT(![cocoaView superview]);
908 void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
910 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoSetSizeWindow(%d,%d,%d,%d,Auto: %s%s)"),this,x,y,width,height,(sizeFlags&wxSIZE_AUTO_WIDTH)?"W":".",sizeFlags&wxSIZE_AUTO_HEIGHT?"H":".");
911 int currentX, currentY;
912 int currentW, currentH;
913 DoGetPosition(¤tX, ¤tY);
914 DoGetSize(¤tW, ¤tH);
915 if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
917 if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
920 AdjustForParentClientOrigin(x,y,sizeFlags);
922 wxSize size(wxDefaultSize);
924 if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
926 if(sizeFlags&wxSIZE_AUTO_WIDTH)
928 size=DoGetBestSize();
934 if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
936 if(sizeFlags&wxSIZE_AUTO_HEIGHT)
939 size=DoGetBestSize();
945 DoMoveWindow(x,y,width,height);
950 void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
952 wxWindowBase::DoSetToolTip(tip);
956 m_tooltip->SetWindow((wxWindow *)this);
962 void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
964 wxAutoNSAutoreleasePool pool;
965 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
967 NSView *nsview = GetNSViewForSuperview();
968 NSView *superview = [nsview superview];
970 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
972 NSRect oldFrameRect = [nsview frame];
973 NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
974 [nsview setFrame:newFrameRect];
975 // Be sure to redraw the parent to reflect the changed position
976 [superview setNeedsDisplayInRect:oldFrameRect];
977 [superview setNeedsDisplayInRect:newFrameRect];
980 void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
982 NSView *nsview = GetNSViewForSuperview();
983 NSView *superview = [nsview superview];
984 wxCHECK_RET(superview,wxT("NSView does not have a superview"));
985 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
986 NSRect frameRect = [nsview frame];
988 frameRect.size.width = size.x;
990 frameRect.size.height = size.y;
991 frameRect.origin.x = pos.x;
992 frameRect.origin.y = pos.y;
993 // Tell Cocoa to change the margin between the bottom of the superview
994 // and the bottom of the control. Keeps the control pinned to the top
995 // of its superview so that its position in the wxWidgets coordinate
996 // system doesn't change.
997 if(![superview isFlipped])
998 [nsview setAutoresizingMask: NSViewMinYMargin];
999 // MUST set the mask before setFrame: which can generate a size event
1000 // and cause a scroller to be added!
1001 frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
1002 [nsview setFrame: frameRect];
1006 void wxWindow::DoGetSize(int *w, int *h) const
1008 NSRect cocoaRect = [GetNSViewForSuperview() frame];
1010 *w=(int)cocoaRect.size.width;
1012 *h=(int)cocoaRect.size.height;
1013 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
1016 void wxWindow::DoGetPosition(int *x, int *y) const
1018 NSView *nsview = GetNSViewForSuperview();
1020 NSRect cocoaRect = [nsview frame];
1021 NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
1023 *x=(int)rectWx.origin.x;
1025 *y=(int)rectWx.origin.y;
1026 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
1029 WXWidget wxWindow::GetHandle() const
1031 return m_cocoaNSView;
1034 wxWindow* wxWindow::GetWxWindow() const
1036 return (wxWindow*) this;
1039 void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1041 [m_cocoaNSView setNeedsDisplay:YES];
1044 void wxWindow::SetFocus()
1046 if([GetNSView() acceptsFirstResponder])
1047 [[GetNSView() window] makeFirstResponder: GetNSView()];
1050 void wxWindow::DoCaptureMouse()
1053 sm_capturedWindow = this;
1056 void wxWindow::DoReleaseMouse()
1059 sm_capturedWindow = NULL;
1062 void wxWindow::DoScreenToClient(int *x, int *y) const
1064 // Point in cocoa screen coordinates:
1065 NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
1066 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1067 NSWindow *theWindow = [clientView window];
1069 // Point in window's base coordinate system:
1070 NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
1071 // Point in view's bounds coordinate system
1072 NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
1073 // Point in wx client coordinates:
1074 NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
1076 *x = theWxClientPoint.x;
1078 *y = theWxClientPoint.y;
1081 void wxWindow::DoClientToScreen(int *x, int *y) const
1083 // Point in wx client coordinates
1084 NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
1086 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1088 // Point in the view's bounds coordinate system
1089 NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
1091 // Point in the window's base coordinate system
1092 NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
1094 NSWindow *theWindow = [clientView window];
1095 // Point in Cocoa's screen coordinates
1096 NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
1098 // Act as though this was the origin of a 0x0 rectangle
1099 NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
1101 // Convert that rectangle to wx coordinates
1102 wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
1104 *x = theWxScreenPoint.x;
1106 *y = theWxScreenPoint.y;
1109 // Get size *available for subwindows* i.e. excluding menu bar etc.
1110 void wxWindow::DoGetClientSize(int *x, int *y) const
1112 wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
1113 if(m_wxCocoaScrollView)
1114 m_wxCocoaScrollView->DoGetClientSize(x,y);
1116 wxWindowCocoa::DoGetSize(x,y);
1119 void wxWindow::DoSetClientSize(int width, int height)
1121 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
1122 if(m_wxCocoaScrollView)
1123 m_wxCocoaScrollView->ClientSizeToSize(width,height);
1124 CocoaSetWxWindowSize(width,height);
1127 void wxWindow::CocoaSetWxWindowSize(int width, int height)
1129 wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1132 void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1134 // Intentional no-op.
1137 wxString wxWindow::GetLabel() const
1139 // General Get/Set of labels is implemented in wxControlBase
1140 wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
1141 return wxEmptyString;
1144 int wxWindow::GetCharHeight() const
1150 int wxWindow::GetCharWidth() const
1156 void wxWindow::GetTextExtent(const wxString& string, int *x, int *y,
1157 int *descent, int *externalLeading, const wxFont *theFont) const
1162 // Coordinates relative to the window
1163 void wxWindow::WarpPointer (int x_pos, int y_pos)
1168 int wxWindow::GetScrollPos(int orient) const
1174 // This now returns the whole range, not just the number
1175 // of positions that we can scroll.
1176 int wxWindow::GetScrollRange(int orient) const
1182 int wxWindow::GetScrollThumb(int orient) const
1188 void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
1193 void wxWindow::CocoaCreateNSScrollView()
1195 if(!m_wxCocoaScrollView)
1197 m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
1201 // New function that will replace some of the above.
1202 void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
1203 int range, bool refresh)
1205 CocoaCreateNSScrollView();
1209 // Does a physical scroll
1210 void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1215 void wxWindow::DoSetVirtualSize( int x, int y )
1217 wxWindowBase::DoSetVirtualSize(x,y);
1218 CocoaCreateNSScrollView();
1219 [m_cocoaNSView setFrameSize:NSMakeSize(m_virtualSize.x,m_virtualSize.y)];
1222 bool wxWindow::SetFont(const wxFont& font)
1224 // FIXME: We may need to handle wx font inheritance.
1225 return wxWindowBase::SetFont(font);
1228 #if 0 // these are used when debugging the algorithm.
1229 static char const * const comparisonresultStrings[] =
1236 class CocoaWindowCompareContext
1238 DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
1240 CocoaWindowCompareContext(); // Not implemented
1241 CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1244 // Cocoa sorts subviews in-place.. make a copy
1245 m_subviews = [subviews copy];
1247 ~CocoaWindowCompareContext()
1248 { // release the copy
1249 [m_subviews release];
1252 { return m_target; }
1254 { return m_subviews; }
1255 /* Helper function that returns the comparison based off of the original ordering */
1256 CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1258 NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1259 NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1260 // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1261 // likely compare higher than the other view which is reasonable considering the only way that
1262 // can happen is if the subview was added after our call to subviews but before the call to
1263 // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
1264 // that case should never occur anyway because that would imply a multi-threaded GUI call
1265 // which is a big no-no with Cocoa.
1267 // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1268 NSComparisonResult result = (firstI < secondI)
1269 ? NSOrderedAscending /* -1 */
1270 : (firstI > secondI)
1271 ? NSOrderedDescending /* 1 */
1272 : NSOrderedSame /* 0 */;
1274 #if 0 // Enable this if you need to debug the algorithm.
1275 NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
1280 /* The subview we are trying to Raise or Lower */
1282 /* A copy of the original array of subviews */
1283 NSArray *m_subviews;
1286 /* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1287 * the target view is always higher than every other view. When comparing two views neither of
1288 * which is the target, it returns the correct response based on the original ordering
1290 static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
1292 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1293 // first should be ordered higher
1294 if(first==compareContext->target())
1295 return NSOrderedDescending;
1296 // second should be ordered higher
1297 if(second==compareContext->target())
1298 return NSOrderedAscending;
1299 return compareContext->CompareUsingOriginalOrdering(first,second);
1302 // Raise the window to the top of the Z order
1303 void wxWindow::Raise()
1305 // wxAutoNSAutoreleasePool pool;
1306 NSView *nsview = GetNSViewForSuperview();
1307 NSView *superview = [nsview superview];
1308 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1310 [superview sortSubviewsUsingFunction:
1311 CocoaRaiseWindowCompareFunction
1312 context: &compareContext];
1315 /* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1316 * the target view is always lower than every other view. When comparing two views neither of
1317 * which is the target, it returns the correct response based on the original ordering
1319 static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
1321 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1322 // first should be ordered lower
1323 if(first==compareContext->target())
1324 return NSOrderedAscending;
1325 // second should be ordered lower
1326 if(second==compareContext->target())
1327 return NSOrderedDescending;
1328 return compareContext->CompareUsingOriginalOrdering(first,second);
1331 // Lower the window to the bottom of the Z order
1332 void wxWindow::Lower()
1334 NSView *nsview = GetNSViewForSuperview();
1335 NSView *superview = [nsview superview];
1336 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1339 NSLog(@"Target:\n%@\n", nsview);
1340 NSLog(@"Before:\n%@\n", compareContext.subviews());
1342 [superview sortSubviewsUsingFunction:
1343 CocoaLowerWindowCompareFunction
1344 context: &compareContext];
1346 NSLog(@"After:\n%@\n", [superview subviews]);
1350 bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1355 // Get the window with the focus
1356 wxWindow *wxWindowBase::DoFindFocus()
1358 // Basically we are somewhat emulating the responder chain here except
1359 // we are only loking for the first responder in the key window or
1360 // upon failing to find one if the main window is different we look
1361 // for the first responder in the main window.
1363 // Note that the firstResponder doesn't necessarily have to be an
1364 // NSView but wxCocoaNSView::GetFromCocoa() will simply return
1365 // NULL unless it finds its argument in its hash map.
1369 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
1370 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
1372 return win->GetWxWindow();
1374 NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1375 if(mainWindow == keyWindow)
1377 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
1379 return win->GetWxWindow();
1384 /* static */ wxWindow *wxWindowBase::GetCapture()
1387 return wxWindowCocoa::sm_capturedWindow;
1390 wxWindow *wxGetActiveWindow()
1396 wxPoint wxGetMousePosition()
1399 return wxDefaultPosition;
1402 wxMouseState wxGetMouseState()
1409 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1411 pt = wxGetMousePosition();
1416 // ========================================================================
1417 // wxCocoaMouseMovedEventSynthesizer
1418 // ========================================================================
1420 /* This class registers one run loop observer to cover all windows registered with it.
1421 * It will register the observer when the first view is registerd and unregister the
1422 * observer when the last view is unregistered.
1423 * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
1424 * is no reason it couldn't be instantiated multiple times.
1426 class wxCocoaMouseMovedEventSynthesizer
1428 DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
1430 wxCocoaMouseMovedEventSynthesizer()
1431 { m_lastScreenMouseLocation = NSZeroPoint;
1433 ~wxCocoaMouseMovedEventSynthesizer();
1434 void RegisterWxCocoaView(wxCocoaNSView *aView);
1435 void UnregisterWxCocoaView(wxCocoaNSView *aView);
1436 void SynthesizeMouseMovedEvent();
1439 void AddRunLoopObserver();
1440 void RemoveRunLoopObserver();
1441 wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
1442 std::list<wxCocoaNSView*> m_registeredViews;
1443 NSPoint m_lastScreenMouseLocation;
1444 static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
1447 void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
1449 m_registeredViews.push_back(aView);
1450 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Registered wxCocoaNSView=%p"), aView);
1452 if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
1454 AddRunLoopObserver();
1458 void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
1460 m_registeredViews.remove(aView);
1461 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Unregistered wxCocoaNSView=%p"), aView);
1462 if(m_registeredViews.empty() && m_runLoopObserver != NULL)
1464 RemoveRunLoopObserver();
1468 wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
1470 if(!m_registeredViews.empty())
1472 // This means failure to clean up so we report on it as a debug message.
1473 wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
1474 m_registeredViews.clear();
1476 if(m_runLoopObserver != NULL)
1478 // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
1479 wxLogDebug(wxT("Removing run loop observer during static destruction time."));
1480 RemoveRunLoopObserver();
1484 void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
1486 reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
1489 void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
1491 CFRunLoopObserverContext observerContext =
1498 m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
1499 CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1500 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
1503 void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
1505 CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1506 m_runLoopObserver.reset();
1507 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
1510 void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
1512 NSPoint screenMouseLocation = [NSEvent mouseLocation];
1513 // Checking the last mouse location is done for a few reasons:
1514 // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
1515 // telling the app the mouse moved when the user hit a key for instance.
1516 // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
1517 // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
1518 // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
1519 // loop causing the tracking rectangles to constantly be reset.
1520 if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
1522 m_lastScreenMouseLocation = screenMouseLocation;
1523 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
1524 for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
1526 (*i)->Cocoa_synthesizeMouseMoved();
1531 // Singleton used for all views:
1532 static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
1534 // ========================================================================
1535 // wxCocoaTrackingRectManager
1536 // ========================================================================
1538 wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
1541 m_isTrackingRectActive = false;
1542 BuildTrackingRect();
1545 void wxCocoaTrackingRectManager::ClearTrackingRect()
1547 if(m_isTrackingRectActive)
1549 [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
1550 m_isTrackingRectActive = false;
1552 // If we were doing periodic events we need to clear those too
1553 StopSynthesizingEvents();
1556 void wxCocoaTrackingRectManager::StopSynthesizingEvents()
1558 s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
1561 void wxCocoaTrackingRectManager::BuildTrackingRect()
1563 // Pool here due to lack of one during wx init phase
1564 wxAutoNSAutoreleasePool pool;
1566 wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
1567 if([m_window->GetNSView() window] != nil)
1569 m_trackingRectTag = [m_window->GetNSView() addTrackingRect:[m_window->GetNSView() visibleRect] owner:m_window->GetNSView() userData:NULL assumeInside:NO];
1570 m_isTrackingRectActive = true;
1574 void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
1576 s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
1579 void wxCocoaTrackingRectManager::RebuildTrackingRect()
1581 ClearTrackingRect();
1582 BuildTrackingRect();
1585 wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
1587 ClearTrackingRect();
1590 bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
1592 return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);