Move wxWindowCocoaScrollView class into its own header.
[wxWidgets.git] / src / cocoa / window.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/window.mm
3 // Purpose:     wxWindowCocoa
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2002/12/26
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2002 David Elliott
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifndef WX_PRECOMP
15     #include "wx/log.h"
16     #include "wx/window.h"
17     #include "wx/dc.h"
18     #include "wx/dcclient.h"
19     #include "wx/utils.h"
20 #endif //WX_PRECOMP
21
22 #include "wx/tooltip.h"
23
24 #include "wx/cocoa/dc.h"
25 #include "wx/cocoa/autorelease.h"
26 #include "wx/cocoa/string.h"
27 #include "wx/cocoa/trackingrectmanager.h"
28 #include "wx/cocoa/private/scrollview.h"
29 #include "wx/mac/corefoundation/cfref.h"
30 #include "wx/cocoa/ObjcRef.h"
31
32 #import <Foundation/NSArray.h>
33 #import <Foundation/NSRunLoop.h>
34 #include "wx/cocoa/objc/NSView.h"
35 #import <AppKit/NSEvent.h>
36 #import <AppKit/NSScrollView.h>
37 #import <AppKit/NSScroller.h>
38 #import <AppKit/NSColor.h>
39 #import <AppKit/NSClipView.h>
40 #import <Foundation/NSException.h>
41 #import <AppKit/NSApplication.h>
42 #import <AppKit/NSWindow.h>
43 #import <AppKit/NSScreen.h>
44
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
52 // STL list used by wxCocoaMouseMovedEventSynthesizer
53 #include <list>
54
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)
62 typedef NSComparisonResult CocoaWindowCompareFunctionResult;
63 #else
64 typedef int CocoaWindowCompareFunctionResult;
65 #endif
66
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
72 // ========================================================================
73 // Helper functions for converting to/from wxWidgets coordinates and a
74 // specified NSView's coordinate system.
75 // ========================================================================
76 NSPoint 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
88 NSRect 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
102 NSPoint 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
114 NSRect 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
128 // ============================================================================
129 // Screen coordinate helpers
130 // ============================================================================
131
132 /*
133 General observation about Cocoa screen coordinates:
134 It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
135
136 It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
137 the BOTTOM-right corner of this screen.  Recall that Cocoa uses cartesian coordinates so y-increase is up.
138
139 It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
140 relative to that screen's frame.  The only real way to test this is to configure two screens one atop
141 the other such that the menubar screen is on top.  The Dock at the bottom of the screen will then
142 eat into the visibleFrame of screen 1 by incrementing it's y-origin.  Thus if you arrange two
143 1920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
144 visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
145
146 In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
147 However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
148 This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
149 */
150
151 namespace { // file namespace
152
153 class wxCocoaPrivateScreenCoordinateTransformer
154 {
155     DECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer)
156 public:
157     wxCocoaPrivateScreenCoordinateTransformer();
158     ~wxCocoaPrivateScreenCoordinateTransformer();
159     wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
160     NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
161
162 protected:
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.
170 inline 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
194 inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
195 {
196     [m_screenZero release];
197     m_screenZero = nil;
198 }
199
200 inline 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
222 inline 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
259 wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
260 {
261     wxCocoaPrivateScreenCoordinateTransformer transformer;
262     return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
263 }
264
265 NSPoint 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
271 // ========================================================================
272 // wxWindowCocoaHider
273 // ========================================================================
274 class wxWindowCocoaHider: protected wxCocoaNSView
275 {
276     DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
277 public:
278     wxWindowCocoaHider(wxWindow *owner);
279     virtual ~wxWindowCocoaHider();
280     inline WX_NSView GetNSView() { return m_dummyNSView; }
281 protected:
282     wxWindowCocoa *m_owner;
283     WX_NSView m_dummyNSView;
284     virtual void Cocoa_FrameChanged(void);
285     virtual void Cocoa_synthesizeMouseMoved(void) {}
286 #ifdef WXCOCOA_FILL_DUMMY_VIEW
287     virtual bool Cocoa_drawRect(const NSRect& rect);
288 #endif //def WXCOCOA_FILL_DUMMY_VIEW
289 private:
290     wxWindowCocoaHider();
291 };
292
293 // ========================================================================
294 // wxDummyNSView
295 // ========================================================================
296 @interface wxDummyNSView : NSView
297 - (NSView *)hitTest:(NSPoint)aPoint;
298 @end
299 WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
300
301 @implementation wxDummyNSView : NSView
302 - (NSView *)hitTest:(NSPoint)aPoint
303 {
304     return nil;
305 }
306
307 @end
308 WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
309
310 // ========================================================================
311 // wxWindowCocoaHider
312 // ========================================================================
313 wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
314 :   m_owner(owner)
315 {
316     wxASSERT(owner);
317     wxASSERT(owner->GetNSViewForHiding());
318     m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
319         initWithFrame:[owner->GetNSViewForHiding() frame]];
320     [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
321     AssociateNSView(m_dummyNSView);
322 }
323
324 wxWindowCocoaHider::~wxWindowCocoaHider()
325 {
326     DisassociateNSView(m_dummyNSView);
327     [m_dummyNSView release];
328 }
329
330 void 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
337
338 #ifdef WXCOCOA_FILL_DUMMY_VIEW
339 bool 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
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
393 static 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
481 WX_IMPLEMENT_GET_OBJC_CLASS(WXManualScrollView,NSScrollView)
482
483 // ========================================================================
484 // wxFlippedNSClipView
485 // ========================================================================
486 @interface wxFlippedNSClipView : NSClipView
487 - (BOOL)isFlipped;
488 @end
489 WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
490
491 @implementation wxFlippedNSClipView : NSClipView
492 - (BOOL)isFlipped
493 {
494     return YES;
495 }
496
497 @end
498 WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
499
500 // ========================================================================
501 // wxWindowCocoaScrollView
502 // ========================================================================
503 wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
504 :   m_owner(owner)
505 ,   m_cocoaNSScrollView() // nil
506 ,   m_scrollRange() // {0,0}
507 ,   m_scrollThumb() // {0,0}
508 ,   m_virtualOrigin(0,0)
509 {
510     wxAutoNSAutoreleasePool pool;
511     wxASSERT(owner);
512     wxASSERT(owner->GetNSView());
513     m_isNativeView = ![owner->GetNSView() isKindOfClass:[WX_GET_OBJC_CLASS(WXNSView) class]];
514     m_cocoaNSScrollView = [(m_isNativeView?[NSScrollView alloc]:[WXManualScrollView alloc])
515         initWithFrame:[owner->GetNSView() frame]];
516     AssociateNSView(m_cocoaNSScrollView);
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     }
534
535     /* Replace the default NSClipView with a flipped one.  This ensures
536        scrolling is "pinned" to the top-left instead of bottom-right. */
537     NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
538         initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
539     [m_cocoaNSScrollView setContentView:flippedClip];
540     [flippedClip release];
541
542     // In all cases we must encapsulate the real NSView properly
543     Encapsulate();
544 }
545
546 void wxWindowCocoaScrollView::Encapsulate()
547 {
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];
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
560 void wxWindowCocoaScrollView::Unencapsulate()
561 {
562     [m_cocoaNSScrollView setDocumentView: nil];
563     m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
564     if(![[m_owner->GetNSView() superview] isFlipped])
565         [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
566 }
567
568 wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
569 {
570     DisassociateNSView(m_cocoaNSScrollView);
571     if(!m_isNativeView)
572     {
573         [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView:NULL];
574     }
575     [m_cocoaNSScrollView release];
576 }
577
578 void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
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]];
585     width = (int)frameSize.width;
586     height = (int)frameSize.height;
587 }
588
589 void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
590 {
591     NSSize nssize = [m_cocoaNSScrollView contentSize];
592     if(x)
593         *x = (int)nssize.width;
594     if(y)
595         *y = (int)nssize.height;
596 }
597
598 static 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
625 void 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
644 void 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
695 int 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
736 int 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
758 int 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  */
821 void 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
840 void 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);
880     m_owner->HandleWindowEvent(event);
881 }
882
883 void 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
900 void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
901 {
902     wxLogTrace(wxTRACE_COCOA,wxT("wxWindowCocoaScrollView=%p::Cocoa_FrameChanged for wxWindow %p"), this, m_owner);
903     wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
904     event.SetEventObject(m_owner);
905     m_owner->HandleWindowEvent(event);
906     UpdateSizes();
907 }
908
909 // ========================================================================
910 // wxWindowCocoa
911 // ========================================================================
912 // normally the base classes aren't included, but wxWindow is special
913 #ifdef __WXUNIVERSAL__
914 IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
915 #else
916 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
917 #endif
918
919 BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
920 END_EVENT_TABLE()
921
922 wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
923
924 // Constructor
925 void wxWindowCocoa::Init()
926 {
927     m_cocoaNSView = NULL;
928     m_cocoaHider = NULL;
929     m_wxCocoaScrollView = NULL;
930     m_isBeingDeleted = false;
931     m_isInPaint = false;
932     m_visibleTrackingRectManager = NULL;
933 }
934
935 // Constructor
936 bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
937            const wxPoint& pos,
938            const wxSize& size,
939            long style,
940            const wxString& name)
941 {
942     if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
943         return false;
944
945     // TODO: create the window
946     m_cocoaNSView = NULL;
947     SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
948     [m_cocoaNSView release];
949
950     if (m_parent)
951     {
952         m_parent->AddChild(this);
953         m_parent->CocoaAddChild(this);
954         SetInitialFrameRect(pos,size);
955     }
956
957     return true;
958 }
959
960 // Destructor
961 wxWindow::~wxWindow()
962 {
963     wxAutoNSAutoreleasePool pool;
964     DestroyChildren();
965
966     // Make sure our parent (in the wxWidgets sense) is our superview
967     // before we go removing from it.
968     if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
969         CocoaRemoveFromParent();
970     delete m_cocoaHider;
971     delete m_wxCocoaScrollView;
972     if(m_cocoaNSView)
973         SendDestroyEvent();
974     SetNSView(NULL);
975 }
976
977 void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
978 {
979     // Pool here due to lack of one during wx init phase
980     wxAutoNSAutoreleasePool pool;
981
982     NSView *childView = child->GetNSViewForSuperview();
983
984     wxASSERT(childView);
985     [m_cocoaNSView addSubview: childView];
986 }
987
988 void wxWindowCocoa::CocoaRemoveFromParent(void)
989 {
990     [GetNSViewForSuperview() removeFromSuperview];
991 }
992
993 void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
994 {
995     // Clear the visible area tracking rect if we have one.
996     delete m_visibleTrackingRectManager;
997     m_visibleTrackingRectManager = NULL;
998
999     bool need_debug = cocoaNSView || m_cocoaNSView;
1000     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
1001     DisassociateNSView(m_cocoaNSView);
1002     wxGCSafeRetain(cocoaNSView);
1003     wxGCSafeRelease(m_cocoaNSView);
1004     m_cocoaNSView = cocoaNSView;
1005     AssociateNSView(m_cocoaNSView);
1006     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
1007 }
1008
1009 WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
1010 {
1011     return m_cocoaHider
1012         ?   m_cocoaHider->GetNSView()
1013         :   m_wxCocoaScrollView
1014             ?   m_wxCocoaScrollView->GetNSScrollView()
1015             :   m_cocoaNSView;
1016 }
1017
1018 WX_NSView wxWindowCocoa::GetNSViewForHiding() const
1019 {
1020     return m_wxCocoaScrollView
1021         ?   m_wxCocoaScrollView->GetNSScrollView()
1022         :   m_cocoaNSView;
1023 }
1024
1025 NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
1026 {
1027     // TODO: Handle scrolling offset
1028     return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
1029 }
1030
1031 NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
1032 {
1033     // TODO: Handle scrolling offset
1034     return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
1035 }
1036
1037 NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
1038 {
1039     // TODO: Handle scrolling offset
1040     return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
1041 }
1042
1043 NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
1044 {
1045     // TODO: Handle scrolling offset
1046     return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
1047 }
1048
1049 WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
1050 {
1051     // TODO: Handle scrolling offset
1052     NSAffineTransform *transform = wxCocoaDCImpl::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
1053     return transform;
1054 }
1055
1056 bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
1057 {
1058     wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
1059     // Recursion can happen if the event loop runs from within the paint
1060     // handler.  For instance, if an assertion dialog is shown.
1061     // FIXME: This seems less than ideal.
1062     if(m_isInPaint)
1063     {
1064         wxLogDebug(wxT("Paint event recursion!"));
1065         return false;
1066     }
1067     m_isInPaint = true;
1068
1069     // Set m_updateRegion
1070     const NSRect *rects = &rect; // The bounding box of the region
1071     NSInteger countRects = 1;
1072     // Try replacing the larger rectangle with a list of smaller ones:
1073     if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
1074         [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
1075
1076     NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
1077     for(int i=0; i<countRects; i++)
1078     {
1079         transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
1080     }
1081     m_updateRegion = wxRegion(transformedRects,countRects);
1082     free(transformedRects);
1083
1084     wxPaintEvent event(m_windowId);
1085     event.SetEventObject(this);
1086     bool ret = HandleWindowEvent(event);
1087     m_isInPaint = false;
1088     return ret;
1089 }
1090
1091 void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
1092 {
1093     wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
1094     // Mouse events happen at the NSWindow level so we need to convert
1095     // into our bounds coordinates then convert to wx coordinates.
1096     NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
1097     if( m_wxCocoaScrollView != NULL)
1098     {
1099         // This gets the wx client area (i.e. the visible portion of the content) in
1100         // the coordinate system of our (the doucment) view.
1101         NSRect documentVisibleRect = [[m_wxCocoaScrollView->GetNSScrollView() contentView] documentVisibleRect];
1102         // For horizontal, simply subtract the origin.
1103         // e.g. if the origin is at 123 and the user clicks as far left as possible then
1104         // the coordinate that wx wants is 0.
1105         cocoaPoint.x -= documentVisibleRect.origin.x;
1106         if([m_cocoaNSView isFlipped])
1107         {
1108             // In the flipped view case this works exactly like horizontal.
1109             cocoaPoint.y -= documentVisibleRect.origin.y;
1110         }
1111         // For vertical we have to mind non-flipped (e.g. y=0 at bottom) views.
1112         // We also need to mind the fact that we're still in Cocoa coordinates
1113         // and not wx coordinates.  The wx coordinate translation will still occur
1114         // and that is going to be wxY = boundsH - cocoaY for non-flipped views.
1115
1116         // When we consider scrolling we are truly interested in how far the top
1117         // edge of the bounds rectangle is scrolled off the screen.
1118         // Assuming the bounds origin is 0 (which is an assumption we make in
1119         // wxCocoa since wxWidgets has no analog to it) then the top edge of
1120         // the bounds rectangle is simply its height.  The top edge of the
1121         // documentVisibleRect (e.g. the client area) is its height plus
1122         // its origin.
1123         // Thus, we simply need add the distance between the bounds top
1124         // and the client (docuemntVisibleRect) top.
1125         // Or putting it another way, we subtract the distance between the
1126         // client top and the bounds top.
1127         else
1128         {
1129             NSRect bounds = [m_cocoaNSView bounds];
1130             CGFloat scrollYOrigin = (bounds.size.height - (documentVisibleRect.origin.y + documentVisibleRect.size.height));
1131             cocoaPoint.y += scrollYOrigin;
1132         }
1133     }
1134
1135     NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
1136     // FIXME: Should we be adjusting for client area origin?
1137     const wxPoint &clientorigin = GetClientAreaOrigin();
1138     event.m_x = (wxCoord)pointWx.x - clientorigin.x;
1139     event.m_y = (wxCoord)pointWx.y - clientorigin.y;
1140
1141     event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
1142     event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
1143     event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
1144     event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
1145
1146     // TODO: set timestamp?
1147     event.SetEventObject(this);
1148     event.SetId(GetId());
1149 }
1150
1151 bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
1152 {
1153     wxMouseEvent event(wxEVT_MOTION);
1154     InitMouseEvent(event,theEvent);
1155     wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
1156     return HandleWindowEvent(event);
1157 }
1158
1159 void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
1160 {
1161     wxMouseEvent event(wxEVT_MOTION);
1162     NSWindow *window = [GetNSView() window];
1163     NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
1164     NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
1165
1166     NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
1167     // FIXME: Should we be adjusting for client area origin?
1168     const wxPoint &clientorigin = GetClientAreaOrigin();
1169     event.m_x = (wxCoord)pointWx.x - clientorigin.x;
1170     event.m_y = (wxCoord)pointWx.y - clientorigin.y;
1171
1172     // TODO: Handle shift, control, alt, meta flags
1173     event.SetEventObject(this);
1174     event.SetId(GetId());
1175
1176     wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
1177     HandleWindowEvent(event);
1178 }
1179
1180 bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
1181 {
1182     if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
1183     {
1184         m_visibleTrackingRectManager->BeginSynthesizingEvents();
1185
1186         // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
1187         // some other event comes in.  That other event is (guess what) mouse moved events that will be sent
1188         // to the NSWindow which will forward them on to the first responder.  We are not likely to be the
1189         // first responder, so the mouseMoved: events are effectively discarded.
1190         [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
1191
1192         wxMouseEvent event(wxEVT_ENTER_WINDOW);
1193         InitMouseEvent(event,theEvent);
1194         wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
1195         return HandleWindowEvent(event);
1196     }
1197     else
1198         return false;
1199 }
1200
1201 bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
1202 {
1203     if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
1204     {
1205         m_visibleTrackingRectManager->StopSynthesizingEvents();
1206
1207         wxMouseEvent event(wxEVT_LEAVE_WINDOW);
1208         InitMouseEvent(event,theEvent);
1209         wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
1210         return HandleWindowEvent(event);
1211     }
1212     else
1213         return false;
1214 }
1215
1216 bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
1217 {
1218     wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
1219     InitMouseEvent(event,theEvent);
1220     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
1221     return HandleWindowEvent(event);
1222 }
1223
1224 bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
1225 {
1226     wxMouseEvent event(wxEVT_MOTION);
1227     InitMouseEvent(event,theEvent);
1228     event.m_leftDown = true;
1229     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
1230     return HandleWindowEvent(event);
1231 }
1232
1233 bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
1234 {
1235     wxMouseEvent event(wxEVT_LEFT_UP);
1236     InitMouseEvent(event,theEvent);
1237     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
1238     return HandleWindowEvent(event);
1239 }
1240
1241 bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
1242 {
1243     wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
1244     InitMouseEvent(event,theEvent);
1245     wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
1246     return HandleWindowEvent(event);
1247 }
1248
1249 bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
1250 {
1251     wxMouseEvent event(wxEVT_MOTION);
1252     InitMouseEvent(event,theEvent);
1253     event.m_rightDown = true;
1254     wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
1255     return HandleWindowEvent(event);
1256 }
1257
1258 bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
1259 {
1260     wxMouseEvent event(wxEVT_RIGHT_UP);
1261     InitMouseEvent(event,theEvent);
1262     wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
1263     return HandleWindowEvent(event);
1264 }
1265
1266 bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
1267 {
1268     return false;
1269 }
1270
1271 bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
1272 {
1273     return false;
1274 }
1275
1276 bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
1277 {
1278     return false;
1279 }
1280
1281 void wxWindowCocoa::Cocoa_FrameChanged(void)
1282 {
1283     // We always get this message for the real NSView which may have been
1284     // enclosed in an NSScrollView.  If that's the case then what we're
1285     // effectively getting here is a notifcation that the
1286     // virtual sized changed.. which we don't need to send on since
1287     // wx has no concept of this whatsoever.
1288     bool isViewForSuperview = (m_wxCocoaScrollView == NULL);
1289     if(isViewForSuperview)
1290     {
1291         wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
1292         if(m_visibleTrackingRectManager != NULL)
1293             m_visibleTrackingRectManager->RebuildTrackingRect();
1294         wxSizeEvent event(GetSize(), m_windowId);
1295         event.SetEventObject(this);
1296         HandleWindowEvent(event);
1297     }
1298     else
1299     {
1300         wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged ignored"),this);
1301     }
1302 }
1303
1304 bool wxWindowCocoa::Cocoa_resetCursorRects()
1305 {
1306     wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
1307
1308     // When we are called there may be a queued tracking rect event (mouse entered or exited) and
1309     // we won't know it.  A specific example is wxGenericHyperlinkCtrl changing the cursor from its
1310     // mouse exited event.  If the control happens to share the edge with its parent window which is
1311     // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
1312     // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
1313     // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
1314     // which willl in turn call us before exiting for the next user event.
1315
1316     // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
1317     // our mouse exited event because the already queued event will have the old tracking rect
1318     // tag.  The simple solution is to only rebuild our tracking rect if we need to.
1319
1320     if(m_visibleTrackingRectManager != NULL)
1321         m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
1322
1323     if(!m_cursor.GetNSCursor())
1324         return false;
1325
1326     [GetNSView() addCursorRect: [GetNSView() visibleRect]  cursor: m_cursor.GetNSCursor()];
1327
1328     return true;
1329 }
1330
1331 bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
1332 {
1333     if(!wxWindowBase::SetCursor(cursor))
1334         return false;
1335
1336     // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
1337     // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
1338     // rects and thus won't ever call resetCursorRects.
1339     [GetNSView() addCursorRect: [GetNSView() visibleRect]  cursor: m_cursor.GetNSCursor()];
1340
1341     // Invalidate the cursor rects so the cursor will change
1342     // Note that it is not enough to remove the old one (if any) and add the new one.
1343     // For the rects to work properly, Cocoa itself must call resetCursorRects.
1344     [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
1345     return true;
1346 }
1347
1348 bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
1349 {
1350     wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
1351     // Set up new tracking rects.  I am reasonably sure the new window must be set before doing this.
1352     if(m_visibleTrackingRectManager != NULL)
1353         m_visibleTrackingRectManager->BuildTrackingRect();
1354     return false;
1355 }
1356
1357 bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
1358 {
1359     wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
1360     // Clear tracking rects.  It is imperative this be done before the new window is set.
1361     if(m_visibleTrackingRectManager != NULL)
1362         m_visibleTrackingRectManager->ClearTrackingRect();
1363     return false;
1364 }
1365
1366 bool wxWindow::Close(bool force)
1367 {
1368     // The only reason this function exists is that it is virtual and
1369     // wxTopLevelWindowCocoa will override it.
1370     return wxWindowBase::Close(force);
1371 }
1372
1373 void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
1374 {
1375     [[oldView superview] replaceSubview:oldView with:newView];
1376 }
1377
1378 void wxWindow::DoEnable(bool enable)
1379 {
1380         CocoaSetEnabled(enable);
1381 }
1382
1383 bool wxWindow::Show(bool show)
1384 {
1385     wxAutoNSAutoreleasePool pool;
1386     // If the window is marked as visible, then it shouldn't have a dummy view
1387     // If the window is marked hidden, then it should have a dummy view
1388     // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
1389 //    wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
1390     // Return false if there isn't a window to show or hide
1391     NSView *cocoaView = GetNSViewForHiding();
1392     if(!cocoaView)
1393         return false;
1394     if(show)
1395     {
1396         // If state isn't changing, return false
1397         if(!m_cocoaHider)
1398             return false;
1399         CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
1400         wxASSERT(![m_cocoaHider->GetNSView() superview]);
1401         delete m_cocoaHider;
1402         m_cocoaHider = NULL;
1403         wxASSERT([cocoaView superview]);
1404     }
1405     else
1406     {
1407         // If state isn't changing, return false
1408         if(m_cocoaHider)
1409             return false;
1410         m_cocoaHider = new wxWindowCocoaHider(this);
1411         // NOTE: replaceSubview:with will cause m_cocaNSView to be
1412         // (auto)released which balances out addSubview
1413         CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
1414         // m_coocaNSView is now only retained by us
1415         wxASSERT([m_cocoaHider->GetNSView() superview]);
1416         wxASSERT(![cocoaView superview]);
1417     }
1418     m_isShown = show;
1419     return true;
1420 }
1421
1422 void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1423 {
1424     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":".");
1425     int currentX, currentY;
1426     int currentW, currentH;
1427     DoGetPosition(&currentX, &currentY);
1428     DoGetSize(&currentW, &currentH);
1429     if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1430         x=currentX;
1431     if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1432         y=currentY;
1433
1434     AdjustForParentClientOrigin(x,y,sizeFlags);
1435
1436     wxSize size(wxDefaultSize);
1437
1438     if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1439     {
1440         if(sizeFlags&wxSIZE_AUTO_WIDTH)
1441         {
1442             size=DoGetBestSize();
1443             width=size.x;
1444         }
1445         else
1446             width=currentW;
1447     }
1448     if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
1449     {
1450         if(sizeFlags&wxSIZE_AUTO_HEIGHT)
1451         {
1452             if(size.x==-1)
1453                 size=DoGetBestSize();
1454             height=size.y;
1455         }
1456         else
1457             height=currentH;
1458     }
1459     DoMoveWindow(x,y,width,height);
1460 }
1461
1462 #if wxUSE_TOOLTIPS
1463
1464 void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
1465 {
1466     wxWindowBase::DoSetToolTip(tip);
1467
1468     if ( m_tooltip )
1469     {
1470         m_tooltip->SetWindow((wxWindow *)this);
1471     }
1472 }
1473
1474 #endif
1475
1476 void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
1477 {
1478     wxAutoNSAutoreleasePool pool;
1479     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
1480
1481     NSView *nsview = GetNSViewForSuperview();
1482     NSView *superview = [nsview superview];
1483
1484     wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
1485
1486     NSRect oldFrameRect = [nsview frame];
1487     NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
1488     [nsview setFrame:newFrameRect];
1489     // Be sure to redraw the parent to reflect the changed position
1490     [superview setNeedsDisplayInRect:oldFrameRect];
1491     [superview setNeedsDisplayInRect:newFrameRect];
1492 }
1493
1494 void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
1495 {
1496     NSView *nsview = GetNSViewForSuperview();
1497     NSView *superview = [nsview superview];
1498     wxCHECK_RET(superview,wxT("NSView does not have a superview"));
1499     wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
1500     NSRect frameRect = [nsview frame];
1501     if(size.x!=-1)
1502         frameRect.size.width = size.x;
1503     if(size.y!=-1)
1504         frameRect.size.height = size.y;
1505     frameRect.origin.x = pos.x;
1506     frameRect.origin.y = pos.y;
1507     // Tell Cocoa to change the margin between the bottom of the superview
1508     // and the bottom of the control.  Keeps the control pinned to the top
1509     // of its superview so that its position in the wxWidgets coordinate
1510     // system doesn't change.
1511     if(![superview isFlipped])
1512         [nsview setAutoresizingMask: NSViewMinYMargin];
1513     // MUST set the mask before setFrame: which can generate a size event
1514     // and cause a scroller to be added!
1515     frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
1516     [nsview setFrame: frameRect];
1517 }
1518
1519 // Get total size
1520 void wxWindow::DoGetSize(int *w, int *h) const
1521 {
1522     NSRect cocoaRect = [GetNSViewForSuperview() frame];
1523     if(w)
1524         *w=(int)cocoaRect.size.width;
1525     if(h)
1526         *h=(int)cocoaRect.size.height;
1527     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
1528 }
1529
1530 void wxWindow::DoGetPosition(int *x, int *y) const
1531 {
1532     NSView *nsview = GetNSViewForSuperview();
1533
1534     NSRect cocoaRect = [nsview frame];
1535     NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
1536     if(x)
1537         *x=(int)rectWx.origin.x;
1538     if(y)
1539         *y=(int)rectWx.origin.y;
1540     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
1541 }
1542
1543 WXWidget wxWindow::GetHandle() const
1544 {
1545     return m_cocoaNSView;
1546 }
1547
1548 wxWindow* wxWindow::GetWxWindow() const
1549 {
1550     return (wxWindow*) this;
1551 }
1552
1553 void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1554 {
1555     [m_cocoaNSView setNeedsDisplay:YES];
1556 }
1557
1558 void wxWindow::SetFocus()
1559 {
1560     if([GetNSView() acceptsFirstResponder])
1561         [[GetNSView() window] makeFirstResponder: GetNSView()];
1562 }
1563
1564 void wxWindow::DoCaptureMouse()
1565 {
1566     // TODO
1567     sm_capturedWindow = this;
1568 }
1569
1570 void wxWindow::DoReleaseMouse()
1571 {
1572     // TODO
1573     sm_capturedWindow = NULL;
1574 }
1575
1576 void wxWindow::DoScreenToClient(int *x, int *y) const
1577 {
1578     // Point in cocoa screen coordinates:
1579     NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
1580     NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1581     NSWindow *theWindow = [clientView window];
1582
1583     // Point in window's base coordinate system:
1584     NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
1585     // Point in view's bounds coordinate system
1586     NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
1587     // Point in wx client coordinates:
1588     NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
1589     if(x!=NULL)
1590         *x = theWxClientPoint.x;
1591     if(y!=NULL)
1592         *y = theWxClientPoint.y;
1593 }
1594
1595 void wxWindow::DoClientToScreen(int *x, int *y) const
1596 {
1597     // Point in wx client coordinates
1598     NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
1599
1600     NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
1601
1602     // Point in the view's bounds coordinate system
1603     NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
1604
1605     // Point in the window's base coordinate system
1606     NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
1607
1608     NSWindow *theWindow = [clientView window];
1609     // Point in Cocoa's screen coordinates
1610     NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
1611
1612     // Act as though this was the origin of a 0x0 rectangle
1613     NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
1614
1615     // Convert that rectangle to wx coordinates
1616     wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
1617     if(*x)
1618         *x = theWxScreenPoint.x;
1619     if(*y)
1620         *y = theWxScreenPoint.y;
1621 }
1622
1623 // Get size *available for subwindows* i.e. excluding menu bar etc.
1624 void wxWindow::DoGetClientSize(int *x, int *y) const
1625 {
1626     wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
1627     if(m_wxCocoaScrollView)
1628         m_wxCocoaScrollView->DoGetClientSize(x,y);
1629     else
1630         wxWindowCocoa::DoGetSize(x,y);
1631 }
1632
1633 void wxWindow::DoSetClientSize(int width, int height)
1634 {
1635     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
1636     if(m_wxCocoaScrollView)
1637         m_wxCocoaScrollView->ClientSizeToSize(width,height);
1638     CocoaSetWxWindowSize(width,height);
1639 }
1640
1641 void wxWindow::CocoaSetWxWindowSize(int width, int height)
1642 {
1643     wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1644 }
1645
1646 void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1647 {
1648     // Intentional no-op.
1649 }
1650
1651 wxString wxWindow::GetLabel() const
1652 {
1653     // General Get/Set of labels is implemented in wxControlBase
1654     wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
1655     return wxEmptyString;
1656 }
1657
1658 int wxWindow::GetCharHeight() const
1659 {
1660     // TODO
1661     return 10;
1662 }
1663
1664 int wxWindow::GetCharWidth() const
1665 {
1666     // TODO
1667     return 5;
1668 }
1669
1670 void wxWindow::GetTextExtent(const wxString& string, int *outX, int *outY,
1671         int *outDescent, int *outExternalLeading, const wxFont *inFont) const
1672 {
1673     // FIXME: This obviously ignores the window's font (if any) along with any size
1674     // transformations.  However, it's better than nothing.
1675     // We don't create a wxClientDC because we don't want to accidently be able to use
1676     // it for drawing.
1677     wxClientDC tmpdc(const_cast<wxWindow*>(this));
1678     return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
1679 }
1680
1681 // Coordinates relative to the window
1682 void wxWindow::WarpPointer (int x_pos, int y_pos)
1683 {
1684     // TODO
1685 }
1686
1687 int wxWindow::GetScrollPos(int orient) const
1688 {
1689     if(m_wxCocoaScrollView != NULL)
1690         return m_wxCocoaScrollView->GetScrollPos(static_cast<wxOrientation>(orient & wxBOTH));
1691     else
1692         return 0;
1693 }
1694
1695 // This now returns the whole range, not just the number
1696 // of positions that we can scroll.
1697 int wxWindow::GetScrollRange(int orient) const
1698 {
1699     if(m_wxCocoaScrollView != NULL)
1700         return m_wxCocoaScrollView->GetScrollRange(static_cast<wxOrientation>(orient & wxBOTH));
1701     else
1702         return 0;
1703 }
1704
1705 int wxWindow::GetScrollThumb(int orient) const
1706 {
1707     if(m_wxCocoaScrollView != NULL)
1708         return m_wxCocoaScrollView->GetScrollThumb(static_cast<wxOrientation>(orient & wxBOTH));
1709     else
1710         return 0;
1711 }
1712
1713 void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
1714 {
1715     if(m_wxCocoaScrollView != NULL)
1716         return m_wxCocoaScrollView->SetScrollPos(static_cast<wxOrientation>(orient & wxBOTH), pos);
1717 }
1718
1719 void wxWindow::CocoaCreateNSScrollView()
1720 {
1721     if(!m_wxCocoaScrollView)
1722     {
1723         m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
1724     }
1725 }
1726
1727 // New function that will replace some of the above.
1728 void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
1729     int range, bool refresh)
1730 {
1731     CocoaCreateNSScrollView();
1732     m_wxCocoaScrollView->SetScrollbar(orient, pos, thumbVisible, range);
1733     // TODO: Handle refresh (if we even need to)
1734 }
1735
1736 // Does a physical scroll
1737 void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1738 {
1739     if(m_wxCocoaScrollView != NULL)
1740         m_wxCocoaScrollView->ScrollWindow(dx, dy, rect);
1741 }
1742
1743 void wxWindow::DoSetVirtualSize( int x, int y )
1744 {
1745     // Call wxWindowBase method which will set m_virtualSize to the appropriate value,
1746     // possibly not what the caller passed in.  For example, the current implementation
1747     // clamps the width and height to within the min/max virtual ranges.
1748     // wxDefaultCoord is passed through unchanged but then GetVirtualSize() will correct
1749     // that by returning effectively max(virtual, client)
1750     wxWindowBase::DoSetVirtualSize(x,y);
1751     // Create the scroll view if it hasn't been already.
1752     CocoaCreateNSScrollView();
1753
1754     // The GetVirtualSize automatically increases the size to max(client,virtual)
1755     m_wxCocoaScrollView->UpdateSizes();
1756 }
1757
1758 bool wxWindow::SetFont(const wxFont& font)
1759 {
1760     // FIXME: We may need to handle wx font inheritance.
1761     return wxWindowBase::SetFont(font);
1762 }
1763
1764 #if 0 // these are used when debugging the algorithm.
1765 static char const * const comparisonresultStrings[] =
1766 {   "<"
1767 ,   "=="
1768 ,   ">"
1769 };
1770 #endif
1771
1772 class CocoaWindowCompareContext
1773 {
1774     DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
1775 public:
1776     CocoaWindowCompareContext(); // Not implemented
1777     CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1778     {
1779         m_target = target;
1780         // Cocoa sorts subviews in-place.. make a copy
1781         m_subviews = [subviews copy];
1782     }
1783     ~CocoaWindowCompareContext()
1784     {   // release the copy
1785         [m_subviews release];
1786     }
1787     NSView* target()
1788     {   return m_target; }
1789     NSArray* subviews()
1790     {   return m_subviews; }
1791     /* Helper function that returns the comparison based off of the original ordering */
1792     CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1793     {
1794         NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1795         NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1796         // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1797         // likely compare higher than the other view which is reasonable considering the only way that
1798         // can happen is if the subview was added after our call to subviews but before the call to
1799         // sortSubviewsUsingFunction:context:.  Thus we don't bother checking.  Particularly because
1800         // that case should never occur anyway because that would imply a multi-threaded GUI call
1801         // which is a big no-no with Cocoa.
1802
1803         // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1804         NSComparisonResult result = (firstI < secondI)
1805             ?   NSOrderedAscending /* -1 */
1806             :   (firstI > secondI)
1807                 ?   NSOrderedDescending /* 1 */
1808                 :   NSOrderedSame /* 0 */;
1809
1810 #if 0 // Enable this if you need to debug the algorithm.
1811         NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
1812 #endif
1813         return result;
1814     }
1815 private:
1816     /* The subview we are trying to Raise or Lower */
1817     NSView *m_target;
1818     /* A copy of the original array of subviews */
1819     NSArray *m_subviews;
1820 };
1821
1822 /* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1823  * the target view is always higher than every other view.  When comparing two views neither of
1824  * which is the target, it returns the correct response based on the original ordering
1825  */
1826 static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
1827 {
1828     CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1829     // first should be ordered higher
1830     if(first==compareContext->target())
1831         return NSOrderedDescending;
1832     // second should be ordered higher
1833     if(second==compareContext->target())
1834         return NSOrderedAscending;
1835     return compareContext->CompareUsingOriginalOrdering(first,second);
1836 }
1837
1838 // Raise the window to the top of the Z order
1839 void wxWindow::Raise()
1840 {
1841 //    wxAutoNSAutoreleasePool pool;
1842     NSView *nsview = GetNSViewForSuperview();
1843     NSView *superview = [nsview superview];
1844     CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1845
1846     [superview sortSubviewsUsingFunction:
1847             CocoaRaiseWindowCompareFunction
1848         context: &compareContext];
1849 }
1850
1851 /* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1852  * the target view is always lower than every other view.  When comparing two views neither of
1853  * which is the target, it returns the correct response based on the original ordering
1854  */
1855 static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
1856 {
1857     CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1858     // first should be ordered lower
1859     if(first==compareContext->target())
1860         return NSOrderedAscending;
1861     // second should be ordered lower
1862     if(second==compareContext->target())
1863         return NSOrderedDescending;
1864     return compareContext->CompareUsingOriginalOrdering(first,second);
1865 }
1866
1867 // Lower the window to the bottom of the Z order
1868 void wxWindow::Lower()
1869 {
1870     NSView *nsview = GetNSViewForSuperview();
1871     NSView *superview = [nsview superview];
1872     CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1873
1874 #if 0
1875     NSLog(@"Target:\n%@\n", nsview);
1876     NSLog(@"Before:\n%@\n", compareContext.subviews());
1877 #endif
1878     [superview sortSubviewsUsingFunction:
1879             CocoaLowerWindowCompareFunction
1880         context: &compareContext];
1881 #if 0
1882     NSLog(@"After:\n%@\n", [superview subviews]);
1883 #endif
1884 }
1885
1886 bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1887 {
1888     return false;
1889 }
1890
1891 // Get the window with the focus
1892 wxWindow *wxWindowBase::DoFindFocus()
1893 {
1894     // Basically we are somewhat emulating the responder chain here except
1895     // we are only loking for the first responder in the key window or
1896     // upon failing to find one if the main window is different we look
1897     // for the first responder in the main window.
1898
1899     // Note that the firstResponder doesn't necessarily have to be an
1900     // NSView but wxCocoaNSView::GetFromCocoa() will simply return
1901     // NULL unless it finds its argument in its hash map.
1902
1903     wxCocoaNSView *win;
1904
1905     NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
1906     win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
1907     if(win)
1908         return win->GetWxWindow();
1909
1910     NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1911     if(mainWindow == keyWindow)
1912         return NULL;
1913     win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
1914     if(win)
1915         return win->GetWxWindow();
1916
1917     return NULL;
1918 }
1919
1920 /* static */ wxWindow *wxWindowBase::GetCapture()
1921 {
1922     // TODO
1923     return wxWindowCocoa::sm_capturedWindow;
1924 }
1925
1926 wxWindow *wxGetActiveWindow()
1927 {
1928     // TODO
1929     return NULL;
1930 }
1931
1932 wxPoint wxGetMousePosition()
1933 {
1934     // TODO
1935     return wxDefaultPosition;
1936 }
1937
1938 wxMouseState wxGetMouseState()
1939 {
1940     wxMouseState ms;
1941     // TODO
1942     return ms;
1943 }
1944
1945 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1946 {
1947     pt = wxGetMousePosition();
1948     return NULL;
1949 }
1950
1951 // ========================================================================
1952 // wxCocoaMouseMovedEventSynthesizer
1953 // ========================================================================
1954
1955 #define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
1956
1957 /* This class registers one run loop observer to cover all windows registered with it.
1958  *  It will register the observer when the first view is registerd and unregister the
1959  * observer when the last view is unregistered.
1960  * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
1961  * is no reason it couldn't be instantiated multiple times.
1962  */
1963 class wxCocoaMouseMovedEventSynthesizer
1964 {
1965     DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
1966 public:
1967     wxCocoaMouseMovedEventSynthesizer()
1968     {   m_lastScreenMouseLocation = NSZeroPoint;
1969     }
1970     ~wxCocoaMouseMovedEventSynthesizer();
1971     void RegisterWxCocoaView(wxCocoaNSView *aView);
1972     void UnregisterWxCocoaView(wxCocoaNSView *aView);
1973     void SynthesizeMouseMovedEvent();
1974     
1975 protected:
1976     void AddRunLoopObserver();
1977     void RemoveRunLoopObserver();
1978     wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
1979     std::list<wxCocoaNSView*> m_registeredViews;
1980     NSPoint m_lastScreenMouseLocation;
1981     static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
1982 };
1983
1984 void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
1985 {
1986     m_registeredViews.push_back(aView);
1987     wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
1988
1989     if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
1990     {
1991         AddRunLoopObserver();
1992     }
1993 }
1994
1995 void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
1996 {
1997     m_registeredViews.remove(aView);
1998     wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
1999     if(m_registeredViews.empty() && m_runLoopObserver != NULL)
2000     {
2001         RemoveRunLoopObserver();
2002     }
2003 }
2004
2005 wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
2006 {
2007     if(!m_registeredViews.empty())
2008     {
2009         // This means failure to clean up so we report on it as a debug message.
2010         wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
2011         m_registeredViews.clear();
2012     }
2013     if(m_runLoopObserver != NULL)
2014     {
2015         // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
2016         wxLogDebug(wxT("Removing run loop observer during static destruction time."));
2017         RemoveRunLoopObserver();
2018     }
2019 }
2020
2021 void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
2022 {
2023     reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
2024 }
2025
2026 void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
2027 {
2028     CFRunLoopObserverContext observerContext =
2029     {   0
2030     ,   this
2031     ,   NULL
2032     ,   NULL
2033     ,   NULL
2034     };
2035
2036     // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
2037     // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
2038
2039     // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
2040     // in our observer procedure being called before the run loop has decided that it is going to return control to
2041     // the Cocoa event loop.  One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
2042     // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
2043
2044     // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
2045     // won't be called until the CFRunLoop gets around to it.  If the CFRunLoop has not yet exited then it will get
2046     // around to it before letting the event loop do its work.  This has some very odd effects on the way the
2047     // newly created tracking rects function.  In particular, we will often miss the mouseExited: message if the
2048     // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
2049     // time the new one is built.
2050
2051     // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
2052     // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
2053     // the mouseExited: message to be sent properly.
2054
2055     m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
2056     CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
2057     wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
2058 }
2059
2060 void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
2061 {
2062     CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
2063     m_runLoopObserver.reset();
2064     wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
2065 }
2066
2067 void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
2068 {
2069     NSPoint screenMouseLocation = [NSEvent mouseLocation];
2070     // Checking the last mouse location is done for a few reasons:
2071     // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
2072     //    telling the app the mouse moved when the user hit a key for instance.
2073     // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
2074     //    call resetCursorRects.  Cocoa does this by using a delayed notification which means the event loop gets
2075     //    pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
2076     //    loop causing the tracking rectangles to constantly be reset.
2077     if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
2078     {
2079         m_lastScreenMouseLocation = screenMouseLocation;
2080         wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
2081         for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
2082         {
2083             (*i)->Cocoa_synthesizeMouseMoved();
2084         }
2085     }
2086 }
2087
2088 // Singleton used for all views:
2089 static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
2090
2091 // ========================================================================
2092 // wxCocoaTrackingRectManager
2093 // ========================================================================
2094
2095 wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
2096 :   m_window(window)
2097 {
2098     m_isTrackingRectActive = false;
2099     BuildTrackingRect();
2100 }
2101
2102 void wxCocoaTrackingRectManager::ClearTrackingRect()
2103 {
2104     if(m_isTrackingRectActive)
2105     {
2106         [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
2107         m_isTrackingRectActive = false;
2108         wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
2109     }
2110     // If we were doing periodic events we need to clear those too
2111     StopSynthesizingEvents();
2112 }
2113
2114 void wxCocoaTrackingRectManager::StopSynthesizingEvents()
2115 {
2116     s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
2117 }
2118
2119 void wxCocoaTrackingRectManager::BuildTrackingRect()
2120 {
2121     // Pool here due to lack of one during wx init phase
2122     wxAutoNSAutoreleasePool pool;
2123
2124     wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
2125
2126     NSView *theView = m_window->GetNSView();
2127
2128     if([theView window] != nil)
2129     {
2130         NSRect visibleRect = [theView visibleRect];
2131
2132         m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
2133         m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
2134         m_isTrackingRectActive = true;
2135
2136         wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
2137     }
2138 }
2139
2140 void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
2141 {
2142     s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
2143 }
2144
2145 void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
2146 {
2147     if(m_isTrackingRectActive)
2148     {
2149         NSView *theView = m_window->GetNSView();
2150         NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
2151         if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
2152         {
2153             wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
2154             return;
2155         }
2156     }
2157     RebuildTrackingRect();
2158 }
2159
2160 void wxCocoaTrackingRectManager::RebuildTrackingRect()
2161 {
2162     ClearTrackingRect();
2163     BuildTrackingRect();
2164 }
2165
2166 wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
2167 {
2168     ClearTrackingRect();
2169 }
2170
2171 bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
2172 {
2173     return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
2174 }
2175