| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218 |
- /////////////////////////////////////////////////////////////////////////////
- // Name: src/cocoa/window.mm
- // Purpose: wxWindowCocoa
- // Author: David Elliott
- // Modified by:
- // Created: 2002/12/26
- // Copyright: (c) 2002 David Elliott
- // Licence: wxWindows licence
- /////////////////////////////////////////////////////////////////////////////
- #include "wx/wxprec.h"
- #ifndef WX_PRECOMP
- #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/cocoa/private/scrollview.h"
- #include "wx/osx/core/cfref.h"
- #include "wx/cocoa/ObjcRef.h"
- #import <Foundation/NSArray.h>
- #import <Foundation/NSRunLoop.h>
- #include "wx/cocoa/objc/NSView.h"
- #import <AppKit/NSEvent.h>
- #import <AppKit/NSScrollView.h>
- #import <AppKit/NSScroller.h>
- #import <AppKit/NSColor.h>
- #import <AppKit/NSClipView.h>
- #import <Foundation/NSException.h>
- #import <AppKit/NSApplication.h>
- #import <AppKit/NSWindow.h>
- #import <AppKit/NSScreen.h>
- // Turn this on to paint green over the dummy views for debugging
- #undef WXCOCOA_FILL_DUMMY_VIEW
- #ifdef WXCOCOA_FILL_DUMMY_VIEW
- #import <AppKit/NSBezierPath.h>
- #endif //def WXCOCOA_FILL_DUMMY_VIEW
- // STL list used by wxCocoaMouseMovedEventSynthesizer
- #include <list>
- /* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
- * NSInteger post-Leopard. Pre-Leopard the Cocoa toolkit expects a function
- * returning int and not NSComparisonResult. Post-Leopard the Cocoa toolkit
- * expects a function returning the new non-enum NSComparsionResult.
- * Hence we create a typedef named CocoaWindowCompareFunctionResult.
- */
- #if defined(NSINTEGER_DEFINED)
- typedef NSComparisonResult CocoaWindowCompareFunctionResult;
- #else
- typedef int CocoaWindowCompareFunctionResult;
- #endif
- // A category for methods that are only present in Panther's SDK
- @interface NSView(wxNSViewPrePantherCompatibility)
- - (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
- @end
- // ========================================================================
- // Helper functions for converting to/from wxWidgets coordinates and a
- // specified NSView's coordinate system.
- // ========================================================================
- NSPoint CocoaTransformNSViewBoundsToWx(NSView *nsview, NSPoint pointBounds)
- {
- wxCHECK_MSG(nsview, pointBounds, wxT("Need to have a Cocoa view to do translation"));
- if([nsview isFlipped])
- return pointBounds;
- NSRect ourBounds = [nsview bounds];
- return NSMakePoint
- ( pointBounds.x
- , ourBounds.size.height - pointBounds.y
- );
- }
- NSRect CocoaTransformNSViewBoundsToWx(NSView *nsview, NSRect rectBounds)
- {
- wxCHECK_MSG(nsview, rectBounds, wxT("Need to have a Cocoa view to do translation"));
- if([nsview isFlipped])
- return rectBounds;
- NSRect ourBounds = [nsview bounds];
- return NSMakeRect
- ( rectBounds.origin.x
- , ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
- , rectBounds.size.width
- , rectBounds.size.height
- );
- }
- NSPoint CocoaTransformNSViewWxToBounds(NSView *nsview, NSPoint pointWx)
- {
- wxCHECK_MSG(nsview, pointWx, wxT("Need to have a Cocoa view to do translation"));
- if([nsview isFlipped])
- return pointWx;
- NSRect ourBounds = [nsview bounds];
- return NSMakePoint
- ( pointWx.x
- , ourBounds.size.height - pointWx.y
- );
- }
- NSRect CocoaTransformNSViewWxToBounds(NSView *nsview, NSRect rectWx)
- {
- wxCHECK_MSG(nsview, rectWx, wxT("Need to have a Cocoa view to do translation"));
- if([nsview isFlipped])
- return rectWx;
- NSRect ourBounds = [nsview bounds];
- return NSMakeRect
- ( rectWx.origin.x
- , ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
- , rectWx.size.width
- , rectWx.size.height
- );
- }
- // ============================================================================
- // Screen coordinate helpers
- // ============================================================================
- /*
- General observation about Cocoa screen coordinates:
- It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
- It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
- the BOTTOM-right corner of this screen. Recall that Cocoa uses cartesian coordinates so y-increase is up.
- It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
- relative to that screen's frame. The only real way to test this is to configure two screens one atop
- the other such that the menubar screen is on top. The Dock at the bottom of the screen will then
- eat into the visibleFrame of screen 1 by incrementing it's y-origin. Thus if you arrange two
- 1920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
- visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
- In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
- However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
- This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
- */
- namespace { // file namespace
- class wxCocoaPrivateScreenCoordinateTransformer
- {
- wxDECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer);
- public:
- wxCocoaPrivateScreenCoordinateTransformer();
- ~wxCocoaPrivateScreenCoordinateTransformer();
- wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
- NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
- protected:
- NSScreen *m_screenZero;
- NSRect m_screenZeroFrame;
- };
- // NOTE: This is intended to be a short-lived object. A future enhancment might
- // make it a global and reconfigure it upon some notification that the screen layout
- // has changed.
- inline wxCocoaPrivateScreenCoordinateTransformer::wxCocoaPrivateScreenCoordinateTransformer()
- {
- NSArray *screens = [NSScreen screens];
- [screens retain];
- m_screenZero = nil;
- if(screens != nil && [screens count] > 0)
- m_screenZero = [[screens objectAtIndex:0] retain];
- [screens release];
- if(m_screenZero != nil)
- m_screenZeroFrame = [m_screenZero frame];
- else
- {
- wxLogWarning(wxT("Can't translate to/from wx screen coordinates and Cocoa screen coordinates"));
- // Just blindly assume 1024x768 so that at least we can sort of flip things around into
- // Cocoa coordinates.
- // NOTE: Theoretically this case should never happen anyway.
- m_screenZeroFrame = NSMakeRect(0,0,1024,768);
- }
- }
- inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
- {
- [m_screenZero release];
- m_screenZero = nil;
- }
- inline wxPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
- {
- // x and y are in wx screen coordinates which we're going to arbitrarily define such that
- // (0,0) is the TOP-left of screen 0 (the one with the menubar)
- // NOTE WELL: This means that (0,0) is _NOT_ an appropriate position for a window.
- wxPoint theWxOrigin;
- // Working in Cocoa's screen coordinates we must realize that the x coordinate we want is
- // the distance between the left side (origin.x) of the window's frame and the left side of
- // screen zero's frame.
- theWxOrigin.x = windowFrame.origin.x - m_screenZeroFrame.origin.x;
- // Working in Cocoa's screen coordinates we must realize that the y coordinate we want is
- // actually the distance between the top-left of the screen zero frame and the top-left
- // of the window's frame.
- theWxOrigin.y = (m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height) - (windowFrame.origin.y + windowFrame.size.height);
- return theWxOrigin;
- }
- inline NSPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
- {
- NSPoint theCocoaOrigin;
- // The position is in wx screen coordinates which we're going to arbitrarily define such that
- // (0,0) is the TOP-left of screen 0 (the one with the menubar)
- // NOTE: The usable rectangle is smaller and hence we have the keepOriginVisible flag
- // which will move the origin downward and/or left as necessary if the origin is
- // inside the screen0 rectangle (i.e. x/y >= 0 in wx coordinates) and outside the
- // visible frame (i.e. x/y < the top/left of the screen0 visible frame in wx coordinates)
- // We don't munge origin coordinates < 0 because it actually is possible that the menubar is on
- // the top of the bottom screen and thus that origin is completely valid!
- if(keepOriginVisible && (m_screenZero != nil))
- {
- // Do al of this in wx coordinates because it's far simpler since we're dealing with top/left points
- wxPoint visibleOrigin = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates([m_screenZero visibleFrame]);
- if(x >= 0 && x < visibleOrigin.x)
- x = visibleOrigin.x;
- if(y >= 0 && y < visibleOrigin.y)
- y = visibleOrigin.y;
- }
- // The x coordinate is simple as it's just relative to screen zero's frame
- theCocoaOrigin.x = m_screenZeroFrame.origin.x + x;
- // Working in Cocoa's coordinates think to start at the bottom of screen zero's frame and add
- // the height of that rect which gives us the coordinate for the top of the visible rect. Now realize that
- // the wx coordinates are flipped so if y is say 10 then we want to be 10 pixels down from that and thus
- // we subtract y. But then we still need to take into account the size of the window which is h and subtract
- // that to get the bottom-left origin of the rectangle.
- theCocoaOrigin.y = m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height - y - height;
- return theCocoaOrigin;
- }
- } // namespace
- wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
- {
- wxCocoaPrivateScreenCoordinateTransformer transformer;
- return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
- }
- NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
- {
- wxCocoaPrivateScreenCoordinateTransformer transformer;
- return transformer.OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,keepOriginVisible);
- }
- // ========================================================================
- // wxWindowCocoaHider
- // ========================================================================
- class wxWindowCocoaHider: protected wxCocoaNSView
- {
- wxDECLARE_NO_COPY_CLASS(wxWindowCocoaHider);
- public:
- wxWindowCocoaHider(wxWindow *owner);
- virtual ~wxWindowCocoaHider();
- inline WX_NSView GetNSView() { return m_dummyNSView; }
- protected:
- wxWindowCocoa *m_owner;
- WX_NSView m_dummyNSView;
- virtual void Cocoa_FrameChanged(void);
- virtual void Cocoa_synthesizeMouseMoved(void) {}
- #ifdef WXCOCOA_FILL_DUMMY_VIEW
- virtual bool Cocoa_drawRect(const NSRect& rect);
- #endif //def WXCOCOA_FILL_DUMMY_VIEW
- private:
- wxWindowCocoaHider();
- };
- // ========================================================================
- // wxDummyNSView
- // ========================================================================
- @interface wxDummyNSView : NSView
- - (NSView *)hitTest:(NSPoint)aPoint;
- @end
- WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
- @implementation wxDummyNSView : NSView
- - (NSView *)hitTest:(NSPoint)aPoint
- {
- return nil;
- }
- @end
- 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)
- {
- wxASSERT(owner);
- wxASSERT(owner->GetNSViewForHiding());
- m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
- 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()
- {
- DisassociateNSView(m_dummyNSView);
- [m_dummyNSView release];
- }
- void wxWindowCocoaHider::Cocoa_FrameChanged(void)
- {
- // Keep the real window in synch with the dummy
- wxASSERT(m_dummyNSView);
- [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
- }
- #ifdef WXCOCOA_FILL_DUMMY_VIEW
- bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
- {
- NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
- [[NSColor greenColor] set];
- [bezpath stroke];
- [bezpath fill];
- return true;
- }
- #endif //def WXCOCOA_FILL_DUMMY_VIEW
- /*! @class WXManualScrollView
- @abstract An NSScrollView subclass which implements wx scroll behaviour
- @discussion
- 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
- document view. This must be done manually using the ScrollWindow
- method of wxWindow.
- */
- @interface WXManualScrollView : NSScrollView
- {
- /*! @field m_wxCocoaScrollView
- */
- wxWindowCocoaScrollView *m_wxCocoaScrollView;
- }
- // Override these to set up the target/action correctly
- - (void)setHorizontalScroller:(NSScroller *)aScroller;
- - (void)setVerticalScroller:(NSScroller *)aScroller;
- - (void)setHasHorizontalScroller:(BOOL)flag;
- - (void)setHasVerticalScroller:(BOOL)flag;
- // NOTE: _wx_ prefix means "private" method like _ that Apple (and only Apple) uses.
- - (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView;
- - (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView;
- /*! @method _wx_doScroller
- @abstract Handles action messages from the scrollers
- @discussion
- Similar to Apple's _doScroller: method which is private and not documented.
- We do not, however, call that method. Instead, we effectively override
- it entirely. We don't override it by naming ourself the same thing because
- the base class code may or may not call that method for other reasons we
- simply cannot know about.
- */
- - (void)_wx_doScroller:(id)sender;
- @end
- @implementation WXManualScrollView : NSScrollView
- static inline void WXManualScrollView_DoSetScrollerTargetAction(WXManualScrollView *self, NSScroller *aScroller)
- {
- if(aScroller != NULL && [self _wx_wxCocoaScrollView] != NULL)
- {
- [aScroller setTarget:self];
- [aScroller setAction:@selector(_wx_doScroller:)];
- }
- }
- - (void)setHorizontalScroller:(NSScroller *)aScroller
- {
- [super setHorizontalScroller:aScroller];
- WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
- }
- - (void)setVerticalScroller:(NSScroller *)aScroller
- {
- [super setVerticalScroller:aScroller];
- WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
- }
- - (void)setHasHorizontalScroller:(BOOL)flag
- {
- [super setHasHorizontalScroller:flag];
- WXManualScrollView_DoSetScrollerTargetAction(self, [self horizontalScroller]);
- }
- - (void)setHasVerticalScroller:(BOOL)flag
- {
- [super setHasVerticalScroller:flag];
- WXManualScrollView_DoSetScrollerTargetAction(self, [self verticalScroller]);
- }
- - (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView
- { return m_wxCocoaScrollView; }
- - (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView
- {
- m_wxCocoaScrollView = theWxScrollView;
- [self setHorizontalScroller:[self horizontalScroller]];
- [self setVerticalScroller:[self verticalScroller]];
- }
- - (void)_wx_doScroller:(id)sender
- {
- if(m_wxCocoaScrollView != NULL)
- m_wxCocoaScrollView->_wx_doScroller(sender);
- else
- wxLogError(wxT("Unexpected action message received from NSScroller"));
- }
- - (void)reflectScrolledClipView:(NSClipView *)aClipView
- {
- struct _ScrollerBackup
- {
- _ScrollerBackup(NSScroller *aScroller)
- : m_scroller(aScroller)
- , m_floatValue(aScroller!=nil?[aScroller floatValue]:0.0)
- , m_knobProportion(aScroller!=nil?[aScroller knobProportion]:1.0)
- , m_isEnabled(aScroller!=nil?[aScroller isEnabled]:false)
- {
- }
- NSScroller *m_scroller;
- CGFloat m_floatValue;
- CGFloat m_knobProportion;
- BOOL m_isEnabled;
- ~_ScrollerBackup()
- {
- if(m_scroller != nil)
- {
- [m_scroller setFloatValue:m_floatValue knobProportion:m_knobProportion];
- [m_scroller setEnabled:m_isEnabled];
- }
- }
- private:
- _ScrollerBackup();
- _ScrollerBackup(_ScrollerBackup const&);
- _ScrollerBackup& operator=(_ScrollerBackup const&);
- };
- _ScrollerBackup _horizontalBackup([self horizontalScroller]);
- _ScrollerBackup _verticalBackup([self verticalScroller]);
- // We MUST call super's implementation or else nothing seems to work right at all.
- // However, we need our scrollers not to change values due to the document window
- // moving so we cheat and save/restore their values across this call.
- [super reflectScrolledClipView: aClipView];
- }
- @end
- WX_IMPLEMENT_GET_OBJC_CLASS(WXManualScrollView,NSScrollView)
- // ========================================================================
- // wxFlippedNSClipView
- // ========================================================================
- @interface wxFlippedNSClipView : NSClipView
- - (BOOL)isFlipped;
- @end
- WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
- @implementation wxFlippedNSClipView : NSClipView
- - (BOOL)isFlipped
- {
- return YES;
- }
- @end
- WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
- // ========================================================================
- // wxWindowCocoaScrollView
- // ========================================================================
- wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
- : m_owner(owner)
- , m_cocoaNSScrollView() // nil
- , m_scrollRange() // {0,0}
- , m_scrollThumb() // {0,0}
- , m_virtualOrigin(0,0)
- {
- wxAutoNSAutoreleasePool pool;
- wxASSERT(owner);
- wxASSERT(owner->GetNSView());
- m_isNativeView = ![owner->GetNSView() isKindOfClass:[WX_GET_OBJC_CLASS(WXNSView) class]];
- m_cocoaNSScrollView = [(m_isNativeView?[NSScrollView alloc]:[WXManualScrollView alloc])
- initWithFrame:[owner->GetNSView() frame]];
- AssociateNSView(m_cocoaNSScrollView);
- if(m_isNativeView)
- {
- /* Set a bezel border around the entire thing because it looks funny without it.
- TODO: Be sure to undo any borders on the real view (if any) and apply them
- to this view if necessary. Right now, there is no border support in wxCocoa
- so this isn't an issue.
- */
- [m_cocoaNSScrollView setBorderType:NSBezelBorder];
- }
- else
- {
- [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView: this];
- // Don't set a bezel because we might be creating a scroll view due to being
- // the "target window" of a wxScrolledWindow. That is to say that the user
- // has absolutely no intention of scrolling the clip view used by this
- // NSScrollView.
- }
- /* Replace the default NSClipView with a flipped one. This ensures
- scrolling is "pinned" to the top-left instead of bottom-right. */
- NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
- initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
- [m_cocoaNSScrollView setContentView:flippedClip];
- [flippedClip release];
- // In all cases we must encapsulate the real NSView properly
- Encapsulate();
- }
- void wxWindowCocoaScrollView::Encapsulate()
- {
- // Set the scroll view autoresizingMask to match the current NSView
- [m_cocoaNSScrollView setAutoresizingMask: [m_owner->GetNSView() autoresizingMask]];
- [m_owner->GetNSView() setAutoresizingMask: NSViewNotSizable];
- // NOTE: replaceSubView will cause m_cocoaNSView to be released
- // except when it hasn't been added into an NSView hierarchy in which
- // case it doesn't need to be and this should work out to a no-op
- m_owner->CocoaReplaceView(m_owner->GetNSView(), m_cocoaNSScrollView);
- // The NSView is still retained by owner
- [m_cocoaNSScrollView setDocumentView: m_owner->GetNSView()];
- // Now it's also retained by the NSScrollView
- }
- void wxWindowCocoaScrollView::Unencapsulate()
- {
- [m_cocoaNSScrollView setDocumentView: nil];
- m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
- if(![[m_owner->GetNSView() superview] isFlipped])
- [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
- }
- wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
- {
- DisassociateNSView(m_cocoaNSScrollView);
- if(!m_isNativeView)
- {
- [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView:NULL];
- }
- [m_cocoaNSScrollView release];
- }
- void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
- {
- NSSize frameSize = [NSScrollView
- frameSizeForContentSize: NSMakeSize(width,height)
- hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
- hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
- borderType: [m_cocoaNSScrollView borderType]];
- width = (int)frameSize.width;
- height = (int)frameSize.height;
- }
- void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
- {
- NSSize nssize = [m_cocoaNSScrollView contentSize];
- if(x)
- *x = (int)nssize.width;
- if(y)
- *y = (int)nssize.height;
- }
- static inline void SetCocoaScroller(NSScroller *aScroller, int WXUNUSED(orientation), int position, int thumbSize, int range)
- {
- wxCHECK_RET(aScroller != nil, wxT("Expected the NSScrollView to have a scroller"));
- // NOTE: thumbSize is already ensured to be >= 1 and <= range by our caller
- // unless range = 0 in which case we shouldn't have been be called.
- wxCHECK_RET(range > 0, wxT("Internal wxCocoa bug: shouldn't have been called with 0 range"));
- // Range of valid position values is from 0 to effectiveRange
- // NOTE: if thumbSize == range then effectiveRange is 0.
- // thumbSize is at least 1 which gives range from 0 to range - 1 inclusive
- // which is exactly what we want.
- int effectiveRange = range - thumbSize;
- // knobProportion is hopefully easy to understand
- // Note that thumbSize is already guaranteed >= 1 by our caller.
- CGFloat const knobProportion = CGFloat(thumbSize)/CGFloat(range);
- // NOTE: When effectiveRange is zero there really is no valid position
- // We arbitrarily pick 0.0 which is the same as a scroller in the home position.
- CGFloat const floatValue = (effectiveRange != 0)?CGFloat(position)/CGFloat(effectiveRange):0.0;
- [aScroller setFloatValue:floatValue knobProportion: knobProportion];
- // Make sure it's visibly working
- [aScroller setEnabled:YES];
- }
- void wxWindowCocoaScrollView::SetScrollPos(wxOrientation orientation, int position)
- {
- // NOTE: Rather than using only setFloatValue: (which we could do) we instead
- // simply share the SetCocoaScroller call because all but the knobProportion
- // calculations have to be done anyway.
- if(orientation & wxHORIZONTAL)
- {
- NSScroller *aScroller = [m_cocoaNSScrollView horizontalScroller];
- if(aScroller != nil)
- SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[0], m_scrollRange[0]);
- }
- if(orientation & wxVERTICAL)
- {
- NSScroller *aScroller = [m_cocoaNSScrollView verticalScroller];
- if(aScroller != nil)
- SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[1], m_scrollRange[1]);
- }
- }
- void wxWindowCocoaScrollView::SetScrollbar(int orientation, int position, int thumbSize, int range)
- {
- // FIXME: API assumptions:
- // 1. If the user wants to remove a scroller he gives range 0.
- // 2. If the user wants to disable a scroller he sets thumbSize == range
- // in which case it is logically impossible to scroll.
- // The scroller shall still be displayed.
- // Ensure that range is >= 0.
- wxASSERT(range >= 0);
- if(range < 0)
- range = 0;
- // Ensure that thumbSize <= range
- wxASSERT(thumbSize <= range);
- // Also ensure thumbSize >= 1 but don't complain if it isn't
- if(thumbSize < 1)
- thumbSize = 1;
- // Now make sure it's really less than range, even if we just set it to 1
- if(thumbSize > range)
- thumbSize = range;
- bool needScroller = (range != 0);
- // Can potentially set both horizontal and vertical at the same time although this is
- // probably not very useful.
- if(orientation & wxHORIZONTAL)
- {
- m_scrollRange[0] = range;
- m_scrollThumb[0] = thumbSize;
- if(!m_isNativeView)
- {
- [m_cocoaNSScrollView setHasHorizontalScroller:needScroller];
- if(needScroller)
- SetCocoaScroller([m_cocoaNSScrollView horizontalScroller], orientation, position, thumbSize, range);
- }
- }
- if(orientation & wxVERTICAL)
- {
- m_scrollRange[1] = range;
- m_scrollThumb[1] = thumbSize;
- if(!m_isNativeView)
- {
- [m_cocoaNSScrollView setHasVerticalScroller:needScroller];
- if(needScroller)
- SetCocoaScroller([m_cocoaNSScrollView verticalScroller], orientation, position, thumbSize, range);
- }
- }
- }
- int wxWindowCocoaScrollView::GetScrollPos(wxOrientation orient)
- {
- if((orient & wxBOTH) == wxBOTH)
- {
- wxLogError(wxT("GetScrollPos called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
- return 0;
- }
- int effectiveScrollRange;
- NSScroller *cocoaScroller;
- if(orient & wxHORIZONTAL)
- {
- effectiveScrollRange = m_scrollRange[0] - m_scrollThumb[0];
- cocoaScroller = [m_cocoaNSScrollView horizontalScroller];
- }
- else if(orient & wxVERTICAL)
- {
- effectiveScrollRange = m_scrollRange[1] - m_scrollThumb[1];
- cocoaScroller = [m_cocoaNSScrollView verticalScroller];
- }
- else
- {
- wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
- return 0;
- }
- if(cocoaScroller == nil)
- { // Document is not scrolled
- return 0;
- }
- /*
- The effective range of a scroll bar as defined by wxWidgets is from 0 to (range - thumbSize).
- That is a scroller at the left/top position is at 0 and a scroller at the bottom/right
- 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 actually
- in contrast to NSSlider which generally has 1.0 at the TOP when it's done vertically.
- */
- CGFloat cocoaScrollPos = [cocoaScroller floatValue];
- return effectiveScrollRange * cocoaScrollPos;
- }
- int wxWindowCocoaScrollView::GetScrollRange(wxOrientation orient)
- {
- if((orient & wxBOTH) == wxBOTH)
- {
- wxLogError(wxT("GetScrollRange called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
- return 0;
- }
- if(orient & wxHORIZONTAL)
- {
- return m_scrollRange[0];
- }
- else if(orient & wxVERTICAL)
- {
- return m_scrollRange[1];
- }
- else
- {
- wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
- return 0;
- }
- }
- int wxWindowCocoaScrollView::GetScrollThumb(wxOrientation orient)
- {
- if((orient & wxBOTH) == wxBOTH)
- {
- wxLogError(wxT("GetScrollThumb called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
- return 0;
- }
- if(orient & wxHORIZONTAL)
- {
- return m_scrollThumb[0];
- }
- else if(orient & wxVERTICAL)
- {
- return m_scrollThumb[1];
- }
- else
- {
- wxLogError(wxT("GetScrollThumb called without an orientation which makes no sense"));
- return 0;
- }
- }
- /*!
- Moves the contents (all existing drawing as well as all all child wxWindow) by the specified
- amount expressed in the wxWindow's own coordinate system. This is used to implement scrolling
- but the usage is rather interesting. When scrolling right (e.g. increasing the value of
- the scroller) you must give a negative delta x (e.g. moving the contents LEFT). Likewise,
- when scrolling the window down, increasing the value of the scroller, you give a negative
- delta y which moves the contents up.
- wxCocoa notes: To accomplish this trick in Cocoa we basically do what NSScrollView would
- have done and that is adjust the content view's bounds origin. The content view is somewhat
- confusingly the NSClipView which is more or less sort of the pImpl for NSScrollView
- The real NSView with the user's content (e.g. the "virtual area" in wxWidgets parlance)
- is called the document view in NSScrollView parlance.
- The bounds origin is basically the exact opposite concept. Whereas in Windows the client
- coordinate system remains constant and the content must shift left/up for increases
- of scrolling, in Cocoa the coordinate system is actually the virtual one. So we must
- instead shift the bounds rectangle right/down to get the effect of the content moving
- left/up. Basically, it's a higher level interface than that provided by wxWidgets
- so essentially we're implementing the low-level move content interface in terms of
- the high-level move the viewport (the bounds) over top that content (the document
- view which is the virtual area to wx).
- For all intents and purposes that basically just means that we subtract the deltas
- from the bounds origin and thus a negative delta actually increases the bounds origin
- and a positive delta actually decreases it. This is absolutely true for the horizontal
- axis but there's a catch in the vertical axis. If the content view (the clip view) is
- flipped (and we do this by default) then it works exactly like the horizontal axis.
- If it is not flipped (i.e. it is in postscript coordinates which are opposite to
- wxWidgets) then the sense needs to be reversed.
- However, this plays hell with window positions. The frame rects of any child views
- do not change origin and this is actually important because if they did, the views
- would send frame changed notifications, not to mention that Cocoa just doesn't really
- do scrolling that way, it does it the way we do it here.
- To fix this we implement GetPosition for child windows to not merely consult its
- superview at the Cocoa level in order to do proper Cocoa->wx coordinate transform
- but to actually consult is parent wxWindow because it makes a big difference if
- the parent is scrolled. Argh. (FIXME: This isn't actually implemented yet)
- */
- void wxWindowCocoaScrollView::ScrollWindow(int dx, int dy, const wxRect*)
- {
- // Update our internal origin so we know how much the application code
- // expects us to have been scrolled.
- m_virtualOrigin.x += dx;
- m_virtualOrigin.y += dy;
- // Scroll the window using the standard Cocoa method of adjusting the
- // clip view's bounds in the opposite fashion.
- NSClipView *contentView = [m_cocoaNSScrollView contentView];
- NSRect clipViewBoundsRect = [contentView bounds];
- clipViewBoundsRect.origin.x -= dx;
- if([contentView isFlipped])
- clipViewBoundsRect.origin.y -= dy;
- else
- clipViewBoundsRect.origin.y += dy;
- [contentView scrollToPoint:clipViewBoundsRect.origin];
- }
- void wxWindowCocoaScrollView::_wx_doScroller(NSScroller *sender)
- {
- wxOrientation orientation;
- if(sender == [m_cocoaNSScrollView horizontalScroller])
- orientation = wxHORIZONTAL;
- else if(sender == [m_cocoaNSScrollView verticalScroller])
- orientation = wxVERTICAL;
- else
- {
- wxLogDebug(wxT("Received action message from unexpected NSScroller"));
- return;
- }
- // NOTE: Cocoa does not move the scroller for page up/down or line
- // up/down events. That means the value will be the old value.
- // For thumbtrack events, the value is the new value.
- int scrollpos = GetScrollPos(orientation);
- int commandType;
- switch([sender hitPart])
- {
- default:
- case NSScrollerNoPart:
- case NSScrollerKnob: // Drag of knob
- case NSScrollerKnobSlot: // Jump directly to position
- commandType = wxEVT_SCROLLWIN_THUMBTRACK;
- break;
- case NSScrollerDecrementPage:
- commandType = wxEVT_SCROLLWIN_PAGEUP;
- break;
- case NSScrollerIncrementPage:
- commandType = wxEVT_SCROLLWIN_PAGEDOWN;
- break;
- case NSScrollerDecrementLine:
- commandType = wxEVT_SCROLLWIN_LINEUP;
- break;
- case NSScrollerIncrementLine:
- commandType = wxEVT_SCROLLWIN_LINEDOWN;
- break;
- }
- wxScrollWinEvent event(commandType, scrollpos, orientation);
- event.SetEventObject(m_owner);
- m_owner->HandleWindowEvent(event);
- }
- void wxWindowCocoaScrollView::UpdateSizes()
- {
- // Using the virtual size, figure out what the document frame size should be
- // NOTE: Assume that the passed in virtualSize is already >= the client size
- wxSize virtualSize = m_owner->GetVirtualSize();
- // Get the document's current frame
- NSRect documentViewFrame = [m_owner->GetNSView() frame];
- NSRect newFrame = documentViewFrame;
- newFrame.size = NSMakeSize(virtualSize.x, virtualSize.y);
- if(!NSEqualRects(newFrame, documentViewFrame))
- {
- [m_owner->GetNSView() setFrame:newFrame];
- }
- }
- void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
- {
- 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->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();
- }
- // ========================================================================
- // wxWindowCocoa
- // ========================================================================
- // normally the base classes aren't included, but wxWindow is special
- #ifdef __WXUNIVERSAL__
- IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
- #endif
- BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
- END_EVENT_TABLE()
- wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
- // Constructor
- void wxWindowCocoa::Init()
- {
- m_cocoaNSView = NULL;
- m_cocoaHider = NULL;
- m_wxCocoaScrollView = NULL;
- m_isInPaint = false;
- m_visibleTrackingRectManager = NULL;
- }
- // Constructor
- bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
- const wxPoint& pos,
- const wxSize& size,
- long style,
- const wxString& name)
- {
- if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
- return false;
- // TODO: create the window
- m_cocoaNSView = NULL;
- SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
- [m_cocoaNSView release];
- if (m_parent)
- {
- m_parent->AddChild(this);
- m_parent->CocoaAddChild(this);
- SetInitialFrameRect(pos,size);
- }
- return true;
- }
- // Destructor
- wxWindow::~wxWindow()
- {
- wxAutoNSAutoreleasePool pool;
- DestroyChildren();
- // Make sure our parent (in the wxWidgets sense) is our superview
- // before we go removing from it.
- if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
- CocoaRemoveFromParent();
- delete m_cocoaHider;
- delete m_wxCocoaScrollView;
- if(m_cocoaNSView)
- SendDestroyEvent();
- SetNSView(NULL);
- }
- void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
- {
- // Pool here due to lack of one during wx init phase
- wxAutoNSAutoreleasePool pool;
- NSView *childView = child->GetNSViewForSuperview();
- wxASSERT(childView);
- [m_cocoaNSView addSubview: childView];
- }
- void wxWindowCocoa::CocoaRemoveFromParent(void)
- {
- [GetNSViewForSuperview() removeFromSuperview];
- }
- void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
- {
- // Clear the visible area tracking rect if we have one.
- delete m_visibleTrackingRectManager;
- m_visibleTrackingRectManager = NULL;
- 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);
- 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_NSView wxWindowCocoa::GetNSViewForSuperview() const
- {
- return m_cocoaHider
- ? m_cocoaHider->GetNSView()
- : m_wxCocoaScrollView
- ? m_wxCocoaScrollView->GetNSScrollView()
- : m_cocoaNSView;
- }
- WX_NSView wxWindowCocoa::GetNSViewForHiding() const
- {
- return m_wxCocoaScrollView
- ? m_wxCocoaScrollView->GetNSScrollView()
- : m_cocoaNSView;
- }
- NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
- {
- // TODO: Handle scrolling offset
- return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
- }
- NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
- {
- // TODO: Handle scrolling offset
- return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
- }
- NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
- {
- // TODO: Handle scrolling offset
- return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
- }
- NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
- {
- // TODO: Handle scrolling offset
- return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
- }
- WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
- {
- // TODO: Handle scrolling offset
- NSAffineTransform *transform = wxCocoaDCImpl::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
- return transform;
- }
- bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
- {
- wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
- // Recursion can happen if the event loop runs from within the paint
- // handler. For instance, if an assertion dialog is shown.
- // FIXME: This seems less than ideal.
- if(m_isInPaint)
- {
- wxLogDebug(wxT("Paint event recursion!"));
- return false;
- }
- m_isInPaint = true;
- // Set m_updateRegion
- const NSRect *rects = ▭ // The bounding box of the region
- NSInteger countRects = 1;
- // Try replacing the larger rectangle with a list of smaller ones:
- if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
- [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
- NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
- for(int i=0; i<countRects; i++)
- {
- transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
- }
- m_updateRegion = wxRegion(transformedRects,countRects);
- free(transformedRects);
- wxPaintEvent event(m_windowId);
- event.SetEventObject(this);
- bool ret = HandleWindowEvent(event);
- m_isInPaint = false;
- return ret;
- }
- void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
- {
- wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
- // 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();
- event.m_x = (wxCoord)pointWx.x - clientorigin.x;
- event.m_y = (wxCoord)pointWx.y - clientorigin.y;
- event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
- event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
- event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
- event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
- // TODO: set timestamp?
- event.SetEventObject(this);
- event.SetId(GetId());
- }
- bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
- {
- 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 HandleWindowEvent(event);
- }
- void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
- {
- wxMouseEvent event(wxEVT_MOTION);
- NSWindow *window = [GetNSView() window];
- NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
- NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
- NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
- // FIXME: Should we be adjusting for client area origin?
- const wxPoint &clientorigin = GetClientAreaOrigin();
- event.m_x = (wxCoord)pointWx.x - clientorigin.x;
- event.m_y = (wxCoord)pointWx.y - clientorigin.y;
- // TODO: Handle shift, control, alt, meta flags
- event.SetEventObject(this);
- event.SetId(GetId());
- wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
- HandleWindowEvent(event);
- }
- bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
- {
- if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
- {
- m_visibleTrackingRectManager->BeginSynthesizingEvents();
- // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
- // some other event comes in. That other event is (guess what) mouse moved events that will be sent
- // to the NSWindow which will forward them on to the first responder. We are not likely to be the
- // first responder, so the mouseMoved: events are effectively discarded.
- [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
- 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 HandleWindowEvent(event);
- }
- else
- return false;
- }
- bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
- {
- if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
- {
- m_visibleTrackingRectManager->StopSynthesizingEvents();
- 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 HandleWindowEvent(event);
- }
- else
- return false;
- }
- bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
- {
- 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 HandleWindowEvent(event);
- }
- bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
- {
- wxMouseEvent event(wxEVT_MOTION);
- InitMouseEvent(event,theEvent);
- event.m_leftDown = true;
- wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
- 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 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 HandleWindowEvent(event);
- }
- bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
- {
- wxMouseEvent event(wxEVT_MOTION);
- InitMouseEvent(event,theEvent);
- event.m_rightDown = true;
- wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
- 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 HandleWindowEvent(event);
- }
- bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
- {
- return false;
- }
- bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
- {
- return false;
- }
- bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
- {
- return false;
- }
- void wxWindowCocoa::Cocoa_FrameChanged(void)
- {
- // We always get this message for the real NSView which may have been
- // enclosed in an NSScrollView. If that's the case then what we're
- // effectively getting here is a notifcation that the
- // virtual sized changed.. which we don't need to send on since
- // wx has no concept of this whatsoever.
- bool isViewForSuperview = (m_wxCocoaScrollView == NULL);
- if(isViewForSuperview)
- {
- wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
- if(m_visibleTrackingRectManager != NULL)
- m_visibleTrackingRectManager->RebuildTrackingRect();
- wxSizeEvent event(GetSize(), m_windowId);
- event.SetEventObject(this);
- HandleWindowEvent(event);
- }
- else
- {
- wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged ignored"),this);
- }
- }
- bool wxWindowCocoa::Cocoa_resetCursorRects()
- {
- wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
- // When we are called there may be a queued tracking rect event (mouse entered or exited) and
- // we won't know it. A specific example is wxGenericHyperlinkCtrl changing the cursor from its
- // mouse exited event. If the control happens to share the edge with its parent window which is
- // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
- // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
- // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
- // which willl in turn call us before exiting for the next user event.
- // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
- // our mouse exited event because the already queued event will have the old tracking rect
- // tag. The simple solution is to only rebuild our tracking rect if we need to.
- if(m_visibleTrackingRectManager != NULL)
- m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
- if(!m_cursor.GetNSCursor())
- return false;
- [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
- return true;
- }
- bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
- {
- if(!wxWindowBase::SetCursor(cursor))
- return false;
- // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
- // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
- // rects and thus won't ever call resetCursorRects.
- [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
- // Invalidate the cursor rects so the cursor will change
- // Note that it is not enough to remove the old one (if any) and add the new one.
- // For the rects to work properly, Cocoa itself must call resetCursorRects.
- [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
- return true;
- }
- bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
- {
- wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
- // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
- if(m_visibleTrackingRectManager != NULL)
- m_visibleTrackingRectManager->BuildTrackingRect();
- return false;
- }
- bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
- {
- wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
- // Clear tracking rects. It is imperative this be done before the new window is set.
- if(m_visibleTrackingRectManager != NULL)
- m_visibleTrackingRectManager->ClearTrackingRect();
- return false;
- }
- bool wxWindow::Close(bool force)
- {
- // The only reason this function exists is that it is virtual and
- // wxTopLevelWindowCocoa will override it.
- return wxWindowBase::Close(force);
- }
- void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
- {
- [[oldView superview] replaceSubview:oldView with:newView];
- }
- void wxWindow::DoEnable(bool enable)
- {
- CocoaSetEnabled(enable);
- }
- bool wxWindow::Show(bool show)
- {
- wxAutoNSAutoreleasePool pool;
- // If the window is marked as visible, then it shouldn't have a dummy view
- // If the window is marked hidden, then it should have a dummy view
- // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
- // wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
- // Return false if there isn't a window to show or hide
- NSView *cocoaView = GetNSViewForHiding();
- if(!cocoaView)
- return false;
- if(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_cocoaNSView to be
- // (auto)released which balances out addSubview
- CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
- // m_cocoaNSView is now only retained by us
- wxASSERT([m_cocoaHider->GetNSView() superview]);
- wxASSERT(![cocoaView superview]);
- }
- m_isShown = show;
- return true;
- }
- void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
- {
- 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":".");
- int currentX, currentY;
- int currentW, currentH;
- DoGetPosition(¤tX, ¤tY);
- DoGetSize(¤tW, ¤tH);
- if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
- x=currentX;
- if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
- y=currentY;
- AdjustForParentClientOrigin(x,y,sizeFlags);
- wxSize size(wxDefaultSize);
- if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
- {
- if(sizeFlags&wxSIZE_AUTO_WIDTH)
- {
- size=DoGetBestSize();
- width=size.x;
- }
- else
- width=currentW;
- }
- if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
- {
- if(sizeFlags&wxSIZE_AUTO_HEIGHT)
- {
- if(size.x==-1)
- size=DoGetBestSize();
- height=size.y;
- }
- else
- height=currentH;
- }
- DoMoveWindow(x,y,width,height);
- }
- #if wxUSE_TOOLTIPS
- void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
- {
- wxWindowBase::DoSetToolTip(tip);
- if ( m_tooltip )
- {
- m_tooltip->SetWindow((wxWindow *)this);
- }
- }
- #endif
- void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
- {
- wxAutoNSAutoreleasePool pool;
- wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
- NSView *nsview = GetNSViewForSuperview();
- NSView *superview = [nsview superview];
- wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
- NSRect oldFrameRect = [nsview frame];
- NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
- [nsview setFrame:newFrameRect];
- // Be sure to redraw the parent to reflect the changed position
- [superview setNeedsDisplayInRect:oldFrameRect];
- [superview setNeedsDisplayInRect:newFrameRect];
- }
- void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
- {
- NSView *nsview = GetNSViewForSuperview();
- NSView *superview = [nsview superview];
- wxCHECK_RET(superview,wxT("NSView does not have a superview"));
- wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
- NSRect frameRect = [nsview frame];
- if(size.x!=-1)
- frameRect.size.width = size.x;
- if(size.y!=-1)
- frameRect.size.height = size.y;
- frameRect.origin.x = pos.x;
- frameRect.origin.y = pos.y;
- // Tell Cocoa to change the margin between the bottom of the superview
- // and the bottom of the control. Keeps the control pinned to the top
- // of its superview so that its position in the wxWidgets coordinate
- // system doesn't change.
- if(![superview isFlipped])
- [nsview setAutoresizingMask: NSViewMinYMargin];
- // MUST set the mask before setFrame: which can generate a size event
- // and cause a scroller to be added!
- frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
- [nsview setFrame: frameRect];
- }
- // Get total size
- void wxWindow::DoGetSize(int *w, int *h) const
- {
- NSRect cocoaRect = [GetNSViewForSuperview() frame];
- if(w)
- *w=(int)cocoaRect.size.width;
- if(h)
- *h=(int)cocoaRect.size.height;
- wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
- }
- void wxWindow::DoGetPosition(int *x, int *y) const
- {
- NSView *nsview = GetNSViewForSuperview();
- NSRect cocoaRect = [nsview frame];
- NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
- if(x)
- *x=(int)rectWx.origin.x;
- if(y)
- *y=(int)rectWx.origin.y;
- wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
- }
- WXWidget wxWindow::GetHandle() const
- {
- return m_cocoaNSView;
- }
- wxWindow* wxWindow::GetWxWindow() const
- {
- return (wxWindow*) this;
- }
- void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
- {
- [m_cocoaNSView setNeedsDisplay:YES];
- }
- void wxWindow::SetFocus()
- {
- if([GetNSView() acceptsFirstResponder])
- [[GetNSView() window] makeFirstResponder: GetNSView()];
- }
- void wxWindow::DoCaptureMouse()
- {
- // TODO
- sm_capturedWindow = this;
- }
- void wxWindow::DoReleaseMouse()
- {
- // TODO
- sm_capturedWindow = NULL;
- }
- void wxWindow::DoScreenToClient(int *x, int *y) const
- {
- // Point in cocoa screen coordinates:
- NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
- NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
- NSWindow *theWindow = [clientView window];
- // Point in window's base coordinate system:
- NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
- // Point in view's bounds coordinate system
- NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
- // Point in wx client coordinates:
- NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
- if(x!=NULL)
- *x = theWxClientPoint.x;
- if(y!=NULL)
- *y = theWxClientPoint.y;
- }
- void wxWindow::DoClientToScreen(int *x, int *y) const
- {
- // Point in wx client coordinates
- NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
- NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
- // Point in the view's bounds coordinate system
- NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
- // Point in the window's base coordinate system
- NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
- NSWindow *theWindow = [clientView window];
- // Point in Cocoa's screen coordinates
- NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
- // Act as though this was the origin of a 0x0 rectangle
- NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
- // Convert that rectangle to wx coordinates
- wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
- if(*x)
- *x = theWxScreenPoint.x;
- if(*y)
- *y = theWxScreenPoint.y;
- }
- // Get size *available for subwindows* i.e. excluding menu bar etc.
- void wxWindow::DoGetClientSize(int *x, int *y) const
- {
- wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
- if(m_wxCocoaScrollView)
- m_wxCocoaScrollView->DoGetClientSize(x,y);
- else
- wxWindowCocoa::DoGetSize(x,y);
- }
- void wxWindow::DoSetClientSize(int width, int height)
- {
- wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
- if(m_wxCocoaScrollView)
- m_wxCocoaScrollView->ClientSizeToSize(width,height);
- CocoaSetWxWindowSize(width,height);
- }
- void wxWindow::CocoaSetWxWindowSize(int width, int height)
- {
- wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
- }
- void wxWindow::SetLabel(const wxString& WXUNUSED(label))
- {
- // Intentional no-op.
- }
- wxString wxWindow::GetLabel() const
- {
- // General Get/Set of labels is implemented in wxControlBase
- wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
- return wxEmptyString;
- }
- int wxWindow::GetCharHeight() const
- {
- // TODO
- return 10;
- }
- int wxWindow::GetCharWidth() const
- {
- // TODO
- return 5;
- }
- 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
- // 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.
- wxClientDC tmpdc(const_cast<wxWindow*>(this));
- return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
- }
- // Coordinates relative to the window
- void wxWindow::WarpPointer (int x_pos, int y_pos)
- {
- // TODO
- }
- int wxWindow::GetScrollPos(int orient) const
- {
- if(m_wxCocoaScrollView != NULL)
- return m_wxCocoaScrollView->GetScrollPos(static_cast<wxOrientation>(orient & wxBOTH));
- else
- return 0;
- }
- // This now returns the whole range, not just the number
- // of positions that we can scroll.
- int wxWindow::GetScrollRange(int orient) const
- {
- if(m_wxCocoaScrollView != NULL)
- return m_wxCocoaScrollView->GetScrollRange(static_cast<wxOrientation>(orient & wxBOTH));
- else
- return 0;
- }
- int wxWindow::GetScrollThumb(int orient) const
- {
- if(m_wxCocoaScrollView != NULL)
- return m_wxCocoaScrollView->GetScrollThumb(static_cast<wxOrientation>(orient & wxBOTH));
- else
- return 0;
- }
- void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
- {
- if(m_wxCocoaScrollView != NULL)
- return m_wxCocoaScrollView->SetScrollPos(static_cast<wxOrientation>(orient & wxBOTH), pos);
- }
- void wxWindow::CocoaCreateNSScrollView()
- {
- if(!m_wxCocoaScrollView)
- {
- m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
- }
- }
- // New function that will replace some of the above.
- void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
- int range, bool refresh)
- {
- CocoaCreateNSScrollView();
- m_wxCocoaScrollView->SetScrollbar(orient, pos, thumbVisible, range);
- // TODO: Handle refresh (if we even need to)
- }
- // Does a physical scroll
- void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
- {
- if(m_wxCocoaScrollView != NULL)
- m_wxCocoaScrollView->ScrollWindow(dx, dy, rect);
- }
- void wxWindow::DoSetVirtualSize( int x, int y )
- {
- // Call wxWindowBase method which will set m_virtualSize to the appropriate value,
- // possibly not what the caller passed in. For example, the current implementation
- // clamps the width and height to within the min/max virtual ranges.
- // wxDefaultCoord is passed through unchanged but then GetVirtualSize() will correct
- // that by returning effectively max(virtual, client)
- wxWindowBase::DoSetVirtualSize(x,y);
- // Create the scroll view if it hasn't been already.
- CocoaCreateNSScrollView();
- // The GetVirtualSize automatically increases the size to max(client,virtual)
- m_wxCocoaScrollView->UpdateSizes();
- }
- bool wxWindow::SetFont(const wxFont& font)
- {
- // FIXME: We may need to handle wx font inheritance.
- return wxWindowBase::SetFont(font);
- }
- #if 0 // these are used when debugging the algorithm.
- static char const * const comparisonresultStrings[] =
- { "<"
- , "=="
- , ">"
- };
- #endif
- class CocoaWindowCompareContext
- {
- wxDECLARE_NO_COPY_CLASS(CocoaWindowCompareContext);
- public:
- CocoaWindowCompareContext(); // Not implemented
- CocoaWindowCompareContext(NSView *target, NSArray *subviews)
- {
- m_target = target;
- // Cocoa sorts subviews in-place.. make a copy
- m_subviews = [subviews copy];
- }
- ~CocoaWindowCompareContext()
- { // release the copy
- [m_subviews release];
- }
- NSView* target()
- { return m_target; }
- NSArray* subviews()
- { return m_subviews; }
- /* Helper function that returns the comparison based off of the original ordering */
- CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
- {
- NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
- NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
- // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
- // likely compare higher than the other view which is reasonable considering the only way that
- // can happen is if the subview was added after our call to subviews but before the call to
- // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
- // that case should never occur anyway because that would imply a multi-threaded GUI call
- // which is a big no-no with Cocoa.
- // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
- NSComparisonResult result = (firstI < secondI)
- ? NSOrderedAscending /* -1 */
- : (firstI > secondI)
- ? NSOrderedDescending /* 1 */
- : NSOrderedSame /* 0 */;
- #if 0 // Enable this if you need to debug the algorithm.
- NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
- #endif
- return result;
- }
- private:
- /* The subview we are trying to Raise or Lower */
- NSView *m_target;
- /* A copy of the original array of subviews */
- NSArray *m_subviews;
- };
- /* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
- * the target view is always higher than every other view. When comparing two views neither of
- * which is the target, it returns the correct response based on the original ordering
- */
- static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
- {
- CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
- // first should be ordered higher
- if(first==compareContext->target())
- return NSOrderedDescending;
- // second should be ordered higher
- if(second==compareContext->target())
- return NSOrderedAscending;
- return compareContext->CompareUsingOriginalOrdering(first,second);
- }
- // Raise the window to the top of the Z order
- void wxWindow::Raise()
- {
- // wxAutoNSAutoreleasePool pool;
- NSView *nsview = GetNSViewForSuperview();
- NSView *superview = [nsview superview];
- CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
- [superview sortSubviewsUsingFunction:
- CocoaRaiseWindowCompareFunction
- context: &compareContext];
- }
- /* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
- * the target view is always lower than every other view. When comparing two views neither of
- * which is the target, it returns the correct response based on the original ordering
- */
- static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
- {
- CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
- // first should be ordered lower
- if(first==compareContext->target())
- return NSOrderedAscending;
- // second should be ordered lower
- if(second==compareContext->target())
- return NSOrderedDescending;
- return compareContext->CompareUsingOriginalOrdering(first,second);
- }
- // Lower the window to the bottom of the Z order
- void wxWindow::Lower()
- {
- NSView *nsview = GetNSViewForSuperview();
- NSView *superview = [nsview superview];
- CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
- #if 0
- NSLog(@"Target:\n%@\n", nsview);
- NSLog(@"Before:\n%@\n", compareContext.subviews());
- #endif
- [superview sortSubviewsUsingFunction:
- CocoaLowerWindowCompareFunction
- context: &compareContext];
- #if 0
- NSLog(@"After:\n%@\n", [superview subviews]);
- #endif
- }
- bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
- {
- return false;
- }
- // Get the window with the focus
- wxWindow *wxWindowBase::DoFindFocus()
- {
- // Basically we are somewhat emulating the responder chain here except
- // we are only loking for the first responder in the key window or
- // upon failing to find one if the main window is different we look
- // for the first responder in the main window.
- // Note that the firstResponder doesn't necessarily have to be an
- // NSView but wxCocoaNSView::GetFromCocoa() will simply return
- // NULL unless it finds its argument in its hash map.
- wxCocoaNSView *win;
- NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
- win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
- if(win)
- return win->GetWxWindow();
- NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
- if(mainWindow == keyWindow)
- return NULL;
- win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
- if(win)
- return win->GetWxWindow();
- return NULL;
- }
- /* static */ wxWindow *wxWindowBase::GetCapture()
- {
- // TODO
- return wxWindowCocoa::sm_capturedWindow;
- }
- wxWindow *wxGetActiveWindow()
- {
- // TODO
- return NULL;
- }
- wxPoint wxGetMousePosition()
- {
- // TODO
- return wxDefaultPosition;
- }
- wxMouseState wxGetMouseState()
- {
- wxMouseState ms;
- // TODO
- return ms;
- }
- wxWindow* wxFindWindowAtPointer(wxPoint& pt)
- {
- pt = wxGetMousePosition();
- return NULL;
- }
- // ========================================================================
- // wxCocoaMouseMovedEventSynthesizer
- // ========================================================================
- #define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
- /* This class registers one run loop observer to cover all windows registered with it.
- * It will register the observer when the first view is registerd and unregister the
- * observer when the last view is unregistered.
- * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
- * is no reason it couldn't be instantiated multiple times.
- */
- class wxCocoaMouseMovedEventSynthesizer
- {
- wxDECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer);
- public:
- wxCocoaMouseMovedEventSynthesizer()
- { m_lastScreenMouseLocation = NSZeroPoint;
- }
- ~wxCocoaMouseMovedEventSynthesizer();
- void RegisterWxCocoaView(wxCocoaNSView *aView);
- void UnregisterWxCocoaView(wxCocoaNSView *aView);
- void SynthesizeMouseMovedEvent();
-
- protected:
- void AddRunLoopObserver();
- void RemoveRunLoopObserver();
- wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
- std::list<wxCocoaNSView*> m_registeredViews;
- NSPoint m_lastScreenMouseLocation;
- static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
- };
- void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
- {
- m_registeredViews.push_back(aView);
- wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
- if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
- {
- AddRunLoopObserver();
- }
- }
- void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
- {
- m_registeredViews.remove(aView);
- wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
- if(m_registeredViews.empty() && m_runLoopObserver != NULL)
- {
- RemoveRunLoopObserver();
- }
- }
- wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
- {
- if(!m_registeredViews.empty())
- {
- // This means failure to clean up so we report on it as a debug message.
- wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
- m_registeredViews.clear();
- }
- if(m_runLoopObserver != NULL)
- {
- // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
- wxLogDebug(wxT("Removing run loop observer during static destruction time."));
- RemoveRunLoopObserver();
- }
- }
- void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
- {
- reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
- }
- void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
- {
- CFRunLoopObserverContext observerContext =
- { 0
- , this
- , NULL
- , NULL
- , NULL
- };
- // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
- // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
- // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
- // in our observer procedure being called before the run loop has decided that it is going to return control to
- // the Cocoa event loop. One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
- // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
- // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
- // won't be called until the CFRunLoop gets around to it. If the CFRunLoop has not yet exited then it will get
- // around to it before letting the event loop do its work. This has some very odd effects on the way the
- // newly created tracking rects function. In particular, we will often miss the mouseExited: message if the
- // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
- // time the new one is built.
- // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
- // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
- // the mouseExited: message to be sent properly.
- m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
- CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
- }
- void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
- {
- CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
- m_runLoopObserver.reset();
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
- }
- void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
- {
- NSPoint screenMouseLocation = [NSEvent mouseLocation];
- // Checking the last mouse location is done for a few reasons:
- // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
- // telling the app the mouse moved when the user hit a key for instance.
- // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
- // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
- // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
- // loop causing the tracking rectangles to constantly be reset.
- if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
- {
- m_lastScreenMouseLocation = screenMouseLocation;
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
- for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
- {
- (*i)->Cocoa_synthesizeMouseMoved();
- }
- }
- }
- // Singleton used for all views:
- static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
- // ========================================================================
- // wxCocoaTrackingRectManager
- // ========================================================================
- wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
- : m_window(window)
- {
- m_isTrackingRectActive = false;
- BuildTrackingRect();
- }
- void wxCocoaTrackingRectManager::ClearTrackingRect()
- {
- if(m_isTrackingRectActive)
- {
- [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
- m_isTrackingRectActive = false;
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
- }
- // If we were doing periodic events we need to clear those too
- StopSynthesizingEvents();
- }
- void wxCocoaTrackingRectManager::StopSynthesizingEvents()
- {
- s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
- }
- void wxCocoaTrackingRectManager::BuildTrackingRect()
- {
- // Pool here due to lack of one during wx init phase
- wxAutoNSAutoreleasePool pool;
- wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
- NSView *theView = m_window->GetNSView();
- if([theView window] != nil)
- {
- NSRect visibleRect = [theView visibleRect];
- m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
- m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
- m_isTrackingRectActive = true;
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
- }
- }
- void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
- {
- s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
- }
- void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
- {
- if(m_isTrackingRectActive)
- {
- NSView *theView = m_window->GetNSView();
- NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
- if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
- {
- wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
- return;
- }
- }
- RebuildTrackingRect();
- }
- void wxCocoaTrackingRectManager::RebuildTrackingRect()
- {
- ClearTrackingRect();
- BuildTrackingRect();
- }
- wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
- {
- ClearTrackingRect();
- }
- bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
- {
- return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
- }
|