]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/window.mm
restored expanders
[wxWidgets.git] / src / cocoa / window.mm
CommitLineData
fb896a32
DE
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/cocoa/window.mm
3// Purpose: wxWindowCocoa
4// Author: David Elliott
5// Modified by:
6// Created: 2002/12/26
8d8d3633 7// RCS-ID: $Id$
fb896a32 8// Copyright: (c) 2002 David Elliott
8d8d3633 9// Licence: wxWidgets licence
fb896a32
DE
10/////////////////////////////////////////////////////////////////////////////
11
449c5673 12#include "wx/wxprec.h"
da80ae71 13
449c5673
DE
14#ifndef WX_PRECOMP
15 #include "wx/log.h"
16 #include "wx/window.h"
4db3c8ac 17 #include "wx/dc.h"
ca5db7b2 18 #include "wx/utils.h"
449c5673 19#endif //WX_PRECOMP
da80ae71 20
e73ae747 21#include "wx/tooltip.h"
fb896a32 22
7fc77f30 23#include "wx/cocoa/autorelease.h"
26191790 24#include "wx/cocoa/string.h"
7c5a378f 25#include "wx/cocoa/trackingrectmanager.h"
8ea3a63e 26#include "wx/mac/corefoundation/cfref.h"
7fc77f30 27
369559ca 28#import <Foundation/NSArray.h>
7c5a378f 29#import <Foundation/NSRunLoop.h>
829a2e95 30#include "wx/cocoa/objc/NSView.h"
69dbb709 31#import <AppKit/NSEvent.h>
816c52cf
DE
32#import <AppKit/NSScrollView.h>
33#import <AppKit/NSColor.h>
34#import <AppKit/NSClipView.h>
dc5bcaef 35#import <Foundation/NSException.h>
67d2dac8
DE
36#import <AppKit/NSApplication.h>
37#import <AppKit/NSWindow.h>
15f37147 38#import <AppKit/NSScreen.h>
dc5bcaef 39
75e4856b
DE
40// Turn this on to paint green over the dummy views for debugging
41#undef WXCOCOA_FILL_DUMMY_VIEW
42
43#ifdef WXCOCOA_FILL_DUMMY_VIEW
44#import <AppKit/NSBezierPath.h>
45#endif //def WXCOCOA_FILL_DUMMY_VIEW
46
8ea3a63e
DE
47// STL list used by wxCocoaMouseMovedEventSynthesizer
48#include <list>
49
4799f3ba
DE
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.
55 */
56#if defined(NSINTEGER_DEFINED)
57typedef NSComparisonResult CocoaWindowCompareFunctionResult;
58#else
59typedef int CocoaWindowCompareFunctionResult;
60#endif
61
5dc47140
DE
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;
65@end
66
15f37147
DE
67// ========================================================================
68// Helper functions for converting to/from wxWidgets coordinates and a
69// specified NSView's coordinate system.
70// ========================================================================
ee022549
DE
71NSPoint CocoaTransformNSViewBoundsToWx(NSView *nsview, NSPoint pointBounds)
72{
73 wxCHECK_MSG(nsview, pointBounds, wxT("Need to have a Cocoa view to do translation"));
74 if([nsview isFlipped])
75 return pointBounds;
76 NSRect ourBounds = [nsview bounds];
77 return NSMakePoint
78 ( pointBounds.x
79 , ourBounds.size.height - pointBounds.y
80 );
81}
82
83NSRect CocoaTransformNSViewBoundsToWx(NSView *nsview, NSRect rectBounds)
84{
85 wxCHECK_MSG(nsview, rectBounds, wxT("Need to have a Cocoa view to do translation"));
86 if([nsview isFlipped])
87 return rectBounds;
88 NSRect ourBounds = [nsview bounds];
89 return NSMakeRect
90 ( rectBounds.origin.x
91 , ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
92 , rectBounds.size.width
93 , rectBounds.size.height
94 );
95}
96
97NSPoint CocoaTransformNSViewWxToBounds(NSView *nsview, NSPoint pointWx)
98{
99 wxCHECK_MSG(nsview, pointWx, wxT("Need to have a Cocoa view to do translation"));
100 if([nsview isFlipped])
101 return pointWx;
102 NSRect ourBounds = [nsview bounds];
103 return NSMakePoint
104 ( pointWx.x
105 , ourBounds.size.height - pointWx.y
106 );
107}
108
109NSRect CocoaTransformNSViewWxToBounds(NSView *nsview, NSRect rectWx)
110{
111 wxCHECK_MSG(nsview, rectWx, wxT("Need to have a Cocoa view to do translation"));
112 if([nsview isFlipped])
113 return rectWx;
114 NSRect ourBounds = [nsview bounds];
115 return NSMakeRect
116 ( rectWx.origin.x
117 , ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
118 , rectWx.size.width
119 , rectWx.size.height
120 );
121}
122
15f37147
DE
123// ============================================================================
124// Screen coordinate helpers
125// ============================================================================
126
127/*
128General observation about Cocoa screen coordinates:
129It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
130
131It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
132the BOTTOM-right corner of this screen. Recall that Cocoa uses cartesian coordinates so y-increase is up.
133
134It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
135relative to that screen's frame. The only real way to test this is to configure two screens one atop
136the other such that the menubar screen is on top. The Dock at the bottom of the screen will then
137eat into the visibleFrame of screen 1 by incrementing it's y-origin. Thus if you arrange two
1381920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
139visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
140
141In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
142However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
143This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
144*/
145
146namespace { // file namespace
147
148class wxCocoaPrivateScreenCoordinateTransformer
149{
150 DECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer)
151public:
152 wxCocoaPrivateScreenCoordinateTransformer();
153 ~wxCocoaPrivateScreenCoordinateTransformer();
154 wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
155 NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
156
157protected:
158 NSScreen *m_screenZero;
159 NSRect m_screenZeroFrame;
160};
161
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
164// has changed.
165inline wxCocoaPrivateScreenCoordinateTransformer::wxCocoaPrivateScreenCoordinateTransformer()
166{
167 NSArray *screens = [NSScreen screens];
168
169 [screens retain];
170
171 m_screenZero = nil;
172 if(screens != nil && [screens count] > 0)
173 m_screenZero = [[screens objectAtIndex:0] retain];
174
175 [screens release];
176
177 if(m_screenZero != nil)
178 m_screenZeroFrame = [m_screenZero frame];
179 else
180 {
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);
186 }
187}
188
189inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
190{
191 [m_screenZero release];
192 m_screenZero = nil;
193}
194
195inline wxPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
196{
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.
200
201 wxPoint theWxOrigin;
202
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;
207
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.
211
212 theWxOrigin.y = (m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height) - (windowFrame.origin.y + windowFrame.size.height);
213
214 return theWxOrigin;
215}
216
217inline NSPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
218{
219 NSPoint theCocoaOrigin;
220
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)
223
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))
231 {
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)
235 x = visibleOrigin.x;
236 if(y >= 0 && y < visibleOrigin.y)
237 y = visibleOrigin.y;
238 }
239
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;
248
249 return theCocoaOrigin;
250}
251
252} // namespace
253
254wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
255{
256 wxCocoaPrivateScreenCoordinateTransformer transformer;
257 return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
258}
259
260NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
261{
262 wxCocoaPrivateScreenCoordinateTransformer transformer;
263 return transformer.OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,keepOriginVisible);
264}
265
a82b8141
DE
266// ========================================================================
267// wxWindowCocoaHider
268// ========================================================================
269class wxWindowCocoaHider: protected wxCocoaNSView
270{
271 DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
272public:
273 wxWindowCocoaHider(wxWindow *owner);
274 virtual ~wxWindowCocoaHider();
275 inline WX_NSView GetNSView() { return m_dummyNSView; }
276protected:
277 wxWindowCocoa *m_owner;
278 WX_NSView m_dummyNSView;
279 virtual void Cocoa_FrameChanged(void);
7c5a378f 280 virtual void Cocoa_synthesizeMouseMoved(void) {}
75e4856b
DE
281#ifdef WXCOCOA_FILL_DUMMY_VIEW
282 virtual bool Cocoa_drawRect(const NSRect& rect);
283#endif //def WXCOCOA_FILL_DUMMY_VIEW
a82b8141
DE
284private:
285 wxWindowCocoaHider();
286};
287
816c52cf 288// ========================================================================
f298b203 289// wxWindowCocoaScrollView
816c52cf 290// ========================================================================
f298b203 291class wxWindowCocoaScrollView: protected wxCocoaNSView
816c52cf 292{
f298b203 293 DECLARE_NO_COPY_CLASS(wxWindowCocoaScrollView)
816c52cf 294public:
f298b203
DE
295 wxWindowCocoaScrollView(wxWindow *owner);
296 virtual ~wxWindowCocoaScrollView();
816c52cf
DE
297 inline WX_NSScrollView GetNSScrollView() { return m_cocoaNSScrollView; }
298 void ClientSizeToSize(int &width, int &height);
299 void DoGetClientSize(int *x, int *y) const;
300 void Encapsulate();
301 void Unencapsulate();
302protected:
303 wxWindowCocoa *m_owner;
304 WX_NSScrollView m_cocoaNSScrollView;
305 virtual void Cocoa_FrameChanged(void);
7c5a378f 306 virtual void Cocoa_synthesizeMouseMoved(void) {}
816c52cf 307private:
f298b203 308 wxWindowCocoaScrollView();
816c52cf
DE
309};
310
75e4856b
DE
311// ========================================================================
312// wxDummyNSView
313// ========================================================================
314@interface wxDummyNSView : NSView
315- (NSView *)hitTest:(NSPoint)aPoint;
316@end
a24aa427 317WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
75e4856b
DE
318
319@implementation wxDummyNSView : NSView
320- (NSView *)hitTest:(NSPoint)aPoint
321{
322 return nil;
323}
324
325@end
a24aa427 326WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
75e4856b 327
a82b8141
DE
328// ========================================================================
329// wxWindowCocoaHider
330// ========================================================================
331wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
332: m_owner(owner)
333{
334 wxASSERT(owner);
335 wxASSERT(owner->GetNSViewForHiding());
a24aa427 336 m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
a82b8141 337 initWithFrame:[owner->GetNSViewForHiding() frame]];
75e4856b 338 [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
a82b8141
DE
339 AssociateNSView(m_dummyNSView);
340}
341
342wxWindowCocoaHider::~wxWindowCocoaHider()
343{
344 DisassociateNSView(m_dummyNSView);
345 [m_dummyNSView release];
346}
347
348void wxWindowCocoaHider::Cocoa_FrameChanged(void)
349{
350 // Keep the real window in synch with the dummy
351 wxASSERT(m_dummyNSView);
352 [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
353}
354
5558135c 355
75e4856b
DE
356#ifdef WXCOCOA_FILL_DUMMY_VIEW
357bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
358{
359 NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
360 [[NSColor greenColor] set];
361 [bezpath stroke];
362 [bezpath fill];
363 return true;
364}
365#endif //def WXCOCOA_FILL_DUMMY_VIEW
366
816c52cf
DE
367// ========================================================================
368// wxFlippedNSClipView
369// ========================================================================
370@interface wxFlippedNSClipView : NSClipView
371- (BOOL)isFlipped;
372@end
a24aa427 373WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
816c52cf
DE
374
375@implementation wxFlippedNSClipView : NSClipView
376- (BOOL)isFlipped
377{
378 return YES;
379}
380
381@end
a24aa427 382WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
816c52cf
DE
383
384// ========================================================================
f298b203 385// wxWindowCocoaScrollView
816c52cf 386// ========================================================================
f298b203 387wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
816c52cf
DE
388: m_owner(owner)
389{
a9861c57 390 wxAutoNSAutoreleasePool pool;
816c52cf
DE
391 wxASSERT(owner);
392 wxASSERT(owner->GetNSView());
393 m_cocoaNSScrollView = [[NSScrollView alloc]
394 initWithFrame:[owner->GetNSView() frame]];
395 AssociateNSView(m_cocoaNSScrollView);
396
397 /* Replace the default NSClipView with a flipped one. This ensures
398 scrolling is "pinned" to the top-left instead of bottom-right. */
a24aa427 399 NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
816c52cf
DE
400 initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
401 [m_cocoaNSScrollView setContentView:flippedClip];
402 [flippedClip release];
403
404 [m_cocoaNSScrollView setBackgroundColor: [NSColor windowBackgroundColor]];
405 [m_cocoaNSScrollView setHasHorizontalScroller: YES];
406 [m_cocoaNSScrollView setHasVerticalScroller: YES];
407 Encapsulate();
408}
409
f298b203 410void wxWindowCocoaScrollView::Encapsulate()
816c52cf 411{
6f2ec3c3
DE
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];
816c52cf
DE
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
422}
423
f298b203 424void wxWindowCocoaScrollView::Unencapsulate()
816c52cf
DE
425{
426 [m_cocoaNSScrollView setDocumentView: nil];
427 m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
6f2ec3c3
DE
428 if(![[m_owner->GetNSView() superview] isFlipped])
429 [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
816c52cf
DE
430}
431
f298b203 432wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
816c52cf
DE
433{
434 DisassociateNSView(m_cocoaNSScrollView);
435 [m_cocoaNSScrollView release];
436}
437
f298b203 438void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
816c52cf
DE
439{
440 NSSize frameSize = [NSScrollView
441 frameSizeForContentSize: NSMakeSize(width,height)
442 hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
443 hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
444 borderType: [m_cocoaNSScrollView borderType]];
e9cece45
VZ
445 width = (int)frameSize.width;
446 height = (int)frameSize.height;
816c52cf
DE
447}
448
f298b203 449void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
816c52cf
DE
450{
451 NSSize nssize = [m_cocoaNSScrollView contentSize];
452 if(x)
e9cece45 453 *x = (int)nssize.width;
816c52cf 454 if(y)
e9cece45 455 *y = (int)nssize.height;
816c52cf
DE
456}
457
f298b203 458void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
816c52cf 459{
48580976 460 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged"));
816c52cf
DE
461 wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
462 event.SetEventObject(m_owner);
463 m_owner->GetEventHandler()->ProcessEvent(event);
464}
465
a82b8141
DE
466// ========================================================================
467// wxWindowCocoa
468// ========================================================================
fb896a32
DE
469// normally the base classes aren't included, but wxWindow is special
470#ifdef __WXUNIVERSAL__
471IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
472#else
473IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
474#endif
475
476BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
477END_EVENT_TABLE()
478
b9505233
DE
479wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
480
fb896a32
DE
481// Constructor
482void wxWindowCocoa::Init()
483{
fb896a32 484 m_cocoaNSView = NULL;
a82b8141 485 m_cocoaHider = NULL;
f298b203 486 m_wxCocoaScrollView = NULL;
8d8d3633
WS
487 m_isBeingDeleted = false;
488 m_isInPaint = false;
7c5a378f 489 m_visibleTrackingRectManager = NULL;
fb896a32
DE
490}
491
492// Constructor
493bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
494 const wxPoint& pos,
495 const wxSize& size,
496 long style,
497 const wxString& name)
498{
499 if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
500 return false;
501
502 // TODO: create the window
fb896a32 503 m_cocoaNSView = NULL;
a24aa427 504 SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
fb896a32
DE
505 [m_cocoaNSView release];
506
507 if (m_parent)
508 {
509 m_parent->AddChild(this);
510 m_parent->CocoaAddChild(this);
6d034f7d 511 SetInitialFrameRect(pos,size);
fb896a32
DE
512 }
513
8d8d3633 514 return true;
fb896a32
DE
515}
516
517// Destructor
518wxWindow::~wxWindow()
519{
7fc77f30 520 wxAutoNSAutoreleasePool pool;
fb896a32
DE
521 DestroyChildren();
522
065e208e 523 // Make sure our parent (in the wxWidgets sense) is our superview
6ba13ca4
DE
524 // before we go removing from it.
525 if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
526 CocoaRemoveFromParent();
a82b8141 527 delete m_cocoaHider;
f298b203 528 delete m_wxCocoaScrollView;
9c85202a
DE
529 if(m_cocoaNSView)
530 SendDestroyEvent();
fb896a32
DE
531 SetNSView(NULL);
532}
533
534void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
535{
b0a207df
DE
536 // Pool here due to lack of one during wx init phase
537 wxAutoNSAutoreleasePool pool;
538
a82b8141
DE
539 NSView *childView = child->GetNSViewForSuperview();
540
541 wxASSERT(childView);
542 [m_cocoaNSView addSubview: childView];
543 child->m_isShown = !m_cocoaHider;
fb896a32
DE
544}
545
546void wxWindowCocoa::CocoaRemoveFromParent(void)
547{
a82b8141 548 [GetNSViewForSuperview() removeFromSuperview];
fb896a32
DE
549}
550
551void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
552{
7c5a378f
DE
553 // Clear the visible area tracking rect if we have one.
554 delete m_visibleTrackingRectManager;
555 m_visibleTrackingRectManager = NULL;
556
fb896a32 557 bool need_debug = cocoaNSView || m_cocoaNSView;
48580976 558 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
bac6f234 559 DisassociateNSView(m_cocoaNSView);
fb896a32
DE
560 [cocoaNSView retain];
561 [m_cocoaNSView release];
562 m_cocoaNSView = cocoaNSView;
bac6f234 563 AssociateNSView(m_cocoaNSView);
48580976 564 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
fb896a32
DE
565}
566
a82b8141
DE
567WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
568{
569 return m_cocoaHider
570 ? m_cocoaHider->GetNSView()
f298b203
DE
571 : m_wxCocoaScrollView
572 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 573 : m_cocoaNSView;
a82b8141
DE
574}
575
576WX_NSView wxWindowCocoa::GetNSViewForHiding() const
577{
f298b203
DE
578 return m_wxCocoaScrollView
579 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 580 : m_cocoaNSView;
a82b8141
DE
581}
582
34c9978d
DE
583NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
584{
585 // TODO: Handle scrolling offset
ee022549 586 return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
34c9978d
DE
587}
588
589NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
590{
591 // TODO: Handle scrolling offset
ee022549 592 return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
34c9978d
DE
593}
594
595NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
596{
597 // TODO: Handle scrolling offset
ee022549 598 return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
34c9978d
DE
599}
600
601NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
602{
603 // TODO: Handle scrolling offset
ee022549 604 return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
34c9978d
DE
605}
606
4db3c8ac
DE
607WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
608{
609 // TODO: Handle scrolling offset
610 NSAffineTransform *transform = wxDC::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
611 return transform;
612}
613
8ea5271e
DE
614bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
615{
48580976 616 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
55c5be5e
DE
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.
620 if(m_isInPaint)
621 {
2b030203 622 wxLogDebug(wxT("Paint event recursion!"));
55c5be5e
DE
623 return false;
624 }
8d8d3633 625 m_isInPaint = true;
dc5bcaef
DE
626
627 // Set m_updateRegion
628 const NSRect *rects = &rect; // The bounding box of the region
4799f3ba 629 NSInteger countRects = 1;
dc5bcaef 630 // Try replacing the larger rectangle with a list of smaller ones:
5dc47140
DE
631 if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
632 [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
34c9978d
DE
633
634 NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
635 for(int i=0; i<countRects; i++)
636 {
637 transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
638 }
639 m_updateRegion = wxRegion(transformedRects,countRects);
640 free(transformedRects);
dc5bcaef 641
8ea5271e
DE
642 wxPaintEvent event(m_windowId);
643 event.SetEventObject(this);
55c5be5e 644 bool ret = GetEventHandler()->ProcessEvent(event);
8d8d3633 645 m_isInPaint = false;
55c5be5e 646 return ret;
8ea5271e
DE
647}
648
69dbb709
DE
649void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
650{
2b030203 651 wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
34c9978d
DE
652 // Mouse events happen at the NSWindow level so we need to convert
653 // into our bounds coordinates then convert to wx coordinates.
a82b8141 654 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
34c9978d
DE
655 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
656 // FIXME: Should we be adjusting for client area origin?
69dbb709 657 const wxPoint &clientorigin = GetClientAreaOrigin();
34c9978d
DE
658 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
659 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
69dbb709
DE
660
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;
665
666 // TODO: set timestamp?
667 event.SetEventObject(this);
668 event.SetId(GetId());
669}
670
671bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
672{
673 wxMouseEvent event(wxEVT_MOTION);
674 InitMouseEvent(event,theEvent);
7c5a378f 675 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
69dbb709
DE
676 return GetEventHandler()->ProcessEvent(event);
677}
678
7c5a378f
DE
679void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
680{
681 wxMouseEvent event(wxEVT_MOTION);
682 NSWindow *window = [GetNSView() window];
683 NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
684 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
685
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;
691
692 // TODO: Handle shift, control, alt, meta flags
693 event.SetEventObject(this);
694 event.SetId(GetId());
695
696 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
697 GetEventHandler()->ProcessEvent(event);
698}
699
69dbb709
DE
700bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
701{
7c5a378f
DE
702 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
703 {
704 m_visibleTrackingRectManager->BeginSynthesizingEvents();
705
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];
711
712 wxMouseEvent event(wxEVT_ENTER_WINDOW);
713 InitMouseEvent(event,theEvent);
18b32eb5 714 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
7c5a378f
DE
715 return GetEventHandler()->ProcessEvent(event);
716 }
717 else
718 return false;
69dbb709
DE
719}
720
721bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
722{
7c5a378f
DE
723 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
724 {
725 m_visibleTrackingRectManager->StopSynthesizingEvents();
726
727 wxMouseEvent event(wxEVT_LEAVE_WINDOW);
728 InitMouseEvent(event,theEvent);
18b32eb5 729 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
7c5a378f
DE
730 return GetEventHandler()->ProcessEvent(event);
731 }
732 else
733 return false;
69dbb709
DE
734}
735
736bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
737{
738 wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
739 InitMouseEvent(event,theEvent);
48580976 740 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
69dbb709
DE
741 return GetEventHandler()->ProcessEvent(event);
742}
743
744bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
745{
746 wxMouseEvent event(wxEVT_MOTION);
747 InitMouseEvent(event,theEvent);
748 event.m_leftDown = true;
48580976 749 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
69dbb709
DE
750 return GetEventHandler()->ProcessEvent(event);
751}
752
753bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
754{
755 wxMouseEvent event(wxEVT_LEFT_UP);
756 InitMouseEvent(event,theEvent);
48580976 757 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
69dbb709
DE
758 return GetEventHandler()->ProcessEvent(event);
759}
760
761bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
762{
eafde5c7
DE
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);
69dbb709
DE
767}
768
769bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
770{
eafde5c7
DE
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);
69dbb709
DE
776}
777
778bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
779{
eafde5c7
DE
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);
69dbb709
DE
784}
785
786bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
787{
788 return false;
789}
790
791bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
792{
793 return false;
794}
795
796bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
797{
798 return false;
799}
800
fb896a32
DE
801void wxWindowCocoa::Cocoa_FrameChanged(void)
802{
7c5a378f
DE
803 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
804 if(m_visibleTrackingRectManager != NULL)
805 m_visibleTrackingRectManager->RebuildTrackingRect();
fb896a32
DE
806 wxSizeEvent event(GetSize(), m_windowId);
807 event.SetEventObject(this);
808 GetEventHandler()->ProcessEvent(event);
809}
810
5558135c
RN
811bool wxWindowCocoa::Cocoa_resetCursorRects()
812{
7c5a378f 813 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
ea29564c
DE
814
815 // When we are called there may be a queued tracking rect event (mouse entered or exited) and
816 // we won't know it. A specific example is wxGenericHyperlinkCtrl changing the cursor from its
817 // mouse exited event. If the control happens to share the edge with its parent window which is
818 // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
819 // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
820 // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
821 // which willl in turn call us before exiting for the next user event.
822
823 // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
824 // our mouse exited event because the already queued event will have the old tracking rect
825 // tag. The simple solution is to only rebuild our tracking rect if we need to.
826
7c5a378f 827 if(m_visibleTrackingRectManager != NULL)
ea29564c 828 m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
7c5a378f 829
5558135c
RN
830 if(!m_cursor.GetNSCursor())
831 return false;
8d8d3633
WS
832
833 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
834
5558135c
RN
835 return true;
836}
837
a8780ad5
DE
838bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
839{
840 if(!wxWindowBase::SetCursor(cursor))
841 return false;
6ebbf01f
DE
842
843 // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
844 // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
845 // rects and thus won't ever call resetCursorRects.
846 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
847
a8780ad5 848 // Invalidate the cursor rects so the cursor will change
6ebbf01f
DE
849 // Note that it is not enough to remove the old one (if any) and add the new one.
850 // For the rects to work properly, Cocoa itself must call resetCursorRects.
a8780ad5
DE
851 [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
852 return true;
853}
854
7c5a378f
DE
855bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
856{
857 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
858 // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
859 if(m_visibleTrackingRectManager != NULL)
860 m_visibleTrackingRectManager->BuildTrackingRect();
861 return false;
862}
863
864bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
865{
866 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
867 // Clear tracking rects. It is imperative this be done before the new window is set.
868 if(m_visibleTrackingRectManager != NULL)
869 m_visibleTrackingRectManager->ClearTrackingRect();
870 return false;
871}
872
fb896a32
DE
873bool wxWindow::Close(bool force)
874{
cc6f960f
DE
875 // The only reason this function exists is that it is virtual and
876 // wxTopLevelWindowCocoa will override it.
877 return wxWindowBase::Close(force);
fb896a32
DE
878}
879
a82b8141
DE
880void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
881{
882 [[oldView superview] replaceSubview:oldView with:newView];
883}
884
47a8a4d5 885void wxWindow::DoEnable(bool enable)
adb4816c 886{
47a8a4d5 887 CocoaSetEnabled(enable);
adb4816c
DE
888}
889
fb896a32
DE
890bool wxWindow::Show(bool show)
891{
7fc77f30 892 wxAutoNSAutoreleasePool pool;
fb896a32
DE
893 // If the window is marked as visible, then it shouldn't have a dummy view
894 // If the window is marked hidden, then it should have a dummy view
addbdd29 895 // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
2b030203 896// wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
fb896a32 897 // Return false if there isn't a window to show or hide
a82b8141
DE
898 NSView *cocoaView = GetNSViewForHiding();
899 if(!cocoaView)
fb896a32 900 return false;
fb896a32
DE
901 if(show)
902 {
addbdd29 903 // If state isn't changing, return false
a82b8141 904 if(!m_cocoaHider)
addbdd29 905 return false;
a82b8141
DE
906 CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
907 wxASSERT(![m_cocoaHider->GetNSView() superview]);
908 delete m_cocoaHider;
909 m_cocoaHider = NULL;
910 wxASSERT([cocoaView superview]);
fb896a32
DE
911 }
912 else
913 {
addbdd29 914 // If state isn't changing, return false
a82b8141 915 if(m_cocoaHider)
addbdd29 916 return false;
a82b8141
DE
917 m_cocoaHider = new wxWindowCocoaHider(this);
918 // NOTE: replaceSubview:with will cause m_cocaNSView to be
919 // (auto)released which balances out addSubview
920 CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
fb896a32 921 // m_coocaNSView is now only retained by us
a82b8141
DE
922 wxASSERT([m_cocoaHider->GetNSView() superview]);
923 wxASSERT(![cocoaView superview]);
fb896a32 924 }
a6b4ff2e
DE
925 m_isShown = show;
926 return true;
fb896a32
DE
927}
928
929void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
930{
9879fa84 931 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":".");
fb896a32
DE
932 int currentX, currentY;
933 int currentW, currentH;
934 DoGetPosition(&currentX, &currentY);
935 DoGetSize(&currentW, &currentH);
936 if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
937 x=currentX;
938 if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
939 y=currentY;
940
941 AdjustForParentClientOrigin(x,y,sizeFlags);
942
8d8d3633 943 wxSize size(wxDefaultSize);
fb896a32
DE
944
945 if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
946 {
947 if(sizeFlags&wxSIZE_AUTO_WIDTH)
948 {
949 size=DoGetBestSize();
950 width=size.x;
951 }
952 else
953 width=currentW;
954 }
955 if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
956 {
957 if(sizeFlags&wxSIZE_AUTO_HEIGHT)
958 {
959 if(size.x==-1)
960 size=DoGetBestSize();
961 height=size.y;
962 }
963 else
964 height=currentH;
965 }
966 DoMoveWindow(x,y,width,height);
967}
968
1e151594 969#if wxUSE_TOOLTIPS
26191790
RN
970
971void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
972{
973 wxWindowBase::DoSetToolTip(tip);
974
26191790
RN
975 if ( m_tooltip )
976 {
977 m_tooltip->SetWindow((wxWindow *)this);
26191790
RN
978 }
979}
980
1e151594
RN
981#endif
982
fb896a32
DE
983void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
984{
bed6fe0c 985 wxAutoNSAutoreleasePool pool;
9879fa84 986 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
fb896a32 987
a82b8141 988 NSView *nsview = GetNSViewForSuperview();
d449cf47 989 NSView *superview = [nsview superview];
fb896a32 990
34c9978d
DE
991 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
992
993 NSRect oldFrameRect = [nsview frame];
994 NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
995 [nsview setFrame:newFrameRect];
b915b805 996 // Be sure to redraw the parent to reflect the changed position
34c9978d
DE
997 [superview setNeedsDisplayInRect:oldFrameRect];
998 [superview setNeedsDisplayInRect:newFrameRect];
fb896a32
DE
999}
1000
d139c3a8
DE
1001void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
1002{
1003 NSView *nsview = GetNSViewForSuperview();
1004 NSView *superview = [nsview superview];
2b030203 1005 wxCHECK_RET(superview,wxT("NSView does not have a superview"));
34c9978d 1006 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
d139c3a8
DE
1007 NSRect frameRect = [nsview frame];
1008 if(size.x!=-1)
1009 frameRect.size.width = size.x;
1010 if(size.y!=-1)
1011 frameRect.size.height = size.y;
1012 frameRect.origin.x = pos.x;
34c9978d 1013 frameRect.origin.y = pos.y;
c5bd9191
DE
1014 // Tell Cocoa to change the margin between the bottom of the superview
1015 // and the bottom of the control. Keeps the control pinned to the top
065e208e 1016 // of its superview so that its position in the wxWidgets coordinate
c5bd9191
DE
1017 // system doesn't change.
1018 if(![superview isFlipped])
1019 [nsview setAutoresizingMask: NSViewMinYMargin];
6f2ec3c3
DE
1020 // MUST set the mask before setFrame: which can generate a size event
1021 // and cause a scroller to be added!
34c9978d 1022 frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
6f2ec3c3 1023 [nsview setFrame: frameRect];
d139c3a8
DE
1024}
1025
fb896a32
DE
1026// Get total size
1027void wxWindow::DoGetSize(int *w, int *h) const
1028{
a82b8141 1029 NSRect cocoaRect = [GetNSViewForSuperview() frame];
fb896a32
DE
1030 if(w)
1031 *w=(int)cocoaRect.size.width;
1032 if(h)
1033 *h=(int)cocoaRect.size.height;
9879fa84 1034 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
fb896a32
DE
1035}
1036
1037void wxWindow::DoGetPosition(int *x, int *y) const
1038{
a82b8141 1039 NSView *nsview = GetNSViewForSuperview();
fb896a32 1040
576a1544 1041 NSRect cocoaRect = [nsview frame];
34c9978d 1042 NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
fb896a32 1043 if(x)
34c9978d 1044 *x=(int)rectWx.origin.x;
fb896a32 1045 if(y)
34c9978d 1046 *y=(int)rectWx.origin.y;
9879fa84 1047 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
fb896a32
DE
1048}
1049
1050WXWidget wxWindow::GetHandle() const
1051{
1052 return m_cocoaNSView;
1053}
1054
f7e98dee
RN
1055wxWindow* wxWindow::GetWxWindow() const
1056{
1057 return (wxWindow*) this;
1058}
1059
ddf7346a
DE
1060void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1061{
1062 [m_cocoaNSView setNeedsDisplay:YES];
1063}
1064
fb896a32
DE
1065void wxWindow::SetFocus()
1066{
67d2dac8
DE
1067 if([GetNSView() acceptsFirstResponder])
1068 [[GetNSView() window] makeFirstResponder: GetNSView()];
fb896a32
DE
1069}
1070
1071void wxWindow::DoCaptureMouse()
1072{
1073 // TODO
b9505233 1074 sm_capturedWindow = this;
fb896a32
DE
1075}
1076
1077void wxWindow::DoReleaseMouse()
1078{
1079 // TODO
b9505233 1080 sm_capturedWindow = NULL;
fb896a32
DE
1081}
1082
1083void wxWindow::DoScreenToClient(int *x, int *y) const
1084{
15f37147
DE
1085 // Point in cocoa screen coordinates:
1086 NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
1087 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1088 NSWindow *theWindow = [clientView window];
1089
1090 // Point in window's base coordinate system:
1091 NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
1092 // Point in view's bounds coordinate system
1093 NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
1094 // Point in wx client coordinates:
1095 NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
1096 if(x!=NULL)
1097 *x = theWxClientPoint.x;
1098 if(y!=NULL)
1099 *y = theWxClientPoint.y;
fb896a32
DE
1100}
1101
1102void wxWindow::DoClientToScreen(int *x, int *y) const
1103{
15f37147
DE
1104 // Point in wx client coordinates
1105 NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
1106
1107 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1108
1109 // Point in the view's bounds coordinate system
1110 NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
1111
1112 // Point in the window's base coordinate system
1113 NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
1114
1115 NSWindow *theWindow = [clientView window];
1116 // Point in Cocoa's screen coordinates
1117 NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
1118
1119 // Act as though this was the origin of a 0x0 rectangle
1120 NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
1121
1122 // Convert that rectangle to wx coordinates
1123 wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
1124 if(*x)
1125 *x = theWxScreenPoint.x;
1126 if(*y)
1127 *y = theWxScreenPoint.y;
fb896a32
DE
1128}
1129
1130// Get size *available for subwindows* i.e. excluding menu bar etc.
1131void wxWindow::DoGetClientSize(int *x, int *y) const
1132{
48580976 1133 wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
f298b203
DE
1134 if(m_wxCocoaScrollView)
1135 m_wxCocoaScrollView->DoGetClientSize(x,y);
816c52cf
DE
1136 else
1137 wxWindowCocoa::DoGetSize(x,y);
fb896a32
DE
1138}
1139
1140void wxWindow::DoSetClientSize(int width, int height)
1141{
48580976 1142 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
f298b203
DE
1143 if(m_wxCocoaScrollView)
1144 m_wxCocoaScrollView->ClientSizeToSize(width,height);
e08efb8d
DE
1145 CocoaSetWxWindowSize(width,height);
1146}
1147
1148void wxWindow::CocoaSetWxWindowSize(int width, int height)
1149{
8d8d3633
WS
1150 wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1151}
1152
1153void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1154{
ba64d0b6 1155 // Intentional no-op.
8d8d3633
WS
1156}
1157
1158wxString wxWindow::GetLabel() const
1159{
ba64d0b6
DE
1160 // General Get/Set of labels is implemented in wxControlBase
1161 wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
8d8d3633 1162 return wxEmptyString;
fb896a32
DE
1163}
1164
1165int wxWindow::GetCharHeight() const
1166{
1167 // TODO
d89e391b 1168 return 10;
fb896a32
DE
1169}
1170
1171int wxWindow::GetCharWidth() const
1172{
1173 // TODO
d89e391b 1174 return 5;
fb896a32
DE
1175}
1176
1177void wxWindow::GetTextExtent(const wxString& string, int *x, int *y,
1178 int *descent, int *externalLeading, const wxFont *theFont) const
1179{
1180 // TODO
1181}
1182
fb896a32
DE
1183// Coordinates relative to the window
1184void wxWindow::WarpPointer (int x_pos, int y_pos)
1185{
1186 // TODO
1187}
1188
1189int wxWindow::GetScrollPos(int orient) const
1190{
1191 // TODO
1192 return 0;
1193}
1194
1195// This now returns the whole range, not just the number
1196// of positions that we can scroll.
1197int wxWindow::GetScrollRange(int orient) const
1198{
1199 // TODO
1200 return 0;
1201}
1202
1203int wxWindow::GetScrollThumb(int orient) const
1204{
1205 // TODO
1206 return 0;
1207}
1208
1209void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
1210{
1211 // TODO
1212}
1213
816c52cf
DE
1214void wxWindow::CocoaCreateNSScrollView()
1215{
f298b203 1216 if(!m_wxCocoaScrollView)
816c52cf 1217 {
f298b203 1218 m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
816c52cf
DE
1219 }
1220}
1221
fb896a32
DE
1222// New function that will replace some of the above.
1223void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
1224 int range, bool refresh)
1225{
e08efb8d 1226 CocoaCreateNSScrollView();
fb896a32
DE
1227 // TODO
1228}
1229
1230// Does a physical scroll
1231void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1232{
1233 // TODO
1234}
1235
816c52cf
DE
1236void wxWindow::DoSetVirtualSize( int x, int y )
1237{
1238 wxWindowBase::DoSetVirtualSize(x,y);
1239 CocoaCreateNSScrollView();
1240 [m_cocoaNSView setFrameSize:NSMakeSize(m_virtualSize.x,m_virtualSize.y)];
1241}
1242
fb896a32
DE
1243bool wxWindow::SetFont(const wxFont& font)
1244{
e7e97a59
DE
1245 // FIXME: We may need to handle wx font inheritance.
1246 return wxWindowBase::SetFont(font);
fb896a32
DE
1247}
1248
aa25b7f9
DE
1249#if 0 // these are used when debugging the algorithm.
1250static char const * const comparisonresultStrings[] =
1251{ "<"
1252, "=="
1253, ">"
1254};
1255#endif
1256
1257class CocoaWindowCompareContext
1258{
1259 DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
1260public:
1261 CocoaWindowCompareContext(); // Not implemented
1262 CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1263 {
1264 m_target = target;
1265 // Cocoa sorts subviews in-place.. make a copy
1266 m_subviews = [subviews copy];
1267 }
1268 ~CocoaWindowCompareContext()
1269 { // release the copy
1270 [m_subviews release];
1271 }
1272 NSView* target()
1273 { return m_target; }
1274 NSArray* subviews()
1275 { return m_subviews; }
1276 /* Helper function that returns the comparison based off of the original ordering */
1277 CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1278 {
1279 NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1280 NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1281 // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1282 // likely compare higher than the other view which is reasonable considering the only way that
1283 // can happen is if the subview was added after our call to subviews but before the call to
1284 // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
1285 // that case should never occur anyway because that would imply a multi-threaded GUI call
1286 // which is a big no-no with Cocoa.
1287
1288 // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1289 NSComparisonResult result = (firstI < secondI)
1290 ? NSOrderedAscending /* -1 */
1291 : (firstI > secondI)
1292 ? NSOrderedDescending /* 1 */
1293 : NSOrderedSame /* 0 */;
1294
1295#if 0 // Enable this if you need to debug the algorithm.
1296 NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
1297#endif
1298 return result;
1299 }
1300private:
1301 /* The subview we are trying to Raise or Lower */
1302 NSView *m_target;
1303 /* A copy of the original array of subviews */
1304 NSArray *m_subviews;
1305};
1306
1307/* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1308 * the target view is always higher than every other view. When comparing two views neither of
1309 * which is the target, it returns the correct response based on the original ordering
1310 */
1311static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1312{
aa25b7f9 1313 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1314 // first should be ordered higher
aa25b7f9 1315 if(first==compareContext->target())
4328a6ba
DE
1316 return NSOrderedDescending;
1317 // second should be ordered higher
aa25b7f9 1318 if(second==compareContext->target())
4328a6ba 1319 return NSOrderedAscending;
aa25b7f9 1320 return compareContext->CompareUsingOriginalOrdering(first,second);
4328a6ba
DE
1321}
1322
fb896a32
DE
1323// Raise the window to the top of the Z order
1324void wxWindow::Raise()
1325{
4328a6ba 1326// wxAutoNSAutoreleasePool pool;
a82b8141 1327 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1328 NSView *superview = [nsview superview];
1329 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1330
1331 [superview sortSubviewsUsingFunction:
4328a6ba 1332 CocoaRaiseWindowCompareFunction
aa25b7f9 1333 context: &compareContext];
4328a6ba
DE
1334}
1335
aa25b7f9
DE
1336/* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1337 * the target view is always lower than every other view. When comparing two views neither of
1338 * which is the target, it returns the correct response based on the original ordering
1339 */
1340static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1341{
aa25b7f9 1342 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1343 // first should be ordered lower
aa25b7f9 1344 if(first==compareContext->target())
4328a6ba
DE
1345 return NSOrderedAscending;
1346 // second should be ordered lower
aa25b7f9 1347 if(second==compareContext->target())
4328a6ba 1348 return NSOrderedDescending;
aa25b7f9 1349 return compareContext->CompareUsingOriginalOrdering(first,second);
fb896a32
DE
1350}
1351
1352// Lower the window to the bottom of the Z order
1353void wxWindow::Lower()
1354{
4328a6ba 1355 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1356 NSView *superview = [nsview superview];
1357 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1358
1359#if 0
1360 NSLog(@"Target:\n%@\n", nsview);
1361 NSLog(@"Before:\n%@\n", compareContext.subviews());
1362#endif
1363 [superview sortSubviewsUsingFunction:
4328a6ba 1364 CocoaLowerWindowCompareFunction
aa25b7f9
DE
1365 context: &compareContext];
1366#if 0
1367 NSLog(@"After:\n%@\n", [superview subviews]);
1368#endif
fb896a32
DE
1369}
1370
1371bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1372{
8d8d3633 1373 return false;
fb896a32
DE
1374}
1375
1376// Get the window with the focus
dcb68102 1377wxWindow *wxWindowBase::DoFindFocus()
fb896a32 1378{
67d2dac8
DE
1379 // Basically we are somewhat emulating the responder chain here except
1380 // we are only loking for the first responder in the key window or
1381 // upon failing to find one if the main window is different we look
1382 // for the first responder in the main window.
1383
1384 // Note that the firstResponder doesn't necessarily have to be an
1385 // NSView but wxCocoaNSView::GetFromCocoa() will simply return
1386 // NULL unless it finds its argument in its hash map.
1387
1388 wxCocoaNSView *win;
1389
1390 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
9151dcec 1391 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
67d2dac8
DE
1392 if(win)
1393 return win->GetWxWindow();
1394
1395 NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1396 if(mainWindow == keyWindow)
f7e98dee 1397 return NULL;
9151dcec 1398 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
67d2dac8
DE
1399 if(win)
1400 return win->GetWxWindow();
1401
1402 return NULL;
fb896a32
DE
1403}
1404
1405/* static */ wxWindow *wxWindowBase::GetCapture()
1406{
1407 // TODO
b9505233 1408 return wxWindowCocoa::sm_capturedWindow;
fb896a32
DE
1409}
1410
1411wxWindow *wxGetActiveWindow()
1412{
1413 // TODO
1414 return NULL;
1415}
1416
7c9428ab
DE
1417wxPoint wxGetMousePosition()
1418{
1419 // TODO
1420 return wxDefaultPosition;
1421}
1422
ca5db7b2
WS
1423wxMouseState wxGetMouseState()
1424{
1425 wxMouseState ms;
1426 // TODO
1427 return ms;
1428}
1429
7c9428ab
DE
1430wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1431{
1432 pt = wxGetMousePosition();
1433 return NULL;
1434}
7c5a378f 1435
8ea3a63e
DE
1436// ========================================================================
1437// wxCocoaMouseMovedEventSynthesizer
1438// ========================================================================
1439
18b32eb5
DE
1440#define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
1441
8ea3a63e
DE
1442/* This class registers one run loop observer to cover all windows registered with it.
1443 * It will register the observer when the first view is registerd and unregister the
1444 * observer when the last view is unregistered.
1445 * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
1446 * is no reason it couldn't be instantiated multiple times.
1447 */
1448class wxCocoaMouseMovedEventSynthesizer
1449{
1450 DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
1451public:
1452 wxCocoaMouseMovedEventSynthesizer()
1453 { m_lastScreenMouseLocation = NSZeroPoint;
1454 }
1455 ~wxCocoaMouseMovedEventSynthesizer();
1456 void RegisterWxCocoaView(wxCocoaNSView *aView);
1457 void UnregisterWxCocoaView(wxCocoaNSView *aView);
1458 void SynthesizeMouseMovedEvent();
1459
1460protected:
1461 void AddRunLoopObserver();
1462 void RemoveRunLoopObserver();
1463 wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
1464 std::list<wxCocoaNSView*> m_registeredViews;
1465 NSPoint m_lastScreenMouseLocation;
1466 static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
1467};
1468
1469void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
1470{
1471 m_registeredViews.push_back(aView);
18b32eb5 1472 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
1473
1474 if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
1475 {
1476 AddRunLoopObserver();
1477 }
1478}
1479
1480void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
1481{
1482 m_registeredViews.remove(aView);
18b32eb5 1483 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
1484 if(m_registeredViews.empty() && m_runLoopObserver != NULL)
1485 {
1486 RemoveRunLoopObserver();
1487 }
1488}
1489
1490wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
1491{
1492 if(!m_registeredViews.empty())
1493 {
1494 // This means failure to clean up so we report on it as a debug message.
1495 wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
1496 m_registeredViews.clear();
1497 }
1498 if(m_runLoopObserver != NULL)
1499 {
1500 // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
1501 wxLogDebug(wxT("Removing run loop observer during static destruction time."));
1502 RemoveRunLoopObserver();
1503 }
1504}
1505
1506void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
1507{
1508 reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
1509}
1510
1511void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
1512{
1513 CFRunLoopObserverContext observerContext =
1514 { 0
1515 , this
1516 , NULL
1517 , NULL
1518 , NULL
1519 };
1f361be2
DE
1520
1521 // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
1522 // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
1523
1524 // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
1525 // in our observer procedure being called before the run loop has decided that it is going to return control to
1526 // the Cocoa event loop. One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
1527 // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
1528
1529 // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
1530 // won't be called until the CFRunLoop gets around to it. If the CFRunLoop has not yet exited then it will get
1531 // around to it before letting the event loop do its work. This has some very odd effects on the way the
1532 // newly created tracking rects function. In particular, we will often miss the mouseExited: message if the
1533 // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
1534 // time the new one is built.
1535
1536 // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
1537 // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
1538 // the mouseExited: message to be sent properly.
1539
1540 m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
8ea3a63e
DE
1541 CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1542 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
1543}
1544
1545void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
1546{
1547 CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1548 m_runLoopObserver.reset();
1549 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
1550}
1551
1552void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
1553{
1554 NSPoint screenMouseLocation = [NSEvent mouseLocation];
1555 // Checking the last mouse location is done for a few reasons:
1556 // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
1557 // telling the app the mouse moved when the user hit a key for instance.
1558 // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
1559 // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
1560 // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
1561 // loop causing the tracking rectangles to constantly be reset.
1562 if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
1563 {
1564 m_lastScreenMouseLocation = screenMouseLocation;
1565 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
1566 for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
1567 {
1568 (*i)->Cocoa_synthesizeMouseMoved();
1569 }
1570 }
1571}
1572
1573// Singleton used for all views:
1574static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
1575
7c5a378f
DE
1576// ========================================================================
1577// wxCocoaTrackingRectManager
1578// ========================================================================
1579
1580wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
1581: m_window(window)
1582{
1583 m_isTrackingRectActive = false;
7c5a378f
DE
1584 BuildTrackingRect();
1585}
1586
1587void wxCocoaTrackingRectManager::ClearTrackingRect()
1588{
1589 if(m_isTrackingRectActive)
1590 {
1591 [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
1592 m_isTrackingRectActive = false;
18b32eb5 1593 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
1594 }
1595 // If we were doing periodic events we need to clear those too
1596 StopSynthesizingEvents();
1597}
1598
1599void wxCocoaTrackingRectManager::StopSynthesizingEvents()
1600{
8ea3a63e 1601 s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
7c5a378f
DE
1602}
1603
1604void wxCocoaTrackingRectManager::BuildTrackingRect()
1605{
b0a207df
DE
1606 // Pool here due to lack of one during wx init phase
1607 wxAutoNSAutoreleasePool pool;
1608
7c5a378f 1609 wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
18b32eb5
DE
1610
1611 NSView *theView = m_window->GetNSView();
1612
1613 if([theView window] != nil)
7c5a378f 1614 {
ea29564c
DE
1615 NSRect visibleRect = [theView visibleRect];
1616
1617 m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
1618 m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
7c5a378f 1619 m_isTrackingRectActive = true;
ea29564c 1620
18b32eb5 1621 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
1622 }
1623}
1624
7c5a378f
DE
1625void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
1626{
8ea3a63e 1627 s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
7c5a378f
DE
1628}
1629
ea29564c
DE
1630void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
1631{
1632 if(m_isTrackingRectActive)
1633 {
1634 NSView *theView = m_window->GetNSView();
1635 NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
1636 if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
1637 {
1638 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
1639 return;
1640 }
1641 }
1642 RebuildTrackingRect();
1643}
1644
7c5a378f
DE
1645void wxCocoaTrackingRectManager::RebuildTrackingRect()
1646{
1647 ClearTrackingRect();
1648 BuildTrackingRect();
1649}
1650
1651wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
1652{
1653 ClearTrackingRect();
1654}
1655
1656bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
1657{
1658 return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
1659}
1660