Revert Mac event loop changes.

"Make QGuiApplication::exec() run within NSApplicationMain()"
"Make Qt process native and timer events on Cocoa applications"
"Cocoa: Fix QFontDialog, QColorDialog auto-tests"

This reverts commits
1e14762b8d
e4b2a0b4ba
df7944e7d7

Change-Id: I80b65b5ee0297b090f807bd420664233dfc44f7b
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Morten Johan Sørvig 2013-09-02 09:30:26 +02:00 committed by The Qt Project
parent 585758389c
commit 8fc97fdfc7
20 changed files with 382 additions and 426 deletions

View File

@ -1,20 +1,6 @@
load(default_post)
!no_objective_c:CONFIG += objective_c
equals(TEMPLATE, app):qt: {
deps = $$replace(QT, -private$, )
deps = $$resolve_depends(deps, "QT.", ".depends" ".private_depends" ".run_depends")
contains(deps, gui): {
DEFINES += QT_NEEDS_QMAIN
CONFIG += link_prl
# When CI'ing Qt we still need to link against libqtcocoamain.a, even before deployment.
# Hence the /get variable, suffix which looks for the libraries location effective path.
QMAKE_LIBDIR += $$[QT_INSTALL_LIBS/get]
QMAKE_LIBS += -lqtcocoamain
}
}
qt:!isEmpty(QT_CONFIG) {
# Pick a suitable default architecture for qmake-based applications.
# If the Qt package contains one of x86 and x86_64, pick that one. If it

View File

@ -1,7 +1,109 @@
TEMPLATE = subdirs
TARGET = qcocoa
cocoamain.file = cocoamain.pro
cocoaplugin.file = cocoaplugin.pro
PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin
load(qt_plugin)
SUBDIRS = cocoamain cocoaplugin
OBJECTIVE_SOURCES += main.mm \
qcocoaintegration.mm \
qcocoatheme.mm \
qcocoabackingstore.mm \
qcocoawindow.mm \
qnsview.mm \
qnsviewaccessibility.mm \
qcocoaautoreleasepool.mm \
qnswindowdelegate.mm \
qcocoaglcontext.mm \
qcocoanativeinterface.mm \
qcocoaeventdispatcher.mm \
qcocoaapplicationdelegate.mm \
qcocoaapplication.mm \
qcocoamenu.mm \
qcocoamenuitem.mm \
qcocoamenubar.mm \
qcocoamenuloader.mm \
qcocoahelpers.mm \
qmultitouch_mac.mm \
qcocoaaccessibilityelement.mm \
qcocoaaccessibility.mm \
qcocoacolordialoghelper.mm \
qcocoafiledialoghelper.mm \
qcocoafontdialoghelper.mm \
qcocoacursor.mm \
qcocoaclipboard.mm \
qcocoadrag.mm \
qmacclipboard.mm \
qmacmime.mm \
qcocoasystemsettings.mm \
qcocoainputcontext.mm \
qcocoaservices.mm \
qcocoasystemtrayicon.mm \
qcocoaintrospection.mm \
qcocoakeymapper.mm \
SOURCES += messages.cpp
HEADERS += qcocoaintegration.h \
qcocoatheme.h \
qcocoabackingstore.h \
qcocoawindow.h \
qnsview.h \
qcocoaautoreleasepool.h \
qnswindowdelegate.h \
qcocoaglcontext.h \
qcocoanativeinterface.h \
qcocoaeventdispatcher.h \
qcocoaapplicationdelegate.h \
qcocoaapplication.h \
qcocoamenu.h \
qcocoamenuitem.h \
qcocoamenubar.h \
qcocoamenuloader.h \
qcocoahelpers.h \
qmultitouch_mac_p.h \
qcocoaaccessibilityelement.h \
qcocoaaccessibility.h \
qcocoacolordialoghelper.h \
qcocoafiledialoghelper.h \
qcocoafontdialoghelper.h \
qcocoacursor.h \
qcocoaclipboard.h \
qcocoadrag.h \
qmacclipboard.h \
qmacmime.h \
qcocoasystemsettings.h \
qcocoainputcontext.h \
qcocoaservices.h \
qcocoasystemtrayicon.h \
qcocoaintrospection.h \
qcocoakeymapper.h \
messages.h
RESOURCES += qcocoaresources.qrc
LIBS += -framework Cocoa -framework Carbon -framework IOKit
QT += core-private gui-private platformsupport-private
qtHaveModule(widgets) {
OBJECTIVE_SOURCES += \
qpaintengine_mac.mm \
qprintengine_mac.mm \
qcocoaprintersupport.mm \
HEADERS += \
qpaintengine_mac_p.h \
qprintengine_mac_p.h \
qcocoaprintersupport.h \
QT += widgets-private printsupport-private
}
OTHER_FILES += cocoa.json
# Acccessibility debug support
# DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
# include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri)
# Window debug support
#DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG

View File

@ -1,19 +0,0 @@
# Additional Qt project file for qtmain lib on OS X
!macx:error("$$_FILE_ is intended only for OS X!")
TEMPLATE = lib
TARGET = qtcocoamain
DESTDIR = $$QT.core.libs
CONFIG += staticlib release
QT = core-private gui-private
LIBS += -framework Cocoa
HEADERS = qcocoaintrospection.h
OBJECTIVE_SOURCES = qcocoamain.mm \
qcocoaintrospection.mm
load(qt_installs)
load(qt_targets)

View File

@ -1,109 +0,0 @@
TARGET = qcocoa
PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin
load(qt_plugin)
OBJECTIVE_SOURCES += main.mm \
qcocoaintegration.mm \
qcocoatheme.mm \
qcocoabackingstore.mm \
qcocoawindow.mm \
qnsview.mm \
qnsviewaccessibility.mm \
qcocoaautoreleasepool.mm \
qnswindowdelegate.mm \
qcocoaglcontext.mm \
qcocoanativeinterface.mm \
qcocoaeventdispatcher.mm \
qcocoaapplicationdelegate.mm \
qcocoaapplication.mm \
qcocoamenu.mm \
qcocoamenuitem.mm \
qcocoamenubar.mm \
qcocoamenuloader.mm \
qcocoahelpers.mm \
qmultitouch_mac.mm \
qcocoaaccessibilityelement.mm \
qcocoaaccessibility.mm \
qcocoacolordialoghelper.mm \
qcocoafiledialoghelper.mm \
qcocoafontdialoghelper.mm \
qcocoacursor.mm \
qcocoaclipboard.mm \
qcocoadrag.mm \
qmacclipboard.mm \
qmacmime.mm \
qcocoasystemsettings.mm \
qcocoainputcontext.mm \
qcocoaservices.mm \
qcocoasystemtrayicon.mm \
qcocoaintrospection.mm \
qcocoakeymapper.mm \
SOURCES += messages.cpp
HEADERS += qcocoaintegration.h \
qcocoatheme.h \
qcocoabackingstore.h \
qcocoawindow.h \
qnsview.h \
qcocoaautoreleasepool.h \
qnswindowdelegate.h \
qcocoaglcontext.h \
qcocoanativeinterface.h \
qcocoaeventdispatcher.h \
qcocoaapplicationdelegate.h \
qcocoaapplication.h \
qcocoamenu.h \
qcocoamenuitem.h \
qcocoamenubar.h \
qcocoamenuloader.h \
qcocoahelpers.h \
qmultitouch_mac_p.h \
qcocoaaccessibilityelement.h \
qcocoaaccessibility.h \
qcocoacolordialoghelper.h \
qcocoafiledialoghelper.h \
qcocoafontdialoghelper.h \
qcocoacursor.h \
qcocoaclipboard.h \
qcocoadrag.h \
qmacclipboard.h \
qmacmime.h \
qcocoasystemsettings.h \
qcocoainputcontext.h \
qcocoaservices.h \
qcocoasystemtrayicon.h \
qcocoaintrospection.h \
qcocoakeymapper.h \
messages.h
RESOURCES += qcocoaresources.qrc
LIBS += -framework Cocoa -framework Carbon -framework IOKit
QT += core-private gui-private platformsupport-private
qtHaveModule(widgets) {
OBJECTIVE_SOURCES += \
qpaintengine_mac.mm \
qprintengine_mac.mm \
qcocoaprintersupport.mm \
HEADERS += \
qpaintengine_mac_p.h \
qprintengine_mac_p.h \
qcocoaprintersupport.h \
QT += widgets-private printsupport-private
}
OTHER_FILES += cocoa.json
# Acccessibility debug support
# DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
# include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri)
# Window debug support
#DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG

View File

@ -102,9 +102,13 @@
- (void)qt_sendPostedMessage:(NSEvent *)event;
- (BOOL)qt_filterEvent:(NSEvent *)event;
- (BOOL)qt_filterOrSendEvent:(NSEvent *)event;
@end
@interface QT_MANGLE_NAMESPACE(QNSApplication) : NSApplication {
}
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSApplication);
QT_BEGIN_NAMESPACE

