Add new methods to transform coordinates expressed in Cocoa's bounds rect to
[wxWidgets.git] / src / cocoa / window.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/window.mm
3 // Purpose:     wxWindowCocoa
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2002/12/26
7 // RCS-ID:      $Id:
8 // Copyright:   (c) 2002 David Elliott
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14     #include "wx/log.h"
15     #include "wx/window.h"
16 #endif //WX_PRECOMP
17 #include "wx/tooltip.h"
18
19 #include "wx/cocoa/autorelease.h"
20 #include "wx/cocoa/string.h"
21
22 #import <AppKit/NSView.h>
23 #import <AppKit/NSEvent.h>
24 #import <AppKit/NSScrollView.h>
25 #import <AppKit/NSColor.h>
26 #import <AppKit/NSClipView.h>
27 #import <Foundation/NSException.h>
28 #import <AppKit/NSApplication.h>
29 #import <AppKit/NSWindow.h>
30
31 // Turn this on to paint green over the dummy views for debugging
32 #undef WXCOCOA_FILL_DUMMY_VIEW
33
34 #ifdef WXCOCOA_FILL_DUMMY_VIEW
35 #import <AppKit/NSBezierPath.h>
36 #endif //def WXCOCOA_FILL_DUMMY_VIEW
37
38 // A category for methods that are only present in Panther's SDK
39 @interface NSView(wxNSViewPrePantherCompatibility)
40 - (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
41 @end
42
43 // ========================================================================
44 // wxWindowCocoaHider
45 // ========================================================================
46 class wxWindowCocoaHider: protected wxCocoaNSView
47 {
48     DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
49 public:
50     wxWindowCocoaHider(wxWindow *owner);
51     virtual ~wxWindowCocoaHider();
52     inline WX_NSView GetNSView() { return m_dummyNSView; }
53 protected:
54     wxWindowCocoa *m_owner;
55     WX_NSView m_dummyNSView;
56     virtual void Cocoa_FrameChanged(void);
57 #ifdef WXCOCOA_FILL_DUMMY_VIEW
58     virtual bool Cocoa_drawRect(const NSRect& rect);
59 #endif //def WXCOCOA_FILL_DUMMY_VIEW
60 private:
61     wxWindowCocoaHider();
62 };
63
64 // ========================================================================
65 // wxWindowCocoaScrollView
66 // ========================================================================
67 class wxWindowCocoaScrollView: protected wxCocoaNSView
68 {
69     DECLARE_NO_COPY_CLASS(wxWindowCocoaScrollView)
70 public:
71     wxWindowCocoaScrollView(wxWindow *owner);
72     virtual ~wxWindowCocoaScrollView();
73     inline WX_NSScrollView GetNSScrollView() { return m_cocoaNSScrollView; }
74     void ClientSizeToSize(int &width, int &height);
75     void DoGetClientSize(int *x, int *y) const;
76     void Encapsulate();
77     void Unencapsulate();
78 protected:
79     wxWindowCocoa *m_owner;
80     WX_NSScrollView m_cocoaNSScrollView;
81     virtual void Cocoa_FrameChanged(void);
82 private:
83     wxWindowCocoaScrollView();
84 };
85
86 // ========================================================================
87 // wxDummyNSView
88 // ========================================================================
89 @interface wxDummyNSView : NSView
90 - (NSView *)hitTest:(NSPoint)aPoint;
91 @end
92
93 @implementation wxDummyNSView : NSView
94 - (NSView *)hitTest:(NSPoint)aPoint
95 {
96     return nil;
97 }
98
99 @end
100
101 // ========================================================================
102 // wxWindowCocoaHider
103 // ========================================================================
104 wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
105 :   m_owner(owner)
106 {
107     wxASSERT(owner);
108     wxASSERT(owner->GetNSViewForHiding());
109     m_dummyNSView = [[wxDummyNSView alloc]
110         initWithFrame:[owner->GetNSViewForHiding() frame]];
111     [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
112     AssociateNSView(m_dummyNSView);
113 }
114
115 wxWindowCocoaHider::~wxWindowCocoaHider()
116 {
117     DisassociateNSView(m_dummyNSView);
118     [m_dummyNSView release];
119 }
120
121 void wxWindowCocoaHider::Cocoa_FrameChanged(void)
122 {
123     // Keep the real window in synch with the dummy
124     wxASSERT(m_dummyNSView);
125     [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
126 }
127
128
129 #ifdef WXCOCOA_FILL_DUMMY_VIEW
130 bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
131 {
132     NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
133     [[NSColor greenColor] set];
134     [bezpath stroke];
135     [bezpath fill];
136     return true;
137 }
138 #endif //def WXCOCOA_FILL_DUMMY_VIEW
139
140 // ========================================================================
141 // wxFlippedNSClipView
142 // ========================================================================
143 @interface wxFlippedNSClipView : NSClipView
144 - (BOOL)isFlipped;
145 @end
146
147 @implementation wxFlippedNSClipView : NSClipView
148 - (BOOL)isFlipped
149 {
150     return YES;
151 }
152
153 @end
154
155 // ========================================================================
156 // wxWindowCocoaScrollView
157 // ========================================================================
158 wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
159 :   m_owner(owner)
160 {
161     wxAutoNSAutoreleasePool pool;
162     wxASSERT(owner);
163     wxASSERT(owner->GetNSView());
164     m_cocoaNSScrollView = [[NSScrollView alloc]
165         initWithFrame:[owner->GetNSView() frame]];
166     AssociateNSView(m_cocoaNSScrollView);
167
168     /* Replace the default NSClipView with a flipped one.  This ensures
169        scrolling is "pinned" to the top-left instead of bottom-right. */
170     NSClipView *flippedClip = [[wxFlippedNSClipView alloc]
171         initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
172     [m_cocoaNSScrollView setContentView:flippedClip];
173     [flippedClip release];
174
175     [m_cocoaNSScrollView setBackgroundColor: [NSColor windowBackgroundColor]];
176     [m_cocoaNSScrollView setHasHorizontalScroller: YES];
177     [m_cocoaNSScrollView setHasVerticalScroller: YES];
178     Encapsulate();
179 }
180
181 void wxWindowCocoaScrollView::Encapsulate()
182 {
183     // Set the scroll view autoresizingMask to match the current NSView
184     [m_cocoaNSScrollView setAutoresizingMask: [m_owner->GetNSView() autoresizingMask]];
185     [m_owner->GetNSView() setAutoresizingMask: NSViewNotSizable];
186     // NOTE: replaceSubView will cause m_cocaNSView to be released
187     // except when it hasn't been added into an NSView hierarchy in which
188     // case it doesn't need to be and this should work out to a no-op
189     m_owner->CocoaReplaceView(m_owner->GetNSView(), m_cocoaNSScrollView);
190     // The NSView is still retained by owner
191     [m_cocoaNSScrollView setDocumentView: m_owner->GetNSView()];
192     // Now it's also retained by the NSScrollView
193 }
194
195 void wxWindowCocoaScrollView::Unencapsulate()
196 {
197     [m_cocoaNSScrollView setDocumentView: nil];
198     m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
199     if(![[m_owner->GetNSView() superview] isFlipped])
200         [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
201 }
202
203 wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
204 {
205     DisassociateNSView(m_cocoaNSScrollView);
206     [m_cocoaNSScrollView release];
207 }
208
209 void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
210 {
211     NSSize frameSize = [NSScrollView
212         frameSizeForContentSize: NSMakeSize(width,height)
213         hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
214         hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
215         borderType: [m_cocoaNSScrollView borderType]];
216     width = (int)frameSize.width;
217     height = (int)frameSize.height;
218 }
219
220 void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
221 {
222     NSSize nssize = [m_cocoaNSScrollView contentSize];
223     if(x)
224         *x = (int)nssize.width;
225     if(y)
226         *y = (int)nssize.height;
227 }
228
229 void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
230 {
231     wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged"));
232     wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
233     event.SetEventObject(m_owner);
234     m_owner->GetEventHandler()->ProcessEvent(event);
235 }
236
237 // ========================================================================
238 // wxWindowCocoa
239 // ========================================================================
240 // normally the base classes aren't included, but wxWindow is special
241 #ifdef __WXUNIVERSAL__
242 IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
243 #else
244 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
245 #endif
246
247 BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
248 END_EVENT_TABLE()
249
250 wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
251
252 // Constructor
253 void wxWindowCocoa::Init()
254 {
255     m_cocoaNSView = NULL;
256     m_cocoaHider = NULL;
257     m_wxCocoaScrollView = NULL;
258     m_isBeingDeleted = FALSE;
259     m_isInPaint = FALSE;
260     m_shouldBeEnabled = true;
261 }
262
263 // Constructor
264 bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
265            const wxPoint& pos,
266            const wxSize& size,
267            long style,
268            const wxString& name)
269 {
270     if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
271         return false;
272
273     // TODO: create the window
274     m_cocoaNSView = NULL;
275     SetNSView([[NSView alloc] initWithFrame: MakeDefaultNSRect(size)]);
276     [m_cocoaNSView release];
277
278     if (m_parent)
279     {
280         m_parent->AddChild(this);
281         m_parent->CocoaAddChild(this);
282         SetInitialFrameRect(pos,size);
283     }
284
285     return TRUE;
286 }
287
288 // Destructor
289 wxWindow::~wxWindow()
290 {
291     wxAutoNSAutoreleasePool pool;
292     DestroyChildren();
293
294     // Make sure our parent (in the wxWidgets sense) is our superview
295     // before we go removing from it.
296     if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
297         CocoaRemoveFromParent();
298     delete m_cocoaHider;
299     delete m_wxCocoaScrollView;
300     if(m_cocoaNSView)
301         SendDestroyEvent();
302     SetNSView(NULL);
303 }
304
305 void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
306 {
307     NSView *childView = child->GetNSViewForSuperview();
308
309     wxASSERT(childView);
310     [m_cocoaNSView addSubview: childView];
311     child->m_isShown = !m_cocoaHider;
312 }
313
314 void wxWindowCocoa::CocoaRemoveFromParent(void)
315 {
316     [GetNSViewForSuperview() removeFromSuperview];
317 }
318
319 void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
320 {
321     bool need_debug = cocoaNSView || m_cocoaNSView;
322     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
323     DisassociateNSView(m_cocoaNSView);
324     [cocoaNSView retain];
325     [m_cocoaNSView release];
326     m_cocoaNSView = cocoaNSView;
327     AssociateNSView(m_cocoaNSView);
328     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
329 }
330
331 WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
332 {
333     return m_cocoaHider
334         ?   m_cocoaHider->GetNSView()
335         :   m_wxCocoaScrollView
336             ?   m_wxCocoaScrollView->GetNSScrollView()
337             :   m_cocoaNSView;
338 }
339
340 WX_NSView wxWindowCocoa::GetNSViewForHiding() const
341 {
342     return m_wxCocoaScrollView
343         ?   m_wxCocoaScrollView->GetNSScrollView()
344         :   m_cocoaNSView;
345 }
346
347 NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
348 {
349     // TODO: Handle scrolling offset
350     wxCHECK_MSG(GetNSView(), pointBounds, wxT("Need to have a Cocoa view to do translation"));
351     if([GetNSView() isFlipped])
352         return pointBounds;
353     NSRect ourBounds = [GetNSView() bounds];
354     return NSMakePoint
355     (   pointBounds.x
356     ,   ourBounds.size.height - pointBounds.y
357     );
358 }
359
360 NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
361 {
362     // TODO: Handle scrolling offset
363     wxCHECK_MSG(GetNSView(), rectBounds, wxT("Need to have a Cocoa view to do translation"));
364     if([GetNSView() isFlipped])
365         return rectBounds;
366     NSRect ourBounds = [GetNSView() bounds];
367     return NSMakeRect
368     (   rectBounds.origin.x
369     ,   ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
370     ,   rectBounds.size.width
371     ,   rectBounds.size.height
372     );
373 }
374
375 NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
376 {
377     // TODO: Handle scrolling offset
378     wxCHECK_MSG(GetNSView(), pointWx, wxT("Need to have a Cocoa view to do translation"));
379     if([GetNSView() isFlipped])
380         return pointWx;
381     NSRect ourBounds = [GetNSView() bounds];
382     return NSMakePoint
383     (   pointWx.x
384     ,   ourBounds.size.height - pointWx.y
385     );
386 }
387
388 NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
389 {
390     // TODO: Handle scrolling offset
391     wxCHECK_MSG(GetNSView(), rectWx, wxT("Need to have a Cocoa view to do translation"));
392     if([GetNSView() isFlipped])
393         return rectWx;
394     NSRect ourBounds = [GetNSView() bounds];
395     return NSMakeRect
396     (   rectWx.origin.x
397     ,   ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
398     ,   rectWx.size.width
399     ,   rectWx.size.height
400     );
401 }
402
403 bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
404 {
405     wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
406     // Recursion can happen if the event loop runs from within the paint
407     // handler.  For instance, if an assertion dialog is shown.
408     // FIXME: This seems less than ideal.
409     if(m_isInPaint)
410     {
411         wxLogDebug(wxT("Paint event recursion!"));
412         return false;
413     }
414     m_isInPaint = TRUE;
415
416     // Set m_updateRegion
417     const NSRect *rects = &rect; // The bounding box of the region
418     int countRects = 1;
419     // Try replacing the larger rectangle with a list of smaller ones:
420     if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
421         [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
422
423     NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
424     for(int i=0; i<countRects; i++)
425     {
426         transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
427     }
428     m_updateRegion = wxRegion(transformedRects,countRects);
429     free(transformedRects);
430
431     wxPaintEvent event(m_windowId);
432     event.SetEventObject(this);
433     bool ret = GetEventHandler()->ProcessEvent(event);
434     m_isInPaint = FALSE;
435     return ret;
436 }
437
438 void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
439 {
440     wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
441     // Mouse events happen at the NSWindow level so we need to convert
442     // into our bounds coordinates then convert to wx coordinates.
443     NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
444     NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
445     // FIXME: Should we be adjusting for client area origin?
446     const wxPoint &clientorigin = GetClientAreaOrigin();
447     event.m_x = (wxCoord)pointWx.x - clientorigin.x;
448     event.m_y = (wxCoord)pointWx.y - clientorigin.y;
449
450     event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
451     event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
452     event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
453     event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
454
455     // TODO: set timestamp?
456     event.SetEventObject(this);
457     event.SetId(GetId());
458 }
459
460 bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
461 {
462     wxMouseEvent event(wxEVT_MOTION);
463     InitMouseEvent(event,theEvent);
464     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
465     return GetEventHandler()->ProcessEvent(event);
466 }
467
468 bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
469 {
470     return false;
471 }
472
473 bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
474 {
475     return false;
476 }
477
478 bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
479 {
480     wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
481     InitMouseEvent(event,theEvent);
482     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
483     return GetEventHandler()->ProcessEvent(event);
484 }
485
486 bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
487 {
488     wxMouseEvent event(wxEVT_MOTION);
489     InitMouseEvent(event,theEvent);
490     event.m_leftDown = true;
491     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
492     return GetEventHandler()->ProcessEvent(event);
493 }
494
495 bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
496 {
497     wxMouseEvent event(wxEVT_LEFT_UP);
498     InitMouseEvent(event,theEvent);
499     wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
500     return GetEventHandler()->ProcessEvent(event);
501 }
502
503 bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
504 {
505     wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
506     InitMouseEvent(event,theEvent);
507     wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
508     return GetEventHandler()->ProcessEvent(event);
509 }
510
511 bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
512 {
513     wxMouseEvent event(wxEVT_MOTION);
514     InitMouseEvent(event,theEvent);
515     event.m_rightDown = true;
516     wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
517     return GetEventHandler()->ProcessEvent(event);
518 }
519
520 bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
521 {
522     wxMouseEvent event(wxEVT_RIGHT_UP);
523     InitMouseEvent(event,theEvent);
524     wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
525     return GetEventHandler()->ProcessEvent(event);
526 }
527
528 bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
529 {
530     return false;
531 }
532
533 bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
534 {
535     return false;
536 }
537
538 bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
539 {
540     return false;
541 }
542
543 void wxWindowCocoa::Cocoa_FrameChanged(void)
544 {
545     wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged"));
546     wxSizeEvent event(GetSize(), m_windowId);
547     event.SetEventObject(this);
548     GetEventHandler()->ProcessEvent(event);
549 }
550
551 bool wxWindowCocoa::Cocoa_resetCursorRects()
552 {
553     if(!m_cursor.GetNSCursor())
554         return false;
555     
556     [GetNSView() addCursorRect: [GetNSView() visibleRect]  cursor: m_cursor.GetNSCursor()];    
557         
558     return true;
559 }
560
561 bool wxWindow::Close(bool force)
562 {
563     // The only reason this function exists is that it is virtual and
564     // wxTopLevelWindowCocoa will override it.
565     return wxWindowBase::Close(force);
566 }
567
568 void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
569 {
570     [[oldView superview] replaceSubview:oldView with:newView];
571 }
572
573 bool wxWindow::EnableSelfAndChildren(bool enable)
574 {
575     // If the state isn't changing, don't do anything
576     if(!wxWindowBase::Enable(enable && m_shouldBeEnabled))
577         return false;
578     // Set the state of the Cocoa window
579     CocoaSetEnabled(m_isEnabled);
580     // Disable all children or (if enabling) return them to their proper state
581     for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
582         node; node = node->GetNext())
583     {
584         node->GetData()->EnableSelfAndChildren(enable);
585     }
586     return true;
587 }
588
589 bool wxWindow::Enable(bool enable)
590 {
591     // Keep track of what the window SHOULD be doing
592     m_shouldBeEnabled = enable;
593     // If the parent is disabled for any reason, then this window will be too.
594     if(!IsTopLevel() && GetParent())
595     {
596         enable = enable && GetParent()->IsEnabled();
597     }
598     return EnableSelfAndChildren(enable);
599 }
600
601 bool wxWindow::Show(bool show)
602 {
603     wxAutoNSAutoreleasePool pool;
604     // If the window is marked as visible, then it shouldn't have a dummy view
605     // If the window is marked hidden, then it should have a dummy view
606     // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
607 //    wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
608     // Return false if there isn't a window to show or hide
609     NSView *cocoaView = GetNSViewForHiding();
610     if(!cocoaView)
611         return false;
612     if(show)
613     {
614         // If state isn't changing, return false
615         if(!m_cocoaHider)
616             return false;
617         CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
618         wxASSERT(![m_cocoaHider->GetNSView() superview]);
619         delete m_cocoaHider;
620         m_cocoaHider = NULL;
621         wxASSERT([cocoaView superview]);
622     }
623     else
624     {
625         // If state isn't changing, return false
626         if(m_cocoaHider)
627             return false;
628         m_cocoaHider = new wxWindowCocoaHider(this);
629         // NOTE: replaceSubview:with will cause m_cocaNSView to be
630         // (auto)released which balances out addSubview
631         CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
632         // m_coocaNSView is now only retained by us
633         wxASSERT([m_cocoaHider->GetNSView() superview]);
634         wxASSERT(![cocoaView superview]);
635     }
636     m_isShown = show;
637     return true;
638 }
639
640 void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
641 {
642     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":".");
643     int currentX, currentY;
644     int currentW, currentH;
645     DoGetPosition(&currentX, &currentY);
646     DoGetSize(&currentW, &currentH);
647     if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
648         x=currentX;
649     if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
650         y=currentY;
651
652     AdjustForParentClientOrigin(x,y,sizeFlags);
653
654     wxSize size(-1,-1);
655
656     if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
657     {
658         if(sizeFlags&wxSIZE_AUTO_WIDTH)
659         {
660             size=DoGetBestSize();
661             width=size.x;
662         }
663         else
664             width=currentW;
665     }
666     if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
667     {
668         if(sizeFlags&wxSIZE_AUTO_HEIGHT)
669         {
670             if(size.x==-1)
671                 size=DoGetBestSize();
672             height=size.y;
673         }
674         else
675             height=currentH;
676     }
677     DoMoveWindow(x,y,width,height);
678 }
679
680 #if wxUSE_TOOLTIPS
681
682 void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
683 {
684     wxWindowBase::DoSetToolTip(tip);
685
686     if ( m_tooltip )
687     {
688         m_tooltip->SetWindow((wxWindow *)this);
689     }
690 }
691
692 #endif
693
694 void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
695 {
696     wxAutoNSAutoreleasePool pool;
697     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
698
699     NSView *nsview = GetNSViewForSuperview();
700     NSView *superview = [nsview superview];
701
702     wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
703
704     NSRect oldFrameRect = [nsview frame];
705     NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
706     [nsview setFrame:newFrameRect];
707     // Be sure to redraw the parent to reflect the changed position
708     [superview setNeedsDisplayInRect:oldFrameRect];
709     [superview setNeedsDisplayInRect:newFrameRect];
710 }
711
712 void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
713 {
714     NSView *nsview = GetNSViewForSuperview();
715     NSView *superview = [nsview superview];
716     wxCHECK_RET(superview,wxT("NSView does not have a superview"));
717     wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
718     NSRect frameRect = [nsview frame];
719     if(size.x!=-1)
720         frameRect.size.width = size.x;
721     if(size.y!=-1)
722         frameRect.size.height = size.y;
723     frameRect.origin.x = pos.x;
724     frameRect.origin.y = pos.y;
725     // Tell Cocoa to change the margin between the bottom of the superview
726     // and the bottom of the control.  Keeps the control pinned to the top
727     // of its superview so that its position in the wxWidgets coordinate
728     // system doesn't change.
729     if(![superview isFlipped])
730         [nsview setAutoresizingMask: NSViewMinYMargin];
731     // MUST set the mask before setFrame: which can generate a size event
732     // and cause a scroller to be added!
733     frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
734     [nsview setFrame: frameRect];
735 }
736
737 // Get total size
738 void wxWindow::DoGetSize(int *w, int *h) const
739 {
740     NSRect cocoaRect = [GetNSViewForSuperview() frame];
741     if(w)
742         *w=(int)cocoaRect.size.width;
743     if(h)
744         *h=(int)cocoaRect.size.height;
745     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
746 }
747
748 void wxWindow::DoGetPosition(int *x, int *y) const
749 {
750     NSView *nsview = GetNSViewForSuperview();
751
752     NSRect cocoaRect = [nsview frame];
753     NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
754     if(x)
755         *x=(int)rectWx.origin.x;
756     if(y)
757         *y=(int)rectWx.origin.y;
758     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
759 }
760
761 WXWidget wxWindow::GetHandle() const
762 {
763     return m_cocoaNSView;
764 }
765
766 wxWindow* wxWindow::GetWxWindow() const
767 {
768     return (wxWindow*) this;
769 }
770
771 void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
772 {
773     [m_cocoaNSView setNeedsDisplay:YES];
774 }
775
776 void wxWindow::SetFocus()
777 {
778     if([GetNSView() acceptsFirstResponder])
779         [[GetNSView() window] makeFirstResponder: GetNSView()];
780 }
781
782 void wxWindow::DoCaptureMouse()
783 {
784     // TODO
785     sm_capturedWindow = this;
786 }
787
788 void wxWindow::DoReleaseMouse()
789 {
790     // TODO
791     sm_capturedWindow = NULL;
792 }
793
794 void wxWindow::DoScreenToClient(int *x, int *y) const
795 {
796     // TODO
797 }
798
799 void wxWindow::DoClientToScreen(int *x, int *y) const
800 {
801     // TODO
802 }
803
804 // Get size *available for subwindows* i.e. excluding menu bar etc.
805 void wxWindow::DoGetClientSize(int *x, int *y) const
806 {
807     wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
808     if(m_wxCocoaScrollView)
809         m_wxCocoaScrollView->DoGetClientSize(x,y);
810     else
811         wxWindowCocoa::DoGetSize(x,y);
812 }
813
814 void wxWindow::DoSetClientSize(int width, int height)
815 {
816     wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
817     if(m_wxCocoaScrollView)
818         m_wxCocoaScrollView->ClientSizeToSize(width,height);
819     CocoaSetWxWindowSize(width,height);
820 }
821
822 void wxWindow::CocoaSetWxWindowSize(int width, int height)
823 {
824     wxWindowCocoa::DoSetSize(-1,-1,width,height,wxSIZE_USE_EXISTING);
825 }
826
827 int wxWindow::GetCharHeight() const
828 {
829     // TODO
830     return 0;
831 }
832
833 int wxWindow::GetCharWidth() const
834 {
835     // TODO
836     return 0;
837 }
838
839 void wxWindow::GetTextExtent(const wxString& string, int *x, int *y,
840         int *descent, int *externalLeading, const wxFont *theFont) const
841 {
842     // TODO
843 }
844
845 // Coordinates relative to the window
846 void wxWindow::WarpPointer (int x_pos, int y_pos)
847 {
848     // TODO
849 }
850
851 int wxWindow::GetScrollPos(int orient) const
852 {
853     // TODO
854     return 0;
855 }
856
857 // This now returns the whole range, not just the number
858 // of positions that we can scroll.
859 int wxWindow::GetScrollRange(int orient) const
860 {
861     // TODO
862     return 0;
863 }
864
865 int wxWindow::GetScrollThumb(int orient) const
866 {
867     // TODO
868     return 0;
869 }
870
871 void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
872 {
873     // TODO
874 }
875
876 void wxWindow::CocoaCreateNSScrollView()
877 {
878     if(!m_wxCocoaScrollView)
879     {
880         m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
881     }
882 }
883
884 // New function that will replace some of the above.
885 void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
886     int range, bool refresh)
887 {
888     CocoaCreateNSScrollView();
889     // TODO
890 }
891
892 // Does a physical scroll
893 void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
894 {
895     // TODO
896 }
897
898 void wxWindow::DoSetVirtualSize( int x, int y )
899 {
900     wxWindowBase::DoSetVirtualSize(x,y);
901     CocoaCreateNSScrollView();
902     [m_cocoaNSView setFrameSize:NSMakeSize(m_virtualSize.x,m_virtualSize.y)];
903 }
904
905 bool wxWindow::SetFont(const wxFont& font)
906 {
907     // TODO
908     return TRUE;
909 }
910
911 static int CocoaRaiseWindowCompareFunction(id first, id second, void *target)
912 {
913     // first should be ordered higher
914     if(first==target)
915         return NSOrderedDescending;
916     // second should be ordered higher
917     if(second==target)
918         return NSOrderedAscending;
919     return NSOrderedSame;
920 }
921
922 // Raise the window to the top of the Z order
923 void wxWindow::Raise()
924 {
925 //    wxAutoNSAutoreleasePool pool;
926     NSView *nsview = GetNSViewForSuperview();
927     [[nsview superview] sortSubviewsUsingFunction:
928             CocoaRaiseWindowCompareFunction
929         context: nsview];
930 }
931
932 static int CocoaLowerWindowCompareFunction(id first, id second, void *target)
933 {
934     // first should be ordered lower
935     if(first==target)
936         return NSOrderedAscending;
937     // second should be ordered lower
938     if(second==target)
939         return NSOrderedDescending;
940     return NSOrderedSame;
941 }
942
943 // Lower the window to the bottom of the Z order
944 void wxWindow::Lower()
945 {
946     NSView *nsview = GetNSViewForSuperview();
947     [[nsview superview] sortSubviewsUsingFunction:
948             CocoaLowerWindowCompareFunction
949         context: nsview];
950 }
951
952 bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
953 {
954     return FALSE;
955 }
956
957 // Get the window with the focus
958 wxWindow *wxWindowBase::DoFindFocus()
959 {
960     // Basically we are somewhat emulating the responder chain here except
961     // we are only loking for the first responder in the key window or
962     // upon failing to find one if the main window is different we look
963     // for the first responder in the main window.
964
965     // Note that the firstResponder doesn't necessarily have to be an
966     // NSView but wxCocoaNSView::GetFromCocoa() will simply return
967     // NULL unless it finds its argument in its hash map.
968
969     wxCocoaNSView *win;
970
971     NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
972     win = wxCocoaNSView::GetFromCocoa([keyWindow firstResponder]);
973     if(win)
974         return win->GetWxWindow();
975
976     NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
977     if(mainWindow == keyWindow)
978         return NULL;
979     win = wxCocoaNSView::GetFromCocoa([mainWindow firstResponder]);
980     if(win)
981         return win->GetWxWindow();
982
983     return NULL;
984 }
985
986 /* static */ wxWindow *wxWindowBase::GetCapture()
987 {
988     // TODO
989     return wxWindowCocoa::sm_capturedWindow;
990 }
991
992 wxWindow *wxGetActiveWindow()
993 {
994     // TODO
995     return NULL;
996 }
997
998 wxPoint wxGetMousePosition()
999 {
1000     // TODO
1001     return wxDefaultPosition;
1002 }
1003
1004 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
1005 {
1006     pt = wxGetMousePosition();
1007     return NULL;
1008 }
1009