]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/window.mm
More doxygen overview updates.
[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"
938156b2 18 #include "wx/dcclient.h"
ca5db7b2 19 #include "wx/utils.h"
449c5673 20#endif //WX_PRECOMP
da80ae71 21
e73ae747 22#include "wx/tooltip.h"
fb896a32 23
938156b2 24#include "wx/cocoa/dc.h"
7fc77f30 25#include "wx/cocoa/autorelease.h"
26191790 26#include "wx/cocoa/string.h"
7c5a378f 27#include "wx/cocoa/trackingrectmanager.h"
19253261 28#include "wx/cocoa/private/scrollview.h"
8ea3a63e 29#include "wx/mac/corefoundation/cfref.h"
6a5c31c2 30#include "wx/cocoa/ObjcRef.h"
7fc77f30 31
369559ca 32#import <Foundation/NSArray.h>
7c5a378f 33#import <Foundation/NSRunLoop.h>
829a2e95 34#include "wx/cocoa/objc/NSView.h"
69dbb709 35#import <AppKit/NSEvent.h>
816c52cf 36#import <AppKit/NSScrollView.h>
618d53b8 37#import <AppKit/NSScroller.h>
816c52cf
DE
38#import <AppKit/NSColor.h>
39#import <AppKit/NSClipView.h>
dc5bcaef 40#import <Foundation/NSException.h>
67d2dac8
DE
41#import <AppKit/NSApplication.h>
42#import <AppKit/NSWindow.h>
15f37147 43#import <AppKit/NSScreen.h>
dc5bcaef 44
75e4856b
DE
45// Turn this on to paint green over the dummy views for debugging
46#undef WXCOCOA_FILL_DUMMY_VIEW
47
48#ifdef WXCOCOA_FILL_DUMMY_VIEW
49#import <AppKit/NSBezierPath.h>
50#endif //def WXCOCOA_FILL_DUMMY_VIEW
51
8ea3a63e
DE
52// STL list used by wxCocoaMouseMovedEventSynthesizer
53#include <list>
54
4799f3ba
DE
55/* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
56 * NSInteger post-Leopard. Pre-Leopard the Cocoa toolkit expects a function
57 * returning int and not NSComparisonResult. Post-Leopard the Cocoa toolkit
58 * expects a function returning the new non-enum NSComparsionResult.
59 * Hence we create a typedef named CocoaWindowCompareFunctionResult.
60 */
61#if defined(NSINTEGER_DEFINED)
62typedef NSComparisonResult CocoaWindowCompareFunctionResult;
63#else
64typedef int CocoaWindowCompareFunctionResult;
65#endif
66
5dc47140
DE
67// A category for methods that are only present in Panther's SDK
68@interface NSView(wxNSViewPrePantherCompatibility)
69- (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
70@end
71
15f37147
DE
72// ========================================================================
73// Helper functions for converting to/from wxWidgets coordinates and a
74// specified NSView's coordinate system.
75// ========================================================================
ee022549
DE
76NSPoint CocoaTransformNSViewBoundsToWx(NSView *nsview, NSPoint pointBounds)
77{
78 wxCHECK_MSG(nsview, pointBounds, wxT("Need to have a Cocoa view to do translation"));
79 if([nsview isFlipped])
80 return pointBounds;
81 NSRect ourBounds = [nsview bounds];
82 return NSMakePoint
83 ( pointBounds.x
84 , ourBounds.size.height - pointBounds.y
85 );
86}
87
88NSRect CocoaTransformNSViewBoundsToWx(NSView *nsview, NSRect rectBounds)
89{
90 wxCHECK_MSG(nsview, rectBounds, wxT("Need to have a Cocoa view to do translation"));
91 if([nsview isFlipped])
92 return rectBounds;
93 NSRect ourBounds = [nsview bounds];
94 return NSMakeRect
95 ( rectBounds.origin.x
96 , ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
97 , rectBounds.size.width
98 , rectBounds.size.height
99 );
100}
101
102NSPoint CocoaTransformNSViewWxToBounds(NSView *nsview, NSPoint pointWx)
103{
104 wxCHECK_MSG(nsview, pointWx, wxT("Need to have a Cocoa view to do translation"));
105 if([nsview isFlipped])
106 return pointWx;
107 NSRect ourBounds = [nsview bounds];
108 return NSMakePoint
109 ( pointWx.x
110 , ourBounds.size.height - pointWx.y
111 );
112}
113
114NSRect CocoaTransformNSViewWxToBounds(NSView *nsview, NSRect rectWx)
115{
116 wxCHECK_MSG(nsview, rectWx, wxT("Need to have a Cocoa view to do translation"));
117 if([nsview isFlipped])
118 return rectWx;
119 NSRect ourBounds = [nsview bounds];
120 return NSMakeRect
121 ( rectWx.origin.x
122 , ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
123 , rectWx.size.width
124 , rectWx.size.height
125 );
126}
127
15f37147
DE
128// ============================================================================
129// Screen coordinate helpers
130// ============================================================================
131
132/*
133General observation about Cocoa screen coordinates:
134It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
135
136It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
137the BOTTOM-right corner of this screen. Recall that Cocoa uses cartesian coordinates so y-increase is up.
138
139It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
140relative to that screen's frame. The only real way to test this is to configure two screens one atop
141the other such that the menubar screen is on top. The Dock at the bottom of the screen will then
142eat into the visibleFrame of screen 1 by incrementing it's y-origin. Thus if you arrange two
1431920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
144visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
145
146In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
147However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
148This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
149*/
150
151namespace { // file namespace
152
153class wxCocoaPrivateScreenCoordinateTransformer
154{
155 DECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer)
156public:
157 wxCocoaPrivateScreenCoordinateTransformer();
158 ~wxCocoaPrivateScreenCoordinateTransformer();
159 wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
160 NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
161
162protected:
163 NSScreen *m_screenZero;
164 NSRect m_screenZeroFrame;
165};
166
167// NOTE: This is intended to be a short-lived object. A future enhancment might
168// make it a global and reconfigure it upon some notification that the screen layout
169// has changed.
170inline wxCocoaPrivateScreenCoordinateTransformer::wxCocoaPrivateScreenCoordinateTransformer()
171{
172 NSArray *screens = [NSScreen screens];
173
174 [screens retain];
175
176 m_screenZero = nil;
177 if(screens != nil && [screens count] > 0)
178 m_screenZero = [[screens objectAtIndex:0] retain];
179
180 [screens release];
181
182 if(m_screenZero != nil)
183 m_screenZeroFrame = [m_screenZero frame];
184 else
185 {
186 wxLogWarning(wxT("Can't translate to/from wx screen coordinates and Cocoa screen coordinates"));
187 // Just blindly assume 1024x768 so that at least we can sort of flip things around into
188 // Cocoa coordinates.
189 // NOTE: Theoretically this case should never happen anyway.
190 m_screenZeroFrame = NSMakeRect(0,0,1024,768);
191 }
192}
193
194inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
195{
196 [m_screenZero release];
197 m_screenZero = nil;
198}
199
200inline wxPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
201{
202 // x and y are in wx screen coordinates which we're going to arbitrarily define such that
203 // (0,0) is the TOP-left of screen 0 (the one with the menubar)
204 // NOTE WELL: This means that (0,0) is _NOT_ an appropriate position for a window.
205
206 wxPoint theWxOrigin;
207
208 // Working in Cocoa's screen coordinates we must realize that the x coordinate we want is
209 // the distance between the left side (origin.x) of the window's frame and the left side of
210 // screen zero's frame.
211 theWxOrigin.x = windowFrame.origin.x - m_screenZeroFrame.origin.x;
212
213 // Working in Cocoa's screen coordinates we must realize that the y coordinate we want is
214 // actually the distance between the top-left of the screen zero frame and the top-left
215 // of the window's frame.
216
217 theWxOrigin.y = (m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height) - (windowFrame.origin.y + windowFrame.size.height);
218
219 return theWxOrigin;
220}
221
222inline NSPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
223{
224 NSPoint theCocoaOrigin;
225
226 // The position is in wx screen coordinates which we're going to arbitrarily define such that
227 // (0,0) is the TOP-left of screen 0 (the one with the menubar)
228
229 // NOTE: The usable rectangle is smaller and hence we have the keepOriginVisible flag
230 // which will move the origin downward and/or left as necessary if the origin is
231 // inside the screen0 rectangle (i.e. x/y >= 0 in wx coordinates) and outside the
232 // visible frame (i.e. x/y < the top/left of the screen0 visible frame in wx coordinates)
233 // We don't munge origin coordinates < 0 because it actually is possible that the menubar is on
234 // the top of the bottom screen and thus that origin is completely valid!
235 if(keepOriginVisible && (m_screenZero != nil))
236 {
237 // Do al of this in wx coordinates because it's far simpler since we're dealing with top/left points
238 wxPoint visibleOrigin = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates([m_screenZero visibleFrame]);
239 if(x >= 0 && x < visibleOrigin.x)
240 x = visibleOrigin.x;
241 if(y >= 0 && y < visibleOrigin.y)
242 y = visibleOrigin.y;
243 }
244
245 // The x coordinate is simple as it's just relative to screen zero's frame
246 theCocoaOrigin.x = m_screenZeroFrame.origin.x + x;
247 // Working in Cocoa's coordinates think to start at the bottom of screen zero's frame and add
248 // the height of that rect which gives us the coordinate for the top of the visible rect. Now realize that
249 // the wx coordinates are flipped so if y is say 10 then we want to be 10 pixels down from that and thus
250 // we subtract y. But then we still need to take into account the size of the window which is h and subtract
251 // that to get the bottom-left origin of the rectangle.
252 theCocoaOrigin.y = m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height - y - height;
253
254 return theCocoaOrigin;
255}
256
257} // namespace
258
259wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
260{
261 wxCocoaPrivateScreenCoordinateTransformer transformer;
262 return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
263}
264
265NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
266{
267 wxCocoaPrivateScreenCoordinateTransformer transformer;
268 return transformer.OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,keepOriginVisible);
269}
270
a82b8141
DE
271// ========================================================================
272// wxWindowCocoaHider
273// ========================================================================
274class wxWindowCocoaHider: protected wxCocoaNSView
275{
276 DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
277public:
278 wxWindowCocoaHider(wxWindow *owner);
279 virtual ~wxWindowCocoaHider();
280 inline WX_NSView GetNSView() { return m_dummyNSView; }
281protected:
282 wxWindowCocoa *m_owner;
283 WX_NSView m_dummyNSView;
284 virtual void Cocoa_FrameChanged(void);
7c5a378f 285 virtual void Cocoa_synthesizeMouseMoved(void) {}
75e4856b
DE
286#ifdef WXCOCOA_FILL_DUMMY_VIEW
287 virtual bool Cocoa_drawRect(const NSRect& rect);
288#endif //def WXCOCOA_FILL_DUMMY_VIEW
a82b8141
DE
289private:
290 wxWindowCocoaHider();
291};
292
75e4856b
DE
293// ========================================================================
294// wxDummyNSView
295// ========================================================================
296@interface wxDummyNSView : NSView
297- (NSView *)hitTest:(NSPoint)aPoint;
298@end
a24aa427 299WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
75e4856b
DE
300
301@implementation wxDummyNSView : NSView
302- (NSView *)hitTest:(NSPoint)aPoint
303{
304 return nil;
305}
306
307@end
a24aa427 308WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
75e4856b 309
a82b8141
DE
310// ========================================================================
311// wxWindowCocoaHider
312// ========================================================================
313wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
314: m_owner(owner)
315{
316 wxASSERT(owner);
317 wxASSERT(owner->GetNSViewForHiding());
a24aa427 318 m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
a82b8141 319 initWithFrame:[owner->GetNSViewForHiding() frame]];
75e4856b 320 [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
a82b8141
DE
321 AssociateNSView(m_dummyNSView);
322}
323
324wxWindowCocoaHider::~wxWindowCocoaHider()
325{
326 DisassociateNSView(m_dummyNSView);
327 [m_dummyNSView release];
328}
329
330void wxWindowCocoaHider::Cocoa_FrameChanged(void)
331{
332 // Keep the real window in synch with the dummy
333 wxASSERT(m_dummyNSView);
334 [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
335}
336
5558135c 337
75e4856b
DE
338#ifdef WXCOCOA_FILL_DUMMY_VIEW
339bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
340{
341 NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
342 [[NSColor greenColor] set];
343 [bezpath stroke];
344 [bezpath fill];
345 return true;
346}
347#endif //def WXCOCOA_FILL_DUMMY_VIEW
348
618d53b8
DE
349
350/*! @class WXManualScrollView
351 @abstract An NSScrollView subclass which implements wx scroll behavior
352 @discussion
353 Overrides default behavior of NSScrollView such that this class receives
354 the scroller action messages and allows the wxCocoaScrollView to act
355 on them accordingly. In particular, because the NSScrollView will not
356 receive action messages from the scroller, it will not adjust the
357 document view. This must be done manually using the ScrollWindow
358 method of wxWindow.
359 */
360@interface WXManualScrollView : NSScrollView
361{
362 /*! @field m_wxCocoaScrollView
363 */
364 wxWindowCocoaScrollView *m_wxCocoaScrollView;
365}
366
367// Override these to set up the target/action correctly
368- (void)setHorizontalScroller:(NSScroller *)aScroller;
369- (void)setVerticalScroller:(NSScroller *)aScroller;
370- (void)setHasHorizontalScroller:(BOOL)flag;
371- (void)setHasVerticalScroller:(BOOL)flag;
372
373// NOTE: _wx_ prefix means "private" method like _ that Apple (and only Apple) uses.
374- (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView;
375- (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView;
376
377/*! @method _wx_doScroller
378 @abstract Handles action messages from the scrollers
379 @discussion
380 Similar to Apple's _doScroller: method which is private and not documented.
381 We do not, however, call that method. Instead, we effectively override
382 it entirely. We don't override it by naming ourself the same thing because
383 the base class code may or may not call that method for other reasons we
384 simply cannot know about.
385 */
386- (void)_wx_doScroller:(id)sender;
387
388@end
389
390
391@implementation WXManualScrollView : NSScrollView
392
393static inline void WXManualScrollView_DoSetScrollerTargetAction(WXManualScrollView *self, NSScroller *aScroller)
394{
395 if(aScroller != NULL && [self _wx_wxCocoaScrollView] != NULL)
396 {
397 [aScroller setTarget:self];
398 [aScroller setAction:@selector(_wx_doScroller:)];
399 }
400}
401
402- (void)setHorizontalScroller:(NSScroller *)aScroller
403{
404 [super setHorizontalScroller:aScroller];
405 WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
406}
407
408- (void)setVerticalScroller:(NSScroller *)aScroller
409{
410 [super setVerticalScroller:aScroller];
411 WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
412}
413
414- (void)setHasHorizontalScroller:(BOOL)flag
415{
416 [super setHasHorizontalScroller:flag];
417 WXManualScrollView_DoSetScrollerTargetAction(self, [self horizontalScroller]);
418}
419
420- (void)setHasVerticalScroller:(BOOL)flag
421{
422 [super setHasVerticalScroller:flag];
423 WXManualScrollView_DoSetScrollerTargetAction(self, [self verticalScroller]);
424}
425
426- (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView
427{ return m_wxCocoaScrollView; }
428
429- (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView
430{
431 m_wxCocoaScrollView = theWxScrollView;
432 [self setHorizontalScroller:[self horizontalScroller]];
433 [self setVerticalScroller:[self verticalScroller]];
434}
435
436- (void)_wx_doScroller:(id)sender
437{
438 if(m_wxCocoaScrollView != NULL)
439 m_wxCocoaScrollView->_wx_doScroller(sender);
440 else
441 wxLogError(wxT("Unexpected action message received from NSScroller"));
442}
443
444- (void)reflectScrolledClipView:(NSClipView *)aClipView
445{
446 struct _ScrollerBackup
447 {
448 _ScrollerBackup(NSScroller *aScroller)
449 : m_scroller(aScroller)
450 , m_floatValue(aScroller!=nil?[aScroller floatValue]:0.0)
451 , m_knobProportion(aScroller!=nil?[aScroller knobProportion]:1.0)
452 , m_isEnabled(aScroller!=nil?[aScroller isEnabled]:false)
453 {
454 }
455 NSScroller *m_scroller;
456 CGFloat m_floatValue;
457 CGFloat m_knobProportion;
458 BOOL m_isEnabled;
459 ~_ScrollerBackup()
460 {
461 if(m_scroller != nil)
462 {
463 [m_scroller setFloatValue:m_floatValue knobProportion:m_knobProportion];
464 [m_scroller setEnabled:m_isEnabled];
465 }
466 }
467 private:
468 _ScrollerBackup();
469 _ScrollerBackup(_ScrollerBackup const&);
470 _ScrollerBackup& operator=(_ScrollerBackup const&);
471 };
472 _ScrollerBackup _horizontalBackup([self horizontalScroller]);
473 _ScrollerBackup _verticalBackup([self verticalScroller]);
474 // We MUST call super's implementation or else nothing seems to work right at all.
475 // However, we need our scrollers not to change values due to the document window
476 // moving so we cheat and save/restore their values across this call.
477 [super reflectScrolledClipView: aClipView];
478}
479
480@end
481WX_IMPLEMENT_GET_OBJC_CLASS(WXManualScrollView,NSScrollView)
482
816c52cf
DE
483// ========================================================================
484// wxFlippedNSClipView
485// ========================================================================
486@interface wxFlippedNSClipView : NSClipView
487- (BOOL)isFlipped;
488@end
a24aa427 489WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
816c52cf
DE
490
491@implementation wxFlippedNSClipView : NSClipView
492- (BOOL)isFlipped
493{
494 return YES;
495}
496
497@end
a24aa427 498WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
816c52cf
DE
499
500// ========================================================================
f298b203 501// wxWindowCocoaScrollView
816c52cf 502// ========================================================================
f298b203 503wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
816c52cf 504: m_owner(owner)
618d53b8
DE
505, m_cocoaNSScrollView() // nil
506, m_scrollRange() // {0,0}
507, m_scrollThumb() // {0,0}
508, m_virtualOrigin(0,0)
816c52cf 509{
a9861c57 510 wxAutoNSAutoreleasePool pool;
816c52cf
DE
511 wxASSERT(owner);
512 wxASSERT(owner->GetNSView());
618d53b8
DE
513 m_isNativeView = ![owner->GetNSView() isKindOfClass:[WX_GET_OBJC_CLASS(WXNSView) class]];
514 m_cocoaNSScrollView = [(m_isNativeView?[NSScrollView alloc]:[WXManualScrollView alloc])
816c52cf
DE
515 initWithFrame:[owner->GetNSView() frame]];
516 AssociateNSView(m_cocoaNSScrollView);
618d53b8
DE
517 if(m_isNativeView)
518 {
519 /* Set a bezel border around the entire thing because it looks funny without it.
520 TODO: Be sure to undo any borders on the real view (if any) and apply them
521 to this view if necessary. Right now, there is no border support in wxCocoa
522 so this isn't an issue.
523 */
524 [m_cocoaNSScrollView setBorderType:NSBezelBorder];
525 }
526 else
527 {
528 [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView: this];
529 // Don't set a bezel because we might be creating a scroll view due to being
530 // the "target window" of a wxScrolledWindow. That is to say that the user
531 // has absolutely no intention of scrolling the clip view used by this
532 // NSScrollView.
533 }
816c52cf
DE
534
535 /* Replace the default NSClipView with a flipped one. This ensures
536 scrolling is "pinned" to the top-left instead of bottom-right. */
a24aa427 537 NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
816c52cf
DE
538 initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
539 [m_cocoaNSScrollView setContentView:flippedClip];
540 [flippedClip release];
541
618d53b8 542 // In all cases we must encapsulate the real NSView properly
816c52cf
DE
543 Encapsulate();
544}
545
f298b203 546void wxWindowCocoaScrollView::Encapsulate()
816c52cf 547{
6f2ec3c3
DE
548 // Set the scroll view autoresizingMask to match the current NSView
549 [m_cocoaNSScrollView setAutoresizingMask: [m_owner->GetNSView() autoresizingMask]];
550 [m_owner->GetNSView() setAutoresizingMask: NSViewNotSizable];
816c52cf
DE
551 // NOTE: replaceSubView will cause m_cocaNSView to be released
552 // except when it hasn't been added into an NSView hierarchy in which
553 // case it doesn't need to be and this should work out to a no-op
554 m_owner->CocoaReplaceView(m_owner->GetNSView(), m_cocoaNSScrollView);
555 // The NSView is still retained by owner
556 [m_cocoaNSScrollView setDocumentView: m_owner->GetNSView()];
557 // Now it's also retained by the NSScrollView
558}
559
f298b203 560void wxWindowCocoaScrollView::Unencapsulate()
816c52cf
DE
561{
562 [m_cocoaNSScrollView setDocumentView: nil];
563 m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
6f2ec3c3
DE
564 if(![[m_owner->GetNSView() superview] isFlipped])
565 [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
816c52cf
DE
566}
567
f298b203 568wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
816c52cf
DE
569{
570 DisassociateNSView(m_cocoaNSScrollView);
618d53b8
DE
571 if(!m_isNativeView)
572 {
573 [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView:NULL];
574 }
816c52cf
DE
575 [m_cocoaNSScrollView release];
576}
577
f298b203 578void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
816c52cf
DE
579{
580 NSSize frameSize = [NSScrollView
581 frameSizeForContentSize: NSMakeSize(width,height)
582 hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
583 hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
584 borderType: [m_cocoaNSScrollView borderType]];
e9cece45
VZ
585 width = (int)frameSize.width;
586 height = (int)frameSize.height;
816c52cf
DE
587}
588
f298b203 589void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
816c52cf
DE
590{
591 NSSize nssize = [m_cocoaNSScrollView contentSize];
592 if(x)
e9cece45 593 *x = (int)nssize.width;
816c52cf 594 if(y)
e9cece45 595 *y = (int)nssize.height;
816c52cf
DE
596}
597
618d53b8
DE
598static inline void SetCocoaScroller(NSScroller *aScroller, int WXUNUSED(orientation), int position, int thumbSize, int range)
599{
600 wxCHECK_RET(aScroller != nil, wxT("Expected the NSScrollView to have a scroller"));
601
602 // NOTE: thumbSize is already ensured to be >= 1 and <= range by our caller
603 // unless range = 0 in which case we shouldn't have been be called.
604 wxCHECK_RET(range > 0, wxT("Internal wxCocoa bug: shouldn't have been called with 0 range"));
605
606 // Range of valid position values is from 0 to effectiveRange
607 // NOTE: if thumbSize == range then effectiveRange is 0.
608 // thumbSize is at least 1 which gives range from 0 to range - 1 inclusive
609 // which is exactly what we want.
610 int effectiveRange = range - thumbSize;
611
612 // knobProportion is hopefully easy to understand
613 // Note that thumbSize is already guaranteed >= 1 by our caller.
614 CGFloat const knobProportion = CGFloat(thumbSize)/CGFloat(range);
615
616 // NOTE: When effectiveRange is zero there really is no valid position
617 // We arbitrarily pick 0.0 which is the same as a scroller in the home position.
618 CGFloat const floatValue = (effectiveRange != 0)?CGFloat(position)/CGFloat(effectiveRange):0.0;
619
620 [aScroller setFloatValue:floatValue knobProportion: knobProportion];
621 // Make sure it's visibly working
622 [aScroller setEnabled:YES];
623}
624
625void wxWindowCocoaScrollView::SetScrollPos(wxOrientation orientation, int position)
626{
627 // NOTE: Rather than using only setFloatValue: (which we could do) we instead
628 // simply share the SetCocoaScroller call because all but the knobProportion
629 // calculations have to be done anyway.
630 if(orientation & wxHORIZONTAL)
631 {
632 NSScroller *aScroller = [m_cocoaNSScrollView horizontalScroller];
633 if(aScroller != nil)
634 SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[0], m_scrollRange[0]);
635 }
636 if(orientation & wxVERTICAL)
637 {
638 NSScroller *aScroller = [m_cocoaNSScrollView verticalScroller];
639 if(aScroller != nil)
640 SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[1], m_scrollRange[1]);
641 }
642}
643
644void wxWindowCocoaScrollView::SetScrollbar(int orientation, int position, int thumbSize, int range)
645{
646 // FIXME: API assumptions:
647 // 1. If the user wants to remove a scroller he gives range 0.
648 // 2. If the user wants to disable a scroller he sets thumbSize == range
649 // in which case it is logically impossible to scroll.
650 // The scroller shall still be displayed.
651
652 // Ensure that range is >= 0.
653 wxASSERT(range >= 0);
654 if(range < 0)
655 range = 0;
656
657 // Ensure that thumbSize <= range
658 wxASSERT(thumbSize <= range);
659 // Also ensure thumbSize >= 1 but don't complain if it isn't
660 if(thumbSize < 1)
661 thumbSize = 1;
662 // Now make sure it's really less than range, even if we just set it to 1
663 if(thumbSize > range)
664 thumbSize = range;
665
666 bool needScroller = (range != 0);
667
668 // Can potentially set both horizontal and vertical at the same time although this is
669 // probably not very useful.
670 if(orientation & wxHORIZONTAL)
671 {
672 m_scrollRange[0] = range;
673 m_scrollThumb[0] = thumbSize;
674 if(!m_isNativeView)
675 {
676 [m_cocoaNSScrollView setHasHorizontalScroller:needScroller];
677 if(needScroller)
678 SetCocoaScroller([m_cocoaNSScrollView horizontalScroller], orientation, position, thumbSize, range);
679 }
680 }
681
682 if(orientation & wxVERTICAL)
683 {
684 m_scrollRange[1] = range;
685 m_scrollThumb[1] = thumbSize;
686 if(!m_isNativeView)
687 {
688 [m_cocoaNSScrollView setHasVerticalScroller:needScroller];
689 if(needScroller)
690 SetCocoaScroller([m_cocoaNSScrollView verticalScroller], orientation, position, thumbSize, range);
691 }
692 }
693}
694
695int wxWindowCocoaScrollView::GetScrollPos(wxOrientation orient)
696{
697 if((orient & wxBOTH) == wxBOTH)
698 {
699 wxLogError(wxT("GetScrollPos called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
700 return 0;
701 }
702 int effectiveScrollRange;
703 NSScroller *cocoaScroller;
704 if(orient & wxHORIZONTAL)
705 {
706 effectiveScrollRange = m_scrollRange[0] - m_scrollThumb[0];
707 cocoaScroller = [m_cocoaNSScrollView horizontalScroller];
708 }
709 else if(orient & wxVERTICAL)
710 {
711 effectiveScrollRange = m_scrollRange[1] - m_scrollThumb[1];
712 cocoaScroller = [m_cocoaNSScrollView verticalScroller];
713 }
714 else
715 {
716 wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
717 return 0;
718 }
719 if(cocoaScroller == nil)
720 { // Document is not scrolled
721 return 0;
722 }
723 /*
724 The effective range of a scroll bar as defined by wxWidgets is from 0 to (range - thumbSize).
725 That is a scroller at the left/top position is at 0 and a scroller at the bottom/right
726 position is at range-thumbsize.
727
728 The range of an NSScroller is 0.0 to 1.0. Much easier! NOTE: Apple doesn't really specify
729 but GNUStep docs do say that 0.0 is top/left and 1.0 is bottom/right. This is actualy
730 in contrast to NSSlider which generally has 1.0 at the TOP when it's done vertically.
731 */
732 CGFloat cocoaScrollPos = [cocoaScroller floatValue];
733 return effectiveScrollRange * cocoaScrollPos;
734}
735
736int wxWindowCocoaScrollView::GetScrollRange(wxOrientation orient)
737{
738 if((orient & wxBOTH) == wxBOTH)
739 {
740 wxLogError(wxT("GetScrollRange called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
741 return 0;
742 }
743 if(orient & wxHORIZONTAL)
744 {
745 return m_scrollRange[0];
746 }
747 else if(orient & wxVERTICAL)
748 {
749 return m_scrollRange[1];
750 }
751 else
752 {
753 wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
754 return 0;
755 }
756}
757
758int wxWindowCocoaScrollView::GetScrollThumb(wxOrientation orient)
759{
760 if((orient & wxBOTH) == wxBOTH)
761 {
762 wxLogError(wxT("GetScrollThumb called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
763 return 0;
764 }
765 if(orient & wxHORIZONTAL)
766 {
767 return m_scrollThumb[0];
768 }
769 else if(orient & wxVERTICAL)
770 {
771 return m_scrollThumb[1];
772 }
773 else
774 {
775 wxLogError(wxT("GetScrollThumb called without an orientation which makes no sense"));
776 return 0;
777 }
778}
779
780/*!
781 Moves the contents (all existing drawing as well as all all child wxWindow) by the specified
782 amount expressed in the wxWindow's own coordinate system. This is used to implement scrolling
783 but the usage is rather interesting. When scrolling right (e.g. increasing the value of
784 the scroller) you must give a negative delta x (e.g. moving the contents LEFT). Likewise,
785 when scrolling the window down, increasing the value of the scroller, you give a negative
786 delta y which moves the contents up.
787
788 wxCocoa notes: To accomplish this trick in Cocoa we basically do what NSScrollView would
789 have done and that is adjust the content view's bounds origin. The content view is somewhat
790 confusingly the NSClipView which is more or less sort of the pImpl for NSScrollView
791 The real NSView with the user's content (e.g. the "virtual area" in wxWidgets parlance)
792 is called the document view in NSScrollView parlance.
793
794 The bounds origin is basically the exact opposite concept. Whereas in Windows the client
795 coordinate system remains constant and the content must shift left/up for increases
796 of scrolling, in Cocoa the coordinate system is actually the virtual one. So we must
797 instead shift the bounds rectangle right/down to get the effect of the content moving
798 left/up. Basically, it's a higher level interface than that provided by wxWidgets
799 so essentially we're implementing the low-level move content interface in terms of
800 the high-level move the viewport (the bounds) over top that content (the document
801 view which is the virtual area to wx).
802
803 For all intents and purposes that basically just means that we subtract the deltas
804 from the bounds origin and thus a negative delta actually increases the bounds origin
805 and a positive delta actually decreases it. This is absolutely true for the horizontal
806 axis but there's a catch in the vertical axis. If the content view (the clip view) is
807 flipped (and we do this by default) then it works exactly like the horizontal axis.
808 If it is not flipped (i.e. it is in postscript coordinates which are opposite to
809 wxWidgets) then the sense needs to be reversed.
810
811 However, this plays hell with window positions. The frame rects of any child views
812 do not change origin and this is actually important because if they did, the views
813 would send frame changed notifications, not to mention that Cocoa just doesn't really
814 do scrolling that way, it does it the way we do it here.
815
816 To fix this we implement GetPosition for child windows to not merely consult its
817 superview at the Cocoa level in order to do proper Cocoa->wx coordinate transform
818 but to actually consult is parent wxWindow because it makes a big difference if
819 the parent is scrolled. Argh. (FIXME: This isn't actually implemented yet)
820 */
821void wxWindowCocoaScrollView::ScrollWindow(int dx, int dy, const wxRect*)
822{
823 // Update our internal origin so we know how much the application code
824 // expects us to have been scrolled.
825 m_virtualOrigin.x += dx;
826 m_virtualOrigin.y += dy;
827
828 // Scroll the window using the standard Cocoa method of adjusting the
829 // clip view's bounds in the opposite fashion.
830 NSClipView *contentView = [m_cocoaNSScrollView contentView];
831 NSRect clipViewBoundsRect = [contentView bounds];
832 clipViewBoundsRect.origin.x -= dx;
833 if([contentView isFlipped])
834 clipViewBoundsRect.origin.y -= dy;
835 else
836 clipViewBoundsRect.origin.y += dy;
837 [contentView scrollToPoint:clipViewBoundsRect.origin];
838}
839
840void wxWindowCocoaScrollView::_wx_doScroller(NSScroller *sender)
841{
842 wxOrientation orientation;
843 if(sender == [m_cocoaNSScrollView horizontalScroller])
844 orientation = wxHORIZONTAL;
845 else if(sender == [m_cocoaNSScrollView verticalScroller])
846 orientation = wxVERTICAL;
847 else
848 {
849 wxLogDebug(wxT("Received action message from unexpected NSScroller"));
850 return;
851 }
852 // NOTE: Cocoa does not move the scroller for page up/down or line
853 // up/down events. That means the value will be the old value.
854 // For thumbtrack events, the value is the new value.
855 int scrollpos = GetScrollPos(orientation);
856 int commandType;
857 switch([sender hitPart])
858 {
859 default:
860 case NSScrollerNoPart:
861 case NSScrollerKnob: // Drag of knob
862 case NSScrollerKnobSlot: // Jump directly to position
863 commandType = wxEVT_SCROLLWIN_THUMBTRACK;
864 break;
865 case NSScrollerDecrementPage:
866 commandType = wxEVT_SCROLLWIN_PAGEUP;
867 break;
868 case NSScrollerIncrementPage:
869 commandType = wxEVT_SCROLLWIN_PAGEDOWN;
870 break;
871 case NSScrollerDecrementLine:
872 commandType = wxEVT_SCROLLWIN_LINEUP;
873 break;
874 case NSScrollerIncrementLine:
875 commandType = wxEVT_SCROLLWIN_LINEDOWN;
876 break;
877 }
878 wxScrollWinEvent event(commandType, scrollpos, orientation);
879 event.SetEventObject(m_owner);
937013e0 880 m_owner->HandleWindowEvent(event);
618d53b8
DE
881}
882
883void wxWindowCocoaScrollView::UpdateSizes()
884{
885 // Using the virtual size, figure out what the document frame size should be
886 // NOTE: Assume that the passed in virtualSize is already >= the client size
887 wxSize virtualSize = m_owner->GetVirtualSize();
888
889 // Get the document's current frame
890 NSRect documentViewFrame = [m_owner->GetNSView() frame];
891 NSRect newFrame = documentViewFrame;
892 newFrame.size = NSMakeSize(virtualSize.x, virtualSize.y);
893
894 if(!NSEqualRects(newFrame, documentViewFrame))
895 {
896 [m_owner->GetNSView() setFrame:newFrame];
897 }
898}
899
f298b203 900void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
816c52cf 901{
618d53b8 902 wxLogTrace(wxTRACE_COCOA,wxT("wxWindowCocoaScrollView=%p::Cocoa_FrameChanged for wxWindow %p"), this, m_owner);
816c52cf
DE
903 wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
904 event.SetEventObject(m_owner);
937013e0 905 m_owner->HandleWindowEvent(event);
e71594c2
DE
906
907 /* If the view is not a native one then it's being managed by wx. In this case the control
908 may decide to change its virtual size and we must update the document view's size to
909 match. For native views the virtual size will never have been set so we do not want
910 to use it at all.
911 */
912 if(!m_isNativeView)
913 UpdateSizes();
816c52cf
DE
914}
915
a82b8141
DE
916// ========================================================================
917// wxWindowCocoa
918// ========================================================================
fb896a32
DE
919// normally the base classes aren't included, but wxWindow is special
920#ifdef __WXUNIVERSAL__
921IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
922#else
923IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
924#endif
925
926BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
927END_EVENT_TABLE()
928
b9505233
DE
929wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
930
fb896a32
DE
931// Constructor
932void wxWindowCocoa::Init()
933{
fb896a32 934 m_cocoaNSView = NULL;
a82b8141 935 m_cocoaHider = NULL;
f298b203 936 m_wxCocoaScrollView = NULL;
8d8d3633
WS
937 m_isBeingDeleted = false;
938 m_isInPaint = false;
7c5a378f 939 m_visibleTrackingRectManager = NULL;
fb896a32
DE
940}
941
942// Constructor
943bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
944 const wxPoint& pos,
945 const wxSize& size,
946 long style,
947 const wxString& name)
948{
949 if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
950 return false;
951
952 // TODO: create the window
fb896a32 953 m_cocoaNSView = NULL;
a24aa427 954 SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
fb896a32
DE
955 [m_cocoaNSView release];
956
957 if (m_parent)
958 {
959 m_parent->AddChild(this);
960 m_parent->CocoaAddChild(this);
6d034f7d 961 SetInitialFrameRect(pos,size);
fb896a32
DE
962 }
963
8d8d3633 964 return true;
fb896a32
DE
965}
966
967// Destructor
968wxWindow::~wxWindow()
969{
7fc77f30 970 wxAutoNSAutoreleasePool pool;
fb896a32
DE
971 DestroyChildren();
972
065e208e 973 // Make sure our parent (in the wxWidgets sense) is our superview
6ba13ca4
DE
974 // before we go removing from it.
975 if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
976 CocoaRemoveFromParent();
a82b8141 977 delete m_cocoaHider;
f298b203 978 delete m_wxCocoaScrollView;
9c85202a
DE
979 if(m_cocoaNSView)
980 SendDestroyEvent();
fb896a32
DE
981 SetNSView(NULL);
982}
983
984void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
985{
b0a207df
DE
986 // Pool here due to lack of one during wx init phase
987 wxAutoNSAutoreleasePool pool;
988
a82b8141
DE
989 NSView *childView = child->GetNSViewForSuperview();
990
991 wxASSERT(childView);
992 [m_cocoaNSView addSubview: childView];
fb896a32
DE
993}
994
995void wxWindowCocoa::CocoaRemoveFromParent(void)
996{
a82b8141 997 [GetNSViewForSuperview() removeFromSuperview];
fb896a32
DE
998}
999
1000void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
1001{
7c5a378f
DE
1002 // Clear the visible area tracking rect if we have one.
1003 delete m_visibleTrackingRectManager;
1004 m_visibleTrackingRectManager = NULL;
1005
fb896a32 1006 bool need_debug = cocoaNSView || m_cocoaNSView;
48580976 1007 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
bac6f234 1008 DisassociateNSView(m_cocoaNSView);
6a5c31c2
DE
1009 wxGCSafeRetain(cocoaNSView);
1010 wxGCSafeRelease(m_cocoaNSView);
fb896a32 1011 m_cocoaNSView = cocoaNSView;
bac6f234 1012 AssociateNSView(m_cocoaNSView);
48580976 1013 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
fb896a32
DE
1014}
1015
a82b8141
DE
1016WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
1017{
1018 return m_cocoaHider
1019 ? m_cocoaHider->GetNSView()
f298b203
DE
1020 : m_wxCocoaScrollView
1021 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 1022 : m_cocoaNSView;
a82b8141
DE
1023}
1024
1025WX_NSView wxWindowCocoa::GetNSViewForHiding() const
1026{
f298b203
DE
1027 return m_wxCocoaScrollView
1028 ? m_wxCocoaScrollView->GetNSScrollView()
816c52cf 1029 : m_cocoaNSView;
a82b8141
DE
1030}
1031
34c9978d
DE
1032NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
1033{
1034 // TODO: Handle scrolling offset
ee022549 1035 return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
34c9978d
DE
1036}
1037
1038NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
1039{
1040 // TODO: Handle scrolling offset
ee022549 1041 return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
34c9978d
DE
1042}
1043
1044NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
1045{
1046 // TODO: Handle scrolling offset
ee022549 1047 return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
34c9978d
DE
1048}
1049
1050NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
1051{
1052 // TODO: Handle scrolling offset
ee022549 1053 return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
34c9978d
DE
1054}
1055
4db3c8ac
DE
1056WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
1057{
1058 // TODO: Handle scrolling offset
938156b2 1059 NSAffineTransform *transform = wxCocoaDCImpl::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
4db3c8ac
DE
1060 return transform;
1061}
1062
8ea5271e
DE
1063bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
1064{
48580976 1065 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
55c5be5e
DE
1066 // Recursion can happen if the event loop runs from within the paint
1067 // handler. For instance, if an assertion dialog is shown.
1068 // FIXME: This seems less than ideal.
1069 if(m_isInPaint)
1070 {
2b030203 1071 wxLogDebug(wxT("Paint event recursion!"));
55c5be5e
DE
1072 return false;
1073 }
8d8d3633 1074 m_isInPaint = true;
dc5bcaef
DE
1075
1076 // Set m_updateRegion
1077 const NSRect *rects = &rect; // The bounding box of the region
4799f3ba 1078 NSInteger countRects = 1;
dc5bcaef 1079 // Try replacing the larger rectangle with a list of smaller ones:
5dc47140
DE
1080 if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
1081 [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
34c9978d
DE
1082
1083 NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
1084 for(int i=0; i<countRects; i++)
1085 {
1086 transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
1087 }
1088 m_updateRegion = wxRegion(transformedRects,countRects);
1089 free(transformedRects);
dc5bcaef 1090
8ea5271e
DE
1091 wxPaintEvent event(m_windowId);
1092 event.SetEventObject(this);
937013e0 1093 bool ret = HandleWindowEvent(event);
8d8d3633 1094 m_isInPaint = false;
55c5be5e 1095 return ret;
8ea5271e
DE
1096}
1097
69dbb709
DE
1098void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
1099{
2b030203 1100 wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
34c9978d
DE
1101 // Mouse events happen at the NSWindow level so we need to convert
1102 // into our bounds coordinates then convert to wx coordinates.
a82b8141 1103 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
a4ee5059
DE
1104 if( m_wxCocoaScrollView != NULL)
1105 {
1106 // This gets the wx client area (i.e. the visible portion of the content) in
1107 // the coordinate system of our (the doucment) view.
1108 NSRect documentVisibleRect = [[m_wxCocoaScrollView->GetNSScrollView() contentView] documentVisibleRect];
1109 // For horizontal, simply subtract the origin.
1110 // e.g. if the origin is at 123 and the user clicks as far left as possible then
1111 // the coordinate that wx wants is 0.
1112 cocoaPoint.x -= documentVisibleRect.origin.x;
1113 if([m_cocoaNSView isFlipped])
1114 {
1115 // In the flipped view case this works exactly like horizontal.
1116 cocoaPoint.y -= documentVisibleRect.origin.y;
1117 }
1118 // For vertical we have to mind non-flipped (e.g. y=0 at bottom) views.
1119 // We also need to mind the fact that we're still in Cocoa coordinates
1120 // and not wx coordinates. The wx coordinate translation will still occur
1121 // and that is going to be wxY = boundsH - cocoaY for non-flipped views.
1122
1123 // When we consider scrolling we are truly interested in how far the top
1124 // edge of the bounds rectangle is scrolled off the screen.
1125 // Assuming the bounds origin is 0 (which is an assumption we make in
1126 // wxCocoa since wxWidgets has no analog to it) then the top edge of
1127 // the bounds rectangle is simply its height. The top edge of the
1128 // documentVisibleRect (e.g. the client area) is its height plus
1129 // its origin.
1130 // Thus, we simply need add the distance between the bounds top
1131 // and the client (docuemntVisibleRect) top.
1132 // Or putting it another way, we subtract the distance between the
1133 // client top and the bounds top.
1134 else
1135 {
1136 NSRect bounds = [m_cocoaNSView bounds];
1137 CGFloat scrollYOrigin = (bounds.size.height - (documentVisibleRect.origin.y + documentVisibleRect.size.height));
1138 cocoaPoint.y += scrollYOrigin;
1139 }
1140 }
1141
34c9978d
DE
1142 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
1143 // FIXME: Should we be adjusting for client area origin?
69dbb709 1144 const wxPoint &clientorigin = GetClientAreaOrigin();
34c9978d
DE
1145 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
1146 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
69dbb709
DE
1147
1148 event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
1149 event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
1150 event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
1151 event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
1152
1153 // TODO: set timestamp?
1154 event.SetEventObject(this);
1155 event.SetId(GetId());
1156}
1157
1158bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
1159{
1160 wxMouseEvent event(wxEVT_MOTION);
1161 InitMouseEvent(event,theEvent);
7c5a378f 1162 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
937013e0 1163 return HandleWindowEvent(event);
69dbb709
DE
1164}
1165
7c5a378f
DE
1166void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
1167{
1168 wxMouseEvent event(wxEVT_MOTION);
1169 NSWindow *window = [GetNSView() window];
1170 NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
1171 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
1172
1173 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
1174 // FIXME: Should we be adjusting for client area origin?
1175 const wxPoint &clientorigin = GetClientAreaOrigin();
1176 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
1177 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
1178
1179 // TODO: Handle shift, control, alt, meta flags
1180 event.SetEventObject(this);
1181 event.SetId(GetId());
1182
1183 wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
937013e0 1184 HandleWindowEvent(event);
7c5a378f
DE
1185}
1186
69dbb709
DE
1187bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
1188{
7c5a378f
DE
1189 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
1190 {
1191 m_visibleTrackingRectManager->BeginSynthesizingEvents();
1192
1193 // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
1194 // some other event comes in. That other event is (guess what) mouse moved events that will be sent
1195 // to the NSWindow which will forward them on to the first responder. We are not likely to be the
1196 // first responder, so the mouseMoved: events are effectively discarded.
1197 [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
1198
1199 wxMouseEvent event(wxEVT_ENTER_WINDOW);
1200 InitMouseEvent(event,theEvent);
18b32eb5 1201 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
937013e0 1202 return HandleWindowEvent(event);
7c5a378f
DE
1203 }
1204 else
1205 return false;
69dbb709
DE
1206}
1207
1208bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
1209{
7c5a378f
DE
1210 if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
1211 {
1212 m_visibleTrackingRectManager->StopSynthesizingEvents();
1213
1214 wxMouseEvent event(wxEVT_LEAVE_WINDOW);
1215 InitMouseEvent(event,theEvent);
18b32eb5 1216 wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
937013e0 1217 return HandleWindowEvent(event);
7c5a378f
DE
1218 }
1219 else
1220 return false;
69dbb709
DE
1221}
1222
1223bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
1224{
1225 wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
1226 InitMouseEvent(event,theEvent);
48580976 1227 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
937013e0 1228 return HandleWindowEvent(event);
69dbb709
DE
1229}
1230
1231bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
1232{
1233 wxMouseEvent event(wxEVT_MOTION);
1234 InitMouseEvent(event,theEvent);
1235 event.m_leftDown = true;
48580976 1236 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
937013e0 1237 return HandleWindowEvent(event);
69dbb709
DE
1238}
1239
1240bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
1241{
1242 wxMouseEvent event(wxEVT_LEFT_UP);
1243 InitMouseEvent(event,theEvent);
48580976 1244 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
937013e0 1245 return HandleWindowEvent(event);
69dbb709
DE
1246}
1247
1248bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
1249{
eafde5c7
DE
1250 wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
1251 InitMouseEvent(event,theEvent);
1252 wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
937013e0 1253 return HandleWindowEvent(event);
69dbb709
DE
1254}
1255
1256bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
1257{
eafde5c7
DE
1258 wxMouseEvent event(wxEVT_MOTION);
1259 InitMouseEvent(event,theEvent);
1260 event.m_rightDown = true;
1261 wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
937013e0 1262 return HandleWindowEvent(event);
69dbb709
DE
1263}
1264
1265bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
1266{
eafde5c7
DE
1267 wxMouseEvent event(wxEVT_RIGHT_UP);
1268 InitMouseEvent(event,theEvent);
1269 wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
937013e0 1270 return HandleWindowEvent(event);
69dbb709
DE
1271}
1272
1273bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
1274{
1275 return false;
1276}
1277
1278bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
1279{
1280 return false;
1281}
1282
1283bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
1284{
1285 return false;
1286}
1287
fb896a32
DE
1288void wxWindowCocoa::Cocoa_FrameChanged(void)
1289{
618d53b8
DE
1290 // We always get this message for the real NSView which may have been
1291 // enclosed in an NSScrollView. If that's the case then what we're
1292 // effectively getting here is a notifcation that the
1293 // virtual sized changed.. which we don't need to send on since
1294 // wx has no concept of this whatsoever.
1295 bool isViewForSuperview = (m_wxCocoaScrollView == NULL);
1296 if(isViewForSuperview)
1297 {
1298 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
1299 if(m_visibleTrackingRectManager != NULL)
1300 m_visibleTrackingRectManager->RebuildTrackingRect();
1301 wxSizeEvent event(GetSize(), m_windowId);
1302 event.SetEventObject(this);
937013e0 1303 HandleWindowEvent(event);
618d53b8
DE
1304 }
1305 else
1306 {
1307 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged ignored"),this);
1308 }
fb896a32
DE
1309}
1310
5558135c
RN
1311bool wxWindowCocoa::Cocoa_resetCursorRects()
1312{
7c5a378f 1313 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
ea29564c
DE
1314
1315 // When we are called there may be a queued tracking rect event (mouse entered or exited) and
1316 // we won't know it. A specific example is wxGenericHyperlinkCtrl changing the cursor from its
1317 // mouse exited event. If the control happens to share the edge with its parent window which is
1318 // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
1319 // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
1320 // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
1321 // which willl in turn call us before exiting for the next user event.
1322
1323 // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
1324 // our mouse exited event because the already queued event will have the old tracking rect
1325 // tag. The simple solution is to only rebuild our tracking rect if we need to.
1326
7c5a378f 1327 if(m_visibleTrackingRectManager != NULL)
ea29564c 1328 m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
7c5a378f 1329
5558135c
RN
1330 if(!m_cursor.GetNSCursor())
1331 return false;
8d8d3633
WS
1332
1333 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
1334
5558135c
RN
1335 return true;
1336}
1337
a8780ad5
DE
1338bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
1339{
1340 if(!wxWindowBase::SetCursor(cursor))
1341 return false;
6ebbf01f
DE
1342
1343 // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
1344 // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
1345 // rects and thus won't ever call resetCursorRects.
1346 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
1347
a8780ad5 1348 // Invalidate the cursor rects so the cursor will change
6ebbf01f
DE
1349 // Note that it is not enough to remove the old one (if any) and add the new one.
1350 // For the rects to work properly, Cocoa itself must call resetCursorRects.
a8780ad5
DE
1351 [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
1352 return true;
1353}
1354
7c5a378f
DE
1355bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
1356{
1357 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
1358 // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
1359 if(m_visibleTrackingRectManager != NULL)
1360 m_visibleTrackingRectManager->BuildTrackingRect();
1361 return false;
1362}
1363
1364bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
1365{
1366 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
1367 // Clear tracking rects. It is imperative this be done before the new window is set.
1368 if(m_visibleTrackingRectManager != NULL)
1369 m_visibleTrackingRectManager->ClearTrackingRect();
1370 return false;
1371}
1372
fb896a32
DE
1373bool wxWindow::Close(bool force)
1374{
cc6f960f
DE
1375 // The only reason this function exists is that it is virtual and
1376 // wxTopLevelWindowCocoa will override it.
1377 return wxWindowBase::Close(force);
fb896a32
DE
1378}
1379
a82b8141
DE
1380void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
1381{
1382 [[oldView superview] replaceSubview:oldView with:newView];
1383}
1384
47a8a4d5 1385void wxWindow::DoEnable(bool enable)
adb4816c 1386{
47a8a4d5 1387 CocoaSetEnabled(enable);
adb4816c
DE
1388}
1389
fb896a32
DE
1390bool wxWindow::Show(bool show)
1391{
7fc77f30 1392 wxAutoNSAutoreleasePool pool;
fb896a32
DE
1393 // If the window is marked as visible, then it shouldn't have a dummy view
1394 // If the window is marked hidden, then it should have a dummy view
addbdd29 1395 // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
2b030203 1396// wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
fb896a32 1397 // Return false if there isn't a window to show or hide
a82b8141
DE
1398 NSView *cocoaView = GetNSViewForHiding();
1399 if(!cocoaView)
fb896a32 1400 return false;
fb896a32
DE
1401 if(show)
1402 {
addbdd29 1403 // If state isn't changing, return false
a82b8141 1404 if(!m_cocoaHider)
addbdd29 1405 return false;
a82b8141
DE
1406 CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
1407 wxASSERT(![m_cocoaHider->GetNSView() superview]);
1408 delete m_cocoaHider;
1409 m_cocoaHider = NULL;
1410 wxASSERT([cocoaView superview]);
fb896a32
DE
1411 }
1412 else
1413 {
addbdd29 1414 // If state isn't changing, return false
a82b8141 1415 if(m_cocoaHider)
addbdd29 1416 return false;
a82b8141
DE
1417 m_cocoaHider = new wxWindowCocoaHider(this);
1418 // NOTE: replaceSubview:with will cause m_cocaNSView to be
1419 // (auto)released which balances out addSubview
1420 CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
fb896a32 1421 // m_coocaNSView is now only retained by us
a82b8141
DE
1422 wxASSERT([m_cocoaHider->GetNSView() superview]);
1423 wxASSERT(![cocoaView superview]);
fb896a32 1424 }
a6b4ff2e
DE
1425 m_isShown = show;
1426 return true;
fb896a32
DE
1427}
1428
1429void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1430{
9879fa84 1431 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
1432 int currentX, currentY;
1433 int currentW, currentH;
1434 DoGetPosition(&currentX, &currentY);
1435 DoGetSize(&currentW, &currentH);
1436 if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1437 x=currentX;
1438 if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1439 y=currentY;
1440
1441 AdjustForParentClientOrigin(x,y,sizeFlags);
1442
8d8d3633 1443 wxSize size(wxDefaultSize);
fb896a32
DE
1444
1445 if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1446 {
1447 if(sizeFlags&wxSIZE_AUTO_WIDTH)
1448 {
1449 size=DoGetBestSize();
1450 width=size.x;
1451 }
1452 else
1453 width=currentW;
1454 }
1455 if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1456 {
1457 if(sizeFlags&wxSIZE_AUTO_HEIGHT)
1458 {
1459 if(size.x==-1)
1460 size=DoGetBestSize();
1461 height=size.y;
1462 }
1463 else
1464 height=currentH;
1465 }
1466 DoMoveWindow(x,y,width,height);
1467}
1468
1e151594 1469#if wxUSE_TOOLTIPS
26191790
RN
1470
1471void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
1472{
1473 wxWindowBase::DoSetToolTip(tip);
1474
26191790
RN
1475 if ( m_tooltip )
1476 {
1477 m_tooltip->SetWindow((wxWindow *)this);
26191790
RN
1478 }
1479}
1480
1e151594
RN
1481#endif
1482
fb896a32
DE
1483void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
1484{
bed6fe0c 1485 wxAutoNSAutoreleasePool pool;
9879fa84 1486 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
fb896a32 1487
a82b8141 1488 NSView *nsview = GetNSViewForSuperview();
d449cf47 1489 NSView *superview = [nsview superview];
fb896a32 1490
34c9978d
DE
1491 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
1492
1493 NSRect oldFrameRect = [nsview frame];
1494 NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
1495 [nsview setFrame:newFrameRect];
b915b805 1496 // Be sure to redraw the parent to reflect the changed position
34c9978d
DE
1497 [superview setNeedsDisplayInRect:oldFrameRect];
1498 [superview setNeedsDisplayInRect:newFrameRect];
fb896a32
DE
1499}
1500
d139c3a8
DE
1501void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
1502{
1503 NSView *nsview = GetNSViewForSuperview();
1504 NSView *superview = [nsview superview];
2b030203 1505 wxCHECK_RET(superview,wxT("NSView does not have a superview"));
34c9978d 1506 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
d139c3a8
DE
1507 NSRect frameRect = [nsview frame];
1508 if(size.x!=-1)
1509 frameRect.size.width = size.x;
1510 if(size.y!=-1)
1511 frameRect.size.height = size.y;
1512 frameRect.origin.x = pos.x;
34c9978d 1513 frameRect.origin.y = pos.y;
c5bd9191
DE
1514 // Tell Cocoa to change the margin between the bottom of the superview
1515 // and the bottom of the control. Keeps the control pinned to the top
065e208e 1516 // of its superview so that its position in the wxWidgets coordinate
c5bd9191
DE
1517 // system doesn't change.
1518 if(![superview isFlipped])
1519 [nsview setAutoresizingMask: NSViewMinYMargin];
6f2ec3c3
DE
1520 // MUST set the mask before setFrame: which can generate a size event
1521 // and cause a scroller to be added!
34c9978d 1522 frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
6f2ec3c3 1523 [nsview setFrame: frameRect];
d139c3a8
DE
1524}
1525
fb896a32
DE
1526// Get total size
1527void wxWindow::DoGetSize(int *w, int *h) const
1528{
a82b8141 1529 NSRect cocoaRect = [GetNSViewForSuperview() frame];
fb896a32
DE
1530 if(w)
1531 *w=(int)cocoaRect.size.width;
1532 if(h)
1533 *h=(int)cocoaRect.size.height;
9879fa84 1534 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
fb896a32
DE
1535}
1536
1537void wxWindow::DoGetPosition(int *x, int *y) const
1538{
a82b8141 1539 NSView *nsview = GetNSViewForSuperview();
fb896a32 1540
576a1544 1541 NSRect cocoaRect = [nsview frame];
34c9978d 1542 NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
fb896a32 1543 if(x)
34c9978d 1544 *x=(int)rectWx.origin.x;
fb896a32 1545 if(y)
34c9978d 1546 *y=(int)rectWx.origin.y;
9879fa84 1547 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
fb896a32
DE
1548}
1549
1550WXWidget wxWindow::GetHandle() const
1551{
1552 return m_cocoaNSView;
1553}
1554
f7e98dee
RN
1555wxWindow* wxWindow::GetWxWindow() const
1556{
1557 return (wxWindow*) this;
1558}
1559
ddf7346a
DE
1560void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1561{
1562 [m_cocoaNSView setNeedsDisplay:YES];
1563}
1564
fb896a32
DE
1565void wxWindow::SetFocus()
1566{
67d2dac8
DE
1567 if([GetNSView() acceptsFirstResponder])
1568 [[GetNSView() window] makeFirstResponder: GetNSView()];
fb896a32
DE
1569}
1570
1571void wxWindow::DoCaptureMouse()
1572{
1573 // TODO
b9505233 1574 sm_capturedWindow = this;
fb896a32
DE
1575}
1576
1577void wxWindow::DoReleaseMouse()
1578{
1579 // TODO
b9505233 1580 sm_capturedWindow = NULL;
fb896a32
DE
1581}
1582
1583void wxWindow::DoScreenToClient(int *x, int *y) const
1584{
15f37147
DE
1585 // Point in cocoa screen coordinates:
1586 NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
1587 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1588 NSWindow *theWindow = [clientView window];
1589
1590 // Point in window's base coordinate system:
1591 NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
1592 // Point in view's bounds coordinate system
1593 NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
1594 // Point in wx client coordinates:
1595 NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
1596 if(x!=NULL)
1597 *x = theWxClientPoint.x;
1598 if(y!=NULL)
1599 *y = theWxClientPoint.y;
fb896a32
DE
1600}
1601
1602void wxWindow::DoClientToScreen(int *x, int *y) const
1603{
15f37147
DE
1604 // Point in wx client coordinates
1605 NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
1606
1607 NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1608
1609 // Point in the view's bounds coordinate system
1610 NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
1611
1612 // Point in the window's base coordinate system
1613 NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
1614
1615 NSWindow *theWindow = [clientView window];
1616 // Point in Cocoa's screen coordinates
1617 NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
1618
1619 // Act as though this was the origin of a 0x0 rectangle
1620 NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
1621
1622 // Convert that rectangle to wx coordinates
1623 wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
1624 if(*x)
1625 *x = theWxScreenPoint.x;
1626 if(*y)
1627 *y = theWxScreenPoint.y;
fb896a32
DE
1628}
1629
1630// Get size *available for subwindows* i.e. excluding menu bar etc.
1631void wxWindow::DoGetClientSize(int *x, int *y) const
1632{
48580976 1633 wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
f298b203
DE
1634 if(m_wxCocoaScrollView)
1635 m_wxCocoaScrollView->DoGetClientSize(x,y);
816c52cf
DE
1636 else
1637 wxWindowCocoa::DoGetSize(x,y);
fb896a32
DE
1638}
1639
1640void wxWindow::DoSetClientSize(int width, int height)
1641{
48580976 1642 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
f298b203
DE
1643 if(m_wxCocoaScrollView)
1644 m_wxCocoaScrollView->ClientSizeToSize(width,height);
e08efb8d
DE
1645 CocoaSetWxWindowSize(width,height);
1646}
1647
1648void wxWindow::CocoaSetWxWindowSize(int width, int height)
1649{
8d8d3633
WS
1650 wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1651}
1652
1653void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1654{
ba64d0b6 1655 // Intentional no-op.
8d8d3633
WS
1656}
1657
1658wxString wxWindow::GetLabel() const
1659{
ba64d0b6
DE
1660 // General Get/Set of labels is implemented in wxControlBase
1661 wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
8d8d3633 1662 return wxEmptyString;
fb896a32
DE
1663}
1664
1665int wxWindow::GetCharHeight() const
1666{
1667 // TODO
d89e391b 1668 return 10;
fb896a32
DE
1669}
1670
1671int wxWindow::GetCharWidth() const
1672{
1673 // TODO
d89e391b 1674 return 5;
fb896a32
DE
1675}
1676
d3978051
DE
1677void wxWindow::GetTextExtent(const wxString& string, int *outX, int *outY,
1678 int *outDescent, int *outExternalLeading, const wxFont *inFont) const
fb896a32 1679{
d3978051
DE
1680 // FIXME: This obviously ignores the window's font (if any) along with any size
1681 // transformations. However, it's better than nothing.
1682 // We don't create a wxClientDC because we don't want to accidently be able to use
1683 // it for drawing.
938156b2 1684 wxClientDC tmpdc(const_cast<wxWindow*>(this));
d3978051 1685 return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
fb896a32
DE
1686}
1687
fb896a32
DE
1688// Coordinates relative to the window
1689void wxWindow::WarpPointer (int x_pos, int y_pos)
1690{
1691 // TODO
1692}
1693
1694int wxWindow::GetScrollPos(int orient) const
1695{
618d53b8
DE
1696 if(m_wxCocoaScrollView != NULL)
1697 return m_wxCocoaScrollView->GetScrollPos(static_cast<wxOrientation>(orient & wxBOTH));
1698 else
1699 return 0;
fb896a32
DE
1700}
1701
1702// This now returns the whole range, not just the number
1703// of positions that we can scroll.
1704int wxWindow::GetScrollRange(int orient) const
1705{
618d53b8
DE
1706 if(m_wxCocoaScrollView != NULL)
1707 return m_wxCocoaScrollView->GetScrollRange(static_cast<wxOrientation>(orient & wxBOTH));
1708 else
1709 return 0;
fb896a32
DE
1710}
1711
1712int wxWindow::GetScrollThumb(int orient) const
1713{
618d53b8
DE
1714 if(m_wxCocoaScrollView != NULL)
1715 return m_wxCocoaScrollView->GetScrollThumb(static_cast<wxOrientation>(orient & wxBOTH));
1716 else
1717 return 0;
fb896a32
DE
1718}
1719
1720void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
1721{
618d53b8
DE
1722 if(m_wxCocoaScrollView != NULL)
1723 return m_wxCocoaScrollView->SetScrollPos(static_cast<wxOrientation>(orient & wxBOTH), pos);
fb896a32
DE
1724}
1725
816c52cf
DE
1726void wxWindow::CocoaCreateNSScrollView()
1727{
f298b203 1728 if(!m_wxCocoaScrollView)
816c52cf 1729 {
f298b203 1730 m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
816c52cf
DE
1731 }
1732}
1733
fb896a32
DE
1734// New function that will replace some of the above.
1735void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
1736 int range, bool refresh)
1737{
e08efb8d 1738 CocoaCreateNSScrollView();
618d53b8
DE
1739 m_wxCocoaScrollView->SetScrollbar(orient, pos, thumbVisible, range);
1740 // TODO: Handle refresh (if we even need to)
fb896a32
DE
1741}
1742
1743// Does a physical scroll
1744void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1745{
618d53b8
DE
1746 if(m_wxCocoaScrollView != NULL)
1747 m_wxCocoaScrollView->ScrollWindow(dx, dy, rect);
d3978051
DE
1748}
1749
816c52cf
DE
1750void wxWindow::DoSetVirtualSize( int x, int y )
1751{
d3978051
DE
1752 // Call wxWindowBase method which will set m_virtualSize to the appropriate value,
1753 // possibly not what the caller passed in. For example, the current implementation
1754 // clamps the width and height to within the min/max virtual ranges.
618d53b8
DE
1755 // wxDefaultCoord is passed through unchanged but then GetVirtualSize() will correct
1756 // that by returning effectively max(virtual, client)
816c52cf 1757 wxWindowBase::DoSetVirtualSize(x,y);
d3978051 1758 // Create the scroll view if it hasn't been already.
816c52cf 1759 CocoaCreateNSScrollView();
618d53b8
DE
1760
1761 // The GetVirtualSize automatically increases the size to max(client,virtual)
1762 m_wxCocoaScrollView->UpdateSizes();
816c52cf
DE
1763}
1764
fb896a32
DE
1765bool wxWindow::SetFont(const wxFont& font)
1766{
e7e97a59
DE
1767 // FIXME: We may need to handle wx font inheritance.
1768 return wxWindowBase::SetFont(font);
fb896a32
DE
1769}
1770
aa25b7f9
DE
1771#if 0 // these are used when debugging the algorithm.
1772static char const * const comparisonresultStrings[] =
1773{ "<"
1774, "=="
1775, ">"
1776};
1777#endif
1778
1779class CocoaWindowCompareContext
1780{
1781 DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
1782public:
1783 CocoaWindowCompareContext(); // Not implemented
1784 CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1785 {
1786 m_target = target;
1787 // Cocoa sorts subviews in-place.. make a copy
1788 m_subviews = [subviews copy];
1789 }
1790 ~CocoaWindowCompareContext()
1791 { // release the copy
1792 [m_subviews release];
1793 }
1794 NSView* target()
1795 { return m_target; }
1796 NSArray* subviews()
1797 { return m_subviews; }
1798 /* Helper function that returns the comparison based off of the original ordering */
1799 CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1800 {
1801 NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1802 NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1803 // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1804 // likely compare higher than the other view which is reasonable considering the only way that
1805 // can happen is if the subview was added after our call to subviews but before the call to
1806 // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
1807 // that case should never occur anyway because that would imply a multi-threaded GUI call
1808 // which is a big no-no with Cocoa.
1809
1810 // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1811 NSComparisonResult result = (firstI < secondI)
1812 ? NSOrderedAscending /* -1 */
1813 : (firstI > secondI)
1814 ? NSOrderedDescending /* 1 */
1815 : NSOrderedSame /* 0 */;
1816
1817#if 0 // Enable this if you need to debug the algorithm.
1818 NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
1819#endif
1820 return result;
1821 }
1822private:
1823 /* The subview we are trying to Raise or Lower */
1824 NSView *m_target;
1825 /* A copy of the original array of subviews */
1826 NSArray *m_subviews;
1827};
1828
1829/* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1830 * the target view is always higher than every other view. When comparing two views neither of
1831 * which is the target, it returns the correct response based on the original ordering
1832 */
1833static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1834{
aa25b7f9 1835 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1836 // first should be ordered higher
aa25b7f9 1837 if(first==compareContext->target())
4328a6ba
DE
1838 return NSOrderedDescending;
1839 // second should be ordered higher
aa25b7f9 1840 if(second==compareContext->target())
4328a6ba 1841 return NSOrderedAscending;
aa25b7f9 1842 return compareContext->CompareUsingOriginalOrdering(first,second);
4328a6ba
DE
1843}
1844
fb896a32
DE
1845// Raise the window to the top of the Z order
1846void wxWindow::Raise()
1847{
4328a6ba 1848// wxAutoNSAutoreleasePool pool;
a82b8141 1849 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1850 NSView *superview = [nsview superview];
1851 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1852
1853 [superview sortSubviewsUsingFunction:
4328a6ba 1854 CocoaRaiseWindowCompareFunction
aa25b7f9 1855 context: &compareContext];
4328a6ba
DE
1856}
1857
aa25b7f9
DE
1858/* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1859 * the target view is always lower than every other view. When comparing two views neither of
1860 * which is the target, it returns the correct response based on the original ordering
1861 */
1862static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
4328a6ba 1863{
aa25b7f9 1864 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
4328a6ba 1865 // first should be ordered lower
aa25b7f9 1866 if(first==compareContext->target())
4328a6ba
DE
1867 return NSOrderedAscending;
1868 // second should be ordered lower
aa25b7f9 1869 if(second==compareContext->target())
4328a6ba 1870 return NSOrderedDescending;
aa25b7f9 1871 return compareContext->CompareUsingOriginalOrdering(first,second);
fb896a32
DE
1872}
1873
1874// Lower the window to the bottom of the Z order
1875void wxWindow::Lower()
1876{
4328a6ba 1877 NSView *nsview = GetNSViewForSuperview();
aa25b7f9
DE
1878 NSView *superview = [nsview superview];
1879 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1880
1881#if 0
1882 NSLog(@"Target:\n%@\n", nsview);
1883 NSLog(@"Before:\n%@\n", compareContext.subviews());
1884#endif
1885 [superview sortSubviewsUsingFunction:
4328a6ba 1886 CocoaLowerWindowCompareFunction
aa25b7f9
DE
1887 context: &compareContext];
1888#if 0
1889 NSLog(@"After:\n%@\n", [superview subviews]);
1890#endif
fb896a32
DE
1891}
1892
1893bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1894{
8d8d3633 1895 return false;
fb896a32
DE
1896}
1897
1898// Get the window with the focus
dcb68102 1899wxWindow *wxWindowBase::DoFindFocus()
fb896a32 1900{
67d2dac8
DE
1901 // Basically we are somewhat emulating the responder chain here except
1902 // we are only loking for the first responder in the key window or
1903 // upon failing to find one if the main window is different we look
1904 // for the first responder in the main window.
1905
1906 // Note that the firstResponder doesn't necessarily have to be an
1907 // NSView but wxCocoaNSView::GetFromCocoa() will simply return
1908 // NULL unless it finds its argument in its hash map.
1909
1910 wxCocoaNSView *win;
1911
1912 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
9151dcec 1913 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
67d2dac8
DE
1914 if(win)
1915 return win->GetWxWindow();
1916
1917 NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1918 if(mainWindow == keyWindow)
f7e98dee 1919 return NULL;
9151dcec 1920 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
67d2dac8
DE
1921 if(win)
1922 return win->GetWxWindow();
1923
1924 return NULL;
fb896a32
DE
1925}
1926
1927/* static */ wxWindow *wxWindowBase::GetCapture()
1928{
1929 // TODO
b9505233 1930 return wxWindowCocoa::sm_capturedWindow;
fb896a32
DE
1931}
1932
1933wxWindow *wxGetActiveWindow()
1934{
1935 // TODO
1936 return NULL;
1937}
1938
7c9428ab
DE
1939wxPoint wxGetMousePosition()
1940{
1941 // TODO
1942 return wxDefaultPosition;
1943}
1944
ca5db7b2
WS
1945wxMouseState wxGetMouseState()
1946{
1947 wxMouseState ms;
1948 // TODO
1949 return ms;
1950}
1951
7c9428ab
DE
1952wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1953{
1954 pt = wxGetMousePosition();
1955 return NULL;
1956}
7c5a378f 1957
8ea3a63e
DE
1958// ========================================================================
1959// wxCocoaMouseMovedEventSynthesizer
1960// ========================================================================
1961
18b32eb5
DE
1962#define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
1963
8ea3a63e
DE
1964/* This class registers one run loop observer to cover all windows registered with it.
1965 * It will register the observer when the first view is registerd and unregister the
1966 * observer when the last view is unregistered.
1967 * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
1968 * is no reason it couldn't be instantiated multiple times.
1969 */
1970class wxCocoaMouseMovedEventSynthesizer
1971{
1972 DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
1973public:
1974 wxCocoaMouseMovedEventSynthesizer()
1975 { m_lastScreenMouseLocation = NSZeroPoint;
1976 }
1977 ~wxCocoaMouseMovedEventSynthesizer();
1978 void RegisterWxCocoaView(wxCocoaNSView *aView);
1979 void UnregisterWxCocoaView(wxCocoaNSView *aView);
1980 void SynthesizeMouseMovedEvent();
1981
1982protected:
1983 void AddRunLoopObserver();
1984 void RemoveRunLoopObserver();
1985 wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
1986 std::list<wxCocoaNSView*> m_registeredViews;
1987 NSPoint m_lastScreenMouseLocation;
1988 static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
1989};
1990
1991void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
1992{
1993 m_registeredViews.push_back(aView);
18b32eb5 1994 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
1995
1996 if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
1997 {
1998 AddRunLoopObserver();
1999 }
2000}
2001
2002void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
2003{
2004 m_registeredViews.remove(aView);
18b32eb5 2005 wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
8ea3a63e
DE
2006 if(m_registeredViews.empty() && m_runLoopObserver != NULL)
2007 {
2008 RemoveRunLoopObserver();
2009 }
2010}
2011
2012wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
2013{
2014 if(!m_registeredViews.empty())
2015 {
2016 // This means failure to clean up so we report on it as a debug message.
2017 wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
2018 m_registeredViews.clear();
2019 }
2020 if(m_runLoopObserver != NULL)
2021 {
2022 // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
2023 wxLogDebug(wxT("Removing run loop observer during static destruction time."));
2024 RemoveRunLoopObserver();
2025 }
2026}
2027
2028void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
2029{
2030 reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
2031}
2032
2033void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
2034{
2035 CFRunLoopObserverContext observerContext =
2036 { 0
2037 , this
2038 , NULL
2039 , NULL
2040 , NULL
2041 };
1f361be2
DE
2042
2043 // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
2044 // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
2045
2046 // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
2047 // in our observer procedure being called before the run loop has decided that it is going to return control to
2048 // the Cocoa event loop. One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
2049 // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
2050
2051 // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
2052 // won't be called until the CFRunLoop gets around to it. If the CFRunLoop has not yet exited then it will get
2053 // around to it before letting the event loop do its work. This has some very odd effects on the way the
2054 // newly created tracking rects function. In particular, we will often miss the mouseExited: message if the
2055 // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
2056 // time the new one is built.
2057
2058 // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
2059 // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
2060 // the mouseExited: message to be sent properly.
2061
2062 m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
8ea3a63e
DE
2063 CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
2064 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
2065}
2066
2067void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
2068{
2069 CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
2070 m_runLoopObserver.reset();
2071 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
2072}
2073
2074void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
2075{
2076 NSPoint screenMouseLocation = [NSEvent mouseLocation];
2077 // Checking the last mouse location is done for a few reasons:
2078 // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
2079 // telling the app the mouse moved when the user hit a key for instance.
2080 // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
2081 // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
2082 // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
2083 // loop causing the tracking rectangles to constantly be reset.
2084 if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
2085 {
2086 m_lastScreenMouseLocation = screenMouseLocation;
2087 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
2088 for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
2089 {
2090 (*i)->Cocoa_synthesizeMouseMoved();
2091 }
2092 }
2093}
2094
2095// Singleton used for all views:
2096static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
2097
7c5a378f
DE
2098// ========================================================================
2099// wxCocoaTrackingRectManager
2100// ========================================================================
2101
2102wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
2103: m_window(window)
2104{
2105 m_isTrackingRectActive = false;
7c5a378f
DE
2106 BuildTrackingRect();
2107}
2108
2109void wxCocoaTrackingRectManager::ClearTrackingRect()
2110{
2111 if(m_isTrackingRectActive)
2112 {
2113 [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
2114 m_isTrackingRectActive = false;
18b32eb5 2115 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
2116 }
2117 // If we were doing periodic events we need to clear those too
2118 StopSynthesizingEvents();
2119}
2120
2121void wxCocoaTrackingRectManager::StopSynthesizingEvents()
2122{
8ea3a63e 2123 s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
7c5a378f
DE
2124}
2125
2126void wxCocoaTrackingRectManager::BuildTrackingRect()
2127{
b0a207df
DE
2128 // Pool here due to lack of one during wx init phase
2129 wxAutoNSAutoreleasePool pool;
2130
7c5a378f 2131 wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
18b32eb5
DE
2132
2133 NSView *theView = m_window->GetNSView();
2134
2135 if([theView window] != nil)
7c5a378f 2136 {
ea29564c
DE
2137 NSRect visibleRect = [theView visibleRect];
2138
2139 m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
2140 m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
7c5a378f 2141 m_isTrackingRectActive = true;
ea29564c 2142
18b32eb5 2143 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
7c5a378f
DE
2144 }
2145}
2146
7c5a378f
DE
2147void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
2148{
8ea3a63e 2149 s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
7c5a378f
DE
2150}
2151
ea29564c
DE
2152void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
2153{
2154 if(m_isTrackingRectActive)
2155 {
2156 NSView *theView = m_window->GetNSView();
2157 NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
2158 if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
2159 {
2160 wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
2161 return;
2162 }
2163 }
2164 RebuildTrackingRect();
2165}
2166
7c5a378f
DE
2167void wxCocoaTrackingRectManager::RebuildTrackingRect()
2168{
2169 ClearTrackingRect();
2170 BuildTrackingRect();
2171}
2172
2173wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
2174{
2175 ClearTrackingRect();
2176}
2177
2178bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
2179{
2180 return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
2181}
2182