6#include <AppKit/AppKit.h>
17#if QT_CONFIG(sessionmanager)
18# include "qcocoasessionmanager.h"
24#include <qguiapplication.h>
25#include <qpa/qwindowsysteminterface.h>
26#include <qwindowdefs.h>
28#include <QtCore/private/qdarwinsecurityscopedfileengine_p.h>
32@implementation QCocoaApplicationDelegate {
33 NSObject <NSApplicationDelegate> *reflectionDelegate;
37+ (instancetype)sharedDelegate
39 static QCocoaApplicationDelegate *shared = nil;
40 static dispatch_once_t onceToken;
41 dispatch_once(&onceToken, ^{
42 shared = [[self alloc] init];
63 if (reflectionDelegate) {
64 [[NSApplication sharedApplication] setDelegate:reflectionDelegate];
65 [reflectionDelegate release];
67 [[NSNotificationCenter defaultCenter] removeObserver:self];
72- (
NSMenu *)applicationDockMenu:(NSApplication *)sender
77 [self.dockMenu.delegate menuWillOpen:self.dockMenu];
78 return [[self.dockMenu retain] autorelease];
82- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
84 if ([reflectionDelegate respondsToSelector:_cmd])
85 return [reflectionDelegate applicationShouldTerminate:sender];
87 if (QGuiApplicationPrivate::instance()->threadData.loadRelaxed()->eventLoops.isEmpty()) {
91 qCDebug(lcQpaApplication) <<
"No running event loops, terminating now";
92 return NSTerminateNow;
95#if QT_CONFIG(sessionmanager)
96 QCocoaSessionManager *cocoaSessionManager = QCocoaSessionManager::instance();
97 cocoaSessionManager->resetCancellation();
98 cocoaSessionManager->appCommitData();
100 if (cocoaSessionManager->wasCanceled()) {
101 qCDebug(lcQpaApplication) <<
"Session management canceled application termination";
102 return NSTerminateCancel;
106 if (!QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>()) {
107 qCDebug(lcQpaApplication) <<
"Application termination canceled";
108 return NSTerminateCancel;
115 qCDebug(lcQpaApplication) <<
"Termination accepted, but returning to runloop for exit through main()";
116 return NSTerminateCancel;
119- (
void)applicationWillFinishLaunching:(NSNotification *)notification
121 if ([reflectionDelegate respondsToSelector:_cmd])
122 [reflectionDelegate applicationWillFinishLaunching:notification];
125
126
127
128
129
130
133
134
135
136
137
138 NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
139 [eventManager setEventHandler:self
140 andSelector:@selector(getUrl:withReplyEvent:)
141 forEventClass:kInternetEventClass
142 andEventID:kAEGetURL];
146- (
void)removeAppleEventHandlers
148 NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
149 [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
157- (
void)applicationDidFinishLaunching:(NSNotification *)aNotification
159 if ([reflectionDelegate respondsToSelector:_cmd])
160 [reflectionDelegate applicationDidFinishLaunching:aNotification];
164 if (qEnvironmentVariableIsEmpty(
"QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
165 auto currentApplication = NSRunningApplication.currentApplication;
166 if (!currentApplication.active) {
174 auto frontmostApplication = NSWorkspace.sharedWorkspace.frontmostApplication;
175 qCDebug(lcQpaApplication) <<
"Launched with" << frontmostApplication
176 <<
"as frontmost application. Activating" << currentApplication <<
"instead.";
177 [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
180 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSonoma) {
192 [currentApplication activateWithOptions:NSApplicationActivateAllWindows];
196 QCocoaMenuBar::insertWindowMenu();
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216- (
void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
221 for (NSString *fileName in filenames) {
222 QString qtFileName = QString::fromNSString(fileName);
228 if (
qApp->arguments().contains(qtFileName))
231 QUrl url = qt_apple_urlFromPossiblySecurityScopedURL([NSURL fileURLWithPath:fileName]);
232 QWindowSystemInterface::handleFileOpenEvent(url);
237 if ([reflectionDelegate respondsToSelector:_cmd])
238 [reflectionDelegate application:sender openFiles:filenames];
242- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
244 if ([reflectionDelegate respondsToSelector:_cmd])
245 return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
250- (
void)applicationDidBecomeActive:(NSNotification *)notification
252 if (QCocoaWindow::s_applicationActivationObserver) {
253 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:QCocoaWindow::s_applicationActivationObserver];
254 QCocoaWindow::s_applicationActivationObserver = nil;
257 if ([reflectionDelegate respondsToSelector:_cmd])
258 [reflectionDelegate applicationDidBecomeActive:notification];
260 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
262 if (QCocoaWindow::s_windowUnderMouse) {
265 QNSView *view = qnsview_cast(QCocoaWindow::s_windowUnderMouse->m_view);
266 [view convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
267 QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
268 qCInfo(lcQpaMouse) <<
"Application activated with mouse at" << windowPoint <<
"; sending" << QEvent::Enter <<
"to" << windowUnderMouse;
269 QWindowSystemInterface::handleEnterEvent(windowUnderMouse, windowPoint, screenPoint);
273- (
void)applicationDidResignActive:(NSNotification *)notification
275 if ([reflectionDelegate respondsToSelector:_cmd])
276 [reflectionDelegate applicationDidResignActive:notification];
278 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
280 if (QCocoaWindow::s_windowUnderMouse) {
281 QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
282 qCInfo(lcQpaMouse) <<
"Application deactivated; sending" << QEvent::Leave <<
"to" << windowUnderMouse;
283 QWindowSystemInterface::handleLeaveEvent(windowUnderMouse);
288
289
290
291
292
293
294
295
296
297
298- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
300 if ([reflectionDelegate respondsToSelector:_cmd])
301 return [reflectionDelegate applicationShouldHandleReopen:theApplication hasVisibleWindows:flag];
304
305
306
307
308
309
310 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive,
true );
315- (
void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
317 [oldDelegate retain];
318 [reflectionDelegate release];
319 reflectionDelegate = oldDelegate;
322- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
324 NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
325 if (!result && reflectionDelegate) {
326 result = [reflectionDelegate methodSignatureForSelector:aSelector];
331- (BOOL)respondsToSelector:(SEL)aSelector
333 return [super respondsToSelector:aSelector] || [reflectionDelegate respondsToSelector:aSelector];
336- (
void)forwardInvocation:(NSInvocation *)invocation
338 SEL invocationSelector = [invocation selector];
339 if ([reflectionDelegate respondsToSelector:invocationSelector])
340 [invocation invokeWithTarget:reflectionDelegate];
342 [self doesNotRecognizeSelector:invocationSelector];
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity
365 restorationHandler:(
void(^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler
368 if ([reflectionDelegate respondsToSelector:_cmd]
369 && [reflectionDelegate application:application continueUserActivity:userActivity
370 restorationHandler:restorationHandler] == YES) {
374 if (!QGuiApplication::instance())
377 if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
378 QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
379 Q_ASSERT(cocoaIntegration);
380 return cocoaIntegration->services()->handleUrl(QUrl::fromNSURL(userActivity.webpageURL));
387
388
389
390
391
392
393
394
395
396
397
398- (
void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
400 Q_UNUSED(replyEvent);
402 NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
403 const QString qurlString = QString::fromNSString(urlString);
405 if (event.eventClass == kInternetEventClass && event.eventID == kAEGetURL) {
407 if (!QGuiApplication::instance())
409 QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
410 Q_ASSERT(cocoaIntegration);
411 if (cocoaIntegration->services()->handleUrl(QUrl(qurlString)))
420 if (
const QUrl url(qurlString); url.isValid())
421 QWindowSystemInterface::handleFileOpenEvent(url);
423 QWindowSystemInterface::handleFileOpenEvent(qurlString);
426- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)application
428 if (@available(macOS 12, *)) {
429 if ([reflectionDelegate respondsToSelector:_cmd])
430 return [reflectionDelegate applicationSupportsSecureRestorableState:application];
444@implementation QCocoaApplicationDelegate (Menus)
446- (BOOL)validateMenuItem:(NSMenuItem*)item
448 qCDebug(lcQpaMenus) <<
"Validating" << item <<
"for" << self;
450 auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
454 auto *platformItem = nativeItem.platformMenuItem;
456 return item.hasSubmenu || (item.enabled && (item.action != @selector(qt_itemFired:)));
459 if (platformItem->menu())
462 return platformItem->isEnabled();
467@implementation QCocoaApplicationDelegate (MenuAPI)
469- (
void)qt_itemFired:(QCocoaNSMenuItem *)item
471 qCDebug(lcQpaMenus) <<
"Activating" << item;
476 auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
477 Q_ASSERT_X(nativeItem, qPrintable(
__FUNCTION__),
"Triggered menu item is not a QCocoaNSMenuItem.");
478 auto *platformItem = nativeItem.platformMenuItem;
481 if (!platformItem || platformItem->menu())
484 QGuiApplicationPrivate::modifier_buttons = QAppleKeyMapper::fromCocoaModifiers([NSEvent modifierFlags]);
486 static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
487 activatedSignal.invoke(platformItem, Qt::QueuedConnection);