View File

@ -83,8 +83,6 @@
QT_USE_NAMESPACE
static SEL qt_sendEvent_original_SEL = @selector(qt_sendEvent_original:);
@implementation NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu
@ -155,22 +153,34 @@ static const QByteArray q_macLocalEventType = QByteArrayLiteral("mac_generic_NSE
return false;
}
- (BOOL)qt_filterOrSendEvent:(NSEvent *)event
{
if ([self qt_filterEvent:event])
return false;
@end
Q_ASSERT_X([self respondsToSelector:qt_sendEvent_original_SEL], "qt_filterOrSendEvent:",
"Running event loop before calling qt_redirectNSApplicationSendEvent()");
[self performSelector:qt_sendEvent_original_SEL withObject:event];
return true;
@implementation QNSApplication
- (void)qt_sendEvent_original:(NSEvent *)event
{
Q_UNUSED(event);
// This method will only be used as a signature
// template for the method we add into NSApplication
// containing the original [NSApplication sendEvent:] implementation
}
- (void)qt_sendEvent_replacement:(NSEvent *)event
{
// This method (or its implementation to be precise) will
// be called instead of sendEvent: after redirection occurs.
[self qt_filterOrSendEvent:event];
// be called instead of sendEvent if redirection occurs.
// 'self' will then be an instance of NSApplication
// (and not QNSApplication)
if (![NSApp qt_filterEvent:event])
[self qt_sendEvent_original:event];
}
- (void)sendEvent:(NSEvent *)event
{
// This method will be called if
// no redirection occurs
if (![NSApp qt_filterEvent:event])
[super sendEvent:event];
}
@end
@ -179,16 +189,22 @@ QT_BEGIN_NAMESPACE
void qt_redirectNSApplicationSendEvent()
{
if ([NSApp isMemberOfClass:[QNSApplication class]]) {
// No need to change implementation since Qt
// already controls a subclass of NSApplication
return;
}
// Change the implementation of [NSApplication sendEvent] to the
// implementation of qt_sendEvent_replacement.
// implementation of qt_sendEvent_replacement found in QNSApplication.
// And keep the old implementation that gets overwritten inside a new
// method 'qt_sendEvent_original' that we add to NSApplication
qt_cocoa_change_implementation(
[NSApplication class],
@selector(sendEvent:),
[NSApplication class],
[QNSApplication class],
@selector(qt_sendEvent_replacement:),
qt_sendEvent_original_SEL);
@selector(qt_sendEvent_original:));
}
void qt_resetNSApplicationSendEvent()

