window.mm 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/cocoa/window.mm
  3. // Purpose: wxWindowCocoa
  4. // Author: David Elliott
  5. // Modified by:
  6. // Created: 2002/12/26
  7. // Copyright: (c) 2002 David Elliott
  8. // Licence: wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10. #include "wx/wxprec.h"
  11. #ifndef WX_PRECOMP
  12. #include "wx/log.h"
  13. #include "wx/window.h"
  14. #include "wx/dc.h"
  15. #include "wx/dcclient.h"
  16. #include "wx/utils.h"
  17. #endif //WX_PRECOMP
  18. #include "wx/tooltip.h"
  19. #include "wx/cocoa/dc.h"
  20. #include "wx/cocoa/autorelease.h"
  21. #include "wx/cocoa/string.h"
  22. #include "wx/cocoa/trackingrectmanager.h"
  23. #include "wx/cocoa/private/scrollview.h"
  24. #include "wx/osx/core/cfref.h"
  25. #include "wx/cocoa/ObjcRef.h"
  26. #import <Foundation/NSArray.h>
  27. #import <Foundation/NSRunLoop.h>
  28. #include "wx/cocoa/objc/NSView.h"
  29. #import <AppKit/NSEvent.h>
  30. #import <AppKit/NSScrollView.h>
  31. #import <AppKit/NSScroller.h>
  32. #import <AppKit/NSColor.h>
  33. #import <AppKit/NSClipView.h>
  34. #import <Foundation/NSException.h>
  35. #import <AppKit/NSApplication.h>
  36. #import <AppKit/NSWindow.h>
  37. #import <AppKit/NSScreen.h>
  38. // Turn this on to paint green over the dummy views for debugging
  39. #undef WXCOCOA_FILL_DUMMY_VIEW
  40. #ifdef WXCOCOA_FILL_DUMMY_VIEW
  41. #import <AppKit/NSBezierPath.h>
  42. #endif //def WXCOCOA_FILL_DUMMY_VIEW
  43. // STL list used by wxCocoaMouseMovedEventSynthesizer
  44. #include <list>
  45. /* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
  46. * NSInteger post-Leopard. Pre-Leopard the Cocoa toolkit expects a function
  47. * returning int and not NSComparisonResult. Post-Leopard the Cocoa toolkit
  48. * expects a function returning the new non-enum NSComparsionResult.
  49. * Hence we create a typedef named CocoaWindowCompareFunctionResult.
  50. */
  51. #if defined(NSINTEGER_DEFINED)
  52. typedef NSComparisonResult CocoaWindowCompareFunctionResult;
  53. #else
  54. typedef int CocoaWindowCompareFunctionResult;
  55. #endif
  56. // A category for methods that are only present in Panther's SDK
  57. @interface NSView(wxNSViewPrePantherCompatibility)
  58. - (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;
  59. @end
  60. // ========================================================================
  61. // Helper functions for converting to/from wxWidgets coordinates and a
  62. // specified NSView's coordinate system.
  63. // ========================================================================
  64. NSPoint CocoaTransformNSViewBoundsToWx(NSView *nsview, NSPoint pointBounds)
  65. {
  66. wxCHECK_MSG(nsview, pointBounds, wxT("Need to have a Cocoa view to do translation"));
  67. if([nsview isFlipped])
  68. return pointBounds;
  69. NSRect ourBounds = [nsview bounds];
  70. return NSMakePoint
  71. ( pointBounds.x
  72. , ourBounds.size.height - pointBounds.y
  73. );
  74. }
  75. NSRect CocoaTransformNSViewBoundsToWx(NSView *nsview, NSRect rectBounds)
  76. {
  77. wxCHECK_MSG(nsview, rectBounds, wxT("Need to have a Cocoa view to do translation"));
  78. if([nsview isFlipped])
  79. return rectBounds;
  80. NSRect ourBounds = [nsview bounds];
  81. return NSMakeRect
  82. ( rectBounds.origin.x
  83. , ourBounds.size.height - (rectBounds.origin.y + rectBounds.size.height)
  84. , rectBounds.size.width
  85. , rectBounds.size.height
  86. );
  87. }
  88. NSPoint CocoaTransformNSViewWxToBounds(NSView *nsview, NSPoint pointWx)
  89. {
  90. wxCHECK_MSG(nsview, pointWx, wxT("Need to have a Cocoa view to do translation"));
  91. if([nsview isFlipped])
  92. return pointWx;
  93. NSRect ourBounds = [nsview bounds];
  94. return NSMakePoint
  95. ( pointWx.x
  96. , ourBounds.size.height - pointWx.y
  97. );
  98. }
  99. NSRect CocoaTransformNSViewWxToBounds(NSView *nsview, NSRect rectWx)
  100. {
  101. wxCHECK_MSG(nsview, rectWx, wxT("Need to have a Cocoa view to do translation"));
  102. if([nsview isFlipped])
  103. return rectWx;
  104. NSRect ourBounds = [nsview bounds];
  105. return NSMakeRect
  106. ( rectWx.origin.x
  107. , ourBounds.size.height - (rectWx.origin.y + rectWx.size.height)
  108. , rectWx.size.width
  109. , rectWx.size.height
  110. );
  111. }
  112. // ============================================================================
  113. // Screen coordinate helpers
  114. // ============================================================================
  115. /*
  116. General observation about Cocoa screen coordinates:
  117. It is documented that the first object of the [NSScreen screens] array is the screen with the menubar.
  118. It is not documented (but true as far as I can tell) that (0,0) in Cocoa screen coordinates is always
  119. the BOTTOM-right corner of this screen. Recall that Cocoa uses cartesian coordinates so y-increase is up.
  120. It isn't clearly documented but visibleFrame returns a rectangle in screen coordinates, not a rectangle
  121. relative to that screen's frame. The only real way to test this is to configure two screens one atop
  122. the other such that the menubar screen is on top. The Dock at the bottom of the screen will then
  123. eat into the visibleFrame of screen 1 by incrementing it's y-origin. Thus if you arrange two
  124. 1920x1200 screens top/bottom then screen 1 (the bottom screen) will have frame origin (0,-1200) and
  125. visibleFrame origin (0,-1149) which is exactly 51 pixels higher than the full frame origin.
  126. In wxCocoa, we somewhat arbitrarily declare that wx (0,0) is the TOP-left of screen 0's frame (the entire screen).
  127. However, this isn't entirely arbitrary because the Quartz Display Services (CGDisplay) uses this same scheme.
  128. This works out nicely because wxCocoa's wxDisplay is implemented using Quartz Display Services instead of NSScreen.
  129. */
  130. namespace { // file namespace
  131. class wxCocoaPrivateScreenCoordinateTransformer
  132. {
  133. wxDECLARE_NO_COPY_CLASS(wxCocoaPrivateScreenCoordinateTransformer);
  134. public:
  135. wxCocoaPrivateScreenCoordinateTransformer();
  136. ~wxCocoaPrivateScreenCoordinateTransformer();
  137. wxPoint OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame);
  138. NSPoint OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible);
  139. protected:
  140. NSScreen *m_screenZero;
  141. NSRect m_screenZeroFrame;
  142. };
  143. // NOTE: This is intended to be a short-lived object. A future enhancment might
  144. // make it a global and reconfigure it upon some notification that the screen layout
  145. // has changed.
  146. inline wxCocoaPrivateScreenCoordinateTransformer::wxCocoaPrivateScreenCoordinateTransformer()
  147. {
  148. NSArray *screens = [NSScreen screens];
  149. [screens retain];
  150. m_screenZero = nil;
  151. if(screens != nil && [screens count] > 0)
  152. m_screenZero = [[screens objectAtIndex:0] retain];
  153. [screens release];
  154. if(m_screenZero != nil)
  155. m_screenZeroFrame = [m_screenZero frame];
  156. else
  157. {
  158. wxLogWarning(wxT("Can't translate to/from wx screen coordinates and Cocoa screen coordinates"));
  159. // Just blindly assume 1024x768 so that at least we can sort of flip things around into
  160. // Cocoa coordinates.
  161. // NOTE: Theoretically this case should never happen anyway.
  162. m_screenZeroFrame = NSMakeRect(0,0,1024,768);
  163. }
  164. }
  165. inline wxCocoaPrivateScreenCoordinateTransformer::~wxCocoaPrivateScreenCoordinateTransformer()
  166. {
  167. [m_screenZero release];
  168. m_screenZero = nil;
  169. }
  170. inline wxPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
  171. {
  172. // x and y are in wx screen coordinates which we're going to arbitrarily define such that
  173. // (0,0) is the TOP-left of screen 0 (the one with the menubar)
  174. // NOTE WELL: This means that (0,0) is _NOT_ an appropriate position for a window.
  175. wxPoint theWxOrigin;
  176. // Working in Cocoa's screen coordinates we must realize that the x coordinate we want is
  177. // the distance between the left side (origin.x) of the window's frame and the left side of
  178. // screen zero's frame.
  179. theWxOrigin.x = windowFrame.origin.x - m_screenZeroFrame.origin.x;
  180. // Working in Cocoa's screen coordinates we must realize that the y coordinate we want is
  181. // actually the distance between the top-left of the screen zero frame and the top-left
  182. // of the window's frame.
  183. theWxOrigin.y = (m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height) - (windowFrame.origin.y + windowFrame.size.height);
  184. return theWxOrigin;
  185. }
  186. inline NSPoint wxCocoaPrivateScreenCoordinateTransformer::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
  187. {
  188. NSPoint theCocoaOrigin;
  189. // The position is in wx screen coordinates which we're going to arbitrarily define such that
  190. // (0,0) is the TOP-left of screen 0 (the one with the menubar)
  191. // NOTE: The usable rectangle is smaller and hence we have the keepOriginVisible flag
  192. // which will move the origin downward and/or left as necessary if the origin is
  193. // inside the screen0 rectangle (i.e. x/y >= 0 in wx coordinates) and outside the
  194. // visible frame (i.e. x/y < the top/left of the screen0 visible frame in wx coordinates)
  195. // We don't munge origin coordinates < 0 because it actually is possible that the menubar is on
  196. // the top of the bottom screen and thus that origin is completely valid!
  197. if(keepOriginVisible && (m_screenZero != nil))
  198. {
  199. // Do al of this in wx coordinates because it's far simpler since we're dealing with top/left points
  200. wxPoint visibleOrigin = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates([m_screenZero visibleFrame]);
  201. if(x >= 0 && x < visibleOrigin.x)
  202. x = visibleOrigin.x;
  203. if(y >= 0 && y < visibleOrigin.y)
  204. y = visibleOrigin.y;
  205. }
  206. // The x coordinate is simple as it's just relative to screen zero's frame
  207. theCocoaOrigin.x = m_screenZeroFrame.origin.x + x;
  208. // Working in Cocoa's coordinates think to start at the bottom of screen zero's frame and add
  209. // the height of that rect which gives us the coordinate for the top of the visible rect. Now realize that
  210. // the wx coordinates are flipped so if y is say 10 then we want to be 10 pixels down from that and thus
  211. // we subtract y. But then we still need to take into account the size of the window which is h and subtract
  212. // that to get the bottom-left origin of the rectangle.
  213. theCocoaOrigin.y = m_screenZeroFrame.origin.y + m_screenZeroFrame.size.height - y - height;
  214. return theCocoaOrigin;
  215. }
  216. } // namespace
  217. wxPoint wxWindowCocoa::OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(NSRect windowFrame)
  218. {
  219. wxCocoaPrivateScreenCoordinateTransformer transformer;
  220. return transformer.OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame);
  221. }
  222. NSPoint wxWindowCocoa::OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(wxCoord x, wxCoord y, wxCoord width, wxCoord height, bool keepOriginVisible)
  223. {
  224. wxCocoaPrivateScreenCoordinateTransformer transformer;
  225. return transformer.OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,keepOriginVisible);
  226. }
  227. // ========================================================================
  228. // wxWindowCocoaHider
  229. // ========================================================================
  230. class wxWindowCocoaHider: protected wxCocoaNSView
  231. {
  232. wxDECLARE_NO_COPY_CLASS(wxWindowCocoaHider);
  233. public:
  234. wxWindowCocoaHider(wxWindow *owner);
  235. virtual ~wxWindowCocoaHider();
  236. inline WX_NSView GetNSView() { return m_dummyNSView; }
  237. protected:
  238. wxWindowCocoa *m_owner;
  239. WX_NSView m_dummyNSView;
  240. virtual void Cocoa_FrameChanged(void);
  241. virtual void Cocoa_synthesizeMouseMoved(void) {}
  242. #ifdef WXCOCOA_FILL_DUMMY_VIEW
  243. virtual bool Cocoa_drawRect(const NSRect& rect);
  244. #endif //def WXCOCOA_FILL_DUMMY_VIEW
  245. private:
  246. wxWindowCocoaHider();
  247. };
  248. // ========================================================================
  249. // wxDummyNSView
  250. // ========================================================================
  251. @interface wxDummyNSView : NSView
  252. - (NSView *)hitTest:(NSPoint)aPoint;
  253. @end
  254. WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView)
  255. @implementation wxDummyNSView : NSView
  256. - (NSView *)hitTest:(NSPoint)aPoint
  257. {
  258. return nil;
  259. }
  260. @end
  261. WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView)
  262. // ========================================================================
  263. // wxWindowCocoaHider
  264. // NOTE: This class and method of hiding predates setHidden: support in
  265. // the toolkit. The hack used here is to replace the view with a stand-in
  266. // that will be subject to the usual Cocoa resizing rules.
  267. // When possible (i.e. when running on 10.3 or higher) we make it hidden
  268. // mostly as an optimization so Cocoa doesn't have to consider it when
  269. // drawing or finding key views.
  270. // ========================================================================
  271. wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner)
  272. : m_owner(owner)
  273. {
  274. wxASSERT(owner);
  275. wxASSERT(owner->GetNSViewForHiding());
  276. m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc]
  277. initWithFrame:[owner->GetNSViewForHiding() frame]];
  278. [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]];
  279. AssociateNSView(m_dummyNSView);
  280. if([m_dummyNSView respondsToSelector:@selector(setHidden:)])
  281. [m_dummyNSView setHidden:YES];
  282. }
  283. wxWindowCocoaHider::~wxWindowCocoaHider()
  284. {
  285. DisassociateNSView(m_dummyNSView);
  286. [m_dummyNSView release];
  287. }
  288. void wxWindowCocoaHider::Cocoa_FrameChanged(void)
  289. {
  290. // Keep the real window in synch with the dummy
  291. wxASSERT(m_dummyNSView);
  292. [m_owner->GetNSViewForHiding() setFrame:[m_dummyNSView frame]];
  293. }
  294. #ifdef WXCOCOA_FILL_DUMMY_VIEW
  295. bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect)
  296. {
  297. NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:rect];
  298. [[NSColor greenColor] set];
  299. [bezpath stroke];
  300. [bezpath fill];
  301. return true;
  302. }
  303. #endif //def WXCOCOA_FILL_DUMMY_VIEW
  304. /*! @class WXManualScrollView
  305. @abstract An NSScrollView subclass which implements wx scroll behaviour
  306. @discussion
  307. Overrides default behaviour of NSScrollView such that this class receives
  308. the scroller action messages and allows the wxCocoaScrollView to act
  309. on them accordingly. In particular, because the NSScrollView will not
  310. receive action messages from the scroller, it will not adjust the
  311. document view. This must be done manually using the ScrollWindow
  312. method of wxWindow.
  313. */
  314. @interface WXManualScrollView : NSScrollView
  315. {
  316. /*! @field m_wxCocoaScrollView
  317. */
  318. wxWindowCocoaScrollView *m_wxCocoaScrollView;
  319. }
  320. // Override these to set up the target/action correctly
  321. - (void)setHorizontalScroller:(NSScroller *)aScroller;
  322. - (void)setVerticalScroller:(NSScroller *)aScroller;
  323. - (void)setHasHorizontalScroller:(BOOL)flag;
  324. - (void)setHasVerticalScroller:(BOOL)flag;
  325. // NOTE: _wx_ prefix means "private" method like _ that Apple (and only Apple) uses.
  326. - (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView;
  327. - (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView;
  328. /*! @method _wx_doScroller
  329. @abstract Handles action messages from the scrollers
  330. @discussion
  331. Similar to Apple's _doScroller: method which is private and not documented.
  332. We do not, however, call that method. Instead, we effectively override
  333. it entirely. We don't override it by naming ourself the same thing because
  334. the base class code may or may not call that method for other reasons we
  335. simply cannot know about.
  336. */
  337. - (void)_wx_doScroller:(id)sender;
  338. @end
  339. @implementation WXManualScrollView : NSScrollView
  340. static inline void WXManualScrollView_DoSetScrollerTargetAction(WXManualScrollView *self, NSScroller *aScroller)
  341. {
  342. if(aScroller != NULL && [self _wx_wxCocoaScrollView] != NULL)
  343. {
  344. [aScroller setTarget:self];
  345. [aScroller setAction:@selector(_wx_doScroller:)];
  346. }
  347. }
  348. - (void)setHorizontalScroller:(NSScroller *)aScroller
  349. {
  350. [super setHorizontalScroller:aScroller];
  351. WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
  352. }
  353. - (void)setVerticalScroller:(NSScroller *)aScroller
  354. {
  355. [super setVerticalScroller:aScroller];
  356. WXManualScrollView_DoSetScrollerTargetAction(self, aScroller);
  357. }
  358. - (void)setHasHorizontalScroller:(BOOL)flag
  359. {
  360. [super setHasHorizontalScroller:flag];
  361. WXManualScrollView_DoSetScrollerTargetAction(self, [self horizontalScroller]);
  362. }
  363. - (void)setHasVerticalScroller:(BOOL)flag
  364. {
  365. [super setHasVerticalScroller:flag];
  366. WXManualScrollView_DoSetScrollerTargetAction(self, [self verticalScroller]);
  367. }
  368. - (wxWindowCocoaScrollView*)_wx_wxCocoaScrollView
  369. { return m_wxCocoaScrollView; }
  370. - (void)_wx_setWxCocoaScrollView:(wxWindowCocoaScrollView*)theWxScrollView
  371. {
  372. m_wxCocoaScrollView = theWxScrollView;
  373. [self setHorizontalScroller:[self horizontalScroller]];
  374. [self setVerticalScroller:[self verticalScroller]];
  375. }
  376. - (void)_wx_doScroller:(id)sender
  377. {
  378. if(m_wxCocoaScrollView != NULL)
  379. m_wxCocoaScrollView->_wx_doScroller(sender);
  380. else
  381. wxLogError(wxT("Unexpected action message received from NSScroller"));
  382. }
  383. - (void)reflectScrolledClipView:(NSClipView *)aClipView
  384. {
  385. struct _ScrollerBackup
  386. {
  387. _ScrollerBackup(NSScroller *aScroller)
  388. : m_scroller(aScroller)
  389. , m_floatValue(aScroller!=nil?[aScroller floatValue]:0.0)
  390. , m_knobProportion(aScroller!=nil?[aScroller knobProportion]:1.0)
  391. , m_isEnabled(aScroller!=nil?[aScroller isEnabled]:false)
  392. {
  393. }
  394. NSScroller *m_scroller;
  395. CGFloat m_floatValue;
  396. CGFloat m_knobProportion;
  397. BOOL m_isEnabled;
  398. ~_ScrollerBackup()
  399. {
  400. if(m_scroller != nil)
  401. {
  402. [m_scroller setFloatValue:m_floatValue knobProportion:m_knobProportion];
  403. [m_scroller setEnabled:m_isEnabled];
  404. }
  405. }
  406. private:
  407. _ScrollerBackup();
  408. _ScrollerBackup(_ScrollerBackup const&);
  409. _ScrollerBackup& operator=(_ScrollerBackup const&);
  410. };
  411. _ScrollerBackup _horizontalBackup([self horizontalScroller]);
  412. _ScrollerBackup _verticalBackup([self verticalScroller]);
  413. // We MUST call super's implementation or else nothing seems to work right at all.
  414. // However, we need our scrollers not to change values due to the document window
  415. // moving so we cheat and save/restore their values across this call.
  416. [super reflectScrolledClipView: aClipView];
  417. }
  418. @end
  419. WX_IMPLEMENT_GET_OBJC_CLASS(WXManualScrollView,NSScrollView)
  420. // ========================================================================
  421. // wxFlippedNSClipView
  422. // ========================================================================
  423. @interface wxFlippedNSClipView : NSClipView
  424. - (BOOL)isFlipped;
  425. @end
  426. WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
  427. @implementation wxFlippedNSClipView : NSClipView
  428. - (BOOL)isFlipped
  429. {
  430. return YES;
  431. }
  432. @end
  433. WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView)
  434. // ========================================================================
  435. // wxWindowCocoaScrollView
  436. // ========================================================================
  437. wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner)
  438. : m_owner(owner)
  439. , m_cocoaNSScrollView() // nil
  440. , m_scrollRange() // {0,0}
  441. , m_scrollThumb() // {0,0}
  442. , m_virtualOrigin(0,0)
  443. {
  444. wxAutoNSAutoreleasePool pool;
  445. wxASSERT(owner);
  446. wxASSERT(owner->GetNSView());
  447. m_isNativeView = ![owner->GetNSView() isKindOfClass:[WX_GET_OBJC_CLASS(WXNSView) class]];
  448. m_cocoaNSScrollView = [(m_isNativeView?[NSScrollView alloc]:[WXManualScrollView alloc])
  449. initWithFrame:[owner->GetNSView() frame]];
  450. AssociateNSView(m_cocoaNSScrollView);
  451. if(m_isNativeView)
  452. {
  453. /* Set a bezel border around the entire thing because it looks funny without it.
  454. TODO: Be sure to undo any borders on the real view (if any) and apply them
  455. to this view if necessary. Right now, there is no border support in wxCocoa
  456. so this isn't an issue.
  457. */
  458. [m_cocoaNSScrollView setBorderType:NSBezelBorder];
  459. }
  460. else
  461. {
  462. [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView: this];
  463. // Don't set a bezel because we might be creating a scroll view due to being
  464. // the "target window" of a wxScrolledWindow. That is to say that the user
  465. // has absolutely no intention of scrolling the clip view used by this
  466. // NSScrollView.
  467. }
  468. /* Replace the default NSClipView with a flipped one. This ensures
  469. scrolling is "pinned" to the top-left instead of bottom-right. */
  470. NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc]
  471. initWithFrame: [[m_cocoaNSScrollView contentView] frame]];
  472. [m_cocoaNSScrollView setContentView:flippedClip];
  473. [flippedClip release];
  474. // In all cases we must encapsulate the real NSView properly
  475. Encapsulate();
  476. }
  477. void wxWindowCocoaScrollView::Encapsulate()
  478. {
  479. // Set the scroll view autoresizingMask to match the current NSView
  480. [m_cocoaNSScrollView setAutoresizingMask: [m_owner->GetNSView() autoresizingMask]];
  481. [m_owner->GetNSView() setAutoresizingMask: NSViewNotSizable];
  482. // NOTE: replaceSubView will cause m_cocoaNSView to be released
  483. // except when it hasn't been added into an NSView hierarchy in which
  484. // case it doesn't need to be and this should work out to a no-op
  485. m_owner->CocoaReplaceView(m_owner->GetNSView(), m_cocoaNSScrollView);
  486. // The NSView is still retained by owner
  487. [m_cocoaNSScrollView setDocumentView: m_owner->GetNSView()];
  488. // Now it's also retained by the NSScrollView
  489. }
  490. void wxWindowCocoaScrollView::Unencapsulate()
  491. {
  492. [m_cocoaNSScrollView setDocumentView: nil];
  493. m_owner->CocoaReplaceView(m_cocoaNSScrollView, m_owner->GetNSView());
  494. if(![[m_owner->GetNSView() superview] isFlipped])
  495. [m_owner->GetNSView() setAutoresizingMask: NSViewMinYMargin];
  496. }
  497. wxWindowCocoaScrollView::~wxWindowCocoaScrollView()
  498. {
  499. DisassociateNSView(m_cocoaNSScrollView);
  500. if(!m_isNativeView)
  501. {
  502. [(WXManualScrollView*)m_cocoaNSScrollView _wx_setWxCocoaScrollView:NULL];
  503. }
  504. [m_cocoaNSScrollView release];
  505. }
  506. void wxWindowCocoaScrollView::ClientSizeToSize(int &width, int &height)
  507. {
  508. NSSize frameSize = [NSScrollView
  509. frameSizeForContentSize: NSMakeSize(width,height)
  510. hasHorizontalScroller: [m_cocoaNSScrollView hasHorizontalScroller]
  511. hasVerticalScroller: [m_cocoaNSScrollView hasVerticalScroller]
  512. borderType: [m_cocoaNSScrollView borderType]];
  513. width = (int)frameSize.width;
  514. height = (int)frameSize.height;
  515. }
  516. void wxWindowCocoaScrollView::DoGetClientSize(int *x, int *y) const
  517. {
  518. NSSize nssize = [m_cocoaNSScrollView contentSize];
  519. if(x)
  520. *x = (int)nssize.width;
  521. if(y)
  522. *y = (int)nssize.height;
  523. }
  524. static inline void SetCocoaScroller(NSScroller *aScroller, int WXUNUSED(orientation), int position, int thumbSize, int range)
  525. {
  526. wxCHECK_RET(aScroller != nil, wxT("Expected the NSScrollView to have a scroller"));
  527. // NOTE: thumbSize is already ensured to be >= 1 and <= range by our caller
  528. // unless range = 0 in which case we shouldn't have been be called.
  529. wxCHECK_RET(range > 0, wxT("Internal wxCocoa bug: shouldn't have been called with 0 range"));
  530. // Range of valid position values is from 0 to effectiveRange
  531. // NOTE: if thumbSize == range then effectiveRange is 0.
  532. // thumbSize is at least 1 which gives range from 0 to range - 1 inclusive
  533. // which is exactly what we want.
  534. int effectiveRange = range - thumbSize;
  535. // knobProportion is hopefully easy to understand
  536. // Note that thumbSize is already guaranteed >= 1 by our caller.
  537. CGFloat const knobProportion = CGFloat(thumbSize)/CGFloat(range);
  538. // NOTE: When effectiveRange is zero there really is no valid position
  539. // We arbitrarily pick 0.0 which is the same as a scroller in the home position.
  540. CGFloat const floatValue = (effectiveRange != 0)?CGFloat(position)/CGFloat(effectiveRange):0.0;
  541. [aScroller setFloatValue:floatValue knobProportion: knobProportion];
  542. // Make sure it's visibly working
  543. [aScroller setEnabled:YES];
  544. }
  545. void wxWindowCocoaScrollView::SetScrollPos(wxOrientation orientation, int position)
  546. {
  547. // NOTE: Rather than using only setFloatValue: (which we could do) we instead
  548. // simply share the SetCocoaScroller call because all but the knobProportion
  549. // calculations have to be done anyway.
  550. if(orientation & wxHORIZONTAL)
  551. {
  552. NSScroller *aScroller = [m_cocoaNSScrollView horizontalScroller];
  553. if(aScroller != nil)
  554. SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[0], m_scrollRange[0]);
  555. }
  556. if(orientation & wxVERTICAL)
  557. {
  558. NSScroller *aScroller = [m_cocoaNSScrollView verticalScroller];
  559. if(aScroller != nil)
  560. SetCocoaScroller(aScroller, orientation, position, m_scrollThumb[1], m_scrollRange[1]);
  561. }
  562. }
  563. void wxWindowCocoaScrollView::SetScrollbar(int orientation, int position, int thumbSize, int range)
  564. {
  565. // FIXME: API assumptions:
  566. // 1. If the user wants to remove a scroller he gives range 0.
  567. // 2. If the user wants to disable a scroller he sets thumbSize == range
  568. // in which case it is logically impossible to scroll.
  569. // The scroller shall still be displayed.
  570. // Ensure that range is >= 0.
  571. wxASSERT(range >= 0);
  572. if(range < 0)
  573. range = 0;
  574. // Ensure that thumbSize <= range
  575. wxASSERT(thumbSize <= range);
  576. // Also ensure thumbSize >= 1 but don't complain if it isn't
  577. if(thumbSize < 1)
  578. thumbSize = 1;
  579. // Now make sure it's really less than range, even if we just set it to 1
  580. if(thumbSize > range)
  581. thumbSize = range;
  582. bool needScroller = (range != 0);
  583. // Can potentially set both horizontal and vertical at the same time although this is
  584. // probably not very useful.
  585. if(orientation & wxHORIZONTAL)
  586. {
  587. m_scrollRange[0] = range;
  588. m_scrollThumb[0] = thumbSize;
  589. if(!m_isNativeView)
  590. {
  591. [m_cocoaNSScrollView setHasHorizontalScroller:needScroller];
  592. if(needScroller)
  593. SetCocoaScroller([m_cocoaNSScrollView horizontalScroller], orientation, position, thumbSize, range);
  594. }
  595. }
  596. if(orientation & wxVERTICAL)
  597. {
  598. m_scrollRange[1] = range;
  599. m_scrollThumb[1] = thumbSize;
  600. if(!m_isNativeView)
  601. {
  602. [m_cocoaNSScrollView setHasVerticalScroller:needScroller];
  603. if(needScroller)
  604. SetCocoaScroller([m_cocoaNSScrollView verticalScroller], orientation, position, thumbSize, range);
  605. }
  606. }
  607. }
  608. int wxWindowCocoaScrollView::GetScrollPos(wxOrientation orient)
  609. {
  610. if((orient & wxBOTH) == wxBOTH)
  611. {
  612. wxLogError(wxT("GetScrollPos called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
  613. return 0;
  614. }
  615. int effectiveScrollRange;
  616. NSScroller *cocoaScroller;
  617. if(orient & wxHORIZONTAL)
  618. {
  619. effectiveScrollRange = m_scrollRange[0] - m_scrollThumb[0];
  620. cocoaScroller = [m_cocoaNSScrollView horizontalScroller];
  621. }
  622. else if(orient & wxVERTICAL)
  623. {
  624. effectiveScrollRange = m_scrollRange[1] - m_scrollThumb[1];
  625. cocoaScroller = [m_cocoaNSScrollView verticalScroller];
  626. }
  627. else
  628. {
  629. wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
  630. return 0;
  631. }
  632. if(cocoaScroller == nil)
  633. { // Document is not scrolled
  634. return 0;
  635. }
  636. /*
  637. The effective range of a scroll bar as defined by wxWidgets is from 0 to (range - thumbSize).
  638. That is a scroller at the left/top position is at 0 and a scroller at the bottom/right
  639. position is at range-thumbsize.
  640. The range of an NSScroller is 0.0 to 1.0. Much easier! NOTE: Apple doesn't really specify
  641. but GNUStep docs do say that 0.0 is top/left and 1.0 is bottom/right. This is actually
  642. in contrast to NSSlider which generally has 1.0 at the TOP when it's done vertically.
  643. */
  644. CGFloat cocoaScrollPos = [cocoaScroller floatValue];
  645. return effectiveScrollRange * cocoaScrollPos;
  646. }
  647. int wxWindowCocoaScrollView::GetScrollRange(wxOrientation orient)
  648. {
  649. if((orient & wxBOTH) == wxBOTH)
  650. {
  651. wxLogError(wxT("GetScrollRange called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
  652. return 0;
  653. }
  654. if(orient & wxHORIZONTAL)
  655. {
  656. return m_scrollRange[0];
  657. }
  658. else if(orient & wxVERTICAL)
  659. {
  660. return m_scrollRange[1];
  661. }
  662. else
  663. {
  664. wxLogError(wxT("GetScrollPos called without an orientation which makes no sense"));
  665. return 0;
  666. }
  667. }
  668. int wxWindowCocoaScrollView::GetScrollThumb(wxOrientation orient)
  669. {
  670. if((orient & wxBOTH) == wxBOTH)
  671. {
  672. wxLogError(wxT("GetScrollThumb called for wxHORIZONTAL and wxVERTICAL together which makes no sense"));
  673. return 0;
  674. }
  675. if(orient & wxHORIZONTAL)
  676. {
  677. return m_scrollThumb[0];
  678. }
  679. else if(orient & wxVERTICAL)
  680. {
  681. return m_scrollThumb[1];
  682. }
  683. else
  684. {
  685. wxLogError(wxT("GetScrollThumb called without an orientation which makes no sense"));
  686. return 0;
  687. }
  688. }
  689. /*!
  690. Moves the contents (all existing drawing as well as all all child wxWindow) by the specified
  691. amount expressed in the wxWindow's own coordinate system. This is used to implement scrolling
  692. but the usage is rather interesting. When scrolling right (e.g. increasing the value of
  693. the scroller) you must give a negative delta x (e.g. moving the contents LEFT). Likewise,
  694. when scrolling the window down, increasing the value of the scroller, you give a negative
  695. delta y which moves the contents up.
  696. wxCocoa notes: To accomplish this trick in Cocoa we basically do what NSScrollView would
  697. have done and that is adjust the content view's bounds origin. The content view is somewhat
  698. confusingly the NSClipView which is more or less sort of the pImpl for NSScrollView
  699. The real NSView with the user's content (e.g. the "virtual area" in wxWidgets parlance)
  700. is called the document view in NSScrollView parlance.
  701. The bounds origin is basically the exact opposite concept. Whereas in Windows the client
  702. coordinate system remains constant and the content must shift left/up for increases
  703. of scrolling, in Cocoa the coordinate system is actually the virtual one. So we must
  704. instead shift the bounds rectangle right/down to get the effect of the content moving
  705. left/up. Basically, it's a higher level interface than that provided by wxWidgets
  706. so essentially we're implementing the low-level move content interface in terms of
  707. the high-level move the viewport (the bounds) over top that content (the document
  708. view which is the virtual area to wx).
  709. For all intents and purposes that basically just means that we subtract the deltas
  710. from the bounds origin and thus a negative delta actually increases the bounds origin
  711. and a positive delta actually decreases it. This is absolutely true for the horizontal
  712. axis but there's a catch in the vertical axis. If the content view (the clip view) is
  713. flipped (and we do this by default) then it works exactly like the horizontal axis.
  714. If it is not flipped (i.e. it is in postscript coordinates which are opposite to
  715. wxWidgets) then the sense needs to be reversed.
  716. However, this plays hell with window positions. The frame rects of any child views
  717. do not change origin and this is actually important because if they did, the views
  718. would send frame changed notifications, not to mention that Cocoa just doesn't really
  719. do scrolling that way, it does it the way we do it here.
  720. To fix this we implement GetPosition for child windows to not merely consult its
  721. superview at the Cocoa level in order to do proper Cocoa->wx coordinate transform
  722. but to actually consult is parent wxWindow because it makes a big difference if
  723. the parent is scrolled. Argh. (FIXME: This isn't actually implemented yet)
  724. */
  725. void wxWindowCocoaScrollView::ScrollWindow(int dx, int dy, const wxRect*)
  726. {
  727. // Update our internal origin so we know how much the application code
  728. // expects us to have been scrolled.
  729. m_virtualOrigin.x += dx;
  730. m_virtualOrigin.y += dy;
  731. // Scroll the window using the standard Cocoa method of adjusting the
  732. // clip view's bounds in the opposite fashion.
  733. NSClipView *contentView = [m_cocoaNSScrollView contentView];
  734. NSRect clipViewBoundsRect = [contentView bounds];
  735. clipViewBoundsRect.origin.x -= dx;
  736. if([contentView isFlipped])
  737. clipViewBoundsRect.origin.y -= dy;
  738. else
  739. clipViewBoundsRect.origin.y += dy;
  740. [contentView scrollToPoint:clipViewBoundsRect.origin];
  741. }
  742. void wxWindowCocoaScrollView::_wx_doScroller(NSScroller *sender)
  743. {
  744. wxOrientation orientation;
  745. if(sender == [m_cocoaNSScrollView horizontalScroller])
  746. orientation = wxHORIZONTAL;
  747. else if(sender == [m_cocoaNSScrollView verticalScroller])
  748. orientation = wxVERTICAL;
  749. else
  750. {
  751. wxLogDebug(wxT("Received action message from unexpected NSScroller"));
  752. return;
  753. }
  754. // NOTE: Cocoa does not move the scroller for page up/down or line
  755. // up/down events. That means the value will be the old value.
  756. // For thumbtrack events, the value is the new value.
  757. int scrollpos = GetScrollPos(orientation);
  758. int commandType;
  759. switch([sender hitPart])
  760. {
  761. default:
  762. case NSScrollerNoPart:
  763. case NSScrollerKnob: // Drag of knob
  764. case NSScrollerKnobSlot: // Jump directly to position
  765. commandType = wxEVT_SCROLLWIN_THUMBTRACK;
  766. break;
  767. case NSScrollerDecrementPage:
  768. commandType = wxEVT_SCROLLWIN_PAGEUP;
  769. break;
  770. case NSScrollerIncrementPage:
  771. commandType = wxEVT_SCROLLWIN_PAGEDOWN;
  772. break;
  773. case NSScrollerDecrementLine:
  774. commandType = wxEVT_SCROLLWIN_LINEUP;
  775. break;
  776. case NSScrollerIncrementLine:
  777. commandType = wxEVT_SCROLLWIN_LINEDOWN;
  778. break;
  779. }
  780. wxScrollWinEvent event(commandType, scrollpos, orientation);
  781. event.SetEventObject(m_owner);
  782. m_owner->HandleWindowEvent(event);
  783. }
  784. void wxWindowCocoaScrollView::UpdateSizes()
  785. {
  786. // Using the virtual size, figure out what the document frame size should be
  787. // NOTE: Assume that the passed in virtualSize is already >= the client size
  788. wxSize virtualSize = m_owner->GetVirtualSize();
  789. // Get the document's current frame
  790. NSRect documentViewFrame = [m_owner->GetNSView() frame];
  791. NSRect newFrame = documentViewFrame;
  792. newFrame.size = NSMakeSize(virtualSize.x, virtualSize.y);
  793. if(!NSEqualRects(newFrame, documentViewFrame))
  794. {
  795. [m_owner->GetNSView() setFrame:newFrame];
  796. }
  797. }
  798. void wxWindowCocoaScrollView::Cocoa_FrameChanged(void)
  799. {
  800. wxLogTrace(wxTRACE_COCOA,wxT("wxWindowCocoaScrollView=%p::Cocoa_FrameChanged for wxWindow %p"), this, m_owner);
  801. wxSizeEvent event(m_owner->GetSize(), m_owner->GetId());
  802. event.SetEventObject(m_owner);
  803. m_owner->HandleWindowEvent(event);
  804. /* If the view is not a native one then it's being managed by wx. In this case the control
  805. may decide to change its virtual size and we must update the document view's size to
  806. match. For native views the virtual size will never have been set so we do not want
  807. to use it at all.
  808. */
  809. if(!m_isNativeView)
  810. UpdateSizes();
  811. }
  812. // ========================================================================
  813. // wxWindowCocoa
  814. // ========================================================================
  815. // normally the base classes aren't included, but wxWindow is special
  816. #ifdef __WXUNIVERSAL__
  817. IMPLEMENT_ABSTRACT_CLASS(wxWindowCocoa, wxWindowBase)
  818. #endif
  819. BEGIN_EVENT_TABLE(wxWindowCocoa, wxWindowBase)
  820. END_EVENT_TABLE()
  821. wxWindow *wxWindowCocoa::sm_capturedWindow = NULL;
  822. // Constructor
  823. void wxWindowCocoa::Init()
  824. {
  825. m_cocoaNSView = NULL;
  826. m_cocoaHider = NULL;
  827. m_wxCocoaScrollView = NULL;
  828. m_isInPaint = false;
  829. m_visibleTrackingRectManager = NULL;
  830. }
  831. // Constructor
  832. bool wxWindow::Create(wxWindow *parent, wxWindowID winid,
  833. const wxPoint& pos,
  834. const wxSize& size,
  835. long style,
  836. const wxString& name)
  837. {
  838. if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
  839. return false;
  840. // TODO: create the window
  841. m_cocoaNSView = NULL;
  842. SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]);
  843. [m_cocoaNSView release];
  844. if (m_parent)
  845. {
  846. m_parent->AddChild(this);
  847. m_parent->CocoaAddChild(this);
  848. SetInitialFrameRect(pos,size);
  849. }
  850. return true;
  851. }
  852. // Destructor
  853. wxWindow::~wxWindow()
  854. {
  855. wxAutoNSAutoreleasePool pool;
  856. DestroyChildren();
  857. // Make sure our parent (in the wxWidgets sense) is our superview
  858. // before we go removing from it.
  859. if(m_parent && m_parent->GetNSView()==[GetNSViewForSuperview() superview])
  860. CocoaRemoveFromParent();
  861. delete m_cocoaHider;
  862. delete m_wxCocoaScrollView;
  863. if(m_cocoaNSView)
  864. SendDestroyEvent();
  865. SetNSView(NULL);
  866. }
  867. void wxWindowCocoa::CocoaAddChild(wxWindowCocoa *child)
  868. {
  869. // Pool here due to lack of one during wx init phase
  870. wxAutoNSAutoreleasePool pool;
  871. NSView *childView = child->GetNSViewForSuperview();
  872. wxASSERT(childView);
  873. [m_cocoaNSView addSubview: childView];
  874. }
  875. void wxWindowCocoa::CocoaRemoveFromParent(void)
  876. {
  877. [GetNSViewForSuperview() removeFromSuperview];
  878. }
  879. void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView)
  880. {
  881. // Clear the visible area tracking rect if we have one.
  882. delete m_visibleTrackingRectManager;
  883. m_visibleTrackingRectManager = NULL;
  884. bool need_debug = cocoaNSView || m_cocoaNSView;
  885. if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]);
  886. DisassociateNSView(m_cocoaNSView);
  887. wxGCSafeRetain(cocoaNSView);
  888. wxGCSafeRelease(m_cocoaNSView);
  889. m_cocoaNSView = cocoaNSView;
  890. AssociateNSView(m_cocoaNSView);
  891. if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [cocoaNSView=%p retainCount]=%d"),this,cocoaNSView,[cocoaNSView retainCount]);
  892. }
  893. WX_NSView wxWindowCocoa::GetNSViewForSuperview() const
  894. {
  895. return m_cocoaHider
  896. ? m_cocoaHider->GetNSView()
  897. : m_wxCocoaScrollView
  898. ? m_wxCocoaScrollView->GetNSScrollView()
  899. : m_cocoaNSView;
  900. }
  901. WX_NSView wxWindowCocoa::GetNSViewForHiding() const
  902. {
  903. return m_wxCocoaScrollView
  904. ? m_wxCocoaScrollView->GetNSScrollView()
  905. : m_cocoaNSView;
  906. }
  907. NSPoint wxWindowCocoa::CocoaTransformBoundsToWx(NSPoint pointBounds)
  908. {
  909. // TODO: Handle scrolling offset
  910. return CocoaTransformNSViewBoundsToWx(GetNSView(), pointBounds);
  911. }
  912. NSRect wxWindowCocoa::CocoaTransformBoundsToWx(NSRect rectBounds)
  913. {
  914. // TODO: Handle scrolling offset
  915. return CocoaTransformNSViewBoundsToWx(GetNSView(), rectBounds);
  916. }
  917. NSPoint wxWindowCocoa::CocoaTransformWxToBounds(NSPoint pointWx)
  918. {
  919. // TODO: Handle scrolling offset
  920. return CocoaTransformNSViewWxToBounds(GetNSView(), pointWx);
  921. }
  922. NSRect wxWindowCocoa::CocoaTransformWxToBounds(NSRect rectWx)
  923. {
  924. // TODO: Handle scrolling offset
  925. return CocoaTransformNSViewWxToBounds(GetNSView(), rectWx);
  926. }
  927. WX_NSAffineTransform wxWindowCocoa::CocoaGetWxToBoundsTransform()
  928. {
  929. // TODO: Handle scrolling offset
  930. NSAffineTransform *transform = wxCocoaDCImpl::CocoaGetWxToBoundsTransform([GetNSView() isFlipped], [GetNSView() bounds].size.height);
  931. return transform;
  932. }
  933. bool wxWindowCocoa::Cocoa_drawRect(const NSRect &rect)
  934. {
  935. wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_drawRect"));
  936. // Recursion can happen if the event loop runs from within the paint
  937. // handler. For instance, if an assertion dialog is shown.
  938. // FIXME: This seems less than ideal.
  939. if(m_isInPaint)
  940. {
  941. wxLogDebug(wxT("Paint event recursion!"));
  942. return false;
  943. }
  944. m_isInPaint = true;
  945. // Set m_updateRegion
  946. const NSRect *rects = &rect; // The bounding box of the region
  947. NSInteger countRects = 1;
  948. // Try replacing the larger rectangle with a list of smaller ones:
  949. if ([GetNSView() respondsToSelector:@selector(getRectsBeingDrawn:count:)])
  950. [GetNSView() getRectsBeingDrawn:&rects count:&countRects];
  951. NSRect *transformedRects = (NSRect*)malloc(sizeof(NSRect)*countRects);
  952. for(int i=0; i<countRects; i++)
  953. {
  954. transformedRects[i] = CocoaTransformBoundsToWx(rects[i]);
  955. }
  956. m_updateRegion = wxRegion(transformedRects,countRects);
  957. free(transformedRects);
  958. wxPaintEvent event(m_windowId);
  959. event.SetEventObject(this);
  960. bool ret = HandleWindowEvent(event);
  961. m_isInPaint = false;
  962. return ret;
  963. }
  964. void wxWindowCocoa::InitMouseEvent(wxMouseEvent& event, WX_NSEvent cocoaEvent)
  965. {
  966. wxASSERT_MSG([m_cocoaNSView window]==[cocoaEvent window],wxT("Mouse event for different NSWindow"));
  967. // Mouse events happen at the NSWindow level so we need to convert
  968. // into our bounds coordinates then convert to wx coordinates.
  969. NSPoint cocoaPoint = [m_cocoaNSView convertPoint:[(NSEvent*)cocoaEvent locationInWindow] fromView:nil];
  970. if( m_wxCocoaScrollView != NULL)
  971. {
  972. // This gets the wx client area (i.e. the visible portion of the content) in
  973. // the coordinate system of our (the doucment) view.
  974. NSRect documentVisibleRect = [[m_wxCocoaScrollView->GetNSScrollView() contentView] documentVisibleRect];
  975. // For horizontal, simply subtract the origin.
  976. // e.g. if the origin is at 123 and the user clicks as far left as possible then
  977. // the coordinate that wx wants is 0.
  978. cocoaPoint.x -= documentVisibleRect.origin.x;
  979. if([m_cocoaNSView isFlipped])
  980. {
  981. // In the flipped view case this works exactly like horizontal.
  982. cocoaPoint.y -= documentVisibleRect.origin.y;
  983. }
  984. // For vertical we have to mind non-flipped (e.g. y=0 at bottom) views.
  985. // We also need to mind the fact that we're still in Cocoa coordinates
  986. // and not wx coordinates. The wx coordinate translation will still occur
  987. // and that is going to be wxY = boundsH - cocoaY for non-flipped views.
  988. // When we consider scrolling we are truly interested in how far the top
  989. // edge of the bounds rectangle is scrolled off the screen.
  990. // Assuming the bounds origin is 0 (which is an assumption we make in
  991. // wxCocoa since wxWidgets has no analog to it) then the top edge of
  992. // the bounds rectangle is simply its height. The top edge of the
  993. // documentVisibleRect (e.g. the client area) is its height plus
  994. // its origin.
  995. // Thus, we simply need add the distance between the bounds top
  996. // and the client (docuemntVisibleRect) top.
  997. // Or putting it another way, we subtract the distance between the
  998. // client top and the bounds top.
  999. else
  1000. {
  1001. NSRect bounds = [m_cocoaNSView bounds];
  1002. CGFloat scrollYOrigin = (bounds.size.height - (documentVisibleRect.origin.y + documentVisibleRect.size.height));
  1003. cocoaPoint.y += scrollYOrigin;
  1004. }
  1005. }
  1006. NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
  1007. // FIXME: Should we be adjusting for client area origin?
  1008. const wxPoint &clientorigin = GetClientAreaOrigin();
  1009. event.m_x = (wxCoord)pointWx.x - clientorigin.x;
  1010. event.m_y = (wxCoord)pointWx.y - clientorigin.y;
  1011. event.m_shiftDown = [cocoaEvent modifierFlags] & NSShiftKeyMask;
  1012. event.m_controlDown = [cocoaEvent modifierFlags] & NSControlKeyMask;
  1013. event.m_altDown = [cocoaEvent modifierFlags] & NSAlternateKeyMask;
  1014. event.m_metaDown = [cocoaEvent modifierFlags] & NSCommandKeyMask;
  1015. // TODO: set timestamp?
  1016. event.SetEventObject(this);
  1017. event.SetId(GetId());
  1018. }
  1019. bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent)
  1020. {
  1021. wxMouseEvent event(wxEVT_MOTION);
  1022. InitMouseEvent(event,theEvent);
  1023. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y);
  1024. return HandleWindowEvent(event);
  1025. }
  1026. void wxWindowCocoa::Cocoa_synthesizeMouseMoved()
  1027. {
  1028. wxMouseEvent event(wxEVT_MOTION);
  1029. NSWindow *window = [GetNSView() window];
  1030. NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream];
  1031. NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil];
  1032. NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint);
  1033. // FIXME: Should we be adjusting for client area origin?
  1034. const wxPoint &clientorigin = GetClientAreaOrigin();
  1035. event.m_x = (wxCoord)pointWx.x - clientorigin.x;
  1036. event.m_y = (wxCoord)pointWx.y - clientorigin.y;
  1037. // TODO: Handle shift, control, alt, meta flags
  1038. event.SetEventObject(this);
  1039. event.SetId(GetId());
  1040. wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y);
  1041. HandleWindowEvent(event);
  1042. }
  1043. bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent)
  1044. {
  1045. if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
  1046. {
  1047. m_visibleTrackingRectManager->BeginSynthesizingEvents();
  1048. // Although we synthesize the mouse moved events we don't poll for them but rather send them only when
  1049. // some other event comes in. That other event is (guess what) mouse moved events that will be sent
  1050. // to the NSWindow which will forward them on to the first responder. We are not likely to be the
  1051. // first responder, so the mouseMoved: events are effectively discarded.
  1052. [[GetNSView() window] setAcceptsMouseMovedEvents:YES];
  1053. wxMouseEvent event(wxEVT_ENTER_WINDOW);
  1054. InitMouseEvent(event,theEvent);
  1055. wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Entered TR#%d @%d,%d"),this,[theEvent trackingNumber], event.m_x,event.m_y);
  1056. return HandleWindowEvent(event);
  1057. }
  1058. else
  1059. return false;
  1060. }
  1061. bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent)
  1062. {
  1063. if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent))
  1064. {
  1065. m_visibleTrackingRectManager->StopSynthesizingEvents();
  1066. wxMouseEvent event(wxEVT_LEAVE_WINDOW);
  1067. InitMouseEvent(event,theEvent);
  1068. wxLogTrace(wxTRACE_COCOA_TrackingRect,wxT("wxwin=%p Mouse Exited TR#%d @%d,%d"),this,[theEvent trackingNumber],event.m_x,event.m_y);
  1069. return HandleWindowEvent(event);
  1070. }
  1071. else
  1072. return false;
  1073. }
  1074. bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent)
  1075. {
  1076. wxMouseEvent event([theEvent clickCount]<2?wxEVT_LEFT_DOWN:wxEVT_LEFT_DCLICK);
  1077. InitMouseEvent(event,theEvent);
  1078. wxLogTrace(wxTRACE_COCOA,wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
  1079. return HandleWindowEvent(event);
  1080. }
  1081. bool wxWindowCocoa::Cocoa_mouseDragged(WX_NSEvent theEvent)
  1082. {
  1083. wxMouseEvent event(wxEVT_MOTION);
  1084. InitMouseEvent(event,theEvent);
  1085. event.m_leftDown = true;
  1086. wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
  1087. return HandleWindowEvent(event);
  1088. }
  1089. bool wxWindowCocoa::Cocoa_mouseUp(WX_NSEvent theEvent)
  1090. {
  1091. wxMouseEvent event(wxEVT_LEFT_UP);
  1092. InitMouseEvent(event,theEvent);
  1093. wxLogTrace(wxTRACE_COCOA,wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
  1094. return HandleWindowEvent(event);
  1095. }
  1096. bool wxWindowCocoa::Cocoa_rightMouseDown(WX_NSEvent theEvent)
  1097. {
  1098. wxMouseEvent event([theEvent clickCount]<2?wxEVT_RIGHT_DOWN:wxEVT_RIGHT_DCLICK);
  1099. InitMouseEvent(event,theEvent);
  1100. wxLogDebug(wxT("Mouse Down @%d,%d num clicks=%d"),event.m_x,event.m_y,[theEvent clickCount]);
  1101. return HandleWindowEvent(event);
  1102. }
  1103. bool wxWindowCocoa::Cocoa_rightMouseDragged(WX_NSEvent theEvent)
  1104. {
  1105. wxMouseEvent event(wxEVT_MOTION);
  1106. InitMouseEvent(event,theEvent);
  1107. event.m_rightDown = true;
  1108. wxLogDebug(wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y);
  1109. return HandleWindowEvent(event);
  1110. }
  1111. bool wxWindowCocoa::Cocoa_rightMouseUp(WX_NSEvent theEvent)
  1112. {
  1113. wxMouseEvent event(wxEVT_RIGHT_UP);
  1114. InitMouseEvent(event,theEvent);
  1115. wxLogDebug(wxT("Mouse Up @%d,%d"),event.m_x,event.m_y);
  1116. return HandleWindowEvent(event);
  1117. }
  1118. bool wxWindowCocoa::Cocoa_otherMouseDown(WX_NSEvent theEvent)
  1119. {
  1120. return false;
  1121. }
  1122. bool wxWindowCocoa::Cocoa_otherMouseDragged(WX_NSEvent theEvent)
  1123. {
  1124. return false;
  1125. }
  1126. bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent)
  1127. {
  1128. return false;
  1129. }
  1130. void wxWindowCocoa::Cocoa_FrameChanged(void)
  1131. {
  1132. // We always get this message for the real NSView which may have been
  1133. // enclosed in an NSScrollView. If that's the case then what we're
  1134. // effectively getting here is a notifcation that the
  1135. // virtual sized changed.. which we don't need to send on since
  1136. // wx has no concept of this whatsoever.
  1137. bool isViewForSuperview = (m_wxCocoaScrollView == NULL);
  1138. if(isViewForSuperview)
  1139. {
  1140. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this);
  1141. if(m_visibleTrackingRectManager != NULL)
  1142. m_visibleTrackingRectManager->RebuildTrackingRect();
  1143. wxSizeEvent event(GetSize(), m_windowId);
  1144. event.SetEventObject(this);
  1145. HandleWindowEvent(event);
  1146. }
  1147. else
  1148. {
  1149. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged ignored"),this);
  1150. }
  1151. }
  1152. bool wxWindowCocoa::Cocoa_resetCursorRects()
  1153. {
  1154. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this);
  1155. // When we are called there may be a queued tracking rect event (mouse entered or exited) and
  1156. // we won't know it. A specific example is wxGenericHyperlinkCtrl changing the cursor from its
  1157. // mouse exited event. If the control happens to share the edge with its parent window which is
  1158. // also tracking mouse events then Cocoa receives two mouse exited events from the window server.
  1159. // The first one will cause wxGenericHyperlinkCtrl to call wxWindow::SetCursor which will
  1160. // invaildate the cursor rect causing Cocoa to schedule cursor rect reset with the run loop
  1161. // which willl in turn call us before exiting for the next user event.
  1162. // If we are the parent window then rebuilding our tracking rectangle will cause us to miss
  1163. // our mouse exited event because the already queued event will have the old tracking rect
  1164. // tag. The simple solution is to only rebuild our tracking rect if we need to.
  1165. if(m_visibleTrackingRectManager != NULL)
  1166. m_visibleTrackingRectManager->RebuildTrackingRectIfNeeded();
  1167. if(!m_cursor.GetNSCursor())
  1168. return false;
  1169. [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
  1170. return true;
  1171. }
  1172. bool wxWindowCocoa::SetCursor(const wxCursor &cursor)
  1173. {
  1174. if(!wxWindowBase::SetCursor(cursor))
  1175. return false;
  1176. // Set up the cursor rect so that invalidateCursorRectsForView: will destroy it.
  1177. // If we don't do this then Cocoa thinks (rightly) that we don't have any cursor
  1178. // rects and thus won't ever call resetCursorRects.
  1179. [GetNSView() addCursorRect: [GetNSView() visibleRect] cursor: m_cursor.GetNSCursor()];
  1180. // Invalidate the cursor rects so the cursor will change
  1181. // Note that it is not enough to remove the old one (if any) and add the new one.
  1182. // For the rects to work properly, Cocoa itself must call resetCursorRects.
  1183. [[GetNSView() window] invalidateCursorRectsForView:GetNSView()];
  1184. return true;
  1185. }
  1186. bool wxWindowCocoa::Cocoa_viewDidMoveToWindow()
  1187. {
  1188. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this);
  1189. // Set up new tracking rects. I am reasonably sure the new window must be set before doing this.
  1190. if(m_visibleTrackingRectManager != NULL)
  1191. m_visibleTrackingRectManager->BuildTrackingRect();
  1192. return false;
  1193. }
  1194. bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
  1195. {
  1196. wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow);
  1197. // Clear tracking rects. It is imperative this be done before the new window is set.
  1198. if(m_visibleTrackingRectManager != NULL)
  1199. m_visibleTrackingRectManager->ClearTrackingRect();
  1200. return false;
  1201. }
  1202. bool wxWindow::Close(bool force)
  1203. {
  1204. // The only reason this function exists is that it is virtual and
  1205. // wxTopLevelWindowCocoa will override it.
  1206. return wxWindowBase::Close(force);
  1207. }
  1208. void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
  1209. {
  1210. [[oldView superview] replaceSubview:oldView with:newView];
  1211. }
  1212. void wxWindow::DoEnable(bool enable)
  1213. {
  1214. CocoaSetEnabled(enable);
  1215. }
  1216. bool wxWindow::Show(bool show)
  1217. {
  1218. wxAutoNSAutoreleasePool pool;
  1219. // If the window is marked as visible, then it shouldn't have a dummy view
  1220. // If the window is marked hidden, then it should have a dummy view
  1221. // wxSpinCtrl (generic) abuses m_isShown, don't use it for any logic
  1222. // wxASSERT_MSG( (m_isShown && !m_dummyNSView) || (!m_isShown && m_dummyNSView),wxT("wxWindow: m_isShown does not agree with m_dummyNSView"));
  1223. // Return false if there isn't a window to show or hide
  1224. NSView *cocoaView = GetNSViewForHiding();
  1225. if(!cocoaView)
  1226. return false;
  1227. if(show)
  1228. {
  1229. // If state isn't changing, return false
  1230. if(!m_cocoaHider)
  1231. return false;
  1232. // Replace the stand-in view with the real one and destroy the dummy view
  1233. CocoaReplaceView(m_cocoaHider->GetNSView(), cocoaView);
  1234. wxASSERT(![m_cocoaHider->GetNSView() superview]);
  1235. delete m_cocoaHider;
  1236. m_cocoaHider = NULL;
  1237. wxASSERT([cocoaView superview]);
  1238. // Schedule an update of the key view loop (NOTE: 10.4+ only.. argh)
  1239. NSWindow *window = [cocoaView window];
  1240. if(window != nil)
  1241. {
  1242. // Delay this until returning to the event loop for a couple of reasons:
  1243. // 1. If a lot of stuff is shown/hidden we avoid recalculating needlessly
  1244. // 2. NSWindow does not seem to see the newly shown views if we do it right now.
  1245. if([window respondsToSelector:@selector(recalculateKeyViewLoop)])
  1246. [window performSelector:@selector(recalculateKeyViewLoop) withObject:nil afterDelay:0.0];
  1247. }
  1248. }
  1249. else
  1250. {
  1251. // If state isn't changing, return false
  1252. if(m_cocoaHider)
  1253. return false;
  1254. // Handle the first responder
  1255. NSWindow *window = [cocoaView window];
  1256. if(window != nil)
  1257. {
  1258. NSResponder *firstResponder = [window firstResponder];
  1259. if([firstResponder isKindOfClass:[NSView class]] && [(NSView*)firstResponder isDescendantOf:cocoaView])
  1260. {
  1261. BOOL didResign = [window makeFirstResponder:nil];
  1262. // If the current first responder (one of our subviews) refuses to give
  1263. // up its status, then fail now and don't hide this view
  1264. if(didResign == NO)
  1265. return false;
  1266. }
  1267. }
  1268. // Create a new view to stand in for the real one (via wxWindowCocoaHider) and replace
  1269. // the real one with the stand in.
  1270. m_cocoaHider = new wxWindowCocoaHider(this);
  1271. // NOTE: replaceSubview:with will cause m_cocoaNSView to be
  1272. // (auto)released which balances out addSubview
  1273. CocoaReplaceView(cocoaView, m_cocoaHider->GetNSView());
  1274. // m_cocoaNSView is now only retained by us
  1275. wxASSERT([m_cocoaHider->GetNSView() superview]);
  1276. wxASSERT(![cocoaView superview]);
  1277. }
  1278. m_isShown = show;
  1279. return true;
  1280. }
  1281. void wxWindowCocoa::DoSetSize(int x, int y, int width, int height, int sizeFlags)
  1282. {
  1283. 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":".");
  1284. int currentX, currentY;
  1285. int currentW, currentH;
  1286. DoGetPosition(&currentX, &currentY);
  1287. DoGetSize(&currentW, &currentH);
  1288. if((x==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
  1289. x=currentX;
  1290. if((y==-1) && !(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
  1291. y=currentY;
  1292. AdjustForParentClientOrigin(x,y,sizeFlags);
  1293. wxSize size(wxDefaultSize);
  1294. if((width==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
  1295. {
  1296. if(sizeFlags&wxSIZE_AUTO_WIDTH)
  1297. {
  1298. size=DoGetBestSize();
  1299. width=size.x;
  1300. }
  1301. else
  1302. width=currentW;
  1303. }
  1304. if((height==-1)&&!(sizeFlags&wxSIZE_ALLOW_MINUS_ONE))
  1305. {
  1306. if(sizeFlags&wxSIZE_AUTO_HEIGHT)
  1307. {
  1308. if(size.x==-1)
  1309. size=DoGetBestSize();
  1310. height=size.y;
  1311. }
  1312. else
  1313. height=currentH;
  1314. }
  1315. DoMoveWindow(x,y,width,height);
  1316. }
  1317. #if wxUSE_TOOLTIPS
  1318. void wxWindowCocoa::DoSetToolTip( wxToolTip *tip )
  1319. {
  1320. wxWindowBase::DoSetToolTip(tip);
  1321. if ( m_tooltip )
  1322. {
  1323. m_tooltip->SetWindow((wxWindow *)this);
  1324. }
  1325. }
  1326. #endif
  1327. void wxWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
  1328. {
  1329. wxAutoNSAutoreleasePool pool;
  1330. wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height);
  1331. NSView *nsview = GetNSViewForSuperview();
  1332. NSView *superview = [nsview superview];
  1333. wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
  1334. NSRect oldFrameRect = [nsview frame];
  1335. NSRect newFrameRect = GetParent()->CocoaTransformWxToBounds(NSMakeRect(x,y,width,height));
  1336. [nsview setFrame:newFrameRect];
  1337. // Be sure to redraw the parent to reflect the changed position
  1338. [superview setNeedsDisplayInRect:oldFrameRect];
  1339. [superview setNeedsDisplayInRect:newFrameRect];
  1340. }
  1341. void wxWindowCocoa::SetInitialFrameRect(const wxPoint& pos, const wxSize& size)
  1342. {
  1343. NSView *nsview = GetNSViewForSuperview();
  1344. NSView *superview = [nsview superview];
  1345. wxCHECK_RET(superview,wxT("NSView does not have a superview"));
  1346. wxCHECK_RET(GetParent(), wxT("Window can only be placed correctly when it has a parent"));
  1347. NSRect frameRect = [nsview frame];
  1348. if(size.x!=-1)
  1349. frameRect.size.width = size.x;
  1350. if(size.y!=-1)
  1351. frameRect.size.height = size.y;
  1352. frameRect.origin.x = pos.x;
  1353. frameRect.origin.y = pos.y;
  1354. // Tell Cocoa to change the margin between the bottom of the superview
  1355. // and the bottom of the control. Keeps the control pinned to the top
  1356. // of its superview so that its position in the wxWidgets coordinate
  1357. // system doesn't change.
  1358. if(![superview isFlipped])
  1359. [nsview setAutoresizingMask: NSViewMinYMargin];
  1360. // MUST set the mask before setFrame: which can generate a size event
  1361. // and cause a scroller to be added!
  1362. frameRect = GetParent()->CocoaTransformWxToBounds(frameRect);
  1363. [nsview setFrame: frameRect];
  1364. }
  1365. // Get total size
  1366. void wxWindow::DoGetSize(int *w, int *h) const
  1367. {
  1368. NSRect cocoaRect = [GetNSViewForSuperview() frame];
  1369. if(w)
  1370. *w=(int)cocoaRect.size.width;
  1371. if(h)
  1372. *h=(int)cocoaRect.size.height;
  1373. wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
  1374. }
  1375. void wxWindow::DoGetPosition(int *x, int *y) const
  1376. {
  1377. NSView *nsview = GetNSViewForSuperview();
  1378. NSRect cocoaRect = [nsview frame];
  1379. NSRect rectWx = GetParent()->CocoaTransformBoundsToWx(cocoaRect);
  1380. if(x)
  1381. *x=(int)rectWx.origin.x;
  1382. if(y)
  1383. *y=(int)rectWx.origin.y;
  1384. wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("wxWindow=%p::DoGetPosition = (%d,%d)"),this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
  1385. }
  1386. WXWidget wxWindow::GetHandle() const
  1387. {
  1388. return m_cocoaNSView;
  1389. }
  1390. wxWindow* wxWindow::GetWxWindow() const
  1391. {
  1392. return (wxWindow*) this;
  1393. }
  1394. void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
  1395. {
  1396. [m_cocoaNSView setNeedsDisplay:YES];
  1397. }
  1398. void wxWindow::SetFocus()
  1399. {
  1400. if([GetNSView() acceptsFirstResponder])
  1401. [[GetNSView() window] makeFirstResponder: GetNSView()];
  1402. }
  1403. void wxWindow::DoCaptureMouse()
  1404. {
  1405. // TODO
  1406. sm_capturedWindow = this;
  1407. }
  1408. void wxWindow::DoReleaseMouse()
  1409. {
  1410. // TODO
  1411. sm_capturedWindow = NULL;
  1412. }
  1413. void wxWindow::DoScreenToClient(int *x, int *y) const
  1414. {
  1415. // Point in cocoa screen coordinates:
  1416. NSPoint cocoaScreenPoint = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x!=NULL?*x:0, y!=NULL?*y:0, 0, 0, false);
  1417. NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
  1418. NSWindow *theWindow = [clientView window];
  1419. // Point in window's base coordinate system:
  1420. NSPoint windowPoint = [theWindow convertScreenToBase:cocoaScreenPoint];
  1421. // Point in view's bounds coordinate system
  1422. NSPoint boundsPoint = [clientView convertPoint:windowPoint fromView:nil];
  1423. // Point in wx client coordinates:
  1424. NSPoint theWxClientPoint = CocoaTransformNSViewBoundsToWx(clientView, boundsPoint);
  1425. if(x!=NULL)
  1426. *x = theWxClientPoint.x;
  1427. if(y!=NULL)
  1428. *y = theWxClientPoint.y;
  1429. }
  1430. void wxWindow::DoClientToScreen(int *x, int *y) const
  1431. {
  1432. // Point in wx client coordinates
  1433. NSPoint theWxClientPoint = NSMakePoint(x!=NULL?*x:0, y!=NULL?*y:0);
  1434. NSView *clientView = const_cast<wxWindow*>(this)->GetNSView();
  1435. // Point in the view's bounds coordinate system
  1436. NSPoint boundsPoint = CocoaTransformNSViewWxToBounds(clientView, theWxClientPoint);
  1437. // Point in the window's base coordinate system
  1438. NSPoint windowPoint = [clientView convertPoint:boundsPoint toView:nil];
  1439. NSWindow *theWindow = [clientView window];
  1440. // Point in Cocoa's screen coordinates
  1441. NSPoint screenPoint = [theWindow convertBaseToScreen:windowPoint];
  1442. // Act as though this was the origin of a 0x0 rectangle
  1443. NSRect screenPointRect = NSMakeRect(screenPoint.x, screenPoint.y, 0, 0);
  1444. // Convert that rectangle to wx coordinates
  1445. wxPoint theWxScreenPoint = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(screenPointRect);
  1446. if(*x)
  1447. *x = theWxScreenPoint.x;
  1448. if(*y)
  1449. *y = theWxScreenPoint.y;
  1450. }
  1451. // Get size *available for subwindows* i.e. excluding menu bar etc.
  1452. void wxWindow::DoGetClientSize(int *x, int *y) const
  1453. {
  1454. wxLogTrace(wxTRACE_COCOA,wxT("DoGetClientSize:"));
  1455. if(m_wxCocoaScrollView)
  1456. m_wxCocoaScrollView->DoGetClientSize(x,y);
  1457. else
  1458. wxWindowCocoa::DoGetSize(x,y);
  1459. }
  1460. void wxWindow::DoSetClientSize(int width, int height)
  1461. {
  1462. wxLogTrace(wxTRACE_COCOA_Window_Size,wxT("DoSetClientSize=(%d,%d)"),width,height);
  1463. if(m_wxCocoaScrollView)
  1464. m_wxCocoaScrollView->ClientSizeToSize(width,height);
  1465. CocoaSetWxWindowSize(width,height);
  1466. }
  1467. void wxWindow::CocoaSetWxWindowSize(int width, int height)
  1468. {
  1469. wxWindowCocoa::DoSetSize(wxDefaultCoord,wxDefaultCoord,width,height,wxSIZE_USE_EXISTING);
  1470. }
  1471. void wxWindow::SetLabel(const wxString& WXUNUSED(label))
  1472. {
  1473. // Intentional no-op.
  1474. }
  1475. wxString wxWindow::GetLabel() const
  1476. {
  1477. // General Get/Set of labels is implemented in wxControlBase
  1478. wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed."));
  1479. return wxEmptyString;
  1480. }
  1481. int wxWindow::GetCharHeight() const
  1482. {
  1483. // TODO
  1484. return 10;
  1485. }
  1486. int wxWindow::GetCharWidth() const
  1487. {
  1488. // TODO
  1489. return 5;
  1490. }
  1491. void wxWindow::DoGetTextExtent(const wxString& string, int *outX, int *outY,
  1492. int *outDescent, int *outExternalLeading, const wxFont *inFont) const
  1493. {
  1494. // FIXME: This obviously ignores the window's font (if any) along with any size
  1495. // transformations. However, it's better than nothing.
  1496. // We don't create a wxClientDC because we don't want to accidently be able to use
  1497. // it for drawing.
  1498. wxClientDC tmpdc(const_cast<wxWindow*>(this));
  1499. return tmpdc.GetTextExtent(string, outX, outY, outDescent, outExternalLeading, inFont);
  1500. }
  1501. // Coordinates relative to the window
  1502. void wxWindow::WarpPointer (int x_pos, int y_pos)
  1503. {
  1504. // TODO
  1505. }
  1506. int wxWindow::GetScrollPos(int orient) const
  1507. {
  1508. if(m_wxCocoaScrollView != NULL)
  1509. return m_wxCocoaScrollView->GetScrollPos(static_cast<wxOrientation>(orient & wxBOTH));
  1510. else
  1511. return 0;
  1512. }
  1513. // This now returns the whole range, not just the number
  1514. // of positions that we can scroll.
  1515. int wxWindow::GetScrollRange(int orient) const
  1516. {
  1517. if(m_wxCocoaScrollView != NULL)
  1518. return m_wxCocoaScrollView->GetScrollRange(static_cast<wxOrientation>(orient & wxBOTH));
  1519. else
  1520. return 0;
  1521. }
  1522. int wxWindow::GetScrollThumb(int orient) const
  1523. {
  1524. if(m_wxCocoaScrollView != NULL)
  1525. return m_wxCocoaScrollView->GetScrollThumb(static_cast<wxOrientation>(orient & wxBOTH));
  1526. else
  1527. return 0;
  1528. }
  1529. void wxWindow::SetScrollPos(int orient, int pos, bool refresh)
  1530. {
  1531. if(m_wxCocoaScrollView != NULL)
  1532. return m_wxCocoaScrollView->SetScrollPos(static_cast<wxOrientation>(orient & wxBOTH), pos);
  1533. }
  1534. void wxWindow::CocoaCreateNSScrollView()
  1535. {
  1536. if(!m_wxCocoaScrollView)
  1537. {
  1538. m_wxCocoaScrollView = new wxWindowCocoaScrollView(this);
  1539. }
  1540. }
  1541. // New function that will replace some of the above.
  1542. void wxWindow::SetScrollbar(int orient, int pos, int thumbVisible,
  1543. int range, bool refresh)
  1544. {
  1545. CocoaCreateNSScrollView();
  1546. m_wxCocoaScrollView->SetScrollbar(orient, pos, thumbVisible, range);
  1547. // TODO: Handle refresh (if we even need to)
  1548. }
  1549. // Does a physical scroll
  1550. void wxWindow::ScrollWindow(int dx, int dy, const wxRect *rect)
  1551. {
  1552. if(m_wxCocoaScrollView != NULL)
  1553. m_wxCocoaScrollView->ScrollWindow(dx, dy, rect);
  1554. }
  1555. void wxWindow::DoSetVirtualSize( int x, int y )
  1556. {
  1557. // Call wxWindowBase method which will set m_virtualSize to the appropriate value,
  1558. // possibly not what the caller passed in. For example, the current implementation
  1559. // clamps the width and height to within the min/max virtual ranges.
  1560. // wxDefaultCoord is passed through unchanged but then GetVirtualSize() will correct
  1561. // that by returning effectively max(virtual, client)
  1562. wxWindowBase::DoSetVirtualSize(x,y);
  1563. // Create the scroll view if it hasn't been already.
  1564. CocoaCreateNSScrollView();
  1565. // The GetVirtualSize automatically increases the size to max(client,virtual)
  1566. m_wxCocoaScrollView->UpdateSizes();
  1567. }
  1568. bool wxWindow::SetFont(const wxFont& font)
  1569. {
  1570. // FIXME: We may need to handle wx font inheritance.
  1571. return wxWindowBase::SetFont(font);
  1572. }
  1573. #if 0 // these are used when debugging the algorithm.
  1574. static char const * const comparisonresultStrings[] =
  1575. { "<"
  1576. , "=="
  1577. , ">"
  1578. };
  1579. #endif
  1580. class CocoaWindowCompareContext
  1581. {
  1582. wxDECLARE_NO_COPY_CLASS(CocoaWindowCompareContext);
  1583. public:
  1584. CocoaWindowCompareContext(); // Not implemented
  1585. CocoaWindowCompareContext(NSView *target, NSArray *subviews)
  1586. {
  1587. m_target = target;
  1588. // Cocoa sorts subviews in-place.. make a copy
  1589. m_subviews = [subviews copy];
  1590. }
  1591. ~CocoaWindowCompareContext()
  1592. { // release the copy
  1593. [m_subviews release];
  1594. }
  1595. NSView* target()
  1596. { return m_target; }
  1597. NSArray* subviews()
  1598. { return m_subviews; }
  1599. /* Helper function that returns the comparison based off of the original ordering */
  1600. CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
  1601. {
  1602. NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
  1603. NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
  1604. // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
  1605. // likely compare higher than the other view which is reasonable considering the only way that
  1606. // can happen is if the subview was added after our call to subviews but before the call to
  1607. // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
  1608. // that case should never occur anyway because that would imply a multi-threaded GUI call
  1609. // which is a big no-no with Cocoa.
  1610. // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
  1611. NSComparisonResult result = (firstI < secondI)
  1612. ? NSOrderedAscending /* -1 */
  1613. : (firstI > secondI)
  1614. ? NSOrderedDescending /* 1 */
  1615. : NSOrderedSame /* 0 */;
  1616. #if 0 // Enable this if you need to debug the algorithm.
  1617. NSLog(@"%@ [%d] %s %@ [%d]\n", first, firstI, comparisonresultStrings[result+1], second, secondI);
  1618. #endif
  1619. return result;
  1620. }
  1621. private:
  1622. /* The subview we are trying to Raise or Lower */
  1623. NSView *m_target;
  1624. /* A copy of the original array of subviews */
  1625. NSArray *m_subviews;
  1626. };
  1627. /* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
  1628. * the target view is always higher than every other view. When comparing two views neither of
  1629. * which is the target, it returns the correct response based on the original ordering
  1630. */
  1631. static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
  1632. {
  1633. CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
  1634. // first should be ordered higher
  1635. if(first==compareContext->target())
  1636. return NSOrderedDescending;
  1637. // second should be ordered higher
  1638. if(second==compareContext->target())
  1639. return NSOrderedAscending;
  1640. return compareContext->CompareUsingOriginalOrdering(first,second);
  1641. }
  1642. // Raise the window to the top of the Z order
  1643. void wxWindow::Raise()
  1644. {
  1645. // wxAutoNSAutoreleasePool pool;
  1646. NSView *nsview = GetNSViewForSuperview();
  1647. NSView *superview = [nsview superview];
  1648. CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
  1649. [superview sortSubviewsUsingFunction:
  1650. CocoaRaiseWindowCompareFunction
  1651. context: &compareContext];
  1652. }
  1653. /* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
  1654. * the target view is always lower than every other view. When comparing two views neither of
  1655. * which is the target, it returns the correct response based on the original ordering
  1656. */
  1657. static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
  1658. {
  1659. CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
  1660. // first should be ordered lower
  1661. if(first==compareContext->target())
  1662. return NSOrderedAscending;
  1663. // second should be ordered lower
  1664. if(second==compareContext->target())
  1665. return NSOrderedDescending;
  1666. return compareContext->CompareUsingOriginalOrdering(first,second);
  1667. }
  1668. // Lower the window to the bottom of the Z order
  1669. void wxWindow::Lower()
  1670. {
  1671. NSView *nsview = GetNSViewForSuperview();
  1672. NSView *superview = [nsview superview];
  1673. CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
  1674. #if 0
  1675. NSLog(@"Target:\n%@\n", nsview);
  1676. NSLog(@"Before:\n%@\n", compareContext.subviews());
  1677. #endif
  1678. [superview sortSubviewsUsingFunction:
  1679. CocoaLowerWindowCompareFunction
  1680. context: &compareContext];
  1681. #if 0
  1682. NSLog(@"After:\n%@\n", [superview subviews]);
  1683. #endif
  1684. }
  1685. bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y)
  1686. {
  1687. return false;
  1688. }
  1689. // Get the window with the focus
  1690. wxWindow *wxWindowBase::DoFindFocus()
  1691. {
  1692. // Basically we are somewhat emulating the responder chain here except
  1693. // we are only loking for the first responder in the key window or
  1694. // upon failing to find one if the main window is different we look
  1695. // for the first responder in the main window.
  1696. // Note that the firstResponder doesn't necessarily have to be an
  1697. // NSView but wxCocoaNSView::GetFromCocoa() will simply return
  1698. // NULL unless it finds its argument in its hash map.
  1699. wxCocoaNSView *win;
  1700. NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
  1701. win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([keyWindow firstResponder]));
  1702. if(win)
  1703. return win->GetWxWindow();
  1704. NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow];
  1705. if(mainWindow == keyWindow)
  1706. return NULL;
  1707. win = wxCocoaNSView::GetFromCocoa(static_cast<NSView*>([mainWindow firstResponder]));
  1708. if(win)
  1709. return win->GetWxWindow();
  1710. return NULL;
  1711. }
  1712. /* static */ wxWindow *wxWindowBase::GetCapture()
  1713. {
  1714. // TODO
  1715. return wxWindowCocoa::sm_capturedWindow;
  1716. }
  1717. wxWindow *wxGetActiveWindow()
  1718. {
  1719. // TODO
  1720. return NULL;
  1721. }
  1722. wxPoint wxGetMousePosition()
  1723. {
  1724. // TODO
  1725. return wxDefaultPosition;
  1726. }
  1727. wxMouseState wxGetMouseState()
  1728. {
  1729. wxMouseState ms;
  1730. // TODO
  1731. return ms;
  1732. }
  1733. wxWindow* wxFindWindowAtPointer(wxPoint& pt)
  1734. {
  1735. pt = wxGetMousePosition();
  1736. return NULL;
  1737. }
  1738. // ========================================================================
  1739. // wxCocoaMouseMovedEventSynthesizer
  1740. // ========================================================================
  1741. #define wxTRACE_COCOA_MouseMovedSynthesizer wxT("COCOA_MouseMovedSynthesizer")
  1742. /* This class registers one run loop observer to cover all windows registered with it.
  1743. * It will register the observer when the first view is registerd and unregister the
  1744. * observer when the last view is unregistered.
  1745. * It is instantiated as a static s_mouseMovedSynthesizer in this file although there
  1746. * is no reason it couldn't be instantiated multiple times.
  1747. */
  1748. class wxCocoaMouseMovedEventSynthesizer
  1749. {
  1750. wxDECLARE_NO_COPY_CLASS(wxCocoaMouseMovedEventSynthesizer);
  1751. public:
  1752. wxCocoaMouseMovedEventSynthesizer()
  1753. { m_lastScreenMouseLocation = NSZeroPoint;
  1754. }
  1755. ~wxCocoaMouseMovedEventSynthesizer();
  1756. void RegisterWxCocoaView(wxCocoaNSView *aView);
  1757. void UnregisterWxCocoaView(wxCocoaNSView *aView);
  1758. void SynthesizeMouseMovedEvent();
  1759. protected:
  1760. void AddRunLoopObserver();
  1761. void RemoveRunLoopObserver();
  1762. wxCFRef<CFRunLoopObserverRef> m_runLoopObserver;
  1763. std::list<wxCocoaNSView*> m_registeredViews;
  1764. NSPoint m_lastScreenMouseLocation;
  1765. static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
  1766. };
  1767. void wxCocoaMouseMovedEventSynthesizer::RegisterWxCocoaView(wxCocoaNSView *aView)
  1768. {
  1769. m_registeredViews.push_back(aView);
  1770. wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Registered wxCocoaNSView=%p"), aView);
  1771. if(!m_registeredViews.empty() && m_runLoopObserver == NULL)
  1772. {
  1773. AddRunLoopObserver();
  1774. }
  1775. }
  1776. void wxCocoaMouseMovedEventSynthesizer::UnregisterWxCocoaView(wxCocoaNSView *aView)
  1777. {
  1778. m_registeredViews.remove(aView);
  1779. wxLogTrace(wxTRACE_COCOA_MouseMovedSynthesizer, wxT("Unregistered wxCocoaNSView=%p"), aView);
  1780. if(m_registeredViews.empty() && m_runLoopObserver != NULL)
  1781. {
  1782. RemoveRunLoopObserver();
  1783. }
  1784. }
  1785. wxCocoaMouseMovedEventSynthesizer::~wxCocoaMouseMovedEventSynthesizer()
  1786. {
  1787. if(!m_registeredViews.empty())
  1788. {
  1789. // This means failure to clean up so we report on it as a debug message.
  1790. wxLogDebug(wxT("There are still %d wxCocoaNSView registered to receive mouse moved events at static destruction time"), m_registeredViews.size());
  1791. m_registeredViews.clear();
  1792. }
  1793. if(m_runLoopObserver != NULL)
  1794. {
  1795. // This should not occur unless m_registeredViews was not empty since the last object unregistered should have done this.
  1796. wxLogDebug(wxT("Removing run loop observer during static destruction time."));
  1797. RemoveRunLoopObserver();
  1798. }
  1799. }
  1800. void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
  1801. {
  1802. reinterpret_cast<wxCocoaMouseMovedEventSynthesizer*>(info)->SynthesizeMouseMovedEvent();
  1803. }
  1804. void wxCocoaMouseMovedEventSynthesizer::AddRunLoopObserver()
  1805. {
  1806. CFRunLoopObserverContext observerContext =
  1807. { 0
  1808. , this
  1809. , NULL
  1810. , NULL
  1811. , NULL
  1812. };
  1813. // The kCFRunLoopExit observation point is used such that we hook the run loop after it has already decided that
  1814. // it is going to exit which is generally for the purpose of letting the event loop process the next Cocoa event.
  1815. // Executing our procedure within the run loop (e.g. kCFRunLoopBeforeWaiting which was used before) results
  1816. // in our observer procedure being called before the run loop has decided that it is going to return control to
  1817. // the Cocoa event loop. One major problem is uncovered by the wxGenericHyperlinkCtrl (consider this to be "user
  1818. // code") which changes the window's cursor and thus causes the cursor rectangle's to be invalidated.
  1819. // Cocoa implements this invalidation using a delayed notification scheme whereby the resetCursorRects method
  1820. // won't be called until the CFRunLoop gets around to it. If the CFRunLoop has not yet exited then it will get
  1821. // around to it before letting the event loop do its work. This has some very odd effects on the way the
  1822. // newly created tracking rects function. In particular, we will often miss the mouseExited: message if the
  1823. // user flicks the mouse quickly enough such that the mouse is already outside of the tracking rect by the
  1824. // time the new one is built.
  1825. // Observing from the kCFRunLoopExit point gives Cocoa's event loop an opportunity to chew some events before it cedes
  1826. // control back to the CFRunLoop, thus causing the delayed notifications to fire at an appropriate time and
  1827. // the mouseExited: message to be sent properly.
  1828. m_runLoopObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext));
  1829. CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
  1830. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Added tracking rect run loop observer"));
  1831. }
  1832. void wxCocoaMouseMovedEventSynthesizer::RemoveRunLoopObserver()
  1833. {
  1834. CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes);
  1835. m_runLoopObserver.reset();
  1836. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Removed tracking rect run loop observer"));
  1837. }
  1838. void wxCocoaMouseMovedEventSynthesizer::SynthesizeMouseMovedEvent()
  1839. {
  1840. NSPoint screenMouseLocation = [NSEvent mouseLocation];
  1841. // Checking the last mouse location is done for a few reasons:
  1842. // 1. We are observing every iteration of the event loop so we'd be sending out a lot of extraneous events
  1843. // telling the app the mouse moved when the user hit a key for instance.
  1844. // 2. When handling the mouse moved event, user code can do something to the view which will cause Cocoa to
  1845. // call resetCursorRects. Cocoa does this by using a delayed notification which means the event loop gets
  1846. // pumped once which would mean that if we didn't check the mouse location we'd get into a never-ending
  1847. // loop causing the tracking rectangles to constantly be reset.
  1848. if(screenMouseLocation.x != m_lastScreenMouseLocation.x || screenMouseLocation.y != m_lastScreenMouseLocation.y)
  1849. {
  1850. m_lastScreenMouseLocation = screenMouseLocation;
  1851. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Synthesizing mouse moved at screen (%f,%f)"), screenMouseLocation.x, screenMouseLocation.y);
  1852. for(std::list<wxCocoaNSView*>::iterator i = m_registeredViews.begin(); i != m_registeredViews.end(); ++i)
  1853. {
  1854. (*i)->Cocoa_synthesizeMouseMoved();
  1855. }
  1856. }
  1857. }
  1858. // Singleton used for all views:
  1859. static wxCocoaMouseMovedEventSynthesizer s_mouseMovedSynthesizer;
  1860. // ========================================================================
  1861. // wxCocoaTrackingRectManager
  1862. // ========================================================================
  1863. wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window)
  1864. : m_window(window)
  1865. {
  1866. m_isTrackingRectActive = false;
  1867. BuildTrackingRect();
  1868. }
  1869. void wxCocoaTrackingRectManager::ClearTrackingRect()
  1870. {
  1871. if(m_isTrackingRectActive)
  1872. {
  1873. [m_window->GetNSView() removeTrackingRect:m_trackingRectTag];
  1874. m_isTrackingRectActive = false;
  1875. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Removed tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
  1876. }
  1877. // If we were doing periodic events we need to clear those too
  1878. StopSynthesizingEvents();
  1879. }
  1880. void wxCocoaTrackingRectManager::StopSynthesizingEvents()
  1881. {
  1882. s_mouseMovedSynthesizer.UnregisterWxCocoaView(m_window);
  1883. }
  1884. void wxCocoaTrackingRectManager::BuildTrackingRect()
  1885. {
  1886. // Pool here due to lack of one during wx init phase
  1887. wxAutoNSAutoreleasePool pool;
  1888. wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared"));
  1889. NSView *theView = m_window->GetNSView();
  1890. if([theView window] != nil)
  1891. {
  1892. NSRect visibleRect = [theView visibleRect];
  1893. m_trackingRectTag = [theView addTrackingRect:visibleRect owner:theView userData:NULL assumeInside:NO];
  1894. m_trackingRectInWindowCoordinates = [theView convertRect:visibleRect toView:nil];
  1895. m_isTrackingRectActive = true;
  1896. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("%s@%p: Added tracking rect #%d"), m_window->GetClassInfo()->GetClassName(), m_window, m_trackingRectTag);
  1897. }
  1898. }
  1899. void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
  1900. {
  1901. s_mouseMovedSynthesizer.RegisterWxCocoaView(m_window);
  1902. }
  1903. void wxCocoaTrackingRectManager::RebuildTrackingRectIfNeeded()
  1904. {
  1905. if(m_isTrackingRectActive)
  1906. {
  1907. NSView *theView = m_window->GetNSView();
  1908. NSRect currentRect = [theView convertRect:[theView visibleRect] toView:nil];
  1909. if(NSEqualRects(m_trackingRectInWindowCoordinates,currentRect))
  1910. {
  1911. wxLogTrace(wxTRACE_COCOA_TrackingRect, wxT("Ignored request to rebuild TR#%d"), m_trackingRectTag);
  1912. return;
  1913. }
  1914. }
  1915. RebuildTrackingRect();
  1916. }
  1917. void wxCocoaTrackingRectManager::RebuildTrackingRect()
  1918. {
  1919. ClearTrackingRect();
  1920. BuildTrackingRect();
  1921. }
  1922. wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager()
  1923. {
  1924. ClearTrackingRect();
  1925. }
  1926. bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent)
  1927. {
  1928. return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]);
  1929. }