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