View File

@ -99,13 +99,15 @@
NSMenu *dockMenu;
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader;
NSObject <NSApplicationDelegate> *reflectionDelegate;
bool inLaunch;
}
+ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate;
- (void)setDockMenu:(NSMenu *)newMenu;
- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader)*)menuLoader;
- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate;
- (NSObject<NSApplicationDelegate> *)reflectionDelegate;
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
- (void) removeAppleEventHandlers;
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate);

View File

@ -101,6 +101,7 @@ static void cleanupCocoaApplicationDelegate()
{
self = [super init];
if (self) {
inLaunch = true;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateScreens:)
@ -240,6 +241,55 @@ static void cleanupCocoaApplicationDelegate()
return NSTerminateCancel;
}
- (void) applicationWillFinishLaunching:(NSNotification *)notification
{
Q_UNUSED(notification);
/*
From the Cocoa documentation: "A good place to install event handlers
is in the applicationWillFinishLaunching: method of the application
delegate. At that point, the Application Kit has installed its default
event handlers, so if you install a handler for one of the same events,
it will replace the Application Kit version."
*/
/*
If Qt is used as a plugin, we let the 3rd party application handle
events like quit and open file events. Otherwise, if we install our own
handlers, we easily end up breaking functionality the 3rd party
application depends on.
*/
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
[eventManager setEventHandler:self
andSelector:@selector(appleEventQuit:withReplyEvent:)
forEventClass:kCoreEventClass
andEventID:kAEQuitApplication];
[eventManager setEventHandler:self
andSelector:@selector(getUrl:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
// called by QCocoaIntegration's destructor before resetting the application delegate to nil
- (void) removeAppleEventHandlers
{
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
[eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication];
[eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Q_UNUSED(aNotification);
inLaunch = false;
// qt_release_apple_event_handler();
// Insert code here to initialize your application
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
Q_UNUSED(filenames);
@ -247,6 +297,14 @@ static void cleanupCocoaApplicationDelegate()
for (NSString *fileName in filenames) {
QString qtFileName = QCFString::toQString(fileName);
if (inLaunch) {
// We need to be careful because Cocoa will be nice enough to take
// command line arguments and send them to us as events. Given the history
// of Qt Applications, this will result in behavior people don't want, as
// they might be doing the opening themselves with the command line parsing.
if (qApp->arguments().contains(qtFileName))
continue;
}
QWindowSystemInterface::handleFileOpenEvent(qtFileName);
}
@ -309,11 +367,6 @@ static void cleanupCocoaApplicationDelegate()
*/
}
- (NSObject<NSApplicationDelegate> *)reflectionDelegate
{
return reflectionDelegate;
}
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
{
[oldDelegate retain];
@ -347,6 +400,20 @@ static void cleanupCocoaApplicationDelegate()
[self doesNotRecognizeSelector:invocationSelector];
}
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
Q_UNUSED(replyEvent);
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
QWindowSystemInterface::handleFileOpenEvent(QCFString::toQString(urlString));
}
- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
Q_UNUSED(event);
Q_UNUSED(replyEvent);
[NSApp terminate:self];
}
- (void)qtDispatcherToQAction:(id)sender
{
Q_UNUSED(sender);

View File

@ -59,8 +59,6 @@ public:
void setCurrentColor(const QColor&);
QColor currentColor() const;
bool event(QEvent *);
};
QT_END_NAMESPACE

View File

@ -161,10 +161,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)closePanel
{
if (mDialogIsExecuting) {
mDialogIsExecuting = false;
[NSApp stopModal];
}
[mColorPanel close];
}
@ -492,22 +488,6 @@ QColor QCocoaColorDialogHelper::currentColor() const
return sharedColorPanel()->currentColor();
}
bool QCocoaColorDialogHelper::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) {
emit accept();
return true;
} else if (ke->key() == Qt::Key_Escape) {
emit reject();
return true;
}
}
return false;
}
QT_END_NAMESPACE
#endif // QT_NO_COLORDIALOG

