1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/utils.mm
3 // Purpose: various cocoa utility functions
4 // Author: Stefan Csomor
7 // Copyright: (c) Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
19 #include "wx/dialog.h"
20 #include "wx/toplevel.h"
25 #include "wx/apptrait.h"
27 #include "wx/osx/private.h"
30 #if wxOSX_USE_COCOA_OR_CARBON
31 #include <CoreServices/CoreServices.h>
32 #include "wx/osx/dcclient.h"
33 #include "wx/osx/private/timer.h"
47 @implementation wxNSAppController
49 - (void)applicationWillFinishLaunching:(NSNotification *)application
51 wxUnusedVar(application);
53 // we must install our handlers later than setting the app delegate, because otherwise our handlers
54 // get overwritten in the meantime
56 NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
58 [appleEventManager setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:)
59 forEventClass:kInternetEventClass andEventID:kAEGetURL];
61 [appleEventManager setEventHandler:self andSelector:@selector(handleOpenAppEvent:withReplyEvent:)
62 forEventClass:kCoreEventClass andEventID:kAEOpenApplication];
64 wxTheApp->OSXOnWillFinishLaunching();
67 - (void)applicationDidFinishLaunching:(NSNotification *)notification
69 wxTheApp->OSXOnDidFinishLaunching();
72 - (void)application:(NSApplication *)sender openFiles:(NSArray *)fileNames
75 wxArrayString fileList;
77 const size_t count = [fileNames count];
78 for (i = 0; i < count; i++)
80 fileList.Add( wxCFStringRef::AsStringWithNormalizationFormC([fileNames objectAtIndex:i]) );
83 if ( wxTheApp->OSXInitWasCalled() )
84 wxTheApp->MacOpenFiles(fileList);
86 wxTheApp->OSXStoreOpenFiles(fileList);
89 - (NSApplicationPrintReply)application:(NSApplication *)sender printFiles:(NSArray *)fileNames withSettings:(NSDictionary *)printSettings showPrintPanels:(BOOL)showPrintPanels
92 wxArrayString fileList;
94 const size_t count = [fileNames count];
95 for (i = 0; i < count; i++)
97 fileList.Add( wxCFStringRef::AsStringWithNormalizationFormC([fileNames objectAtIndex:i]) );
100 if ( wxTheApp->OSXInitWasCalled() )
101 wxTheApp->MacPrintFiles(fileList);
103 wxTheApp->OSXStorePrintFiles(fileList);
105 return NSPrintingSuccess;
108 - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
112 wxTheApp->MacReopenApp() ;
116 - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event
117 withReplyEvent:(NSAppleEventDescriptor *)replyEvent
119 wxUnusedVar(replyEvent);
120 NSString* url = [[event descriptorAtIndex:1] stringValue];
121 wxCFStringRef cf(wxCFRetain(url));
122 if ( wxTheApp->OSXInitWasCalled() )
123 wxTheApp->MacOpenURL(cf.AsString()) ;
125 wxTheApp->OSXStoreOpenURL(cf.AsString());
128 - (void)handleOpenAppEvent:(NSAppleEventDescriptor *)event
129 withReplyEvent:(NSAppleEventDescriptor *)replyEvent
131 wxUnusedVar(replyEvent);
135 Allowable return values are:
136 NSTerminateNow - it is ok to proceed with termination
137 NSTerminateCancel - the application should not be terminated
138 NSTerminateLater - it may be ok to proceed with termination later. The application must call -replyToApplicationShouldTerminate: with YES or NO once the answer is known
139 this return value is for delegates who need to provide document modal alerts (sheets) in order to decide whether to quit.
141 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
144 if ( !wxTheApp->OSXOnShouldTerminate() )
145 return NSTerminateCancel;
147 return NSTerminateNow;
150 - (void)applicationWillTerminate:(NSNotification *)application {
151 wxUnusedVar(application);
152 wxTheApp->OSXOnWillTerminate();
155 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
158 // let wx do this, not cocoa
162 - (void)applicationDidBecomeActive:(NSNotification *)notification
164 wxUnusedVar(notification);
166 for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
167 end = wxTopLevelWindows.end();
171 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
172 wxNonOwnedWindowImpl* winimpl = win ? win->GetNonOwnedPeer() : NULL;
173 WXWindow nswindow = win ? win->GetWXWindow() : nil;
175 if ( nswindow && [nswindow hidesOnDeactivate] == NO && winimpl)
176 winimpl->RestoreWindowLevel();
179 wxTheApp->SetActive( true , NULL ) ;
182 - (void)applicationWillResignActive:(NSNotification *)notification
184 wxUnusedVar(notification);
185 for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
186 end = wxTopLevelWindows.end();
190 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
191 WXWindow nswindow = win ? win->GetWXWindow() : nil;
193 if ( nswindow && [nswindow level] == kCGFloatingWindowLevel && [nswindow hidesOnDeactivate] == NO )
194 [nswindow setLevel:kCGNormalWindowLevel];
198 - (void)applicationDidResignActive:(NSNotification *)notification
200 wxUnusedVar(notification);
202 wxTheApp->SetActive( false , NULL ) ;
208 allows ShowModal to work when using sheets.
209 see include/wx/osx/cocoa/private.h for more info
211 @implementation ModalDialogDelegate
221 - (void)setImplementation: (wxDialog *)dialog
228 return sheetFinished;
236 - (void)waitForSheetToFinish
238 while (!sheetFinished)
244 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
246 wxUnusedVar(contextInfo);
247 resultCode = returnCode;
249 // NSAlerts don't need nor respond to orderOut
250 if ([sheet respondsToSelector:@selector(orderOut:)])
251 [sheet orderOut: self];
254 impl->ModalFinishedCallback(sheet, returnCode);
258 // here we subclass NSApplication, for the purpose of being able to override sendEvent.
259 @interface wxNSApplication : NSApplication
266 - (void)sendEvent:(NSEvent *)anEvent;
270 @implementation wxNSApplication
279 /* This is needed because otherwise we don't receive any key-up events for command-key
280 combinations (an AppKit bug, apparently) */
281 - (void)sendEvent:(NSEvent *)anEvent
283 if ([anEvent type] == NSKeyUp && ([anEvent modifierFlags] & NSCommandKeyMask))
284 [[self keyWindow] sendEvent:anEvent];
286 [super sendEvent:anEvent];
298 WX_NSObject appcontroller = nil;
300 NSLayoutManager* gNSLayoutManager = nil;
302 WX_NSObject wxApp::OSXCreateAppController()
304 return [[wxNSAppController alloc] init];
307 bool wxApp::DoInitGui()
309 wxMacAutoreleasePool pool;
313 [wxNSApplication sharedApplication];
315 appcontroller = OSXCreateAppController();
316 [NSApp setDelegate:appcontroller];
317 [NSColor setIgnoresAlpha:NO];
319 // calling finishLaunching so early before running the loop seems to trigger some 'MenuManager compatibility' which leads
320 // to the duplication of menus under 10.5 and a warning under 10.6
322 [NSApp finishLaunching];
325 gNSLayoutManager = [[NSLayoutManager alloc] init];
330 bool wxApp::CallOnInit()
332 wxMacAutoreleasePool autoreleasepool;
333 m_onInitResult = false;
336 m_onInitResult = OnInit();
338 // 2016.5.20. Toshi Nagata
339 // Calling [NSApp run] before OnInit() causes crash because MacNewFile() is called
340 // before m_docManager is initialized.
345 if ( m_onInitResult )
347 if ( m_openFiles.GetCount() > 0 )
348 MacOpenFiles(m_openFiles);
349 else if ( m_printFiles.GetCount() > 0 )
350 MacPrintFiles(m_printFiles);
351 else if ( m_getURL.Len() > 0 )
352 MacOpenURL(m_getURL);
356 return m_onInitResult;
359 void wxApp::DoCleanUp()
361 if ( appcontroller != nil )
363 [NSApp setDelegate:nil];
364 [appcontroller release];
367 if ( gNSLayoutManager != nil )
369 [gNSLayoutManager release];
370 gNSLayoutManager = nil;
374 void wxClientDisplayRect(int *x, int *y, int *width, int *height)
376 NSRect displayRect = [wxOSXGetMenuScreen() visibleFrame];
377 wxRect r = wxFromNSRect( NULL, displayRect );
383 *width = r.GetWidth();
385 *height = r.GetHeight();
389 void wxGetMousePosition( int* x, int* y )
391 wxPoint pt = wxFromNSPoint(NULL, [NSEvent mouseLocation]);
398 #if wxOSX_USE_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
400 wxMouseState wxGetMouseState()
404 wxPoint pt = wxGetMousePosition();
408 NSUInteger modifiers = [NSEvent modifierFlags];
409 NSUInteger buttons = [NSEvent pressedMouseButtons];
411 ms.SetLeftDown( (buttons & 0x01) != 0 );
412 ms.SetMiddleDown( (buttons & 0x04) != 0 );
413 ms.SetRightDown( (buttons & 0x02) != 0 );
415 ms.SetRawControlDown(modifiers & NSControlKeyMask);
416 ms.SetShiftDown(modifiers & NSShiftKeyMask);
417 ms.SetAltDown(modifiers & NSAlternateKeyMask);
418 ms.SetControlDown(modifiers & NSCommandKeyMask);
426 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
428 return new wxOSXTimerImpl(timer);
431 int gs_wxBusyCursorCount = 0;
432 extern wxCursor gMacCurrentCursor;
433 wxCursor gMacStoredActiveCursor;
435 // Set the cursor to the busy cursor for all windows
436 void wxBeginBusyCursor(const wxCursor *cursor)
438 if (gs_wxBusyCursorCount++ == 0)
440 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
443 while ((object = [enumerator nextObject])) {
444 [(NSWindow*) object disableCursorRects];
447 gMacStoredActiveCursor = gMacCurrentCursor;
448 cursor->MacInstall();
450 wxSetCursor(*cursor);
452 //else: nothing to do, already set
455 // Restore cursor to normal
456 void wxEndBusyCursor()
458 wxCHECK_RET( gs_wxBusyCursorCount > 0,
459 wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") );
461 if (--gs_wxBusyCursorCount == 0)
463 NSEnumerator *enumerator = [[[NSApplication sharedApplication] windows] objectEnumerator];
466 while ((object = [enumerator nextObject])) {
467 [(NSWindow*) object enableCursorRects];
470 wxSetCursor(wxNullCursor);
472 gMacStoredActiveCursor.MacInstall();
473 gMacStoredActiveCursor = wxNullCursor;
477 // true if we're between the above two calls
480 return (gs_wxBusyCursorCount > 0);
483 wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const
485 // wxScreenDC is derived from wxWindowDC, so a screen dc will
486 // call this method when a Blit is performed with it as a source.
490 wxSize sz = m_window->GetSize();
492 int width = subrect != NULL ? subrect->width : sz.x;
493 int height = subrect != NULL ? subrect->height : sz.y ;
495 wxBitmap bitmap(width, height);
497 NSView* view = (NSView*) m_window->GetHandle();
498 if ( [view isHiddenOrHasHiddenAncestor] == NO )
501 // we use this method as other methods force a repaint, and this method can be
502 // called from OnPaint, even with the window's paint dc as source (see wxHTMLWindow)
503 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [view bounds]];
505 if ( [rep respondsToSelector:@selector(CGImage)] )
507 CGImageRef cgImageRef = (CGImageRef)[rep CGImage];
509 CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) );
510 // since our context is upside down we dont use CGContextDrawImage
511 wxMacDrawCGImage( (CGContextRef) bitmap.GetHBITMAP() , &r, cgImageRef ) ;
515 // TODO for 10.4 in case we can support this for osx_cocoa
525 #endif // wxOSX_USE_COCOA