]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/window.mm
Halfway reasonable implementation of wxFont for wxCocoa.
[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];
fb896a32
DE
543}
544
545void wxWindowCocoa::CocoaRemoveFromParent(void)
546{
a82b8141 547 [GetNSViewForSuperview() removeFromSuperview];
fb896a32
DE
548}
549
550void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
551{
7c5a378f
DE
552 // Clear the visible area tracking rect if we have one.
553 delete m_visibleTrackingRectManager;
554 m_visibleTrackingRectManager = NULL;
555
fb896a32 556 bool need_debug = cocoaNSView || m_cocoaNSView;
48580976 557 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
bac6f234 558 DisassociateNSView(m_cocoaNSView);
fb896a32
DE
559 [cocoaNSView retain];
560 [m_cocoaNSView release];
561 m_cocoaNSView = cocoaNSView;
bac6f234 562 AssociateNSView(m_cocoaNSView);
48580976 563 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
fb896a32
DE
564}
565
a82b8141
DE
566WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
567{
568 return m_cocoaHider
569 ? m_cocoaHider->GetNSView()
f298b203
DE
570 : m_wxCocoaScrollView
571 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 572 : m_cocoaNSView;
a82b8141
DE
573}
574
575WX_NSView wxWindowCocoa::GetNSViewForHiding() const
576{
f298b203
DE
577 return m_wxCocoaScrollView
578 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 579 : m_cocoaNSView;
a82b8141
DE
580}
581
34c9978d
DE
582NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
583{
584 // TODO: Handle scrolling offset
ee022549 585 return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
34c9978d
DE
586}
587
588NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
589{
590 // TODO: Handle scrolling offset
ee022549 591 return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
34c9978d
DE
592}
593
594NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
595{
596 // TODO: Handle scrolling offset
ee022549 597 return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
34c9978d
DE
598}
599
600NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
601{
602 // TODO: Handle scrolling offset
ee022549 603 return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
34c9978d
DE
604}
605
4db3c8ac
DE
606WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
607{
608 // TODO: Handle scrolling offset
609 NSAffineTransform *transform = wxDC::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
610 return transform;
611}
612
8ea5271e
DE
613bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
614{
48580976 615 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
55c5be5e
DE
616 // Recursion can happen if the event loop runs from within the paint
617 // handler. For instance, if an assertion dialog is shown.
618 // FIXME: This seems less than ideal.
619 if(m_isInPaint)
620 {
2b030203 621 wxLogDebug(wxT("Paint event recursion!"));
55c5be5e
DE
622 return false;
623 }
8d8d3633 624 m_isInPaint = true;
dc5bcaef
DE
625
626 // Set m_updateRegion
627 const NSRect *rects = &rect; // The bounding box of the region
4799f3ba 628 NSInteger countRects = 1;
dc5bcaef 629 // Try replacing the larger rectangle with a list of smaller ones:
5dc47140
DE
630 if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
631 [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
34c9978d
DE
632
633 NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
634 for(int i=0; i<countRects; i++)
635 {
636 transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
637 }
638 m_updateRegion = wxRegion(transformedRects,countRects);
639 free(transformedRects);
dc5bcaef 640
8ea5271e
DE
641 wxPaintEvent event(m_windowId);
642 event.SetEventObject(this);
55c5be5e 643 bool ret = GetEventHandler()->ProcessEvent(event);
8d8d3633 644 m_isInPaint = false;
55c5be5e 645 return ret;
8ea5271e
DE
646}
647
69dbb709
DE
648void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
649{
2b030203 650 wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
34c9978d
DE
651 // Mouse events happen at the NSWindow level so we need to convert
652 // into our bounds coordinates then convert to wx coordinates.
a82b8141 653 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
34c9978d
DE
654 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
655 // FIXME: Should we be adjusting for client area origin?
69dbb709 656 const wxPoint &clientorigin = GetClientAreaOrigin();
34c9978d
DE
657 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
658 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
69dbb709
DE
659
660 event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
661 event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
662 event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
663 event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
664
665 // TODO: set timestamp?
666 event.SetEventObject(this);
667 event.SetId(GetId());
668}
669
670bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
671{
672 wxMouseEvent event(wxEVT_MOTION);
673 InitMouseEvent(event,theEvent);
7c5a378f 674 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
69dbb709
DE
675 return GetEventHandler()->ProcessEvent(event);
676}
677
7c5a378f
DE
678void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
679{
680 wxMouseEvent event(wxEVT_MOTION);
681 NSWindow *window = [GetNSView() window];
682 NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
683 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
684
685 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
686 // FIXME: Should we be adjusting for client area origin?
687 const wxPoint &clientorigin = GetClientAreaOrigin();
688 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
689 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
690
691 // TODO: Handle shift, control, alt, meta flags
692 event.SetEventObject(this);
693 event.SetId(GetId());
694
695 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
696 GetEventHandler()->ProcessEvent(event);
697}
698
69dbb709
DE
699bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
700{
7c5a378f
DE
701 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
702 {
703 m_visibleTrackingRectManager->BeginSynthesizingEvents();
704
705 // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
706 // some other event comes in. That other event is (guess what) mouse moved events that will be sent
707 // to the NSWindow which will forward them on to the first responder. We are not likely to be the
708 // first responder, so the mouseMoved: events are effectively discarded.
709 [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
710
711 wxMouseEvent event(wxEVT_ENTER_WINDOW);
712 InitMouseEvent(event,theEvent);
18b32eb5 713 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
7c5a378f
DE
714 return GetEventHandler()->ProcessEvent(event);
715 }
716 else
717 return false;
69dbb709
DE
718}
719
720bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
721{
7c5a378f
DE
722 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
723 {
724 m_visibleTrackingRectManager->StopSynthesizingEvents();
725
726 wxMouseEvent event(wxEVT_LEAVE_WINDOW);
727 InitMouseEvent(event,theEvent);
18b32eb5 728 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
7c5a378f
DE
729 return GetEventHandler()->ProcessEvent(event);
730 }
731 else
732 return false;
69dbb709
DE
733}
734
735bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
736{
737 wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
738 InitMouseEvent(event,theEvent);
48580976 739 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
69dbb709
DE
740 return GetEventHandler()->ProcessEvent(event);
741}
742
743bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
744{
745 wxMouseEvent event(wxEVT_MOTION);
746 InitMouseEvent(event,theEvent);
747 event.m_leftDown = true;
48580976 748 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
69dbb709
DE
749 return GetEventHandler()->ProcessEvent(event);
750}
751
752bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
753{
754 wxMouseEvent event(wxEVT_LEFT_UP);
755 InitMouseEvent(event,theEvent);
48580976 756 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
69dbb709
DE
757 return GetEventHandler()->ProcessEvent(event);
758}
759
760bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
761{
eafde5c7
DE
762 wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
763 InitMouseEvent(event,theEvent);
764 wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
765 return GetEventHandler()->ProcessEvent(event);
69dbb709
DE
766}
767
768bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
769{
eafde5c7
DE
770 wxMouseEvent event(wxEVT_MOTION);
771 InitMouseEvent(event,theEvent);
772 event.m_rightDown = true;
773 wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
774 return GetEventHandler()->ProcessEvent(event);
69dbb709
DE
775}
776
777bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
778{
eafde5c7
DE
779 wxMouseEvent event(wxEVT_RIGHT_UP);
780 InitMouseEvent(event,theEvent);
781 wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
782 return GetEventHandler()->ProcessEvent(event);
69dbb709
DE
783}
784
785bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
786{
787 return false;
788}
789
790bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
791{
792 return false;
793}
794
795bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
796{
797 return false;
798}
799
fb896a32
DE
800void wxWindowCocoa::Cocoa_FrameChanged(void)
801{
7c5a378f
DE
802 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
803 if(m_visibleTrackingRectManager != NULL)
804 m_visibleTrackingRectManager->RebuildTrackingRect();
fb896a32
DE
805 wxSizeEvent event(GetSize(), m_windowId);
806 event.SetEventObject(this);
807 GetEventHandler()->ProcessEvent(event);
808}
809
5558135c
RN
810bool wxWindowCocoa::Cocoa_resetCursorRects()
811{
7c5a378f 812 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
ea29564c
DE
813
814 // When we are called there may be a queued tracking rect event (mouse entered or exited) and
815 // we won't know it. A specific example is wxGenericHyperlinkCtrl changing the cursor from its
816 // mouse exited event. If the control happens to share the edge with its parent window which is
817 // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
818 // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
819 // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
820 // which willl in turn call us before exiting for the next user event.
821
822 // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
823 // our mouse exited event because the already queued event will have the old tracking rect
824 // tag. The simple solution is to only rebuild our tracking rect if we need to.
825
7c5a378f 826 if(m_visibleTrackingRectManager != NULL)
ea29564c 827 m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
7c5a378f 828
5558135c
RN
829 if(!m_cursor.GetNSCursor())
830 return false;
8d8d3633
WS
831
832 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
833
5558135c
RN
834 return true;
835}
836
a8780ad5
DE
837bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
838{
839 if(!wxWindowBase::SetCursor(cursor))
840 return false;
6ebbf01f
DE
841
842 // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
843 // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
844 // rects and thus won't ever call resetCursorRects.
845 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
846
a8780ad5 847 // Invalidate the cursor rects so the cursor will change
6ebbf01f
DE
848 // Note that it is not enough to remove the old one (if any) and add the new one.
849 // For the rects to work properly, Cocoa itself must call resetCursorRects.
a8780ad5
DE
850 [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
851 return true;
852}
853
7c5a378f
DE
854bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
855{
856 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
857 // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
858 if(m_visibleTrackingRectManager != NULL)
859 m_visibleTrackingRectManager->BuildTrackingRect();
860 return false;
861}
862
863bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
864{
865 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
866 // Clear tracking rects. It is imperative this be done before the new window is set.
867 if(m_visibleTrackingRectManager != NULL)
868 m_visibleTrackingRectManager->ClearTrackingRect();
869 return false;
870}
871
fb896a32
DE
872bool wxWindow::Close(bool force)
873{
cc6f960f
DE
874 // The only reason this function exists is that it is virtual and
875 // wxTopLevelWindowCocoa will override it.
876 return wxWindowBase::Close(force);
fb896a32
DE
877}
878
a82b8141
DE
879void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
880{
881 [[oldView superview] replaceSubview:oldView with:newView];
882}
883
47a8a4d5 884void wxWindow::DoEnable(bool enable)
adb4816c 885{
47a8a4d5 886 CocoaSetEnabled(enable);
adb4816c
DE
887}
888
fb896a32
DE
889bool wxWindow::Show(bool show)
890{
7fc77f30 891 wxAutoNSAutoreleasePool pool;
fb896a32
DE
892 // If the window is marked as visible, then it shouldn't have a dummy view
893 // If the window is marked hidden, then it should have a dummy view
addbdd29 894 // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
2b030203 895// wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
fb896a32 896 // Return false if there isn't a window to show or hide
a82b8141
DE
897 NSView *cocoaView = GetNSViewForHiding();
898 if(!cocoaView)
fb896a32 899 return false;
fb896a32
DE
900 if(show)
901 {
addbdd29 902 // If state isn't changing, return false
a82b8141 903 if(!m_cocoaHider)
addbdd29 904 return false;
a82b8141
DE
905 CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
906 wxASSERT(![m_cocoaHider->GetNSView() superview]);
907 delete m_cocoaHider;
908 m_cocoaHider = NULL;
909 wxASSERT([cocoaView superview]);
fb896a32
DE
910 }
911 else
912 {
addbdd29 913 // If state isn't changing, return false
a82b8141 914 if(m_cocoaHider)
addbdd29 915 return false;
a82b8141
DE
916 m_cocoaHider = new wxWindowCocoaHider(this);
917 // NOTE: replaceSubview:with will cause m_cocaNSView to be
918 // (auto)released which balances out addSubview
919 CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
fb896a32 920 // m_coocaNSView is now only retained by us
a82b8141
DE
921 wxASSERT([m_cocoaHider->GetNSView() superview]);
922 wxASSERT(![cocoaView superview]);
fb896a32 923 }
a6b4ff2e
DE
924 m_isShown = show;
925 return true;
fb896a32
DE
926}
927
928void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
929{
9879fa84 930 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
931 int currentX, currentY;
932 int currentW, currentH;
933 DoGetPosition(&currentX, &currentY);
934 DoGetSize(&currentW, &currentH);
935 if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
936 x=currentX;
937 if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
938 y=currentY;
939
940 AdjustForParentClientOrigin(x,y,sizeFlags);
941
8d8d3633 942 wxSize size(wxDefaultSize);
fb896a32
DE
943
944 if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
945 {
946 if(sizeFlags&wxSIZE_AUTO_WIDTH)
947 {
948 size=DoGetBestSize();
949 width=size.x;
950 }
951 else
952 width=currentW;
953 }
954 if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
955 {
956 if(sizeFlags&wxSIZE_AUTO_HEIGHT)
957 {
958 if(size.x==-1)
959 size=DoGetBestSize();
960 height=size.y;
961 }
962 else
963 height=currentH;
964 }
965 DoMoveWindow(x,y,width,height);
966}
967
1e151594 968#if wxUSE_TOOLTIPS
26191790
RN
969
970void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
971{
972 wxWindowBase::DoSetToolTip(tip);
973
26191790
RN
974 if ( m_tooltip )
975 {
976 m_tooltip->SetWindow((wxWindow *)this);
26191790
RN
977 }
978}
979
1e151594
RN
980#endif
981
fb896a32
DE
982void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
983{
bed6fe0c 984 wxAutoNSAutoreleasePool pool;
9879fa84 985 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
fb896a32 986
a82b8141 987 NSView *nsview = GetNSViewForSuperview();
d449cf47 988 NSView *superview = [nsview superview];
fb896a32 989
34c9978d
DE
990 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
991
992 NSRect oldFrameRect = [nsview frame];
993 NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
994 [nsview setFrame:newFrameRect];
b915b805 995 // Be sure to redraw the parent to reflect the changed position
34c9978d
DE
996 [superview setNeedsDisplayInRect:oldFrameRect];
997 [superview setNeedsDisplayInRect:newFrameRect];
fb896a32
DE
998}
999
d139c3a8
DE
1000void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
1001{
1002 NSView *nsview = GetNSViewForSuperview();
1003 NSView *superview = [nsview superview];
2b030203 1004 wxCHECK_RET(superview,wxT("NSView does not have a superview"));
34c9978d 1005 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
d139c3a8
DE
1006 NSRect frameRect = [nsview frame];
1007 if(size.x!=-1)
1008 frameRect.size.width = size.x;
1009 if(size.y!=-1)
1010 frameRect.size.height = size.y;
1011 frameRect.origin.x = pos.x;
34c9978d 1012 frameRect.origin.y = pos.y;
c5bd9191
DE
1013 // Tell Cocoa to change the margin between the bottom of the superview
1014 // and the bottom of the control. Keeps the control pinned to the top
065e208e 1015 // of its superview so that its position in the wxWidgets coordinate
c5bd9191
DE
1016 // system doesn't change.
1017 if(![superview isFlipped])
1018 [nsview setAutoresizingMask: NSViewMinYMargin];
6f2ec3c3
DE
1019 // MUST set the mask before setFrame: which can generate a size event
1020 // and cause a scroller to be added!
34c9978d 1021 frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
6f2ec3c3 1022 [nsview setFrame: frameRect];
d139c3a8
DE
1023}
1024
fb896a32
DE
1025// Get total size
1026void wxWindow::DoGetSize(int *w, int *h) const
1027{
a82b8141 1028 NSRect cocoaRect = [GetNSViewForSuperview() frame];
fb896a32
DE
1029 if(w)
1030 *w=(int)cocoaRect.size.width;
1031 if(h)
1032 *h=(int)cocoaRect.size.height;
9879fa84 1033 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
fb896a32
DE
1034}
1035
1036void wxWindow::DoGetPosition(int *x, int *y) const
1037{
a82b8141 1038 NSView *nsview = GetNSViewForSuperview();
fb896a32 1039
576a1544 1040 NSRect cocoaRect = [nsview frame];
34c9978d 1041 NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
fb896a32 1042 if(x)
34c9978d 1043 *x=(int)rectWx.origin.x;
fb896a32 1044 if(y)
34c9978d 1045 *y=(int)rectWx.origin.y;
9879fa84 1046 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
fb896a32
DE
1047}
1048
1049WXWidget wxWindow::GetHandle() const
1050{
1051 return m_cocoaNSView;
1052}
1053
f7e98dee
RN
1054wxWindow* wxWindow::GetWxWindow() const
1055{
1056 return (wxWindow*) this;
1057}
1058
ddf7346a
DE
1059void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1060{
1061 [m_cocoaNSView setNeedsDisplay:YES];
1062}
1063
fb896a32
DE
1064void wxWindow::SetFocus()
1065{
67d2dac8
DE
1066 if([GetNSView() acceptsFirstResponder])
1067 [[GetNSView() window] makeFirstResponder: GetNSView()];
fb896a32
DE
1068}
1069
1070void wxWindow::DoCaptureMouse()
1071{
1072 // TODO
b9505233 1073 sm_capturedWindow = this;
fb896a32
DE
1074}
1075
1076void wxWindow::DoReleaseMouse()
1077{
1078 // TODO
b9505233 1079 sm_capturedWindow = NULL;
fb896a32
DE
1080}
1081
1082void wxWindow::DoScreenToClient(int *x, int *y) const
1083{
15f37147
DE
1084 // Point in cocoa screen coordinates:
1085 NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
1086 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1087 NSWindow *theWindow = [clientView window];
1088
1089 // Point in window's base coordinate system:
1090 NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
1091 // Point in view's bounds coordinate system
1092 NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
1093 // Point in wx client coordinates:
1094 NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
1095 if(x!=NULL)
1096 *x = theWxClientPoint.x;
1097 if(y!=NULL)
1098 *y = theWxClientPoint.y;
fb896a32
DE
1099}
1100
1101void wxWindow::DoClientToScreen(int *x, int *y) const
1102{
15f37147
DE
1103 // Point in wx client coordinates
1104 NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
1105
1106 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1107
1108 // Point in the view's bounds coordinate system
1109 NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
1110
1111 // Point in the window's base coordinate system
1112 NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
1113
1114 NSWindow *theWindow = [clientView window];
1115 // Point in Cocoa's screen coordinates
1116 NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
1117
1118 // Act as though this was the origin of a 0x0 rectangle
1119 NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
1120
1121 // Convert that rectangle to wx coordinates
1122 wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
1123 if(*x)
1124 *x = theWxScreenPoint.x;
1125 if(*y)
1126 *y = theWxScreenPoint.y;
fb896a32
DE
1127}
1128
1129// Get size *available for subwindows* i.e. excluding menu bar etc.
1130void wxWindow::DoGetClientSize(int *x, int *y) const
1131{
48580976 1132 wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
f298b203
DE
1133 if(m_wxCocoaScrollView)
1134 m_wxCocoaScrollView->DoGetClientSize(x,y);
816c52cf
DE
1135 else
1136 wxWindowCocoa::DoGetSize(x,y);
fb896a32
DE
1137}
1138
1139void wxWindow::DoSetClientSize(int width, int height)
1140{
48580976 1141 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
f298b203
DE
1142 if(m_wxCocoaScrollView)
1143 m_wxCocoaScrollView->ClientSizeToSize(width,height);
e08efb8d
DE
1144 CocoaSetWxWindowSize(width,height);
1145}
1146
1147void wxWindow::CocoaSetWxWindowSize(int width, int height)
1148{
8d8d3633
WS
1149 wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1150}
1151
1152void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1153{
ba64d0b6 1154 // Intentional no-op.
8d8d3633
WS
1155}
1156
1157wxString wxWindow::GetLabel() const
1158{
ba64d0b6
DE
1159 // General Get/Set of labels is implemented in wxControlBase
1160 wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
8d8d3633 1161 return wxEmptyString;
fb896a32
DE
1162}
1163
1164int wxWindow::GetCharHeight() const
1165{
1166 // TODO
d89e391b 1167 return 10;
fb896a32
DE
1168}
1169
1170int wxWindow::GetCharWidth() const
1171{
1172 // TODO
d89e391b 1173 return 5;
fb896a32
DE
1174}
1175
d3978051
DE
1176void wxWindow::GetTextExtent(const wxString& string, int *outX, int *outY,
1177 int *outDescent, int *outExternalLeading, const wxFont *inFont) const
fb896a32 1178{
d3978051
DE
1179 // FIXME: This obviously ignores the window's font (if any) along with any size
1180 // transformations. However, it's better than nothing.
1181 // We don't create a wxClientDC because we don't want to accidently be able to use
1182 // it for drawing.
1183 wxDC tmpdc;
1184 return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
fb896a32
DE
1185}
1186
fb896a32
DE
1187// Coordinates relative to the window
1188void wxWindow::WarpPointer (int x_pos, int y_pos)
1189{
1190 // TODO
1191}
1192
1193int wxWindow::GetScrollPos(int orient) const
1194{
1195 // TODO
1196 return 0;
1197}
1198
1199// This now returns the whole range, not just the number
1200// of positions that we can scroll.
1201int wxWindow::GetScrollRange(int orient) const
1202{
1203 // TODO
1204 return 0;
1205}
1206
1207int wxWindow::GetScrollThumb(int orient) const
1208{
1209 // TODO
1210 return 0;
1211}
1212
1213void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
1214{
1215 // TODO
1216}
1217
816c52cf
DE
1218void wxWindow::CocoaCreateNSScrollView()
1219{
f298b203 1220 if(!m_wxCocoaScrollView)
816c52cf 1221 {
f298b203 1222 m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
816c52cf
DE
1223 }
1224}
1225
fb896a32
DE
1226// New function that will replace some of the above.
1227void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
1228 int range, bool refresh)
1229{
e08efb8d 1230 CocoaCreateNSScrollView();
fb896a32
DE
1231 // TODO
1232}
1233
1234// Does a physical scroll
1235void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1236{
1237 // TODO
1238}
1239
d3978051
DE
1240static inline int _DoFixupDistance(int vDistance, int cDistance)
1241{
1242 // If the virtual distance is wxDefaultCoord, set it to the client distance
1243 // This definitely has to be done or else we literally get views with a -1 size component!
1244 if(vDistance == wxDefaultCoord)
1245 vDistance = cDistance;
1246 // NOTE: Since cDistance should always be >= 0 and since wxDefaultCoord is -1, the above
1247 // test is more or less useless because it gets covered by the next one. However, just in
1248 // case anyone decides that the next test is not correct, I want them to be aware that
1249 // the above test would still be needed.
1250
1251 // I am not entirely sure about this next one but I believe it makes sense because
1252 // otherwise the virtual view (which is the m_cocoaNSView that got wrapped by the scrolling
1253 // machinery) can be smaller than the NSClipView (the client area) which
1254 // means that, for instance, mouse clicks inside the client area as wx sees it but outside
1255 // the virtual area as wx sees it won't be seen by the m_cocoaNSView.
1256 // We make the assumption that if a virtual distance is less than the client distance that
1257 // the real view must already be or will soon be positioned at coordinate 0 within the
1258 // NSClipView that represents the client area. This way, when we increase the distance to
1259 // be the client distance, the real view will exactly fit in the clip view.
1260 else if(vDistance < cDistance)
1261 vDistance = cDistance;
1262 return vDistance;
1263}
1264
816c52cf
DE
1265void wxWindow::DoSetVirtualSize( int x, int y )
1266{
d3978051
DE
1267 // Call wxWindowBase method which will set m_virtualSize to the appropriate value,
1268 // possibly not what the caller passed in. For example, the current implementation
1269 // clamps the width and height to within the min/max virtual ranges.
1270 // wxDefaultCoord is passed through unchanged which means we need to handle it ourselves
1271 // which we do by using the _DoFixupDistance helper method.
816c52cf 1272 wxWindowBase::DoSetVirtualSize(x,y);
d3978051 1273 // Create the scroll view if it hasn't been already.
816c52cf 1274 CocoaCreateNSScrollView();
d3978051
DE
1275 // Now use fixed-up distances when setting the frame size
1276 wxSize clientSize = GetClientSize();
1277 [m_cocoaNSView setFrameSize:NSMakeSize(_DoFixupDistance(m_virtualSize.x, clientSize.x), _DoFixupDistance(m_virtualSize.y, clientSize.y))];
816c52cf
DE
1278}
1279
fb896a32
DE
1280bool wxWindow::SetFont(const wxFont& font)
1281{
e7e97a59
DE
1282 // FIXME: We may need to handle wx font inheritance.
1283 return wxWindowBase::SetFont(font);
fb896a32
DE
1284}
1285
aa25b7f9
DE
1286#if 0 // these are used when debugging the algorithm.
1287static char const * const comparisonresultStrings[] =
1288{ "<"
1289, "=="
1290, ">"
1291};
1292#endif
1293
1294class CocoaWindowCompareContext
1295{
1296 DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
1297public:
1298 CocoaWindowCompareContext(); // Not implemented
1299 CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1300 {
1301 m_target = target;
1302 // Cocoa sorts subviews in-place.. make a copy
1303 m_subviews = [subviews copy];
1304 }
1305 ~CocoaWindowCompareContext()
1306 { // release the copy
1307 [m_subviews release];
1308 }
1309 NSView* target()
1310 { return m_target; }
1311 NSArray* subviews()
1312 { return m_subviews; }
1313 /* Helper function that returns the comparison based off of the original ordering */
1314 CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1315 {
1316 NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1317 NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1318 // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1319 // likely compare higher than the other view which is reasonable considering the only way that
1320 // can happen is if the subview was added after our call to subviews but before the call to
1321 // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
1322 // that case should never occur anyway because that would imply a multi-threaded GUI call
1323 // which is a big no-no with Cocoa.
1324
1325 // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1326 NSComparisonResult result = (firstI < secondI)
1327 ? NSOrderedAscending /* -1 */
1328 : (firstI > secondI)
1329 ? NSOrderedDescending /* 1 */
1330 : NSOrderedSame /* 0 */;
1331
1332#if 0 // Enable this if you need to debug the algorithm.
1333 NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
1334#endif
1335 return result;
1336 }
1337private:
1338 /* The subview we are trying to Raise or Lower */
1339 NSView *m_target;
1340 /* A copy of the original array of subviews */
1341 NSArray *m_subviews;
1342};
1343
1344/* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1345 * the target view is always higher than every other view. When comparing two views neither of
1346 * which is the target, it returns the correct response based on the original ordering
1347 */
1348static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1349{
aa25b7f9 1350 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1351 // first should be ordered higher
aa25b7f9 1352 if(first==compareContext->target())
4328a6ba
DE
1353 return NSOrderedDescending;
1354 // second should be ordered higher
aa25b7f9 1355 if(second==compareContext->target())
4328a6ba 1356 return NSOrderedAscending;
aa25b7f9 1357 return compareContext->CompareUsingOriginalOrdering(first,second);
4328a6ba
DE
1358}
1359
fb896a32
DE
1360// Raise the window to the top of the Z order
1361void wxWindow::Raise()
1362{
4328a6ba 1363// wxAutoNSAutoreleasePool pool;
a82b8141 1364 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1365 NSView *superview = [nsview superview];
1366 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1367
1368 [superview sortSubviewsUsingFunction:
4328a6ba 1369 CocoaRaiseWindowCompareFunction
aa25b7f9 1370 context: &compareContext];
4328a6ba
DE
1371}
1372
aa25b7f9
DE
1373/* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1374 * the target view is always lower than every other view. When comparing two views neither of
1375 * which is the target, it returns the correct response based on the original ordering
1376 */
1377static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1378{
aa25b7f9 1379 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1380 // first should be ordered lower
aa25b7f9 1381 if(first==compareContext->target())
4328a6ba
DE
1382 return NSOrderedAscending;
1383 // second should be ordered lower
aa25b7f9 1384 if(second==compareContext->target())
4328a6ba 1385 return NSOrderedDescending;
aa25b7f9 1386 return compareContext->CompareUsingOriginalOrdering(first,second);
fb896a32
DE
1387}
1388
1389// Lower the window to the bottom of the Z order
1390void wxWindow::Lower()
1391{
4328a6ba 1392 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1393 NSView *superview = [nsview superview];
1394 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1395
1396#if 0
1397 NSLog(@"Target:\n%@\n", nsview);
1398 NSLog(@"Before:\n%@\n", compareContext.subviews());
1399#endif
1400 [superview sortSubviewsUsingFunction:
4328a6ba 1401 CocoaLowerWindowCompareFunction
aa25b7f9
DE
1402 context: &compareContext];
1403#if 0
1404 NSLog(@"After:\n%@\n", [superview subviews]);
1405#endif
fb896a32
DE
1406}
1407
1408bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1409{
8d8d3633 1410 return false;
fb896a32
DE
1411}
1412
1413// Get the window with the focus
dcb68102 1414wxWindow *wxWindowBase::DoFindFocus()
fb896a32 1415{
67d2dac8
DE
1416 // Basically we are somewhat emulating the responder chain here except
1417 // we are only loking for the first responder in the key window or
1418 // upon failing to find one if the main window is different we look
1419 // for the first responder in the main window.
1420
1421 // Note that the firstResponder doesn't necessarily have to be an
1422 // NSView but wxCocoaNSView::GetFromCocoa() will simply return
1423 // NULL unless it finds its argument in its hash map.
1424
1425 wxCocoaNSView *win;
1426
1427 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
9151dcec 1428 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
67d2dac8
DE
1429 if(win)
1430 return win->GetWxWindow();
1431
1432 NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1433 if(mainWindow == keyWindow)
f7e98dee 1434 return NULL;
9151dcec 1435 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
67d2dac8
DE
1436 if(win)
1437 return win->GetWxWindow();
1438
1439 return NULL;
fb896a32
DE
1440}
1441
1442/* static */ wxWindow *wxWindowBase::GetCapture()
1443{
1444 // TODO
b9505233 1445 return wxWindowCocoa::sm_capturedWindow;
fb896a32
DE
1446}
1447
1448wxWindow *wxGetActiveWindow()
1449{
1450 // TODO
1451 return NULL;
1452}
1453
7c9428ab
DE
1454wxPoint wxGetMousePosition()
1455{
1456 // TODO
1457 return wxDefaultPosition;
1458}
1459
ca5db7b2
WS
1460wxMouseState wxGetMouseState()
1461{
1462 wxMouseState ms;
1463 // TODO
1464 return ms;
1465}
1466
7c9428ab
DE
1467wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1468{
1469 pt = wxGetMousePosition();
1470 return NULL;
1471}
7c5a378f 1472
8ea3a63e
DE
1473// ========================================================================
1474// wxCocoaMouseMovedEventSynthesizer
1475// ========================================================================
1476
18b32eb5
DE
1477#define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
1478
8ea3a63e
DE
1479/* This class registers one run loop observer to cover all windows registered with it.
1480 * It will register the observer when the first view is registerd and unregister the
1481 * observer when the last view is unregistered.
1482 * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
1483 * is no reason it couldn't be instantiated multiple times.
1484 */
1485class wxCocoaMouseMovedEventSynthesizer
1486{
1487 DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
1488public:
1489 wxCocoaMouseMovedEventSynthesizer()
1490 { m_lastScreenMouseLocation = NSZeroPoint;
1491 }
1492 ~wxCocoaMouseMovedEventSynthesizer();
1493 void RegisterWxCocoaView(wxCocoaNSView *aView);
1494 void UnregisterWxCocoaView(wxCocoaNSView *aView);
1495 void SynthesizeMouseMovedEvent();
1496
1497protected:
1498 void AddRunLoopObserver();
1499 void RemoveRunLoopObserver();
1500 wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
1501 std::list<wxCocoaNSView*> m_registeredViews;
1502 NSPoint m_lastScreenMouseLocation;
1503 static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
1504};
1505
1506void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
1507{
1508 m_registeredViews.push_back(aView);
18b32eb5 1509 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
1510
1511 if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
1512 {
1513 AddRunLoopObserver();
1514 }
1515}
1516
1517void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
1518{
1519 m_registeredViews.remove(aView);
18b32eb5 1520 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
1521 if(m_registeredViews.empty() && m_runLoopObserver != NULL)
1522 {
1523 RemoveRunLoopObserver();
1524 }
1525}
1526
1527wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
1528{
1529 if(!m_registeredViews.empty())
1530 {
1531 // This means failure to clean up so we report on it as a debug message.
1532 wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
1533 m_registeredViews.clear();
1534 }
1535 if(m_runLoopObserver != NULL)
1536 {
1537 // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
1538 wxLogDebug(wxT("Removing run loop observer during static destruction time."));
1539 RemoveRunLoopObserver();
1540 }
1541}
1542
1543void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
1544{
1545 reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
1546}
1547
1548void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
1549{
1550 CFRunLoopObserverContext observerContext =
1551 { 0
1552 , this
1553 , NULL
1554 , NULL
1555 , NULL
1556 };
1f361be2
DE
1557
1558 // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
1559 // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
1560
1561 // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
1562 // in our observer procedure being called before the run loop has decided that it is going to return control to
1563 // the Cocoa event loop. One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
1564 // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
1565
1566 // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
1567 // won't be called until the CFRunLoop gets around to it. If the CFRunLoop has not yet exited then it will get
1568 // around to it before letting the event loop do its work. This has some very odd effects on the way the
1569 // newly created tracking rects function. In particular, we will often miss the mouseExited: message if the
1570 // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
1571 // time the new one is built.
1572
1573 // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
1574 // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
1575 // the mouseExited: message to be sent properly.
1576
1577 m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
8ea3a63e
DE
1578 CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1579 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
1580}
1581
1582void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
1583{
1584 CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
1585 m_runLoopObserver.reset();
1586 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
1587}
1588
1589void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
1590{
1591 NSPoint screenMouseLocation = [NSEvent mouseLocation];
1592 // Checking the last mouse location is done for a few reasons:
1593 // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
1594 // telling the app the mouse moved when the user hit a key for instance.
1595 // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
1596 // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
1597 // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
1598 // loop causing the tracking rectangles to constantly be reset.
1599 if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
1600 {
1601 m_lastScreenMouseLocation = screenMouseLocation;
1602 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
1603 for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
1604 {
1605 (*i)->Cocoa_synthesizeMouseMoved();
1606 }
1607 }
1608}
1609
1610// Singleton used for all views:
1611static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
1612
7c5a378f
DE
1613// ========================================================================
1614// wxCocoaTrackingRectManager
1615// ========================================================================
1616
1617wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
1618: m_window(window)
1619{
1620 m_isTrackingRectActive = false;
7c5a378f
DE
1621 BuildTrackingRect();
1622}
1623
1624void wxCocoaTrackingRectManager::ClearTrackingRect()
1625{
1626 if(m_isTrackingRectActive)
1627 {
1628 [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
1629 m_isTrackingRectActive = false;
18b32eb5 1630 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
1631 }
1632 // If we were doing periodic events we need to clear those too
1633 StopSynthesizingEvents();
1634}
1635
1636void wxCocoaTrackingRectManager::StopSynthesizingEvents()
1637{
8ea3a63e 1638 s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
7c5a378f
DE
1639}
1640
1641void wxCocoaTrackingRectManager::BuildTrackingRect()
1642{
b0a207df
DE
1643 // Pool here due to lack of one during wx init phase
1644 wxAutoNSAutoreleasePool pool;
1645
7c5a378f 1646 wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
18b32eb5
DE
1647
1648 NSView *theView = m_window->GetNSView();
1649
1650 if([theView window] != nil)
7c5a378f 1651 {
ea29564c
DE
1652 NSRect visibleRect = [theView visibleRect];
1653
1654 m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
1655 m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
7c5a378f 1656 m_isTrackingRectActive = true;
ea29564c 1657
18b32eb5 1658 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
1659 }
1660}
1661
7c5a378f
DE
1662void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
1663{
8ea3a63e 1664 s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
7c5a378f
DE
1665}
1666
ea29564c
DE
1667void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
1668{
1669 if(m_isTrackingRectActive)
1670 {
1671 NSView *theView = m_window->GetNSView();
1672 NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
1673 if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
1674 {
1675 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
1676 return;
1677 }
1678 }
1679 RebuildTrackingRect();
1680}
1681
7c5a378f
DE
1682void wxCocoaTrackingRectManager::RebuildTrackingRect()
1683{
1684 ClearTrackingRect();
1685 BuildTrackingRect();
1686}
1687
1688wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
1689{
1690 ClearTrackingRect();
1691}
1692
1693bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
1694{
1695 return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
1696}
1697