View File

@ -162,6 +162,8 @@ public:
bool blockSendPostedEvents;
// The following variables help organizing modal sessions:
QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
bool currentExecIsNSAppRun;
bool nsAppRunCalledByQt;
bool cleanupModalSessionsNeeded;
NSModalSession currentModalSessionCached;
NSModalSession currentModalSession();
@ -173,6 +175,7 @@ public:
void cancelWaitForMoreEvents();
void maybeCancelWaitForMoreEvents();
void ensureNSAppInitialized();
QCFSocketNotifier cfSocketNotifier;
QList<void *> queuedUserInputEvents; // NSEvent *

View File

@ -86,13 +86,20 @@
#include "private/qthread_p.h"
#include "private/qguiapplication_p.h"
#include <qdebug.h>
#include "qcocoahelpers.h"
#include "qcocoaapplication.h"
#undef slots
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
QT_BEGIN_NAMESPACE
QT_USE_NAMESPACE
enum {
QtCocoaEventSubTypeWakeup = SHRT_MAX,
QtCocoaEventSubTypePostMessage = SHRT_MAX-1
};
static inline CFRunLoopRef mainRunLoop()
{
return CFRunLoopGetMain();
@ -111,6 +118,11 @@ static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2)
void QCocoaEventDispatcherPrivate::runLoopTimerCallback(CFRunLoopTimerRef, void *info)
{
QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info);
if ((d->processEventsFlags & QEventLoop::EventLoopExec) == 0) {
// processEvents() was called "manually," ignore this source for now
d->maybeCancelWaitForMoreEvents();
return;
}
CFRunLoopSourceSignal(d->activateTimersSourceRef);
}
@ -365,17 +377,53 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if (!excludeUserEvents) {
while (!d->queuedUserInputEvents.isEmpty()) {
event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
if ([NSApp qt_filterOrSendEvent:event])
if (!filterNativeEvent("NSEvent", event, 0)) {
[NSApp sendEvent:event];
retVal = true;
}
[event release];
}
}
{
// If Qt is used as a plugin, or as an extension in a native cocoa
// application, we should not run or stop NSApplication; This will be
// done from the application itself. And if processEvents is called
// manually (rather than from a QEventLoop), we cannot enter a tight
// loop and block this call, but instead we need to return after one flush.
// Finally, if we are to exclude user input events, we cannot call [NSApp run]
// as we then loose control over which events gets dispatched:
const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning];
const bool canExec_Qt = (!excludeUserEvents
&& ((d->processEventsFlags & QEventLoop::DialogExec)
|| (d->processEventsFlags & QEventLoop::EventLoopExec)));
if (canExec_Qt && canExec_3rdParty) {
// We can use exec-mode, meaning that we can stay in a tight loop until
// interrupted. This is mostly an optimization, but it allow us to use
// [NSApp run], which is the normal code path for cocoa applications.
if (NSModalSession session = d->currentModalSession()) {
QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
qt_mac_waitForMoreEvents(NSModalPanelRunLoopMode);
if (!d->interrupt && session == d->currentModalSessionCached) {
// Someone called [NSApp stopModal:] from outside the event
// dispatcher (e.g to stop a native dialog). But that call wrongly stopped
// 'session' as well. As a result, we need to restart all internal sessions:
d->temporarilyStopAllModalSessions();
}
} else {
d->nsAppRunCalledByQt = true;
QBoolBlocker execGuard(d->currentExecIsNSAppRun, true);
[NSApp run];
}
retVal = true;
} else {
int lastSerialCopy = d->lastSerial;
bool hadModalSession = d->currentModalSessionCached != 0;
// We cannot block the thread (and run in a tight loop).
// Instead we will process all current pending events and return.
d->ensureNSAppInitialized();
if (NSModalSession session = d->currentModalSession()) {
// INVARIANT: a modal window is executing.
if (!excludeUserEvents) {
@ -396,9 +444,9 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
// this case, we need more control over which events gets dispatched, and
// cannot use [NSApp runModalSession:session]:
event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSModalPanelRunLoopMode
dequeue:YES];
untilDate:nil
inMode:NSModalPanelRunLoopMode
dequeue: YES];
if (event) {
if (IsMouseOrKeyEvent(event)) {
@ -406,16 +454,18 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
d->queuedUserInputEvents.append(event);
continue;
}
if ([NSApp qt_filterOrSendEvent:event])
if (!filterNativeEvent("NSEvent", event, 0)) {
[NSApp sendEvent:event];
retVal = true;
}
}
} while (!d->interrupt && event != nil);
} else do {
// INVARIANT: No modal window is executing.
event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue: YES];
if (event) {
if (flags & QEventLoop::ExcludeUserInputEvents) {
@ -425,8 +475,10 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
continue;
}
}
if ([NSApp qt_filterOrSendEvent:event])
if (!filterNativeEvent("NSEvent", event, 0)) {
[NSApp sendEvent:event];
retVal = true;
}
}
} while (!d->interrupt && event != nil);
@ -505,6 +557,26 @@ void QCocoaEventDispatcher::wakeUp()
QEventDispatcherMac Implementation
*****************************************************************************/
void QCocoaEventDispatcherPrivate::ensureNSAppInitialized()
{
// Some elements in Cocoa require NSApplication to be running before
// they get fully initialized, in particular the menu bar. This
// function is intended for cases where a dialog is told to execute before
// QGuiApplication::exec is called, or the application spins the events loop
// manually rather than calling QGuiApplication:exec.
// The function makes sure that NSApplication starts running, but stops
// it again as soon as the send posted events callback is called. That way
// we let Cocoa finish the initialization it seems to need. We'll only
// apply this trick at most once for any application, and we avoid doing it
// for the common case where main just starts QGuiApplication::exec.
if (nsAppRunCalledByQt || [NSApp isRunning])
return;
nsAppRunCalledByQt = true;
QBoolBlocker block1(interrupt, true);
QBoolBlocker block2(currentExecIsNSAppRun, true);
[NSApp run];
}
void QCocoaEventDispatcherPrivate::temporarilyStopAllModalSessions()
{
// Flush, and Stop, all created modal session, and as
@ -549,6 +621,7 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession()
if (!nswindow)
continue;
ensureNSAppInitialized();
QBoolBlocker block1(blockSendPostedEvents, true);
info.nswindow = nswindow;
[(NSWindow*) info.nswindow retain];
@ -693,6 +766,8 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate()
: processEventsFlags(0),
runLoopTimerRef(0),
blockSendPostedEvents(false),
currentExecIsNSAppRun(false),
nsAppRunCalledByQt(false),
cleanupModalSessionsNeeded(false),
currentModalSessionCached(0),
lastSerial(-1),
@ -783,8 +858,19 @@ void QCocoaEventDispatcherPrivate::processPostedEvents()
if (cleanupModalSessionsNeeded)
cleanupModalSessions();
if (interrupt)
if (interrupt) {
if (currentExecIsNSAppRun) {
// The event dispatcher has been interrupted. But since
// [NSApplication run] is running the event loop, we
// delayed stopping it until now (to let cocoa process
// pending cocoa events first).
if (currentModalSessionCached)
temporarilyStopAllModalSessions();
[NSApp stop:NSApp];
cancelWaitForMoreEvents();
}
return;
}
int serial = serialNumber.load();
if (!threadData->canWait || (serial != lastSerial)) {
@ -805,6 +891,11 @@ void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info)
{
QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info);
if ((d->processEventsFlags & QEventLoop::EventLoopExec) == 0) {
// processEvents() was called "manually," ignore this source for now
d->maybeCancelWaitForMoreEvents();
return;
}
d->processPostedEvents();
d->maybeCancelWaitForMoreEvents();
}

