]> git.saurik.com Git - wxWidgets.git/blobdiff - src/cocoa/window.mm
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / cocoa / window.mm
index 8bb0a6567cf9c94cd1c26be114425904b5f3f23d..3d52a1944097dad98a94459e92ac3078fbda9e07 100644 (file)
@@ -4,9 +4,8 @@
 // Author:      David Elliott
 // Modified by:
 // Created:     2002/12/26
-// RCS-ID:      $Id$
 // Copyright:   (c) 2002 David Elliott
-// Licence:     wxWidgets licence
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 #include "wx/wxprec.h"
@@ -25,7 +24,9 @@
 #include "wx/cocoa/autorelease.h"
 #include "wx/cocoa/string.h"
 #include "wx/cocoa/trackingrectmanager.h"
-#include "wx/mac/corefoundation/cfref.h"
+#include "wx/cocoa/private/scrollview.h"
+#include "wx/osx/core/cfref.h"
+#include "wx/cocoa/ObjcRef.h"
 
 #import <Foundation/NSArray.h>
 #import <Foundation/NSRunLoop.h>
@@ -150,7 +151,7 @@ namespace { // file namespace
 
 class wxCocoaPrivateScreenCoordinateTransformer
 {
-    DECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer)
+    wxDECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer);
 public:
     wxCocoaPrivateScreenCoordinateTransformer();
     ~wxCocoaPrivateScreenCoordinateTransformer();
@@ -271,7 +272,7 @@ NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinat
 // ========================================================================
 class wxWindowCocoaHider: protected wxCocoaNSView
 {
-    DECLARE_NO_COPY_CLASS(wxWindowCocoaHider)
+    wxDECLARE_NO_COPY_CLASS(wxWindowCocoaHider);
 public:
     wxWindowCocoaHider(wxWindow *owner);
     virtual ~wxWindowCocoaHider();
@@ -288,118 +289,6 @@ private:
     wxWindowCocoaHider();
 };
 
-// ========================================================================
-// wxWindowCocoaScrollView
-// ========================================================================
-class wxWindowCocoaScrollView: protected wxCocoaNSView
-{
-    DECLARE_NO_COPY_CLASS(wxWindowCocoaScrollView)
-public:
-    wxWindowCocoaScrollView(wxWindow *owner);
-    virtual ~wxWindowCocoaScrollView();
-    inline WX_NSScrollView GetNSScrollView() { return m_cocoaNSScrollView; }
-    void ClientSizeToSize(int &width, int &height);
-    void DoGetClientSize(int *x, int *y) const;
-    void Encapsulate();
-    void Unencapsulate();
-
-    // wxWindow calls this to do the work.  Note that we don't have the refresh parameter
-    // because wxWindow handles that itself.
-    void SetScrollbar(int orientation, int position, int thumbSize, int range);
-    int GetScrollPos(wxOrientation orient);
-    void SetScrollPos(wxOrientation orient, int position);
-    int GetScrollRange(wxOrientation orient);
-    int GetScrollThumb(wxOrientation orient);
-    void ScrollWindow(int dx, int dy, const wxRect*);
-    void UpdateSizes();
-
-    void _wx_doScroller(NSScroller *sender);
-
-protected:
-    wxWindowCocoa *m_owner;
-    WX_NSScrollView m_cocoaNSScrollView;
-    virtual void Cocoa_FrameChanged(void);
-    virtual void Cocoa_synthesizeMouseMoved(void) {}
-    /*!
-        Flag as to whether we're scrolling for a native view or a custom
-        wxWindow.  This controls the scrolling behavior.  When providing
-        scrolling for a native view we don't catch scroller action messages
-        and thus don't send scroll events and we don't actually scroll the
-        window when the application calls ScrollWindow.
-
-        When providing scrolling for a custom wxWindow, we make the NSScroller
-        send their action messages to us which we in turn package as wx window
-        scrolling events.  At this point, the window will not physically be
-        scrolled.  The application will most likely handle the event by calling
-        ScrollWindow which will do the real scrolling.  On the other hand,
-        the application may instead not call ScrollWindow until some threshold
-        is reached.  This causes the window to only scroll in steps which is
-        what, for instance, wxScrolledWindow does.
-     */
-    bool m_isNativeView;
-    /*!
-        The range as the application code wishes to see it.  That is, the
-        range from the last SetScrollbar call for the appropriate dimension.
-        The horizontal dimension is the first [0] element and the vertical
-        dimension the second [1] element.
-
-        In wxMSW, a SCROLLINFO with nMin=0 and nMax=range-1 is used which
-        gives exactly range possible positions so long as nPage (which is
-        the thumb size) is less than or equal to 1.
-     */
-    int m_scrollRange[2];
-    /*!
-        The thumb size is intended to reflect the size of the visible portion
-        of the scrolled document.  As the document size increases, the thumb
-        visible thumb size decreases.  As document size decreases, the visible
-        thumb size increases.  However, the thumb size on wx is defined in
-        terms of scroll units (which are effectively defined by the scroll
-        range) and so increasing the number of scroll units to reflect increased
-        document size will have the effect of decreasing the visible thumb
-        size even though the number doesn't change.
-
-        It's also important to note that subtracting the thumb size from the
-        full range gives you the real range that can be used.  Microsoft
-        defines nPos (the current scrolling position) to be within the range
-        from nMin to nMax - max(nPage - 1, 0).  We know that wxMSW code always
-        sets nMin = 0 and nMax = range -1.  So let's algebraically reduce the
-        definition of the maximum allowed position:
-
-        Begin:
-        = nMax - max(nPage - 1, 0)
-        Substitute (range - 1) for nMax and thumbSize for nPage:
-        = range - 1 - max(thumbSize - 1, 0)
-        Add one inside the max conditional and subtract one outside of it:
-        = range - 1 - (max(thumbSize - 1 + 1, 1) - 1)
-        Reduce some constants:
-        = range - 1 - (max(thumbSize, 1) - 1)
-        Distribute the negative across the parenthesis:
-        = range - 1 - max(thumbSize, 1) + 1
-        Reduce the constants:
-        = range - max(thumbSize, 1)
-
-        Also keep in mind that thumbSize may never be greater than range but
-        can be equal to it.  Thus for the smallest possible thumbSize there
-        are exactly range possible scroll positions (numbered from 0 to
-        range - 1) and for the largest possible thumbSize there is exactly
-        one possible scroll position (numbered 0).
-     */
-    int m_scrollThumb[2];
-
-    /*!
-        The origin of the virtual coordinate space expressed in terms of client
-        coordinates.  Starts at (0,0) and each call to ScrollWindow accumulates
-        into it.  Thus if the user scrolls the window right (thus causing the
-        contents to move left with respect to the client origin, the
-        application code (typically wxScrolledWindow) will be called with
-        dx of -something, for example -20.  This is added to m_virtualOrigin
-        and thus m_virtualOrigin will be (-20,0) in this example.
-     */
-    wxPoint m_virtualOrigin;
-private:
-    wxWindowCocoaScrollView();
-};
-
 // ========================================================================
 // wxDummyNSView
 // ========================================================================
