#include "wx/log.h"
#include "wx/window.h"
#include "wx/dc.h"
+ #include "wx/dcclient.h"
#include "wx/utils.h"
#endif //WX_PRECOMP
#include "wx/tooltip.h"
+#include "wx/cocoa/dc.h"
#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>
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
// ========================================================================
// ========================================================================
// 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)
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()
}
wxScrollWinEvent event(commandType, scrollpos, orientation);
event.SetEventObject(m_owner);
- m_owner->GetEventHandler()->ProcessEvent(event);
+ m_owner->HandleWindowEvent(event);
}
void wxWindowCocoaScrollView::UpdateSizes()
wxLogTrace(wxTRACE_COCOA,wxT("wxWindowCocoaScrollView=%p::Cocoa_FrameChanged for wxWindow %p"), this, m_owner);
wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
event.SetEventObject(m_owner);
- m_owner->GetEventHandler()->ProcessEvent(event);
- UpdateSizes();
+ m_owner->HandleWindowEvent(event);
+
+ /* 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();
}
// ========================================================================
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]);
WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
{
// TODO: Handle scrolling offset
- NSAffineTransform *transform = wxDC::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
+ NSAffineTransform *transform = wxCocoaDCImpl::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
return transform;
}
wxPaintEvent event(m_windowId);
event.SetEventObject(this);
- bool ret = GetEventHandler()->ProcessEvent(event);
+ bool ret = HandleWindowEvent(event);
m_isInPaint = false;
return ret;
}
// Mouse events happen at the NSWindow level so we need to convert
// into our bounds coordinates then convert to wx coordinates.
NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
+ if( m_wxCocoaScrollView != NULL)
+ {
+ // This gets the wx client area (i.e. the visible portion of the content) in
+ // the coordinate system of our (the doucment) view.
+ NSRect documentVisibleRect = [[m_wxCocoaScrollView->GetNSScrollView() contentView] documentVisibleRect];
+ // For horizontal, simply subtract the origin.
+ // e.g. if the origin is at 123 and the user clicks as far left as possible then
+ // the coordinate that wx wants is 0.
+ cocoaPoint.x -= documentVisibleRect.origin.x;
+ if([m_cocoaNSView isFlipped])
+ {
+ // In the flipped view case this works exactly like horizontal.
+ cocoaPoint.y -= documentVisibleRect.origin.y;
+ }
+ // For vertical we have to mind non-flipped (e.g. y=0 at bottom) views.
+ // We also need to mind the fact that we're still in Cocoa coordinates
+ // and not wx coordinates. The wx coordinate translation will still occur
+ // and that is going to be wxY = boundsH - cocoaY for non-flipped views.
+
+ // When we consider scrolling we are truly interested in how far the top
+ // edge of the bounds rectangle is scrolled off the screen.
+ // Assuming the bounds origin is 0 (which is an assumption we make in
+ // wxCocoa since wxWidgets has no analog to it) then the top edge of
+ // the bounds rectangle is simply its height. The top edge of the
+ // documentVisibleRect (e.g. the client area) is its height plus
+ // its origin.
+ // Thus, we simply need add the distance between the bounds top
+ // and the client (docuemntVisibleRect) top.
+ // Or putting it another way, we subtract the distance between the
+ // client top and the bounds top.
+ else
+ {
+ NSRect bounds = [m_cocoaNSView bounds];
+ CGFloat scrollYOrigin = (bounds.size.height - (documentVisibleRect.origin.y + documentVisibleRect.size.height));
+ cocoaPoint.y += scrollYOrigin;
+ }
+ }
+
NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
// FIXME: Should we be adjusting for client area origin?
const wxPoint &clientorigin = GetClientAreaOrigin();
wxMouseEvent event(wxEVT_MOTION);
InitMouseEvent(event,theEvent);
wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
event.SetId(GetId());
wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
- GetEventHandler()->ProcessEvent(event);
+ HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
wxMouseEvent event(wxEVT_ENTER_WINDOW);
InitMouseEvent(event,theEvent);
wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
else
return false;
wxMouseEvent event(wxEVT_LEAVE_WINDOW);
InitMouseEvent(event,theEvent);
wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
else
return false;
wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
InitMouseEvent(event,theEvent);
wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
InitMouseEvent(event,theEvent);
event.m_leftDown = true;
wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
wxMouseEvent event(wxEVT_LEFT_UP);
InitMouseEvent(event,theEvent);
wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
InitMouseEvent(event,theEvent);
wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
InitMouseEvent(event,theEvent);
event.m_rightDown = true;
wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
wxMouseEvent event(wxEVT_RIGHT_UP);
InitMouseEvent(event,theEvent);
wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
- return GetEventHandler()->ProcessEvent(event);
+ return HandleWindowEvent(event);
}
bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
m_visibleTrackingRectManager->RebuildTrackingRect();
wxSizeEvent event(GetSize(), m_windowId);
event.SetEventObject(this);
- GetEventHandler()->ProcessEvent(event);
+ HandleWindowEvent(event);
}
else
{
// 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
// transformations. However, it's better than nothing.
// We don't create a wxClientDC because we don't want to accidently be able to use
// it for drawing.
- wxDC tmpdc;
+ wxClientDC tmpdc(const_cast<wxWindow*>(this));
return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
}