View File

@ -203,10 +203,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (void)closePanel
{
if (mDialogIsExecuting) {
mDialogIsExecuting = false;
[NSApp stopModal];
}
[mFontPanel close];
}

View File

@ -232,7 +232,7 @@ QCocoaIntegration::QCocoaIntegration()
qApp->setAttribute(Qt::AA_DontUseNativeMenuBar, false);
NSApplication *cocoaApplication = NSApp;
NSApplication *cocoaApplication = [QNSApplication sharedApplication];
qt_redirectNSApplicationSendEvent();
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
@ -280,8 +280,9 @@ QCocoaIntegration::~QCocoaIntegration()
if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
// remove the apple event handlers installed by QCocoaApplicationDelegate
QCocoaApplicationDelegate *delegate = [QCocoaApplicationDelegate sharedDelegate];
[delegate removeAppleEventHandlers];
// reset the application delegate
[[NSApplication sharedApplication] setDelegate:[delegate reflectionDelegate]];
[[NSApplication sharedApplication] setDelegate: 0];
}
// Delete the clipboard integration and destroy mime type converters.

View File

@ -79,33 +79,43 @@ QT_BEGIN_NAMESPACE
void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel)
{
// The following code replaces the _implementation_ for the selector we want to hack
// (originalSel) with the implementation found in proxyClass. Then it creates
// a new 'backup' method inside baseClass containing the old, original,
// implementation (fakeSel). You can let the proxy implementation of originalSel
// call fakeSel if needed (similar approach to calling a super class implementation).
// fakeSel must also be implemented in proxyClass, as the signature is used
// as template for the method one we add into baseClass.
// NB: You will typically never create any instances of proxyClass; we use it
// only for stealing its contents and put it into baseClass.
if (!replacementSel)
replacementSel = originalSel;
#ifndef QT_MAC_USE_COCOA
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
#endif
{
// The following code replaces the _implementation_ for the selector we want to hack
// (originalSel) with the implementation found in proxyClass. Then it creates
// a new 'backup' method inside baseClass containing the old, original,
// implementation (fakeSel). You can let the proxy implementation of originalSel
// call fakeSel if needed (similar approach to calling a super class implementation).
// fakeSel must also be implemented in proxyClass, as the signature is used
// as template for the method one we add into baseClass.
// NB: You will typically never create any instances of proxyClass; we use it
// only for stealing its contents and put it into baseClass.
if (!replacementSel)
replacementSel = originalSel;
Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel);
IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod));
Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel);
IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod));
if (backupSel) {
Method backupMethod = class_getInstanceMethod(proxyClass, backupSel);
class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod));
if (backupSel) {
Method backupMethod = class_getInstanceMethod(proxyClass, backupSel);
class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod));
}
}
}
void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel)
{
Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel);
method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass));
#ifndef QT_MAC_USE_COCOA
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
#endif
{
Method originalMethod = class_getInstanceMethod(baseClass, originalSel);
Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel);
method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass));
}
}
QT_END_NAMESPACE