@@ -419,6 +308,12 @@ WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
 
 // ========================================================================
 // wxWindowCocoaHider
+//  NOTE: This class and method of hiding predates setHidden: support in
+//  the toolkit.  The hack used here is to replace the view with a stand-in
+//  that will be subject to the usual Cocoa resizing rules.
+//  When possible (i.e. when running on 10.3 or higher) we make it hidden
+//  mostly as an optimization so Cocoa doesn't have to consider it when
+//  drawing or finding key views.
 // ========================================================================
 wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
 :   m_owner(owner)
@@ -429,6 +324,9 @@ wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
         initWithFrame:[owner->GetNSViewForHiding() frame]];
     [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
     AssociateNSView(m_dummyNSView);
+
+    if([m_dummyNSView respondsToSelector:@selector(setHidden:)])
+        [m_dummyNSView setHidden:YES];
 }
 
 wxWindowCocoaHider::~wxWindowCocoaHider()
@@ -458,9 +356,9 @@ bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
 
 
 /*! @class WXManualScrollView
-    @abstract   An NSScrollView subclass which implements wx scroll behavior
+    @abstract   An NSScrollView subclass which implements wx scroll behaviour
     @discussion
-    Overrides default behavior of NSScrollView such that this class receives
+    Overrides default behaviour of NSScrollView such that this class receives
     the scroller action messages and allows the wxCocoaScrollView to act
     on them accordingly.  In particular, because the NSScrollView will not
     receive action messages from the scroller, it will not adjust the
@@ -836,7 +734,7 @@ int wxWindowCocoaScrollView::GetScrollPos(wxOrientation orient)
         position is at range-thumbsize.
 
         The range of an NSScroller is 0.0 to 1.0.  Much easier! NOTE: Apple doesn't really specify
-        but GNUStep docs do say that 0.0 is top/left and 1.0 is bottom/right.  This is actualy
+        but GNUStep docs do say that 0.0 is top/left and 1.0 is bottom/right.  This is actually
         in contrast to NSSlider which generally has 1.0 at the TOP when it's done vertically.
      */
     CGFloat cocoaScrollPos = [cocoaScroller floatValue];
@@ -1013,7 +911,14 @@ void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
     wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
     event.SetEventObject(m_owner);
     m_owner->HandleWindowEvent(event);
