From 5be0f78985f73515645762b2895b6969be69a6e4 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Wed, 10 Feb 2010 09:04:50 +0100 Subject: Cocoa: Implement our own NSApplication subclass We have avoided doing so up till now, since we cannot always know if we will be able to use it. If some 3rd-party application creates NSApplication before Qt, our subclass will never be used (because of the singleton pattern that NSApplication follows). However, in most cases, Qt will be used in standalone applications, or the 3rd-party application will not subclass NSApplication. And in those cases, we can make certain functionallity more robust. Rev-By:msorvig --- src/gui/kernel/qapplication_mac.mm | 2 +- src/gui/kernel/qcocoaapplication_mac.mm | 45 ++++++++++++++++++++++++ src/gui/kernel/qcocoaapplication_mac_p.h | 8 +++++ src/gui/kernel/qcocoasharedwindowmethods_mac_p.h | 24 +++++-------- src/gui/kernel/qt_cocoa_helpers_mac.mm | 45 +++++++++++------------- src/gui/kernel/qt_cocoa_helpers_mac_p.h | 13 ++++--- 6 files changed, 89 insertions(+), 48 deletions(-) diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 3fba833..0199e87 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1237,7 +1237,7 @@ void qt_init(QApplicationPrivate *priv, int) // Cocoa application delegate #ifdef QT_MAC_USE_COCOA - NSApplication *cocoaApp = [NSApplication sharedApplication]; + NSApplication *cocoaApp = [QNSApplication sharedApplication]; QMacCocoaAutoReleasePool pool; NSObject *oldDelegate = [cocoaApp delegate]; QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; diff --git a/src/gui/kernel/qcocoaapplication_mac.mm b/src/gui/kernel/qcocoaapplication_mac.mm index 5b98420..5629940 100644 --- a/src/gui/kernel/qcocoaapplication_mac.mm +++ b/src/gui/kernel/qcocoaapplication_mac.mm @@ -107,5 +107,50 @@ | NSFontPanelStrikethroughEffectModeMask; } + +- (void)qt_sendPostedMessage:(NSEvent *)event +{ + // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! + // That is why we need to split the address in two parts: + quint64 lower = [event data1]; + quint64 upper = [event data2]; + QCocoaPostMessageArgs *args = reinterpret_cast(lower | (upper << 32)); + [args->target performSelector:args->selector]; + delete args; +} + +- (BOOL)qt_sendEvent:(NSEvent *)event +{ + if ([event type] == NSApplicationDefined) { + switch ([event subtype]) { + case QtCocoaEventSubTypePostMessage: + [NSApp qt_sendPostedMessage:event]; + return true; + default: + break; + } + } + return false; +} + @end + +@implementation QNSApplication + +// WARNING: If Qt did not create NSApplication (this can e.g. +// happend if Qt is used as a plugin from a 3rd-party cocoa +// application), QNSApplication::sendEvent will never be called. +// SO DO NOT RELY ON THIS FUNCTION BEING AVAILABLE. +// Plugin developers that _do_ control the NSApplication sub-class +// implementation of the 3rd-party application can call qt_sendEvent +// from the sub-class event handler (like we do here) to work around +// any issues. +- (void)sendEvent:(NSEvent *)event +{ + if (![self qt_sendEvent:event]) + [super sendEvent:event]; +} + +@end + #endif diff --git a/src/gui/kernel/qcocoaapplication_mac_p.h b/src/gui/kernel/qcocoaapplication_mac_p.h index e845d58..5569feb 100644 --- a/src/gui/kernel/qcocoaapplication_mac_p.h +++ b/src/gui/kernel/qcocoaapplication_mac_p.h @@ -99,5 +99,13 @@ QT_FORWARD_DECLARE_CLASS(QApplicationPrivate) - (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate); - (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader); - (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel; + +- (void)qt_sendPostedMessage:(NSEvent *)event; +- (BOOL)qt_sendEvent:(NSEvent *)event; +@end + +@interface QNSApplication : NSApplication { +} @end + #endif diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h index 3b92ce2..24a3bb5 100644 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h @@ -178,28 +178,20 @@ QT_END_NAMESPACE [NSApp terminate:sender]; } -- (void)sendPostedMessage:(NSEvent *)event -{ - // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! - // That is why we need to split the address in two parts: - quint64 lower = [event data1]; - quint64 upper = [event data2]; - QCocoaPostCallArgs *args = reinterpret_cast(lower | (upper << 32)); - if (args != 0) { - [args->target performSelector:args->selector]; - delete args; - } -} - - (void)sendEvent:(NSEvent *)event { if ([event type] == NSApplicationDefined) { - if ([event subtype] == QtCocoaEventSubTypePostMessage) - [self sendPostedMessage:event]; + switch ([event subtype]) { + case QtCocoaEventSubTypePostMessage: + [NSApp qt_sendPostedMessage:event]; + return; + default: + break; + } return; } - QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; + QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; // Cocoa can hold onto the window after we've disavowed its knowledge. So, // if we get sent an event afterwards just have it go through the super's // version and don't do any stuff with Qt. diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 74514a0..8c23902 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -1279,46 +1280,42 @@ void qt_cocoaChangeOverrideCursor(const QCursor &cursor) QMacCocoaAutoReleasePool pool; [static_cast(qt_mac_nsCursorForQCursor(cursor)) set]; } -#endif +// WARNING: If Qt did not create NSApplication (e.g. in case it is +// used as a plugin), and at the same time, there is no window on +// screen (or the window that the event is sendt to becomes hidden etc +// before the event gets delivered), the message will not be performed. bool qt_cocoaPostMessage(id target, SEL selector) { if (!target) return false; - NSWindow *nswin = [NSApp mainWindow]; - if (!nswin) { - nswin = [NSApp keyWindow]; - if (!nswin) - return false; + + NSInteger windowNumber = 0; + if (![NSApp isMemberOfClass:[QNSApplication class]]) { + // INVARIANT: Cocoa is not using our NSApplication subclass. That means + // we don't control the main event handler either. So target the event + // for one of the windows on screen: + NSWindow *nswin = [NSApp mainWindow]; + if (!nswin) { + nswin = [NSApp keyWindow]; + if (!nswin) + return false; + } + windowNumber = [nswin windowNumber]; } // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! // That is why we need to split the address in two parts: - QCocoaPostCallArgs *args = new QCocoaPostCallArgs(target, selector); + QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector); quint32 lower = quintptr(args); quint32 upper = quintptr(args) >> 32; NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined - location:NSZeroPoint modifierFlags:0 timestamp:0 - windowNumber:[nswin windowNumber] + location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:windowNumber context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; [NSApp postEvent:e atStart:NO]; return true; } - -@implementation DebugNSApplication { -} -- (void)sendEvent:(NSEvent *)event -{ - NSLog(@"NSAppDebug: sendEvent: %@", event); - return [super sendEvent:event]; -} - -- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender -{ - NSLog(@"NSAppDebug: sendAction: %s to %@ from %@", anAction, aTarget, sender); - return [super sendAction:anAction to:aTarget from:sender]; -} -@end +#endif QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() { diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 0b961b1..c43ea55 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -188,24 +188,23 @@ inline QString qt_mac_NSStringToQString(const NSString *nsstr) inline NSString *qt_mac_QStringToNSString(const QString &qstr) { return [reinterpret_cast(QCFString::toCFStringRef(qstr)) autorelease]; } -class QCocoaPostCallArgs { - public: +#ifdef QT_MAC_USE_COCOA +class QCocoaPostMessageArgs { +public: id target; SEL selector; - QCocoaPostCallArgs(id target, SEL selector) : target(target), selector(selector) + QCocoaPostMessageArgs(id target, SEL selector) : target(target), selector(selector) { [target retain]; } - ~QCocoaPostCallArgs() + ~QCocoaPostMessageArgs() { [target release]; } }; bool qt_cocoaPostMessage(id target, SEL selector); - -@interface DebugNSApplication : NSApplication {} -@end +#endif #endif -- cgit v0.12