View File

@ -1,181 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#import <Cocoa/Cocoa.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtCore/private/qcore_mac_p.h>
#include "qcocoaintrospection.h"
extern int qMain(int argc, char *argv[]);
@interface QCocoaMainWrapper : NSObject
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
- (void)runUserMain;
@end
@implementation QCocoaMainWrapper
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
if ([notification object] != NSApp) // Shouldn't happen AFAIK, but still
return;
/*
From the Cocoa documentation: "A good place to install event handlers
is in the applicationWillFinishLaunching: method of the application
delegate. At that point, the Application Kit has installed its default
event handlers, so if you install a handler for one of the same events,
it will replace the Application Kit version."
*/
/*
If Qt is used as a plugin, we let the 3rd party application handle
events like quit and open file events. Otherwise, if we install our own
handlers, we easily end up breaking functionality the 3rd party
application depends on.
*/
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
[eventManager setEventHandler:self
andSelector:@selector(appleEventQuit:withReplyEvent:)
forEventClass:kCoreEventClass
andEventID:kAEQuitApplication];
[eventManager setEventHandler:self
andSelector:@selector(getUrl:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
if ([notification object] != NSApp) // Shouldn't happen AFAIK, but still
return;
// We schedule the main-redirection for the next eventloop pass so that we
// can return from this function and let NSApplicationMain finish its job.
[NSTimer scheduledTimerWithTimeInterval:0 target:self
selector:@selector(runUserMain) userInfo:nil repeats:NO];
}
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
Q_UNUSED(replyEvent);
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
QWindowSystemInterface::handleFileOpenEvent(QCFString::toQString(urlString));
}
- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
Q_UNUSED(event);
Q_UNUSED(replyEvent);
[NSApp terminate:self];
}
- (void)runUserMain
{
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
int argc = arguments.count;
char **argv = new char*[argc];
for (int i = 0; i < argc; ++i) {
NSString *arg = [arguments objectAtIndex:i];
argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]]));
strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}
qMain(argc, argv);
delete[] argv;
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
[eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication];
[eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
[NSApp terminate:self];
}
@end
static SEL qt_infoDictionary_original_SEL = @selector(qt_infoDictionary_original);
@implementation NSBundle (QT_MANGLE_NAMESPACE(QCocoaMain))
- (Class)qt_infoDictionary_replacement
{
if (self == [NSBundle mainBundle]) {
static NSMutableDictionary *infoDict = nil;
if (!infoDict) {
infoDict = [[self performSelector:qt_infoDictionary_original_SEL] mutableCopy];
[infoDict setValue:@"NSApplication" forKey:@"NSPrincipalClass"];
}
return infoDict;
}
return [self performSelector:qt_infoDictionary_original_SEL];
}
@end
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
QCocoaMainWrapper *mainWrapper = [[QCocoaMainWrapper alloc] init];
[[NSNotificationCenter defaultCenter]
addObserver:mainWrapper selector:@selector(applicationWillFinishLaunching:)
name:NSApplicationWillFinishLaunchingNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:mainWrapper selector:@selector(applicationDidFinishLaunching:)
name:NSApplicationDidFinishLaunchingNotification object:nil];
NSBundle *mainBundle = [NSBundle mainBundle];
if (!mainBundle.principalClass) {
// Since several of the GUI based Qt utilities (e.g., qmlscene) are command
// line applications, meaning non-bundle applications, we need to make Cocoa
// believe everything is fine. So we fake the main bundle's dictionary by
// adding the "NSPrincipalClass" property. So far, this seems to be enough to
// keep NSApplicationMain() happy and running...
qt_cocoa_change_implementation([NSBundle class], @selector(infoDictionary),
[NSBundle class], @selector(qt_infoDictionary_replacement), qt_infoDictionary_original_SEL);
}
return NSApplicationMain(argc, (const char **)argv);
}