-    UpdateSizes();
+
+    /*  If the view is not a native one then it's being managed by wx.  In this case the control
+        may decide to change its virtual size and we must update the document view's size to
+        match.  For native views the virtual size will never have been set so we do not want
+        to use it at all.
+     */
+    if(!m_isNativeView)
+        UpdateSizes();
 }
 
 // ========================================================================
@@ -1022,8 +927,6 @@ void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
 // normally the base classes aren't included, but wxWindow is special
 #ifdef __WXUNIVERSAL__
 IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
-#else
-IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
 #endif
 
 BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
@@ -1037,7 +940,6 @@ void wxWindowCocoa::Init()
     m_cocoaNSView = NULL;
     m_cocoaHider = NULL;
     m_wxCocoaScrollView = NULL;
-    m_isBeingDeleted = false;
     m_isInPaint = false;
     m_visibleTrackingRectManager = NULL;
 }
@@ -1109,8 +1011,8 @@ void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
     bool need_debug = cocoaNSView || m_cocoaNSView;
     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
     DisassociateNSView(m_cocoaNSView);
-    [cocoaNSView retain];
-    [m_cocoaNSView release];
+    wxGCSafeRetain(cocoaNSView);
+    wxGCSafeRelease(m_cocoaNSView);
     m_cocoaNSView = cocoaNSView;
     AssociateNSView(m_cocoaNSView);
     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
@@ -1506,17 +1408,48 @@ bool wxWindow::Show(bool show)
         // If state isn't changing, return false
         if(!m_cocoaHider)
             return false;
+
+        // Replace the stand-in view with the real one and destroy the dummy view
         CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
         wxASSERT(![m_cocoaHider->GetNSView() superview]);
         delete m_cocoaHider;
         m_cocoaHider = NULL;
         wxASSERT([cocoaView superview]);
+
+        // Schedule an update of the key view loop (NOTE: 10.4+ only.. argh)
+        NSWindow *window = [cocoaView window];
+        if(window != nil)
+        {
+            // Delay this until returning to the event loop for a couple of reasons:
+            // 1. If a lot of stuff is shown/hidden we avoid recalculating needlessly
+            // 2. NSWindow does not seem to see the newly shown views if we do it right now.
+            if([window respondsToSelector:@selector(recalculateKeyViewLoop)])
+                [window performSelector:@selector(recalculateKeyViewLoop) withObject:nil afterDelay:0.0];
+        }
     }
     else
     {
         // If state isn't changing, return false
         if(m_cocoaHider)
             return false;
+
+        // Handle the first responder
+        NSWindow *window = [cocoaView window];
+        if(window != nil)
+        {
+            NSResponder *firstResponder = [window firstResponder];
+            if([firstResponder isKindOfClass:[NSView class]] && [(NSView*)firstResponder isDescendantOf:cocoaView])
+            {
+                BOOL didResign = [window makeFirstResponder:nil];
+                // If the current first responder (one of our subviews) refuses to give
+                // up its status, then fail now and don't hide this view
+                if(didResign == NO)
+                    return false;
+            }
+        }
+
+        // Create a new view to stand in for the real one (via wxWindowCocoaHider) and replace
+        // the real one with the stand in.
         m_cocoaHider = new wxWindowCocoaHider(this);
         // NOTE: replaceSubview:with will cause m_cocaNSView to be
         // (auto)released which balances out addSubview
@@ -1777,7 +1710,7 @@ int wxWindow::GetCharWidth() const
     return 5;
 }
 
-void wxWindow::GetTextExtent(const wxString& string, int *outX, int *outY,
+void wxWindow::DoGetTextExtent(const wxString& string, int *outX, int *outY,
         int *outDescent, int *outExternalLeading, const wxFont *inFont) const
 {
     // FIXME: This obviously ignores the window's font (if any) along with any size
@@ -1881,7 +1814,7 @@ static char const * const comparisonresultStrings[] =
 
 class CocoaWindowCompareContext
 {
-    DECLARE_NO_COPY_CLASS(CocoaWindowCompareContext)
+    wxDECLARE_NO_COPY_CLASS(CocoaWindowCompareContext);
 public:
     CocoaWindowCompareContext(); // Not implemented
     CocoaWindowCompareContext(NSView *target, NSArray *subviews)
@@ -2072,7 +2005,7 @@ wxWindow* wxFindWindowAtPointer(wxPoint& pt)
  */
 class wxCocoaMouseMovedEventSynthesizer
 {
-    DECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer)
+    wxDECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer);
 public:
     wxCocoaMouseMovedEventSynthesizer()
     {   m_lastScreenMouseLocation = NSZeroPoint;