]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/cocoa/window.mm
Tell NSTextField to send us its action message and respond to it so we can generate...
[wxWidgets.git] / src / cocoa / window.mm
... / ...
CommitLineData
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/utils.h"
19#endif //WX_PRECOMP
20
21#include "wx/tooltip.h"
22
23#include "wx/cocoa/autorelease.h"
24#include "wx/cocoa/string.h"
25#include "wx/cocoa/trackingrectmanager.h"
26
27#import <Foundation/NSArray.h>
28#import <Foundation/NSRunLoop.h>
29#include "wx/cocoa/objc/NSView.h"
30#import <AppKit/NSEvent.h>
31#import <AppKit/NSScrollView.h>
32#import <AppKit/NSColor.h>
33#import <AppKit/NSClipView.h>
34#import <Foundation/NSException.h>
35#import <AppKit/NSApplication.h>
36#import <AppKit/NSWindow.h>
37#import <AppKit/NSScreen.h>
38
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
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
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
63// ========================================================================
64// Helper functions for converting to/from wxWidgets coordinates and a
65// specified NSView's coordinate system.
66// ========================================================================
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
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
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);
276 virtual void Cocoa_synthesizeMouseMoved(void) {}
277#ifdef WXCOCOA_FILL_DUMMY_VIEW
278 virtual bool Cocoa_drawRect(const NSRect& rect);
279#endif //def WXCOCOA_FILL_DUMMY_VIEW
280private:
281 wxWindowCocoaHider();
282};
283
284// ========================================================================
285// wxWindowCocoaScrollView
286// ========================================================================
287class wxWindowCocoaScrollView: protected wxCocoaNSView
288{
289 DECLARE_NO_COPY_CLASS(wxWindowCocoaScrollView)
290public:
291 wxWindowCocoaScrollView(wxWindow *owner);
292 virtual ~wxWindowCocoaScrollView();
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);
302 virtual void Cocoa_synthesizeMouseMoved(void) {}
303private:
304 wxWindowCocoaScrollView();
305};
306
307// ========================================================================
308// wxDummyNSView
309// ========================================================================
310@interface wxDummyNSView : NSView
311- (NSView *)hitTest:(NSPoint)aPoint;
312@end
313WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
314
315@implementation wxDummyNSView : NSView
316- (NSView *)hitTest:(NSPoint)aPoint
317{
318 return nil;
319}
320
321@end
322WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
323
324// ========================================================================
325// wxWindowCocoaHider
326// ========================================================================
327wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
328: m_owner(owner)
329{
330 wxASSERT(owner);
331 wxASSERT(owner->GetNSViewForHiding());
332 m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
333 initWithFrame:[owner->GetNSViewForHiding() frame]];
334 [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
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
351
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
363// ========================================================================
364// wxFlippedNSClipView
365// ========================================================================
366@interface wxFlippedNSClipView : NSClipView
367- (BOOL)isFlipped;
368@end
369WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
370
371@implementation wxFlippedNSClipView : NSClipView
372- (BOOL)isFlipped
373{
374 return YES;
375}
376
377@end
378WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
379
380// ========================================================================
381// wxWindowCocoaScrollView
382// ========================================================================
383wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
384: m_owner(owner)
385{
386 wxAutoNSAutoreleasePool pool;
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. */
395 NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
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
406void wxWindowCocoaScrollView::Encapsulate()
407{
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];
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
420void wxWindowCocoaScrollView::Unencapsulate()
421{
422 [m_cocoaNSScrollView setDocumentView: nil];
423 m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
424 if(![[m_owner->GetNSView() superview] isFlipped])
425 [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
426}
427
428wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
429{
430 DisassociateNSView(m_cocoaNSScrollView);
431 [m_cocoaNSScrollView release];
432}
433
434void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
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]];
441 width = (int)frameSize.width;
442 height = (int)frameSize.height;
443}
444
445void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
446{
447 NSSize nssize = [m_cocoaNSScrollView contentSize];
448 if(x)
449 *x = (int)nssize.width;
450 if(y)
451 *y = (int)nssize.height;
452}
453
454void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
455{
456 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged"));
457 wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
458 event.SetEventObject(m_owner);
459 m_owner->GetEventHandler()->ProcessEvent(event);
460}
461
462// ========================================================================
463// wxWindowCocoa
464// ========================================================================
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
475wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
476
477// Constructor
478void wxWindowCocoa::Init()
479{
480 m_cocoaNSView = NULL;
481 m_cocoaHider = NULL;
482 m_wxCocoaScrollView = NULL;
483 m_isBeingDeleted = false;
484 m_isInPaint = false;
485 m_visibleTrackingRectManager = NULL;
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
499 m_cocoaNSView = NULL;
500 SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
501 [m_cocoaNSView release];
502
503 if (m_parent)
504 {
505 m_parent->AddChild(this);
506 m_parent->CocoaAddChild(this);
507 SetInitialFrameRect(pos,size);
508 }
509
510 return true;
511}
512
513// Destructor
514wxWindow::~wxWindow()
515{
516 wxAutoNSAutoreleasePool pool;
517 DestroyChildren();
518
519 // Make sure our parent (in the wxWidgets sense) is our superview
520 // before we go removing from it.
521 if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
522 CocoaRemoveFromParent();
523 delete m_cocoaHider;
524 delete m_wxCocoaScrollView;
525 if(m_cocoaNSView)
526 SendDestroyEvent();
527 SetNSView(NULL);
528}
529
530void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
531{
532 // Pool here due to lack of one during wx init phase
533 wxAutoNSAutoreleasePool pool;
534
535 NSView *childView = child->GetNSViewForSuperview();
536
537 wxASSERT(childView);
538 [m_cocoaNSView addSubview: childView];
539 child->m_isShown = !m_cocoaHider;
540}
541
542void wxWindowCocoa::CocoaRemoveFromParent(void)
543{
544 [GetNSViewForSuperview() removeFromSuperview];
545}
546
547void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
548{
549 // Clear the visible area tracking rect if we have one.
550 delete m_visibleTrackingRectManager;
551 m_visibleTrackingRectManager = NULL;
552
553 bool need_debug = cocoaNSView || m_cocoaNSView;
554 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
555 DisassociateNSView(m_cocoaNSView);
556 [cocoaNSView retain];
557 [m_cocoaNSView release];
558 m_cocoaNSView = cocoaNSView;
559 AssociateNSView(m_cocoaNSView);
560 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
561}
562
563WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
564{
565 return m_cocoaHider
566 ? m_cocoaHider->GetNSView()
567 : m_wxCocoaScrollView
568 ? m_wxCocoaScrollView->GetNSScrollView()
569 : m_cocoaNSView;
570}
571
572WX_NSView wxWindowCocoa::GetNSViewForHiding() const
573{
574 return m_wxCocoaScrollView
575 ? m_wxCocoaScrollView->GetNSScrollView()
576 : m_cocoaNSView;
577}
578
579NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
580{
581 // TODO: Handle scrolling offset
582 return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
583}
584
585NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
586{
587 // TODO: Handle scrolling offset
588 return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
589}
590
591NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
592{
593 // TODO: Handle scrolling offset
594 return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
595}
596
597NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
598{
599 // TODO: Handle scrolling offset
600 return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
601}
602
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
610bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
611{
612 wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
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 {
618 wxLogDebug(wxT("Paint event recursion!"));
619 return false;
620 }
621 m_isInPaint = true;
622
623 // Set m_updateRegion
624 const NSRect *rects = &rect; // The bounding box of the region
625 NSInteger countRects = 1;
626 // Try replacing the larger rectangle with a list of smaller ones:
627 if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
628 [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
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);
637
638 wxPaintEvent event(m_windowId);
639 event.SetEventObject(this);
640 bool ret = GetEventHandler()->ProcessEvent(event);
641 m_isInPaint = false;
642 return ret;
643}
644
645void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
646{
647 wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
648 // Mouse events happen at the NSWindow level so we need to convert
649 // into our bounds coordinates then convert to wx coordinates.
650 NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
651 NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
652 // FIXME: Should we be adjusting for client area origin?
653 const wxPoint &clientorigin = GetClientAreaOrigin();
654 event.m_x = (wxCoord)pointWx.x - clientorigin.x;
655 event.m_y = (wxCoord)pointWx.y - clientorigin.y;
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);
671 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
672 return GetEventHandler()->ProcessEvent(event);
673}
674
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
696bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
697{
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;
715}
716
717bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
718{
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;
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);
736 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
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;
745 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
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);
753 wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
754 return GetEventHandler()->ProcessEvent(event);
755}
756
757bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
758{
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);
763}
764
765bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
766{
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);
772}
773
774bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
775{
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);
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
797void wxWindowCocoa::Cocoa_FrameChanged(void)
798{
799 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
800 if(m_visibleTrackingRectManager != NULL)
801 m_visibleTrackingRectManager->RebuildTrackingRect();
802 wxSizeEvent event(GetSize(), m_windowId);
803 event.SetEventObject(this);
804 GetEventHandler()->ProcessEvent(event);
805}
806
807bool wxWindowCocoa::Cocoa_resetCursorRects()
808{
809 wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
810 if(m_visibleTrackingRectManager != NULL)
811 m_visibleTrackingRectManager->RebuildTrackingRect();
812
813 if(!m_cursor.GetNSCursor())
814 return false;
815
816 [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
817
818 return true;
819}
820
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
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
848bool wxWindow::Close(bool force)
849{
850 // The only reason this function exists is that it is virtual and
851 // wxTopLevelWindowCocoa will override it.
852 return wxWindowBase::Close(force);
853}
854
855void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
856{
857 [[oldView superview] replaceSubview:oldView with:newView];
858}
859
860void wxWindow::DoEnable(bool enable)
861{
862 CocoaSetEnabled(enable);
863}
864
865bool wxWindow::Show(bool show)
866{
867 wxAutoNSAutoreleasePool pool;
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
870 // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
871// wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
872 // Return false if there isn't a window to show or hide
873 NSView *cocoaView = GetNSViewForHiding();
874 if(!cocoaView)
875 return false;
876 if(show)
877 {
878 // If state isn't changing, return false
879 if(!m_cocoaHider)
880 return false;
881 CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
882 wxASSERT(![m_cocoaHider->GetNSView() superview]);
883 delete m_cocoaHider;
884 m_cocoaHider = NULL;
885 wxASSERT([cocoaView superview]);
886 }
887 else
888 {
889 // If state isn't changing, return false
890 if(m_cocoaHider)
891 return false;
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());
896 // m_coocaNSView is now only retained by us
897 wxASSERT([m_cocoaHider->GetNSView() superview]);
898 wxASSERT(![cocoaView superview]);
899 }
900 m_isShown = show;
901 return true;
902}
903
904void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
905{
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":".");
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
918 wxSize size(wxDefaultSize);
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
944#if wxUSE_TOOLTIPS
945
946void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
947{
948 wxWindowBase::DoSetToolTip(tip);
949
950 if ( m_tooltip )
951 {
952 m_tooltip->SetWindow((wxWindow *)this);
953 }
954}
955
956#endif
957
958void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
959{
960 wxAutoNSAutoreleasePool pool;
961 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
962
963 NSView *nsview = GetNSViewForSuperview();
964 NSView *superview = [nsview superview];
965
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];
971 // Be sure to redraw the parent to reflect the changed position
972 [superview setNeedsDisplayInRect:oldFrameRect];
973 [superview setNeedsDisplayInRect:newFrameRect];
974}
975
976void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
977{
978 NSView *nsview = GetNSViewForSuperview();
979 NSView *superview = [nsview superview];
980 wxCHECK_RET(superview,wxT("NSView does not have a superview"));
981 wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
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;
988 frameRect.origin.y = pos.y;
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
991 // of its superview so that its position in the wxWidgets coordinate
992 // system doesn't change.
993 if(![superview isFlipped])
994 [nsview setAutoresizingMask: NSViewMinYMargin];
995 // MUST set the mask before setFrame: which can generate a size event
996 // and cause a scroller to be added!
997 frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
998 [nsview setFrame: frameRect];
999}
1000
1001// Get total size
1002void wxWindow::DoGetSize(int *w, int *h) const
1003{
1004 NSRect cocoaRect = [GetNSViewForSuperview() frame];
1005 if(w)
1006 *w=(int)cocoaRect.size.width;
1007 if(h)
1008 *h=(int)cocoaRect.size.height;
1009 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
1010}
1011
1012void wxWindow::DoGetPosition(int *x, int *y) const
1013{
1014 NSView *nsview = GetNSViewForSuperview();
1015
1016 NSRect cocoaRect = [nsview frame];
1017 NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
1018 if(x)
1019 *x=(int)rectWx.origin.x;
1020 if(y)
1021 *y=(int)rectWx.origin.y;
1022 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
1023}
1024
1025WXWidget wxWindow::GetHandle() const
1026{
1027 return m_cocoaNSView;
1028}
1029
1030wxWindow* wxWindow::GetWxWindow() const
1031{
1032 return (wxWindow*) this;
1033}
1034
1035void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
1036{
1037 [m_cocoaNSView setNeedsDisplay:YES];
1038}
1039
1040void wxWindow::SetFocus()
1041{
1042 if([GetNSView() acceptsFirstResponder])
1043 [[GetNSView() window] makeFirstResponder: GetNSView()];
1044}
1045
1046void wxWindow::DoCaptureMouse()
1047{
1048 // TODO
1049 sm_capturedWindow = this;
1050}
1051
1052void wxWindow::DoReleaseMouse()
1053{
1054 // TODO
1055 sm_capturedWindow = NULL;
1056}
1057
1058void wxWindow::DoScreenToClient(int *x, int *y) const
1059{
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;
1075}
1076
1077void wxWindow::DoClientToScreen(int *x, int *y) const
1078{
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;
1103}
1104
1105// Get size *available for subwindows* i.e. excluding menu bar etc.
1106void wxWindow::DoGetClientSize(int *x, int *y) const
1107{
1108 wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
1109 if(m_wxCocoaScrollView)
1110 m_wxCocoaScrollView->DoGetClientSize(x,y);
1111 else
1112 wxWindowCocoa::DoGetSize(x,y);
1113}
1114
1115void wxWindow::DoSetClientSize(int width, int height)
1116{
1117 wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
1118 if(m_wxCocoaScrollView)
1119 m_wxCocoaScrollView->ClientSizeToSize(width,height);
1120 CocoaSetWxWindowSize(width,height);
1121}
1122
1123void wxWindow::CocoaSetWxWindowSize(int width, int height)
1124{
1125 wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
1126}
1127
1128void wxWindow::SetLabel(const wxString& WXUNUSED(label))
1129{
1130 // Intentional no-op.
1131}
1132
1133wxString wxWindow::GetLabel() const
1134{
1135 // General Get/Set of labels is implemented in wxControlBase
1136 wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
1137 return wxEmptyString;
1138}
1139
1140int wxWindow::GetCharHeight() const
1141{
1142 // TODO
1143 return 0;
1144}
1145
1146int wxWindow::GetCharWidth() const
1147{
1148 // TODO
1149 return 0;
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
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
1189void wxWindow::CocoaCreateNSScrollView()
1190{
1191 if(!m_wxCocoaScrollView)
1192 {
1193 m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
1194 }
1195}
1196
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{
1201 CocoaCreateNSScrollView();
1202 // TODO
1203}
1204
1205// Does a physical scroll
1206void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
1207{
1208 // TODO
1209}
1210
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
1218bool wxWindow::SetFont(const wxFont& font)
1219{
1220 // FIXME: We may need to handle wx font inheritance.
1221 return wxWindowBase::SetFont(font);
1222}
1223
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)
1287{
1288 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1289 // first should be ordered higher
1290 if(first==compareContext->target())
1291 return NSOrderedDescending;
1292 // second should be ordered higher
1293 if(second==compareContext->target())
1294 return NSOrderedAscending;
1295 return compareContext->CompareUsingOriginalOrdering(first,second);
1296}
1297
1298// Raise the window to the top of the Z order
1299void wxWindow::Raise()
1300{
1301// wxAutoNSAutoreleasePool pool;
1302 NSView *nsview = GetNSViewForSuperview();
1303 NSView *superview = [nsview superview];
1304 CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1305
1306 [superview sortSubviewsUsingFunction:
1307 CocoaRaiseWindowCompareFunction
1308 context: &compareContext];
1309}
1310
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)
1316{
1317 CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1318 // first should be ordered lower
1319 if(first==compareContext->target())
1320 return NSOrderedAscending;
1321 // second should be ordered lower
1322 if(second==compareContext->target())
1323 return NSOrderedDescending;
1324 return compareContext->CompareUsingOriginalOrdering(first,second);
1325}
1326
1327// Lower the window to the bottom of the Z order
1328void wxWindow::Lower()
1329{
1330 NSView *nsview = GetNSViewForSuperview();
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:
1339 CocoaLowerWindowCompareFunction
1340 context: &compareContext];
1341#if 0
1342 NSLog(@"After:\n%@\n", [superview subviews]);
1343#endif
1344}
1345
1346bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
1347{
1348 return false;
1349}
1350
1351// Get the window with the focus
1352wxWindow *wxWindowBase::DoFindFocus()
1353{
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];
1366 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
1367 if(win)
1368 return win->GetWxWindow();
1369
1370 NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
1371 if(mainWindow == keyWindow)
1372 return NULL;
1373 win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
1374 if(win)
1375 return win->GetWxWindow();
1376
1377 return NULL;
1378}
1379
1380/* static */ wxWindow *wxWindowBase::GetCapture()
1381{
1382 // TODO
1383 return wxWindowCocoa::sm_capturedWindow;
1384}
1385
1386wxWindow *wxGetActiveWindow()
1387{
1388 // TODO
1389 return NULL;
1390}
1391
1392wxPoint wxGetMousePosition()
1393{
1394 // TODO
1395 return wxDefaultPosition;
1396}
1397
1398wxMouseState wxGetMouseState()
1399{
1400 wxMouseState ms;
1401 // TODO
1402 return ms;
1403}
1404
1405wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1406{
1407 pt = wxGetMousePosition();
1408 return NULL;
1409}
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{
1447 // Pool here due to lack of one during wx init phase
1448 wxAutoNSAutoreleasePool pool;
1449
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