View File

@ -2166,8 +2166,6 @@ void QColorDialog::keyPressEvent(QKeyEvent *e)
}
e->accept();
return;
} else if (d->nativeDialogInUse && d->platformColorDialogHelper()->event(e)) {
return;
}
QDialog::keyPressEvent(e);
}

View File

@ -983,9 +983,14 @@ void QFontDialog::setVisible(bool visible)
Q_D(QFontDialog);
if (d->canBeNativeDialog())
d->setNativeDialogVisible(visible);
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
// updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen, d->nativeDialogInUse);
if (d->nativeDialogInUse) {
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
// updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen, true);
} else {
d->nativeDialogInUse = false;
setAttribute(Qt::WA_DontShowOnScreen, false);
}
QDialog::setVisible(visible);
}

View File

@ -144,10 +144,13 @@ void tst_QColorDialog::postKeyReturn() {
void tst_QColorDialog::testGetRgba()
{
QColorDialog cd;
cd.show();
QTimer::singleShot(0, this, SLOT(postKeyReturn()));
QTRY_COMPARE(cd.result(), int(QDialog::Accepted));
#ifdef Q_OS_MAC
QEXPECT_FAIL("", "Sending QTest::keyClick to OSX color dialog helper fails, see QTBUG-24320", Continue);
#endif
bool ok = false;
QTimer::singleShot(500, this, SLOT(postKeyReturn()));
QColorDialog::getRgba(0xffffffff, &ok);
QVERIFY(ok);
}
void tst_QColorDialog::defaultOkButton()

View File

@ -64,7 +64,7 @@ public:
public slots:
void postKeyReturn();
void testDefaultOkButton();
void testGetFont();
void testSetFont();
public slots:
@ -116,12 +116,15 @@ void tst_QFontDialog::postKeyReturn() {
}
}
void tst_QFontDialog::testDefaultOkButton()
void tst_QFontDialog::testGetFont()
{
QFontDialog fd;
fd.show();
QTimer::singleShot(0, this, SLOT(postKeyReturn()));
QTRY_COMPARE(fd.result(), int(QDialog::Accepted));
#ifdef Q_OS_MAC
QEXPECT_FAIL("", "Sending QTest::keyClick to OSX font dialog helper fails, see QTBUG-24321", Continue);
#endif
bool ok = false;
QTimer::singleShot(2000, this, SLOT(postKeyReturn()));
QFontDialog::getFont(&ok);
QVERIFY(ok);
}
void tst_QFontDialog::runSlotWithFailsafeTimer(const char *member)
@ -141,7 +144,7 @@ void tst_QFontDialog::runSlotWithFailsafeTimer(const char *member)
void tst_QFontDialog::defaultOkButton()
{
runSlotWithFailsafeTimer(SLOT(testDefaultOkButton()));
runSlotWithFailsafeTimer(SLOT(testGetFont()));
}
void tst_QFontDialog::testSetFont()