summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro42
-rw-r--r--src/plugins/platforms/cocoa/images/copyarrowcursor.pngbin0 -> 1976 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/forbiddencursor.pngbin0 -> 1745 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.pngbin0 -> 356 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/pluscursor.pngbin0 -> 688 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/spincursor.pngbin0 -> 748 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/waitcursor.pngbin0 -> 724 bytes
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.h116
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.mm227
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h127
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm354
-rw-r--r--src/plugins/platforms/cocoa/qcocoaautoreleasepool.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h (renamed from src/plugins/platforms/cocoa/qcocoawindowsurface.h)18
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm (renamed from src/plugins/platforms/cocoa/qcocoawindowsurface.mm)27
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h220
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm1123
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm112
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.h43
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm99
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h90
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm448
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h16
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm92
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.h80
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm267
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.h95
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.mm314
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.h (renamed from src/plugins/platforms/cocoa/qcocoaeventloopintegration.h)24
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm59
-rw-r--r--src/plugins/platforms/cocoa/qcocoaresources.qrc17
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h37
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm318
-rw-r--r--src/plugins/platforms/cocoa/qmenu_mac.h85
-rw-r--r--src/plugins/platforms/cocoa/qmenu_mac.mm1274
-rw-r--r--src/plugins/platforms/cocoa/qnsview.h11
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm193
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.h21
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm12
-rw-r--r--src/plugins/platforms/cocoa/qt_menu.nib/classes.nib59
-rw-r--r--src/plugins/platforms/cocoa/qt_menu.nib/info.nib18
-rw-r--r--src/plugins/platforms/cocoa/qt_menu.nib/keyedobjects.nibbin0 -> 5560 bytes
41 files changed, 5776 insertions, 264 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index bfa147f948..d6801e0bed 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -2,28 +2,46 @@ TARGET = qcocoa
load(qt_plugin)
DESTDIR = $$QT.gui.plugins/platforms
-OBJECTIVE_SOURCES = main.mm \
+OBJECTIVE_SOURCES += main.mm \
qcocoaintegration.mm \
- qcocoawindowsurface.mm \
+ qcocoabackingstore.mm \
qcocoawindow.mm \
qnsview.mm \
- qcocoaeventloopintegration.mm \
qcocoaautoreleasepool.mm \
- qnswindowdelegate.mm
+ qnswindowdelegate.mm \
+ qcocoaglcontext.mm \
+ qcocoanativeinterface.mm \
+ qcocoaeventdispatcher.mm \
+ qcocoamenuloader.mm \
+ qcocoaapplicationdelegate.mm \
+ qcocoaapplication.mm \
+ qcocoamenu.mm \
+ qmenu_mac.mm \
+ qcocoahelpers.mm \
-OBJECTIVE_HEADERS = qcocoaintegration.h \
- qcocoawindowsurface.h \
+HEADERS += qcocoaintegration.h \
+ qcocoabackingstore.h \
qcocoawindow.h \
qnsview.h \
- qcocoaeventloopintegration.h \
qcocoaautoreleasepool.h \
- qnswindowdelegate.h
+ qnswindowdelegate.h \
+ qcocoaglcontext.h \
+ qcocoanativeinterface.h \
+ qcocoaeventdispatcher.h \
+ qcocoamenuloader.h \
+ qcocoaapplicationdelegate.h \
+ qcocoaapplication.h \
+ qcocoamenu.h \
+ qmenu_mac.h \
+ qcocoahelpers.h \
+
+RESOURCES += qcocoaresources.qrc
#add libz for freetype.
-LIBS += -lz
-LIBS += -framework cocoa
+LIBS += -lz -framework Cocoa
+
+QT += core-private gui-private widgets-private platformsupport-private
-include(../fontdatabases/coretext/coretext.pri)
+CONFIG += qpa/basicunixfontdatabase
target.path += $$[QT_INSTALL_PLUGINS]/platforms
INSTALLS += target
-
diff --git a/src/plugins/platforms/cocoa/images/copyarrowcursor.png b/src/plugins/platforms/cocoa/images/copyarrowcursor.png
new file mode 100644
index 0000000000..13dfca95bc
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/copyarrowcursor.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/forbiddencursor.png b/src/plugins/platforms/cocoa/images/forbiddencursor.png
new file mode 100644
index 0000000000..a9f21b4a5e
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/forbiddencursor.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png b/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png
new file mode 100644
index 0000000000..6716597046
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/pluscursor.png b/src/plugins/platforms/cocoa/images/pluscursor.png
new file mode 100644
index 0000000000..c583c088c9
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/pluscursor.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/spincursor.png b/src/plugins/platforms/cocoa/images/spincursor.png
new file mode 100644
index 0000000000..ca44ab50fd
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/spincursor.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/waitcursor.png b/src/plugins/platforms/cocoa/images/waitcursor.png
new file mode 100644
index 0000000000..a9abe61320
--- /dev/null
+++ b/src/plugins/platforms/cocoa/images/waitcursor.png
Binary files differ
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.h b/src/plugins/platforms/cocoa/qcocoaapplication.h
new file mode 100644
index 0000000000..5b6b2f48f2
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+/*
+ Cocoa Application Categories
+*/
+#include "qglobal.h"
+
+#import <AppKit/AppKit.h>
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+@interface NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu;
+- (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_filterEvent:(NSEvent *)event;
+@end
+
+@interface QNSApplication : NSApplication {
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+void qt_redirectNSApplicationSendEvent();
+
+QT_END_NAMESPACE
+
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm
new file mode 100644
index 0000000000..2adf6a57f0
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include <qcocoaapplication.h>
+
+#include <qcocoaapplicationdelegate.h>
+#include <qcocoahelpers.h>
+#include <qguiapplication.h>
+#include <qdebug.h>
+
+QT_USE_NAMESPACE
+
+@implementation NSApplication (QT_MANGLE_NAMESPACE(QApplicationIntegration))
+
+- (void)QT_MANGLE_NAMESPACE(qt_setDockMenu):(NSMenu *)newMenu
+{
+ [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] setDockMenu:newMenu];
+}
+
+- (QApplicationPrivate *)QT_MANGLE_NAMESPACE(qt_qappPrivate)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate];
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)
+{
+ return [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] menuLoader];
+}
+
+- (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel
+{
+ Q_UNUSED(fontPanel);
+ // only display those things that QFont can handle
+ return NSFontPanelFaceModeMask
+ | NSFontPanelSizeModeMask
+ | NSFontPanelCollectionModeMask
+ | NSFontPanelUnderlineEffectModeMask
+ | 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<QCocoaPostMessageArgs *>(lower | (upper << 32));
+ // Special case for convenience: if the argument is an NSNumber, we unbox it directly.
+ // Use NSValue instead if this behaviour is unwanted.
+ id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1;
+ id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2;
+ switch (args->argCount) {
+ case 0:
+ [args->target performSelector:args->selector];
+ break;
+ case 1:
+ [args->target performSelector:args->selector withObject:a1];
+ break;
+ case 3:
+ [args->target performSelector:args->selector withObject:a1 withObject:a2];
+ break;
+ }
+
+ delete args;
+*/
+}
+
+- (BOOL)qt_filterEvent:(NSEvent *)event
+{
+/*
+ if (qApp->macEventFilter(0, reinterpret_cast<EventRef>(event)))
+ return true;
+
+ if ([event type] == NSApplicationDefined) {
+ switch ([event subtype]) {
+ case QtCocoaEventSubTypePostMessage:
+ [NSApp qt_sendPostedMessage:event];
+ return true;
+ default:
+ break;
+ }
+ }
+*/
+ return false;
+}
+
+@end
+
+@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 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];
+}
+
+- (void)qtDispatcherToQAction:(id)sender
+{
+ // Forward actions sendt from the menu bar (e.g. quit) to the menu loader.
+ // Having this method here means that we are the last stop in the responder
+ // chain, and that we are able to handle menu actions even when no window is
+ // visible on screen. Note: If Qt is used as a plugin, Qt will not use a
+ // native menu bar. Hence, we will also not need to do any redirection etc. as
+ // we do with sendEvent.
+ [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
+}
+
+@end
+
+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 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:),
+ [QNSApplication class],
+ @selector(qt_sendEvent_replacement:),
+ @selector(qt_sendEvent_original:));
+ */
+ }
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h
new file mode 100644
index 0000000000..7f8d1dfacd
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/****************************************************************************
+ **
+ ** Copyright (c) 2007-2008, Apple, Inc.
+ **
+ ** All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are met:
+ **
+ ** * Redistributions of source code must retain the above copyright notice,
+ ** this list of conditions and the following disclaimer.
+ **
+ ** * Redistributions in binary form must reproduce the above copyright notice,
+ ** this list of conditions and the following disclaimer in the documentation
+ ** and/or other materials provided with the distribution.
+ **
+ ** * Neither the name of Apple, Inc. nor the names of its contributors
+ ** may be used to endorse or promote products derived from this software
+ ** without specific prior written permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **
+ ****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp, qcolor_x11.cpp, qfiledialog.cpp
+// and many other. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#import <Cocoa/Cocoa.h>
+
+#include <qglobal.h>
+
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate);
+
+@class QT_MANGLE_NAMESPACE(QCocoaMenuLoader);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+
+@protocol NSApplicationDelegate <NSObject>
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames;
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender;
+- (void)applicationDidBecomeActive:(NSNotification *)notification;
+- (void)applicationDidResignActive:(NSNotification *)notification;
+@end
+
+#endif
+
+@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> {
+ bool startedQuit;
+ QApplicationPrivate *qtPrivate;
+ NSMenu *dockMenu;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader;
+ NSObject <NSApplicationDelegate> *reflectionDelegate;
+ bool inLaunch;
+}
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate;
+- (void)setDockMenu:(NSMenu *)newMenu;
+- (void)setQtPrivate:(QApplicationPrivate *)value;
+- (QApplicationPrivate *)qAppPrivate;
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader)*)menuLoader;
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader;
+- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate;
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+@end
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
new file mode 100644
index 0000000000..6a2508359b
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+ **
+ ** Copyright (c) 2007-2008, Apple, Inc.
+ **
+ ** All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are met:
+ **
+ ** * Redistributions of source code must retain the above copyright notice,
+ ** this list of conditions and the following disclaimer.
+ **
+ ** * Redistributions in binary form must reproduce the above copyright notice,
+ ** this list of conditions and the following disclaimer in the documentation
+ ** and/or other materials provided with the distribution.
+ **
+ ** * Neither the name of Apple, Inc. nor the names of its contributors
+ ** may be used to endorse or promote products derived from this software
+ ** without specific prior written permission.
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **
+ ****************************************************************************/
+
+
+#import "qcocoaapplicationdelegate.h"
+#import "qnswindowdelegate.h"
+#include <qevent.h>
+#include <qurl.h>
+#include <qdebug.h>
+#include <qguiapplication.h>
+
+QT_USE_NAMESPACE
+
+static QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *sharedCocoaApplicationDelegate = nil;
+
+static void cleanupCocoaApplicationDelegate()
+{
+ [sharedCocoaApplicationDelegate release];
+}
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)
+
+- (id)init
+{
+ self = [super init];
+ if (self)
+ inLaunch = true;
+ return self;
+}
+
+- (void)dealloc
+{
+ sharedCocoaApplicationDelegate = nil;
+ [dockMenu release];
+ [qtMenuLoader release];
+ if (reflectionDelegate) {
+ [NSApp setDelegate:reflectionDelegate];
+ [reflectionDelegate release];
+ }
+ [super dealloc];
+}
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil) {
+ sharedCocoaApplicationDelegate = [super allocWithZone:zone];
+ return sharedCocoaApplicationDelegate;
+ qAddPostRoutine(cleanupCocoaApplicationDelegate);
+ }
+ }
+ return nil;
+}
+
++ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate
+{
+ @synchronized(self) {
+ if (sharedCocoaApplicationDelegate == nil)
+ [[self alloc] init];
+ }
+ return [[sharedCocoaApplicationDelegate retain] autorelease];
+}
+
+- (void)setDockMenu:(NSMenu*)newMenu
+{
+ [newMenu retain];
+ [dockMenu release];
+ dockMenu = newMenu;
+}
+
+- (NSMenu *)applicationDockMenu
+{
+ return [[dockMenu retain] autorelease];
+}
+
+- (QApplicationPrivate *)qAppPrivate
+{
+ return qtPrivate;
+}
+
+- (void)setQtPrivate:(QApplicationPrivate *)value
+{
+ qtPrivate = value;
+}
+
+- (void)setMenuLoader:(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader
+{
+ [menuLoader retain];
+ [qtMenuLoader release];
+ qtMenuLoader = menuLoader;
+}
+
+- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader
+{
+ return [[qtMenuLoader retain] autorelease];
+}
+
+// This function will only be called when NSApp is actually running. Before
+// that, the kAEQuitApplication Apple event will be sent to
+// QApplicationPrivate::globalAppleEventProcessor in qapplication_mac.mm
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+/*
+ Q_UNUSED(sender);
+ // The reflection delegate gets precedence
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) {
+ return [reflectionDelegate applicationShouldTerminate:sender];
+ }
+
+ if (qtPrivate->canQuit()) {
+ if (!startedQuit) {
+ startedQuit = true;
+ qAppInstance()->quit();
+ startedQuit = false;
+ }
+ }
+
+ if (qtPrivate->threadData->eventLoops.size() == 0) {
+ // INVARIANT: No event loop is executing. This probably
+ // means that Qt is used as a plugin, or as a part of a native
+ // Cocoa application. In any case it should be fine to
+ // terminate now:
+ return NSTerminateNow;
+ }
+
+ return NSTerminateCancel;
+*/
+ return NSTerminateNow;
+}
+
+- (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
+{
+/*
+ for (NSString *fileName in filenames) {
+ QString qtFileName = qt_mac_NSStringToQString(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;
+ }
+ QFileOpenEvent foe(qtFileName);
+ qt_sendSpontaneousEvent(qAppInstance(), &foe);
+ }
+
+ if (reflectionDelegate &&
+ [reflectionDelegate respondsToSelector:@selector(application:openFiles:)])
+ [reflectionDelegate application:sender openFiles:filenames];
+*/
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
+{
+ // If we have a reflection delegate, that will get to call the shots.
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:
+ @selector(applicationShouldTerminateAfterLastWindowClosed:)])
+ return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
+ return NO; // Someday qApp->quitOnLastWindowClosed(); when QApp and NSApp work closer together.
+}
+
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification
+{
+/*
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)])
+ [reflectionDelegate applicationDidBecomeActive:notification];
+
+ onApplicationChangedActivation(true);
+
+ if (!QWidget::mouseGrabber()){
+ // Update enter/leave immidiatly, don't wait for a move event. But only
+ // if no grab exists (even if the grab points to this widget, it seems, ref X11)
+ QPoint qlocal, qglobal;
+ QWidget *widgetUnderMouse = 0;
+ qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse);
+ QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0);
+ qt_last_mouse_receiver = widgetUnderMouse;
+ qt_last_native_mouse_receiver = widgetUnderMouse ?
+ (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0;
+ }
+*/
+}
+
+- (void)applicationDidResignActive:(NSNotification *)notification
+{
+/*
+ if (reflectionDelegate
+ && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)])
+ [reflectionDelegate applicationDidResignActive:notification];
+
+ onApplicationChangedActivation(false);
+
+ if (!QWidget::mouseGrabber())
+ QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver);
+ qt_last_mouse_receiver = 0;
+ qt_last_native_mouse_receiver = 0;
+ qt_button_down = 0;
+*/
+}
+
+- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ //QDesktopWidgetImplementation::instance()->onResize();
+}
+
+- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
+{
+ [oldDelegate retain];
+ [reflectionDelegate release];
+ reflectionDelegate = oldDelegate;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
+{
+ NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
+ if (!result && reflectionDelegate) {
+ result = [reflectionDelegate methodSignatureForSelector:aSelector];
+ }
+ return result;
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector
+{
+ BOOL result = [super respondsToSelector:aSelector];
+ if (!result && reflectionDelegate)
+ result = [reflectionDelegate respondsToSelector:aSelector];
+ return result;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ SEL invocationSelector = [invocation selector];
+ if (reflectionDelegate && [reflectionDelegate respondsToSelector:invocationSelector])
+ [invocation invokeWithTarget:reflectionDelegate];
+ else
+ [self doesNotRecognizeSelector:invocationSelector];
+}
+
+- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(replyEvent);
+/*
+ NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+ QUrl url(qt_mac_NSStringToQString(urlString));
+ QFileOpenEvent qtEvent(url);
+ qt_sendSpontaneousEvent(qAppInstance(), &qtEvent);
+*/
+}
+
+- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
+{
+ Q_UNUSED(event);
+ Q_UNUSED(replyEvent);
+ qDebug() << "appleEventQuit";
+
+ [NSApp terminate:self];
+}
+
+- (void)qtDispatcherToQAction:(id)sender
+{
+ //[[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender];
+}
+
+@end
diff --git a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h b/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h
index 862bc27f9d..359b5d34d0 100644
--- a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h
+++ b/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h
@@ -42,6 +42,8 @@
#ifndef QCOCOAAUTORELEASEPOOL_H
#define QCOCOAAUTORELEASEPOOL_H
+#undef slots
+
#include <Cocoa/Cocoa.h>
class QCocoaAutoReleasePool
diff --git a/src/plugins/platforms/cocoa/qcocoawindowsurface.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 95eea2b7ea..938e27347c 100644
--- a/src/plugins/platforms/cocoa/qcocoawindowsurface.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -39,33 +39,31 @@
**
****************************************************************************/
-#ifndef QWINDOWSURFACE_COCOA_H
-#define QWINDOWSURFACE_COCOA_H
+#ifndef QBACKINGSTORE_COCOA_H
+#define QBACKINGSTORE_COCOA_H
#include <Cocoa/Cocoa.h>
#include "qcocoawindow.h"
#include "qnsview.h"
-#include <QtGui/private/qwindowsurface_p.h>
+#include <QPlatformBackingStore>
QT_BEGIN_NAMESPACE
-class QCocoaWindowSurface : public QWindowSurface
+class QCocoaBackingStore : public QPlatformBackingStore
{
public:
- QCocoaWindowSurface(QWidget *window, WId wid);
- ~QCocoaWindowSurface();
+ QCocoaBackingStore(QWindow *window);
+ ~QCocoaBackingStore();
QPaintDevice *paintDevice();
- void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
- void resize (const QSize &size);
+ void flush(QWindow *widget, const QRegion &region, const QPoint &offset);
+ void resize (const QSize &size, const QRegion &);
private:
-
QCocoaWindow *m_cocoaWindow;
QImage *m_image;
- QNSView *m_contentView;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoawindowsurface.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 16bb327196..5a59fb5c49 100644
--- a/src/plugins/platforms/cocoa/qcocoawindowsurface.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -39,10 +39,9 @@
**
****************************************************************************/
-#include "qcocoawindowsurface.h"
+#include "qcocoabackingstore.h"
#include <QtCore/qdebug.h>
-
#include <QtGui/QPainter>
QT_BEGIN_NAMESPACE
@@ -56,30 +55,28 @@ QRect flipedRect(const QRect &sourceRect,int height)
return flippedRect;
}
-QCocoaWindowSurface::QCocoaWindowSurface(QWidget *window, WId wId)
- : QWindowSurface(window)
+QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
+ : QPlatformBackingStore(window)
{
- m_cocoaWindow = static_cast<QCocoaWindow *>(window->platformWindow());
+ m_cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
const QRect geo = window->geometry();
NSRect rect = NSMakeRect(geo.x(),geo.y(),geo.width(),geo.height());
- m_contentView = [[QNSView alloc] initWithWidget:window];
- m_cocoaWindow->setContentView(m_contentView);
- m_image = new QImage(window->size(),QImage::Format_ARGB32);
+ m_image = new QImage(window->geometry().size(),QImage::Format_ARGB32);
}
-QCocoaWindowSurface::~QCocoaWindowSurface()
+QCocoaBackingStore::~QCocoaBackingStore()
{
delete m_image;
}
-QPaintDevice *QCocoaWindowSurface::paintDevice()
+QPaintDevice *QCocoaBackingStore::paintDevice()
{
return m_image;
}
-void QCocoaWindowSurface::flush(QWidget *widget, const QRegion &region, const QPoint &offset)
+void QCocoaBackingStore::flush(QWindow *widget, const QRegion &region, const QPoint &offset)
{
Q_UNUSED(widget);
Q_UNUSED(offset);
@@ -87,17 +84,15 @@ void QCocoaWindowSurface::flush(QWidget *widget, const QRegion &region, const QP
QRect geo = region.boundingRect();
NSRect rect = NSMakeRect(geo.x(), geo.y(), geo.width(), geo.height());
- [m_contentView displayRect:rect];
+ [m_cocoaWindow->m_contentView displayRect:rect];
}
-void QCocoaWindowSurface::resize(const QSize &size)
+void QCocoaBackingStore::resize(const QSize &size, const QRegion &)
{
- QWindowSurface::resize(size);
delete m_image;
m_image = new QImage(size,QImage::Format_ARGB32_Premultiplied);
NSSize newSize = NSMakeSize(size.width(),size.height());
- [m_contentView setImage:m_image];
-
+ [static_cast<QNSView *>(m_cocoaWindow->m_contentView) setImage:m_image];
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
new file mode 100644
index 0000000000..7184db84fa
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_MAC_P_H
+#define QEVENTDISPATCHER_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qhash.h>
+#include <QtCore/qstack.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/private/qeventdispatcher_unix_p.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef struct _NSModalSession *NSModalSession;
+typedef struct _QCocoaModalSessionInfo {
+ QPointer<QWindow> window;
+ NSModalSession session;
+ void *nswindow;
+} QCocoaModalSessionInfo;
+
+class QCocoaEventDispatcherPrivate;
+class QCocoaEventDispatcher : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QCocoaEventDispatcher)
+
+public:
+ QCocoaEventDispatcher(QAbstractEventDispatcherPrivate &priv, QObject *parent = 0);
+ explicit QCocoaEventDispatcher(QObject *parent = 0);
+ ~QCocoaEventDispatcher();
+
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags);
+ bool hasPendingEvents();
+
+ void registerSocketNotifier(QSocketNotifier *notifier);
+ void unregisterSocketNotifier(QSocketNotifier *notifier);
+
+ void registerTimer(int timerId, int interval, QObject *object);
+ bool unregisterTimer(int timerId);
+ bool unregisterTimers(QObject *object);
+ QList<TimerInfo> registeredTimers(QObject *object) const;
+
+ void wakeUp();
+ void interrupt();
+
+private:
+ //friend void qt_mac_select_timer_callbk(__EventLoopTimer*, void*);
+ friend class QApplicationPrivate;
+};
+
+struct MacTimerInfo {
+ int id;
+ int interval;
+ QObject *obj;
+ bool pending;
+ CFRunLoopTimerRef runLoopTimer;
+ bool operator==(const MacTimerInfo &other)
+ {
+ return (id == other.id);
+ }
+};
+typedef QHash<int, MacTimerInfo *> MacTimerHash;
+
+struct MacSocketInfo {
+ MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {}
+ CFSocketRef socket;
+ CFRunLoopSourceRef runloop;
+ QObject *readNotifier;
+ QObject *writeNotifier;
+};
+typedef QHash<int, MacSocketInfo *> MacSocketHash;
+
+class QCocoaEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
+{
+ Q_DECLARE_PUBLIC(QCocoaEventDispatcher)
+
+public:
+ QCocoaEventDispatcherPrivate();
+
+ static MacTimerHash macTimerHash;
+ // Set 'blockSendPostedEvents' to true if you _really_ need
+ // to make sure that qt events are not posted while calling
+ // low-level cocoa functions (like beginModalForWindow). And
+ // use a QBoolBlocker to be safe:
+ static bool blockSendPostedEvents;
+ // The following variables help organizing modal sessions:
+ static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack;
+ static bool currentExecIsNSAppRun;
+ static bool nsAppRunCalledByQt;
+ static bool cleanupModalSessionsNeeded;
+ static NSModalSession currentModalSessionCached;
+ static NSModalSession currentModalSession();
+ static void updateChildrenWorksWhenModal();
+ static void temporarilyStopAllModalSessions();
+ static void beginModalSession(QWindow *widget);
+ static void endModalSession(QWindow *widget);
+ static void cancelWaitForMoreEvents();
+ static void cleanupModalSessions();
+ static void ensureNSAppInitialized();
+
+ MacSocketHash macSockets;
+ QList<void *> queuedUserInputEvents; // NSEvent *
+ CFRunLoopSourceRef postedEventsSource;
+ CFRunLoopObserverRef waitingObserver;
+ CFRunLoopObserverRef firstTimeObserver;
+ QAtomicInt serialNumber;
+ int lastSerial;
+ static bool interrupt;
+private:
+ static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2);
+ static void postedEventsSourcePerformCallback(void *info);
+ static void activateTimer(CFRunLoopTimerRef, void *info);
+ static void waitingObserverCallback(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void *info);
+ static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info);
+ friend void processPostedEvents(QCocoaEventDispatcherPrivate *const d, const bool blockSendPostedEvents);
+};
+
+class QtCocoaInterruptDispatcher : public QObject
+{
+ static QtCocoaInterruptDispatcher *instance;
+ bool cancelled;
+
+ QtCocoaInterruptDispatcher();
+ ~QtCocoaInterruptDispatcher();
+
+ public:
+ static void interruptLater();
+ static void cancelInterruptLater();
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVENTDISPATCHER_MAC_P_H
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
new file mode 100644
index 0000000000..9525b47c65
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -0,0 +1,1123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** Copyright (c) 2007-2008, Apple, Inc.
+**
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**
+** * Redistributions of source code must retain the above copyright notice,
+** this list of conditions and the following disclaimer.
+**
+** * Redistributions in binary form must reproduce the above copyright notice,
+** this list of conditions and the following disclaimer in the documentation
+** and/or other materials provided with the distribution.
+**
+** * Neither the name of Apple, Inc. nor the names of its contributors
+** may be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+****************************************************************************/
+
+#include "qcocoaeventdispatcher.h"
+#include "qcocoaautoreleasepool.h"
+
+#include "qguiapplication.h"
+#include "qevent.h"
+#include "qhash.h"
+#include "qmutex.h"
+#include "qsocketnotifier.h"
+#include <qplatformwindow_qpa.h>
+#include "private/qthread_p.h"
+#include "private/qguiapplication_p.h"
+#include <qdebug.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();
+}
+
+/*****************************************************************************
+ Timers stuff
+ *****************************************************************************/
+
+/* timer call back */
+void QCocoaEventDispatcherPrivate::activateTimer(CFRunLoopTimerRef, void *info)
+{
+ int timerID =
+#ifdef Q_OS_MAC64
+ qint64(info);
+#else
+ int(info);
+#endif
+
+ MacTimerInfo *tmr;
+ tmr = macTimerHash.value(timerID);
+ if (tmr == 0 || tmr->pending == true)
+ return; // Can't send another timer event if it's pending.
+
+
+ if (blockSendPostedEvents) {
+ QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id));
+ } else {
+ tmr->pending = true;
+ QTimerEvent e(tmr->id);
+
+ QCoreApplication::sendSpontaneousEvent(tmr->obj, &e);
+ // Get the value again in case the timer gets unregistered during the sendEvent.
+ tmr = macTimerHash.value(timerID);
+ if (tmr != 0)
+ tmr->pending = false;
+ }
+
+}
+
+void QCocoaEventDispatcher::registerTimer(int timerId, int interval, QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1 || interval < 0 || !obj) {
+ qWarning("QEventDispatcherMac::registerTimer: invalid arguments");
+ return;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::startTimer: timers cannot be started from another thread");
+ return;
+ }
+#endif
+
+ MacTimerInfo *t = new MacTimerInfo();
+ t->id = timerId;
+ t->interval = interval;
+ t->obj = obj;
+ t->runLoopTimer = 0;
+ t->pending = false;
+
+ CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent();
+ CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001);
+ fireDate += cfinterval;
+ QCocoaEventDispatcherPrivate::macTimerHash.insert(timerId, t);
+ CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 };
+ t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0,
+ QCocoaEventDispatcherPrivate::activateTimer, &info);
+ if (t->runLoopTimer == 0) {
+ qFatal("QEventDispatcherMac::registerTimer: Cannot create timer");
+ }
+ CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes);
+}
+
+bool QCocoaEventDispatcher::unregisterTimer(int identifier)
+{
+#ifndef QT_NO_DEBUG
+ if (identifier < 1) {
+ qWarning("QEventDispatcherMac::unregisterTimer: invalid argument");
+ return false;
+ } else if (thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimer: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+ if (identifier <= 0)
+ return false; // not init'd or invalid timer
+
+ MacTimerInfo *timerInfo = QCocoaEventDispatcherPrivate::macTimerHash.take(identifier);
+ if (timerInfo == 0)
+ return false;
+
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(identifier);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+
+ return true;
+}
+
+bool QCocoaEventDispatcher::unregisterTimers(QObject *obj)
+{
+#ifndef QT_NO_DEBUG
+ if (!obj) {
+ qWarning("QEventDispatcherMac::unregisterTimers: invalid argument");
+ return false;
+ } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QObject::killTimers: timers cannot be stopped from another thread");
+ return false;
+ }
+#endif
+
+ MacTimerHash::iterator it = QCocoaEventDispatcherPrivate::macTimerHash.begin();
+ while (it != QCocoaEventDispatcherPrivate::macTimerHash.end()) {
+ MacTimerInfo *timerInfo = it.value();
+ if (timerInfo->obj != obj) {
+ ++it;
+ } else {
+ if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id);
+ CFRunLoopTimerInvalidate(timerInfo->runLoopTimer);
+ CFRelease(timerInfo->runLoopTimer);
+ delete timerInfo;
+ it = QCocoaEventDispatcherPrivate::macTimerHash.erase(it);
+ }
+ }
+ return true;
+}
+
+QList<QCocoaEventDispatcher::TimerInfo>
+QCocoaEventDispatcher::registeredTimers(QObject *object) const
+{
+ if (!object) {
+ qWarning("QEventDispatcherMac:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+
+ QList<TimerInfo> list;
+
+ MacTimerHash::const_iterator it = QCocoaEventDispatcherPrivate::macTimerHash.constBegin();
+ while (it != QCocoaEventDispatcherPrivate::macTimerHash.constEnd()) {
+ MacTimerInfo *t = it.value();
+ if (t->obj == object)
+ list << TimerInfo(t->id, t->interval);
+ ++it;
+ }
+ return list;
+}
+
+/**************************************************************************
+ Socket Notifiers
+ *************************************************************************/
+void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef,
+ const void *, void *info) {
+ QCocoaEventDispatcherPrivate *const eventDispatcher
+ = static_cast<QCocoaEventDispatcherPrivate *>(info);
+ int nativeSocket = CFSocketGetNative(s);
+ MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket);
+ QEvent notifierEvent(QEvent::SockAct);
+
+ // There is a race condition that happen where we disable the notifier and
+ // the kernel still has a notification to pass on. We then get this
+ // notification after we've successfully disabled the CFSocket, but our Qt
+ // notifier is now gone. The upshot is we have to check the notifier
+ // everytime.
+ if (callbackType == kCFSocketReadCallBack) {
+ if (socketInfo->readNotifier)
+ QGuiApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
+ } else if (callbackType == kCFSocketWriteCallBack) {
+ if (socketInfo->writeNotifier)
+ QGuiApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
+ }
+}
+
+/*
+ Adds a loop source for the given socket to the current run loop.
+*/
+CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
+{
+ CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
+ if (!loopSource)
+ return 0;
+
+ CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes);
+ return loopSource;
+}
+
+/*
+ Removes the loop source for the given socket from the current run loop.
+*/
+void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
+{
+ Q_ASSERT(runloop);
+ CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes);
+ CFSocketDisableCallBacks(socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
+ CFRunLoopSourceInvalidate(runloop);
+}
+
+/*
+ Register a QSocketNotifier with the mac event system by creating a CFSocket with
+ with a read/write callback.
+
+ Qt has separate socket notifiers for reading and writing, but on the mac there is
+ a limitation of one CFSocket object for each native socket.
+*/
+void QCocoaEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread()
+ || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QCocoaEventDispatcher);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+
+ // Check if we have a CFSocket for the native socket, create one if not.
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ socketInfo = new MacSocketInfo();
+
+ // Create CFSocket, specify that we want both read and write callbacks (the callbacks
+ // are enabled/disabled later on).
+ const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack;
+ CFSocketContext context = {0, d, 0, 0, 0};
+ socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context);
+ if (CFSocketIsValid(socketInfo->socket) == false) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket");
+ return;
+ }
+
+ CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket);
+ flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write
+ flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation
+ CFSocketSetSocketFlags(socketInfo->socket, flags);
+
+ // Add CFSocket to runloop.
+ if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) {
+ qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop");
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ return;
+ }
+
+ // Disable both callback types by default. This must be done after
+ // we add the CFSocket to the runloop, or else these calls will have
+ // no effect.
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+
+ d->macSockets.insert(nativeSocket, socketInfo);
+ }
+
+ // Increment read/write counters and select enable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(socketInfo->readNotifier == 0);
+ socketInfo->readNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(socketInfo->writeNotifier == 0);
+ socketInfo->writeNotifier = notifier;
+ CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+}
+
+/*
+ Unregister QSocketNotifer. The CFSocket correspoding to this notifier is
+ removed from the runloop of this is the last notifier that users
+ that CFSocket.
+*/
+void QCocoaEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_ASSERT(notifier);
+ int nativeSocket = notifier->socket();
+ int type = notifier->type();
+#ifndef QT_NO_DEBUG
+ if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
+ qWarning("QSocketNotifier: Internal error");
+ return;
+ } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
+ return;
+ }
+#endif
+
+ Q_D(QCocoaEventDispatcher);
+
+ if (type == QSocketNotifier::Exception) {
+ qWarning("QSocketNotifier::Exception is not supported on Mac OS X");
+ return;
+ }
+ MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket);
+ if (!socketInfo) {
+ qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier");
+ return;
+ }
+
+ // Decrement read/write counters and disable callbacks if necessary.
+ if (type == QSocketNotifier::Read) {
+ Q_ASSERT(notifier == socketInfo->readNotifier);
+ socketInfo->readNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
+ } else if (type == QSocketNotifier::Write) {
+ Q_ASSERT(notifier == socketInfo->writeNotifier);
+ socketInfo->writeNotifier = 0;
+ CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
+ }
+
+ // Remove CFSocket from runloop if this was the last QSocketNotifier.
+ if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) {
+ if (CFSocketIsValid(socketInfo->socket))
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ delete socketInfo;
+ d->macSockets.remove(nativeSocket);
+ }
+}
+
+bool QCocoaEventDispatcher::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount();
+ extern bool qt_is_gui_used; //qapplication.cpp
+ return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue()));
+}
+
+static bool IsMouseOrKeyEvent( NSEvent* event )
+{
+ bool result = false;
+
+ switch( [event type] )
+ {
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSMouseMoved: // ??
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSMouseEntered:
+ case NSMouseExited:
+ case NSKeyDown:
+ case NSKeyUp:
+ case NSFlagsChanged: // key modifiers changed?
+ case NSCursorUpdate: // ??
+ case NSScrollWheel:
+ case NSTabletPoint:
+ case NSTabletProximity:
+ case NSOtherMouseDown:
+ case NSOtherMouseUp:
+ case NSOtherMouseDragged:
+#ifndef QT_NO_GESTURES
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ case NSEventTypeGesture: // touch events
+ case NSEventTypeMagnify:
+ case NSEventTypeSwipe:
+ case NSEventTypeRotate:
+ case NSEventTypeBeginGesture:
+ case NSEventTypeEndGesture:
+#endif
+#endif // QT_NO_GESTURES
+ result = true;
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+
+static inline void qt_mac_waitForMoreEvents()
+{
+ // If no event exist in the cocoa event que, wait
+ // (and free up cpu time) until at least one event occur.
+ // This implementation is a bit on the edge, but seems to
+ // work fine:
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+}
+
+static inline void qt_mac_waitForMoreModalSessionEvents()
+{
+ // If no event exist in the cocoa event que, wait
+ // (and free up cpu time) until at least one event occur.
+ // This implementation is a bit on the edge, but seems to
+ // work fine:
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantFuture]
+ inMode:NSModalPanelRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp postEvent:event atStart:YES];
+}
+
+bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(QCocoaEventDispatcher);
+ d->interrupt = false;
+
+ bool interruptLater = false;
+ QtCocoaInterruptDispatcher::cancelInterruptLater();
+
+ // In case we end up recursing while we now process events, make sure
+ // that we send remaining posted Qt events before this call returns:
+ wakeUp();
+ emit awake();
+
+ bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents;
+ bool retVal = false;
+ forever {
+ if (d->interrupt)
+ break;
+
+ QCocoaAutoReleasePool pool;
+ NSEvent* event = 0;
+
+ // First, send all previously excluded input events, if any:
+ if (!excludeUserEvents) {
+ while (!d->queuedUserInputEvents.isEmpty()) {
+ event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
+ if (!filterEvent(event)) {
+ [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 &&
+ (flags & QEventLoop::DialogExec || flags & 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_waitForMoreModalSessionEvents();
+
+ 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 {
+ // 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) {
+ // Since we can dispatch all kinds of events, we choose
+ // to use cocoa's native way of running modal sessions:
+ if (flags & QEventLoop::WaitForMoreEvents)
+ qt_mac_waitForMoreModalSessionEvents();
+ NSInteger status = [NSApp runModalSession:session];
+ if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
+ // INVARIANT: 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();
+ }
+ retVal = true;
+ } else do {
+ // Dispatch all non-user events (but que non-user events up for later). In
+ // 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];
+
+ if (event) {
+ if (IsMouseOrKeyEvent(event)) {
+ [event retain];
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ if (!filterEvent(event)) {
+ [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];
+
+ if (event) {
+ if (flags & QEventLoop::ExcludeUserInputEvents) {
+ if (IsMouseOrKeyEvent(event)) {
+ [event retain];
+ d->queuedUserInputEvents.append(event);
+ continue;
+ }
+ }
+ if (!filterEvent(event)) {
+ [NSApp sendEvent:event];
+ retVal = true;
+ }
+ }
+ } while (!d->interrupt && event != nil);
+
+ // Be sure to flush the Qt posted events when not using exec mode
+ // (exec mode will always do this call from the event loop source):
+ if (!d->interrupt)
+ QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+
+ // Since the window that holds modality might have changed while processing
+ // events, we we need to interrupt when we return back the previous process
+ // event recursion to ensure that we spin the correct modal session.
+ // We do the interruptLater at the end of the function to ensure that we don't
+ // disturb the 'wait for more events' below (as deleteLater will post an event):
+ interruptLater = true;
+ }
+ bool canWait = (d->threadData->canWait
+ && !retVal
+ && !d->interrupt
+ && (flags & QEventLoop::WaitForMoreEvents));
+ if (canWait) {
+ // INVARIANT: We haven't processed any events yet. And we're told
+ // to stay inside this function until at least one event is processed.
+ qt_mac_waitForMoreEvents();
+ flags &= ~QEventLoop::WaitForMoreEvents;
+ } else {
+ // Done with event processing for now.
+ // Leave the function:
+ break;
+ }
+ }
+
+ // If we're interrupted, we need to interrupt the _current_
+ // recursion as well to check if it is still supposed to be
+ // executing. This way we wind down the stack until we land
+ // on a recursion that again calls processEvents (typically
+ // from QEventLoop), and set interrupt to false:
+ if (d->interrupt)
+ interrupt();
+
+ if (interruptLater)
+ QtCocoaInterruptDispatcher::interruptLater();
+
+ return retVal;
+}
+
+void QCocoaEventDispatcher::wakeUp()
+{
+ Q_D(QCocoaEventDispatcher);
+ d->serialNumber.ref();
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ CFRunLoopWakeUp(mainRunLoop());
+}
+
+/*****************************************************************************
+ QEventDispatcherMac Implementation
+ *****************************************************************************/
+MacTimerHash QCocoaEventDispatcherPrivate::macTimerHash;
+bool QCocoaEventDispatcherPrivate::blockSendPostedEvents = false;
+bool QCocoaEventDispatcherPrivate::interrupt = false;
+
+
+QStack<QCocoaModalSessionInfo> QCocoaEventDispatcherPrivate::cocoaModalSessionStack;
+bool QCocoaEventDispatcherPrivate::currentExecIsNSAppRun = false;
+bool QCocoaEventDispatcherPrivate::nsAppRunCalledByQt = false;
+bool QCocoaEventDispatcherPrivate::cleanupModalSessionsNeeded = false;
+NSModalSession QCocoaEventDispatcherPrivate::currentModalSessionCached = 0;
+
+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
+ // such, make them pending again. The next call to
+ // currentModalSession will recreate them again. The
+ // reason to stop all session like this is that otherwise
+ // a call [NSApp stop] would not stop NSApp, but rather
+ // the current modal session. So if we need to stop NSApp
+ // we need to stop all the modal session first. To avoid changing
+ // the stacking order of the windows while doing so, we put
+ // up a block that is used in QCocoaWindow and QCocoaPanel:
+ int stackSize = cocoaModalSessionStack.size();
+ for (int i=0; i<stackSize; ++i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ info.session = 0;
+ }
+ }
+ currentModalSessionCached = 0;
+}
+
+NSModalSession QCocoaEventDispatcherPrivate::currentModalSession()
+{
+ // If we have one or more modal windows, this function will create
+ // a session for each of those, and return the one for the top.
+ if (currentModalSessionCached)
+ return currentModalSessionCached;
+
+ if (cocoaModalSessionStack.isEmpty())
+ return 0;
+
+ int sessionCount = cocoaModalSessionStack.size();
+ for (int i=0; i<sessionCount; ++i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (!info.window)
+ continue;
+// ### port
+// if (info.window->testAttribute(Qt::WA_DontShowOnScreen))
+// continue;
+
+ if (!info.session) {
+ QCocoaAutoReleasePool pool;
+ NSWindow *window = reinterpret_cast<NSWindow *>(info.window->handle()->winId());
+ if (!window)
+ continue;
+
+ ensureNSAppInitialized();
+ QBoolBlocker block1(blockSendPostedEvents, true);
+ info.nswindow = window;
+ [(NSWindow*) info.nswindow retain];
+ int levelBeforeEnterModal = [window level];
+ info.session = [NSApp beginModalSessionForWindow:window];
+ // Make sure we don't stack the window lower that it was before
+ // entering modal, in case it e.g. had the stays-on-top flag set:
+ if (levelBeforeEnterModal > [window level])
+ [window setLevel:levelBeforeEnterModal];
+ }
+ currentModalSessionCached = info.session;
+ cleanupModalSessionsNeeded = false;
+ }
+ return currentModalSessionCached;
+}
+
+static void setChildrenWorksWhenModal(QWindow *window, bool worksWhenModal)
+{
+ // For NSPanels (but not NSWindows, sadly), we can set the flag
+ // worksWhenModal, so that they are active even when they are not modal.
+/*
+ ### not ported
+ QList<QDialog *> dialogs = window->findChildren<QDialog *>();
+ for (int i=0; i<dialogs.size(); ++i){
+ NSWindow *window = qt_mac_window_for(dialogs[i]);
+ if (window && [window isKindOfClass:[NSPanel class]]) {
+ [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal];
+ if (worksWhenModal && [window isVisible]){
+ [window orderFront:window];
+ }
+ }
+ }
+*/
+}
+
+void QCocoaEventDispatcherPrivate::updateChildrenWorksWhenModal()
+{
+ // Make the dialog children of the window
+ // active. And make the dialog children of
+ // the previous modal dialog unactive again:
+ QCocoaAutoReleasePool pool;
+ int size = cocoaModalSessionStack.size();
+ if (size > 0){
+ if (QWindow *prevModal = cocoaModalSessionStack[size-1].window)
+ setChildrenWorksWhenModal(prevModal, true);
+ if (size > 1){
+ if (QWindow *prevModal = cocoaModalSessionStack[size-2].window)
+ setChildrenWorksWhenModal(prevModal, false);
+ }
+ }
+}
+
+void QCocoaEventDispatcherPrivate::cleanupModalSessions()
+{
+ // Go through the list of modal sessions, and end those
+ // that no longer has a window assosiated; no window means
+ // the the session has logically ended. The reason we wait like
+ // this to actually end the sessions for real (rather than at the
+ // point they were marked as stopped), is that ending a session
+ // when no other session runs below it on the stack will make cocoa
+ // drop some events on the floor.
+ QCocoaAutoReleasePool pool;
+ int stackSize = cocoaModalSessionStack.size();
+
+ for (int i=stackSize-1; i>=0; --i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.window) {
+ // This session has a window, and is therefore not marked
+ // as stopped. So just make it current. There might still be other
+ // stopped sessions on the stack, but those will be stopped on
+ // a later "cleanup" call.
+ currentModalSessionCached = info.session;
+ break;
+ }
+ cocoaModalSessionStack.remove(i);
+ currentModalSessionCached = 0;
+ if (info.session) {
+ [NSApp endModalSession:info.session];
+ [(NSWindow *)info.nswindow release];
+ }
+ }
+
+ updateChildrenWorksWhenModal();
+ cleanupModalSessionsNeeded = false;
+}
+
+void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window)
+{
+ // Add a new, empty (null), NSModalSession to the stack.
+ // It will become active the next time QEventDispatcher::processEvents is called.
+ // A QCocoaModalSessionInfo is considered pending to become active if the window pointer
+ // is non-zero, and the session pointer is zero (it will become active upon a call to
+ // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if
+ // the window pointer is zero, and the session pointer is non-zero (it will be fully
+ // stopped in cleanupModalSessions()).
+ QCocoaModalSessionInfo info = {window, 0, 0};
+ cocoaModalSessionStack.push(info);
+ updateChildrenWorksWhenModal();
+ currentModalSessionCached = 0;
+}
+
+void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window)
+{
+ // Mark all sessions attached to window as pending to be stopped. We do this
+ // by setting the window pointer to zero, but leave the session pointer.
+ // We don't tell cocoa to stop any sessions just yet, because cocoa only understands
+ // when we stop the _current_ modal session (which is the session on top of
+ // the stack, and might not belong to 'window').
+ int stackSize = cocoaModalSessionStack.size();
+ for (int i=stackSize-1; i>=0; --i) {
+ QCocoaModalSessionInfo &info = cocoaModalSessionStack[i];
+ if (info.window == window) {
+ info.window = 0;
+ if (i == stackSize-1) {
+ // The top sessions ended. Interrupt the event dispatcher
+ // to start spinning the correct session immidiatly:
+ currentModalSessionCached = 0;
+ cleanupModalSessionsNeeded = true;
+ QCocoaEventDispatcher::instance()->interrupt();
+ }
+ }
+ }
+}
+
+QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate()
+{
+}
+
+QCocoaEventDispatcher::QCocoaEventDispatcher(QObject *parent)
+ : QEventDispatcherUNIX(*new QCocoaEventDispatcherPrivate, parent)
+{
+ Q_D(QCocoaEventDispatcher);
+ CFRunLoopSourceContext context;
+ bzero(&context, sizeof(CFRunLoopSourceContext));
+ context.info = d;
+ context.equal = QCocoaEventDispatcherPrivate::postedEventSourceEqualCallback;
+ context.perform = QCocoaEventDispatcherPrivate::postedEventsSourcePerformCallback;
+ d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context);
+ Q_ASSERT(d->postedEventsSource);
+ CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+
+ CFRunLoopObserverContext observerContext;
+ bzero(&observerContext, sizeof(CFRunLoopObserverContext));
+ observerContext.info = this;
+ d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
+ kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting,
+ true, 0,
+ QCocoaEventDispatcherPrivate::waitingObserverCallback,
+ &observerContext);
+ CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes);
+
+ /* The first cycle in the loop adds the source and the events of the source
+ are not processed.
+ We use an observer to process the posted events for the first
+ execution of the loop. */
+ CFRunLoopObserverContext firstTimeObserverContext;
+ bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext));
+ firstTimeObserverContext.info = d;
+ d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
+ kCFRunLoopEntry,
+ /* repeats = */ false,
+ 0,
+ QCocoaEventDispatcherPrivate::firstLoopEntry,
+ &firstTimeObserverContext);
+ CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes);
+}
+
+void QCocoaEventDispatcherPrivate::waitingObserverCallback(CFRunLoopObserverRef,
+ CFRunLoopActivity activity, void *info)
+{
+ if (activity == kCFRunLoopBeforeWaiting)
+ emit static_cast<QCocoaEventDispatcher*>(info)->aboutToBlock();
+ else
+ emit static_cast<QCocoaEventDispatcher*>(info)->awake();
+}
+
+Boolean QCocoaEventDispatcherPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2)
+{
+ return info1 == info2;
+}
+
+void processPostedEvents(QCocoaEventDispatcherPrivate *const d, const bool blockSendPostedEvents)
+{
+ if (blockSendPostedEvents) {
+ // We're told to not send posted events (because the event dispatcher
+ // is currently working on setting up the correct session to run). But
+ // we still need to make sure that we don't fall asleep until pending events
+ // are sendt, so we just signal this need, and return:
+ CFRunLoopSourceSignal(d->postedEventsSource);
+ return;
+ }
+
+ if (d->cleanupModalSessionsNeeded)
+ d->cleanupModalSessions();
+
+ if (d->interrupt) {
+ if (d->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 (d->currentModalSessionCached)
+ d->temporarilyStopAllModalSessions();
+ [NSApp stop:NSApp];
+ d->cancelWaitForMoreEvents();
+ }
+ return;
+ }
+
+ if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) {
+ d->lastSerial = d->serialNumber;
+ QWindowSystemInterface::sendWindowSystemEvents(d->q_func(), QEventLoop::AllEvents);
+ }
+}
+
+void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
+ CFRunLoopActivity activity,
+ void *info)
+{
+ Q_UNUSED(ref);
+ Q_UNUSED(activity);
+/*
+ // This function is called when NSApplication has finished initialization,
+ // which appears to be just after [NSApplication run] has started to execute.
+ // By setting up our apple events handlers this late, we override the ones
+ // set up by NSApplication.
+
+ // 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 functionallity the 3rd party application depend on:
+ if (QGuiApplication::testAttribute(Qt::AA_MacPluginApplication))
+ return;
+
+ QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
+ NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
+ [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:)
+ forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
+ [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:)
+ forEventClass:kInternetEventClass andEventID:kAEGetURL];
+*/
+
+ processPostedEvents(static_cast<QCocoaEventDispatcherPrivate *>(info), blockSendPostedEvents);
+}
+
+void QCocoaEventDispatcherPrivate::postedEventsSourcePerformCallback(void *info)
+{
+ processPostedEvents(static_cast<QCocoaEventDispatcherPrivate *>(info), blockSendPostedEvents);
+}
+
+void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents()
+{
+ // In case the event dispatcher is waiting for more
+ // events somewhere, we post a dummy event to wake it up:
+ QCocoaAutoReleasePool pool;
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint
+ modifierFlags:0 timestamp:0. windowNumber:0 context:0
+ subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO];
+}
+
+void QCocoaEventDispatcher::interrupt()
+{
+ Q_D(QCocoaEventDispatcher);
+ d->interrupt = true;
+ wakeUp();
+
+ // We do nothing more here than setting d->interrupt = true, and
+ // poke the event loop if it is sleeping. Actually stopping
+ // NSApp, or the current modal session, is done inside the send
+ // posted events callback. We do this to ensure that all current pending
+ // cocoa events gets delivered before we stop. Otherwise, if we now stop
+ // the last event loop recursion, cocoa will just drop pending posted
+ // events on the floor before we get a chance to reestablish a new session.
+ d->cancelWaitForMoreEvents();
+}
+
+QCocoaEventDispatcher::~QCocoaEventDispatcher()
+{
+ Q_D(QCocoaEventDispatcher);
+ //timer cleanup
+ MacTimerHash::iterator it = QCocoaEventDispatcherPrivate::macTimerHash.begin();
+ while (it != QCocoaEventDispatcherPrivate::macTimerHash.end()) {
+ MacTimerInfo *t = it.value();
+ if (t->runLoopTimer) {
+ CFRunLoopTimerInvalidate(t->runLoopTimer);
+ CFRelease(t->runLoopTimer);
+ }
+ delete t;
+ ++it;
+ }
+ QCocoaEventDispatcherPrivate::macTimerHash.clear();
+
+ // Remove CFSockets from the runloop.
+ for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) {
+ MacSocketInfo *socketInfo = (*it);
+ if (CFSocketIsValid(socketInfo->socket)) {
+ qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
+ CFRunLoopSourceInvalidate(socketInfo->runloop);
+ CFRelease(socketInfo->runloop);
+ CFSocketInvalidate(socketInfo->socket);
+ CFRelease(socketInfo->socket);
+ }
+ }
+ CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes);
+ CFRelease(d->postedEventsSource);
+
+ CFRunLoopObserverInvalidate(d->waitingObserver);
+ CFRelease(d->waitingObserver);
+
+ CFRunLoopObserverInvalidate(d->firstTimeObserver);
+ CFRelease(d->firstTimeObserver);
+}
+
+QtCocoaInterruptDispatcher* QtCocoaInterruptDispatcher::instance = 0;
+
+QtCocoaInterruptDispatcher::QtCocoaInterruptDispatcher() : cancelled(false)
+{
+ // The whole point of this class is that we enable a way to interrupt
+ // the event dispatcher when returning back to a lower recursion level
+ // than where interruptLater was called. This is needed to detect if
+ // [NSApp run] should still be running at the recursion level it is at.
+ // Since the interrupt is canceled if processEvents is called before
+ // this object gets deleted, we also avoid interrupting unnecessary.
+ deleteLater();
+}
+
+QtCocoaInterruptDispatcher::~QtCocoaInterruptDispatcher()
+{
+ if (cancelled)
+ return;
+ instance = 0;
+ QCocoaEventDispatcher::instance()->interrupt();
+}
+
+void QtCocoaInterruptDispatcher::cancelInterruptLater()
+{
+ if (!instance)
+ return;
+ instance->cancelled = true;
+ delete instance;
+ instance = 0;
+}
+
+void QtCocoaInterruptDispatcher::interruptLater()
+{
+ cancelInterruptLater();
+ instance = new QtCocoaInterruptDispatcher;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm b/src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm
deleted file mode 100644
index ac0b75e9ea..0000000000
--- a/src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** 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, Nokia gives you certain additional
-** rights. These rights are described in the Nokia 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.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qcocoaeventloopintegration.h"
-
-#import <Cocoa/Cocoa.h>
-
-#include "qcocoaautoreleasepool.h"
-
-#include <QtCore/QElapsedTimer>
-
-#include <QDebug>
-#include <QApplication>
-
-void wakeupCallback ( void * ) {
- QPlatformEventLoopIntegration::processEvents();
-}
-
-void timerCallback( CFRunLoopTimerRef timer, void *info)
-{
- QPlatformEventLoopIntegration::processEvents();
- QCocoaEventLoopIntegration *eventLoopIntegration =
- static_cast<QCocoaEventLoopIntegration *>(info);
- qint64 nextTime = eventLoopIntegration->nextTimerEvent();
- CFAbsoluteTime nexttime = CFAbsoluteTimeGetCurrent();
- nexttime = nexttime + (double(nextTime)/1000);
- CFRunLoopTimerSetNextFireDate(timer,nexttime);
-}
-
-QCocoaEventLoopIntegration::QCocoaEventLoopIntegration() :
- QPlatformEventLoopIntegration()
-{
- [NSApplication sharedApplication];
- m_sourceContext.version = 0;
- m_sourceContext.info = this;
- m_sourceContext.retain = 0;
- m_sourceContext.release = 0;
- m_sourceContext.copyDescription = 0;
- m_sourceContext.equal = 0;
- m_sourceContext.hash = 0;
- m_sourceContext.schedule = 0;
- m_sourceContext.cancel = 0;
- m_sourceContext.perform = wakeupCallback;
-
- m_source = CFRunLoopSourceCreate(0,0,&m_sourceContext);
- CFRunLoopAddSource(CFRunLoopGetMain(),m_source,kCFRunLoopCommonModes);
-
- m_timerContext.version = 0;
- m_timerContext.info = this;
- m_timerContext.retain = 0;
- m_timerContext.release = 0;
- m_timerContext.copyDescription = 0;
- CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent ();
- CFTimeInterval interval = 30;
-
- CFRunLoopTimerRef m_timerSource = CFRunLoopTimerCreate(0,fireDate,interval,0,0,timerCallback,&m_timerContext);
- CFRunLoopAddTimer(CFRunLoopGetMain(),m_timerSource,kCFRunLoopCommonModes);
-}
-
-void QCocoaEventLoopIntegration::startEventLoop()
-{
- [[NSApplication sharedApplication] run];
-}
-
-void QCocoaEventLoopIntegration::quitEventLoop()
-{
- [[NSApplication sharedApplication] terminate:nil];
-}
-
-void QCocoaEventLoopIntegration::qtNeedsToProcessEvents()
-{
- CFRunLoopSourceSignal(m_source);
-}
-
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h
new file mode 100644
index 0000000000..1b84e7b305
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h
@@ -0,0 +1,43 @@
+#ifndef QCOCOAGLCONTEXT_H
+#define QCOCOAGLCONTEXT_H
+
+#include <QtCore/QWeakPointer>
+#include <QtGui/QPlatformOpenGLContext>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QWindow>
+
+#undef slots
+#include <Cocoa/Cocoa.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCocoaGLContext : public QPlatformOpenGLContext
+{
+public:
+ QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share);
+
+ QSurfaceFormat format() const;
+
+ void swapBuffers(QPlatformSurface *surface);
+
+ bool makeCurrent(QPlatformSurface *surface);
+ void doneCurrent();
+
+ void (*getProcAddress(const QByteArray &procName)) ();
+
+ void update();
+
+ static NSOpenGLPixelFormat *createNSOpenGLPixelFormat();
+ NSOpenGLContext *nsOpenGLContext() const;
+
+private:
+ void setActiveWindow(QWindow *window);
+
+ NSOpenGLContext *m_context;
+ QSurfaceFormat m_format;
+ QWeakPointer<QWindow> m_currentWindow;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOCOAGLCONTEXT_H
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
new file mode 100644
index 0000000000..8b07315378
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -0,0 +1,99 @@
+#include "qcocoaglcontext.h"
+#include "qcocoawindow.h"
+#include "qcocoaautoreleasepool.h"
+#include <qdebug.h>
+#include <QtCore/private/qcore_mac_p.h>
+#include <QtPlatformSupport/private/cglconvenience_p.h>
+
+#import <Cocoa/Cocoa.h>
+
+QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share)
+ : m_format(format)
+{
+ QCocoaAutoReleasePool pool; // For the SG Canvas render thread.
+
+ NSOpenGLPixelFormat *pixelFormat = static_cast <NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat());
+ NSOpenGLContext *actualShare = share ? static_cast<QCocoaGLContext *>(share)->m_context : 0;
+
+ m_context = [NSOpenGLContext alloc];
+ [m_context initWithFormat:pixelFormat shareContext:actualShare];
+
+ const GLint interval = 1;
+ [m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+
+}
+
+// Match up with createNSOpenGLPixelFormat!
+QSurfaceFormat QCocoaGLContext::format() const
+{
+ return m_format;
+}
+
+void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
+{
+ QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
+ setActiveWindow(window);
+
+ [m_context flushBuffer];
+}
+
+bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
+{
+ QCocoaAutoReleasePool pool;
+
+ QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
+ setActiveWindow(window);
+
+ [m_context makeCurrentContext];
+ return true;
+}
+
+void QCocoaGLContext::setActiveWindow(QWindow *window)
+{
+ if (window == m_currentWindow.data())
+ return;
+
+ if (m_currentWindow)
+ static_cast<QCocoaWindow *>(m_currentWindow.data()->handle())->setCurrentContext(0);
+
+ Q_ASSERT(window->handle());
+
+ m_currentWindow = window;
+
+ QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
+ cocoaWindow->setCurrentContext(this);
+
+ NSView *view = cocoaWindow->contentView();
+ [m_context setView:view];
+}
+
+void QCocoaGLContext::doneCurrent()
+{
+ if (m_currentWindow)
+ static_cast<QCocoaWindow *>(m_currentWindow.data()->handle())->setCurrentContext(0);
+
+ m_currentWindow.clear();
+
+ [NSOpenGLContext clearCurrentContext];
+}
+
+void (*QCocoaGLContext::getProcAddress(const QByteArray &procName))()
+{
+ return qcgl_getProcAddress(procName);
+}
+
+void QCocoaGLContext::update()
+{
+ [m_context update];
+}
+
+NSOpenGLPixelFormat *QCocoaGLContext::createNSOpenGLPixelFormat()
+{
+ return static_cast<NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat());
+}
+
+NSOpenGLContext *QCocoaGLContext::nsOpenGLContext() const
+{
+ return m_context;
+}
+
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
new file mode 100644
index 0000000000..8e807cc288
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the plugins of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** 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, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia 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.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#ifndef QCOCOAHELPERS_H
+#define QCOCOAHELPERS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It provides helper functions
+// for the Cocoa lighthouse plugin. This header file may
+// change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qt_mac_p.h>
+
+class QPixmap;
+class QString;
+
+// Conversion functions
+QStringList qt_mac_NSArrayToQStringList(void *nsarray);
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list);
+
+inline NSMutableArray *qt_mac_QStringListToNSMutableArray(const QStringList &qstrlist)
+{ return reinterpret_cast<NSMutableArray *>(qt_mac_QStringListToNSMutableArrayVoid(qstrlist)); }
+
+inline QString qt_mac_NSStringToQString(const NSString *nsstr)
+{ return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
+
+inline NSString *qt_mac_QStringToNSString(const QString &qstr)
+{ return [const_cast<NSString *>(reinterpret_cast<const NSString *>(QCFString::toCFStringRef(qstr))) autorelease]; }
+
+CGImageRef qt_mac_image_to_cgimage(const QImage &image);
+NSImage *qt_mac_cgimage_to_nsimage(CGImageRef iamge);
+NSImage *qt_mac_create_nsimage(const QPixmap &pm);
+
+QChar qt_mac_qtKey2CocoaKey(Qt::Key key);
+Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode);
+
+// Misc
+void qt_mac_transformProccessToForegroundApplication();
+QString qt_mac_removeMnemonics(const QString &original);
+CGColorSpaceRef qt_mac_genericColorSpace();
+CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget);
+QString qt_mac_applicationName();
+
+
+#endif //QCOCOAHELPERS_H
+
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
new file mode 100644
index 0000000000..03e83f1130
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -0,0 +1,448 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the plugins of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** GNU Lesser General Public License Usage
+ ** 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, Nokia gives you certain additional
+ ** rights. These rights are described in the Nokia 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.
+ **
+ ** Other Usage
+ ** Alternatively, this file may be used in accordance with the terms and
+ ** conditions contained in a signed written agreement between you and Nokia.
+ **
+ **
+ **
+ **
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include "qcocoahelpers.h"
+
+#include "qcocoaautoreleasepool.h"
+
+#include <QtCore>
+#include <QtGui>
+
+//
+// Conversion Functions
+//
+
+QStringList qt_mac_NSArrayToQStringList(void *nsarray)
+{
+ QStringList result;
+ NSArray *array = static_cast<NSArray *>(nsarray);
+ for (NSUInteger i=0; i<[array count]; ++i)
+ result << qt_mac_NSStringToQString([array objectAtIndex:i]);
+ return result;
+}
+
+void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
+{
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()];
+ for (int i=0; i<list.size(); ++i){
+ [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))];
+ }
+ return result;
+}
+
+static void drawImageReleaseData (void *info, const void *, size_t)
+{
+ delete static_cast<QImage *>(info);
+}
+
+CGImageRef qt_mac_image_to_cgimage(const QImage &img)
+{
+ QImage *image;
+ if (img.depth() != 32)
+ image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+ else
+ image = new QImage(img);
+
+ uint cgflags = kCGImageAlphaNone;
+ switch (image->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ cgflags = kCGImageAlphaPremultipliedFirst;
+ break;
+ case QImage::Format_ARGB32:
+ cgflags = kCGImageAlphaFirst;
+ break;
+ case QImage::Format_RGB32:
+ cgflags = kCGImageAlphaNoneSkipFirst;
+ default:
+ break;
+ }
+ cgflags |= kCGBitmapByteOrder32Host;
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
+ static_cast<const QImage *>(image)->bits(),
+ image->byteCount(),
+ drawImageReleaseData);
+
+ return CGImageCreate(image->width(), image->height(), 8, 32,
+ image->bytesPerLine(),
+ qt_mac_genericColorSpace(),
+ cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
+
+}
+
+NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
+{
+ QCocoaAutoReleasePool pool;
+ NSImage *newImage = 0;
+ NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));
+ newImage = [[NSImage alloc] initWithSize:imageRect.size];
+ [newImage lockFocus];
+ {
+ CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image);
+ }
+ [newImage unlockFocus];
+ return newImage;
+}
+
+NSImage *qt_mac_create_nsimage(const QPixmap &pm)
+{
+ QImage image = pm.toImage();
+ return qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(image));
+}
+
+
+// Use this method to keep all the information in the TextSegment. As long as it is ordered
+// we are in OK shape, and we can influence that ourselves.
+struct KeyPair
+{
+ QChar cocoaKey;
+ Qt::Key qtKey;
+};
+
+bool operator==(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey == qchar;
+}
+
+bool operator<(const KeyPair &entry, QChar qchar)
+{
+ return entry.cocoaKey < qchar;
+}
+
+bool operator<(QChar qchar, const KeyPair &entry)
+{
+ return qchar < entry.cocoaKey;
+}
+
+bool operator<(const Qt::Key &key, const KeyPair &entry)
+{
+ return key < entry.qtKey;
+}
+
+bool operator<(const KeyPair &entry, const Qt::Key &key)
+{
+ return entry.qtKey < key;
+}
+
+static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2)
+{
+ return entry1.qtKey < entry2.qtKey;
+}
+
+static const int NumEntries = 59;
+static const KeyPair entries[NumEntries] = {
+ { NSEnterCharacter, Qt::Key_Enter },
+ { NSBackspaceCharacter, Qt::Key_Backspace },
+ { NSTabCharacter, Qt::Key_Tab },
+ { NSNewlineCharacter, Qt::Key_Return },
+ { NSCarriageReturnCharacter, Qt::Key_Return },
+ { NSBackTabCharacter, Qt::Key_Backtab },
+ { kEscapeCharCode, Qt::Key_Escape },
+ // Cocoa sends us delete when pressing backspace!
+ // (NB when we reverse this list in qtKey2CocoaKey, there
+ // will be two indices of Qt::Key_Backspace. But is seems to work
+ // ok for menu shortcuts (which uses that function):
+ { NSDeleteCharacter, Qt::Key_Backspace },
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ { NSF1FunctionKey, Qt::Key_F1 },
+ { NSF2FunctionKey, Qt::Key_F2 },
+ { NSF3FunctionKey, Qt::Key_F3 },
+ { NSF4FunctionKey, Qt::Key_F4 },
+ { NSF5FunctionKey, Qt::Key_F5 },
+ { NSF6FunctionKey, Qt::Key_F6 },
+ { NSF7FunctionKey, Qt::Key_F7 },
+ { NSF8FunctionKey, Qt::Key_F8 },
+ { NSF9FunctionKey, Qt::Key_F8 },
+ { NSF10FunctionKey, Qt::Key_F10 },
+ { NSF11FunctionKey, Qt::Key_F11 },
+ { NSF12FunctionKey, Qt::Key_F12 },
+ { NSF13FunctionKey, Qt::Key_F13 },
+ { NSF14FunctionKey, Qt::Key_F14 },
+ { NSF15FunctionKey, Qt::Key_F15 },
+ { NSF16FunctionKey, Qt::Key_F16 },
+ { NSF17FunctionKey, Qt::Key_F17 },
+ { NSF18FunctionKey, Qt::Key_F18 },
+ { NSF19FunctionKey, Qt::Key_F19 },
+ { NSF20FunctionKey, Qt::Key_F20 },
+ { NSF21FunctionKey, Qt::Key_F21 },
+ { NSF22FunctionKey, Qt::Key_F22 },
+ { NSF23FunctionKey, Qt::Key_F23 },
+ { NSF24FunctionKey, Qt::Key_F24 },
+ { NSF25FunctionKey, Qt::Key_F25 },
+ { NSF26FunctionKey, Qt::Key_F26 },
+ { NSF27FunctionKey, Qt::Key_F27 },
+ { NSF28FunctionKey, Qt::Key_F28 },
+ { NSF29FunctionKey, Qt::Key_F29 },
+ { NSF30FunctionKey, Qt::Key_F30 },
+ { NSF31FunctionKey, Qt::Key_F31 },
+ { NSF32FunctionKey, Qt::Key_F32 },
+ { NSF33FunctionKey, Qt::Key_F33 },
+ { NSF34FunctionKey, Qt::Key_F34 },
+ { NSF35FunctionKey, Qt::Key_F35 },
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSHelpFunctionKey, Qt::Key_Help },
+};
+static const KeyPair * const end = entries + NumEntries;
+
+QChar qt_mac_qtKey2CocoaKey(Qt::Key key)
+{
+ // The first time this function is called, create a reverse
+ // looup table sorted on Qt Key rather than Cocoa key:
+ static QVector<KeyPair> rev_entries(NumEntries);
+ static bool mustInit = true;
+ if (mustInit){
+ mustInit = false;
+ for (int i=0; i<NumEntries; ++i)
+ rev_entries[i] = entries[i];
+ qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan);
+ }
+ const QVector<KeyPair>::iterator i
+ = qBinaryFind(rev_entries.begin(), rev_entries.end(), key);
+ if (i == rev_entries.end())
+ return QChar();
+ return i->cocoaKey;
+}
+
+Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
+{
+ const KeyPair *i = qBinaryFind(entries, end, keyCode);
+ if (i == end)
+ return Qt::Key(keyCode.unicode());
+ return i->qtKey;
+}
+
+//
+// Misc
+//
+
+// Changes the process type for this process to kProcessTransformToForegroundApplication,
+// unless either LSUIElement or LSBackgroundOnly is set in the Info.plist.
+void qt_mac_transformProccessToForegroundApplication()
+{
+ ProcessSerialNumber psn;
+ if (GetCurrentProcess(&psn) == noErr) {
+ bool forceTransform = true;
+ CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSUIElement"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ // Officially it's supposed to be a string, a boolean makes sense, so we'll check.
+ // A number less so, but OK.
+ if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+
+ if (forceTransform) {
+ value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
+ CFSTR("LSBackgroundOnly"));
+ if (value) {
+ CFTypeID valueType = CFGetTypeID(value);
+ if (valueType == CFBooleanGetTypeID())
+ forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ else if (valueType == CFStringGetTypeID())
+ forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt());
+ else if (valueType == CFNumberGetTypeID()) {
+ int valueAsInt;
+ CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
+ forceTransform = !valueAsInt;
+ }
+ }
+ }
+
+ if (forceTransform) {
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ }
+ }
+}
+
+QString qt_mac_removeMnemonics(const QString &original)
+{
+ QString returnText(original.size(), 0);
+ int finalDest = 0;
+ int currPos = 0;
+ int l = original.length();
+ while (l) {
+ if (original.at(currPos) == QLatin1Char('&')
+ && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
+ ++currPos;
+ --l;
+ if (l == 0)
+ break;
+ }
+ returnText[finalDest] = original.at(currPos);
+ ++currPos;
+ ++finalDest;
+ --l;
+ }
+ returnText.truncate(finalDest);
+ return returnText;
+}
+
+
+CGColorSpaceRef m_genericColorSpace = 0;
+QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
+bool m_postRoutineRegistered = false;
+
+CGColorSpaceRef qt_mac_genericColorSpace()
+{
+#if 0
+ if (!m_genericColorSpace) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ } else
+#endif
+ {
+ m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ }
+ return m_genericColorSpace;
+#else
+ // Just return the main display colorspace for the moment.
+ return qt_mac_displayColorSpace(0);
+#endif
+}
+
+/*
+ Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
+ to support multiple displays correctly.
+*/
+CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
+{
+ CGColorSpaceRef colorSpace;
+
+ CGDirectDisplayID displayID;
+ CMProfileRef displayProfile = 0;
+ if (widget == 0) {
+ displayID = CGMainDisplayID();
+ } else {
+ displayID = CGMainDisplayID();
+ /*
+ ### get correct display
+ const QRect &qrect = widget->window()->geometry();
+ CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
+ CGDisplayCount throwAway;
+ CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
+ if (dErr != kCGErrorSuccess)
+ return macDisplayColorSpace(0); // fall back on main display
+ */
+ }
+ if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
+ return colorSpace;
+
+ CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
+ if (err == noErr) {
+ colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
+ } else if (widget) {
+ return qt_mac_displayColorSpace(0); // fall back on main display
+ }
+
+ if (colorSpace == 0)
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ m_displayColorSpaceHash.insert(displayID, colorSpace);
+ CMCloseProfile(displayProfile);
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ void qt_mac_cleanUpMacColorSpaces();
+ qAddPostRoutine(qt_mac_cleanUpMacColorSpaces);
+ }
+ return colorSpace;
+}
+
+void qt_mac_cleanUpMacColorSpaces()
+{
+ if (m_genericColorSpace) {
+ CFRelease(m_genericColorSpace);
+ m_genericColorSpace = 0;
+ }
+ QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
+ while (it != m_displayColorSpaceHash.constEnd()) {
+ if (it.value())
+ CFRelease(it.value());
+ ++it;
+ }
+ m_displayColorSpaceHash.clear();
+}
+
+QString qt_mac_applicationName()
+{
+ QString appName;
+ CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
+ if (string)
+ appName = QCFString::toQString(static_cast<CFStringRef>(string));
+
+ if (appName.isEmpty()) {
+ QString arg0 = qApp->arguments().at(0);
+ if (arg0.contains("/")) {
+ QStringList parts = arg0.split("/");
+ appName = parts.at(parts.count() - 1);
+ } else {
+ appName = arg0;
+ }
+ }
+ return appName;
+}
+
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index 120bee46b7..a253a6bea3 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -76,19 +76,19 @@ public:
~QCocoaIntegration();
bool hasCapability(QPlatformIntegration::Capability cap) const;
- QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
- QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId = 0) const;
- QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const;
-
- QList<QPlatformScreen *> screens() const { return mScreens; }
+ QPlatformWindow *createPlatformWindow(QWindow *window) const;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const;
+ QAbstractEventDispatcher *guiThreadEventDispatcher() const;
QPlatformFontDatabase *fontDatabase() const;
+ QPlatformMenu *createPlatformMenu(QMenu *menu = 0) const;
+ QPlatformMenuBar *createPlatformMenuBar(QMenuBar *menuBar = 0) const;
- QPlatformEventLoopIntegration *createEventLoopIntegration() const;
-
+ QPlatformNativeInterface *nativeInterface() const;
private:
- QList<QPlatformScreen *> mScreens;
QPlatformFontDatabase *mFontDb;
+ QAbstractEventDispatcher *mEventDispatcher;
QCocoaAutoReleasePool *mPool;
};
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 086f7b62e9..e3e204226f 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -42,14 +42,18 @@
#include "qcocoaintegration.h"
#include "qcocoawindow.h"
-#include "qcocoawindowsurface.h"
-#include "qcocoaeventloopintegration.h"
+#include "qcocoabackingstore.h"
+#include "qcocoanativeinterface.h"
+#include "qcocoamenuloader.h"
+#include "qcocoaeventdispatcher.h"
+#include "qcocoahelpers.h"
+#include "qcocoaapplication.h"
+#include "qcocoaapplicationdelegate.h"
+#include "qmenu_mac.h"
-#include "qcoretextfontdatabase.h"
+#include <QtCore/qcoreapplication.h>
-#include <QtGui/QApplication>
-
-#include <private/qpixmap_raster_p.h>
+#include <QtPlatformSupport/private/qbasicunixfontdatabase_p.h>
QT_BEGIN_NAMESPACE
@@ -74,18 +78,47 @@ QCocoaScreen::~QCocoaScreen()
}
QCocoaIntegration::QCocoaIntegration()
- : mFontDb(new QCoreTextFontDatabase())
+ : mFontDb(new QBasicUnixFontDatabase())
+ , mEventDispatcher(new QCocoaEventDispatcher())
{
mPool = new QCocoaAutoReleasePool;
- //Make sure we have a nsapplication :)
- [NSApplication sharedApplication];
-// [[OurApplication alloc] init];
+ QNSApplication *cocoaApplication = [QNSApplication sharedApplication];
+
+ // Applications launched from plain executables (without an app
+ // bundle) are "background" applications that does not take keybaord
+ // focus or have a dock icon or task switcher entry. Qt Gui apps generally
+ // wants to be foreground applications so change the process type. (But
+ // see the function implementation for exceptions.)
+ qt_mac_transformProccessToForegroundApplication();
+
+ // Move the application window to front to avoid launching behind the terminal.
+ // Ignoring other apps is neccessary (we must ignore the terminal), but makes
+ // Qt apps play slightly less nice with other apps when lanching from Finder
+ // (See the activateIgnoringOtherApps docs.)
+ [cocoaApplication activateIgnoringOtherApps : YES];
+
+ // ### For AA_MacPluginApplication we don't want to load the menu nib.
+ // Qt 4 also does not set the application delegate, so that behavior
+ // is matched here.
+ if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) {
+
+ // Set app delegate, link to the current delegate (if any)
+ QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate];
+ [newDelegate setReflectionDelegate:[cocoaApplication delegate]];
+ [cocoaApplication setDelegate:newDelegate];
+
+ // Load the application menu. This menu contains Preferences, Hide, Quit.
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
+ qt_mac_loadMenuNib(qtMenuLoader);
+ [cocoaApplication setMenu:[qtMenuLoader menu]];
+ [newDelegate setMenuLoader:qtMenuLoader];
+ }
NSArray *screens = [NSScreen screens];
for (uint i = 0; i < [screens count]; i++) {
QCocoaScreen *screen = new QCocoaScreen(i);
- mScreens.append(screen);
+ screenAdded(screen);
}
}
@@ -98,26 +131,32 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons
{
switch (cap) {
case ThreadedPixmaps: return true;
+ case OpenGL : return true;
+ case ThreadedOpenGL : return true;
default: return QPlatformIntegration::hasCapability(cap);
}
}
-QPixmapData *QCocoaIntegration::createPixmapData(QPixmapData::PixelType type) const
+QPlatformWindow *QCocoaIntegration::createPlatformWindow(QWindow *window) const
+{
+ return new QCocoaWindow(window);
+}
+
+QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
- return new QRasterPixmapData(type);
+ return new QCocoaGLContext(context->format(), context->shareHandle());
}
-QPlatformWindow *QCocoaIntegration::createPlatformWindow(QWidget *widget, WId winId) const
+QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *window) const
{
- Q_UNUSED(winId);
- return new QCocoaWindow(widget);
+ return new QCocoaBackingStore(window);
}
-QWindowSurface *QCocoaIntegration::createWindowSurface(QWidget *widget, WId winId) const
+QAbstractEventDispatcher *QCocoaIntegration::guiThreadEventDispatcher() const
{
- return new QCocoaWindowSurface(widget,winId);
+ return mEventDispatcher;
}
QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const
@@ -125,8 +164,21 @@ QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const
return mFontDb;
}
-QPlatformEventLoopIntegration *QCocoaIntegration::createEventLoopIntegration() const
+QPlatformMenu *QCocoaIntegration::createPlatformMenu(QMenu *menu) const
+{
+ // return new QCocoaMenu(menu);
+ return 0;
+}
+
+QPlatformMenuBar *QCocoaIntegration::createPlatformMenuBar(QMenuBar *menuBar) const
+{
+ //return new QCocoaMenuBar(menuBar);
+ return 0;
+}
+
+QPlatformNativeInterface *QCocoaIntegration::nativeInterface() const
{
- return new QCocoaEventLoopIntegration();
+ return new QCocoaNativeInterface();
}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h
new file mode 100644
index 0000000000..4e8ce20580
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenu.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmacdefines_mac.h"
+#import <Cocoa/Cocoa.h>
+
+QT_FORWARD_DECLARE_CLASS(QMenu)
+QT_FORWARD_DECLARE_CLASS(QAction)
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+
+@protocol NSMenuDelegate <NSObject>
+- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item;
+- (void)menuWillOpen:(NSMenu*)menu;
+- (void)menuDidClose:(NSMenu*)menu;
+- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
+ whichItem:(NSMenuItem**)outItem;
+@end
+
+#endif
+
+@interface QT_MANGLE_NAMESPACE(QNativeCocoaMenu) : NSMenu <NSMenuDelegate>
+{
+ QMenu *qmenu;
+ QAction *previousAction;
+}
+- (id)initWithQMenu:(QMenu*)menu;
+- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action;
+- (NSInteger)indexOfItemWithTarget:(id)anObject andAction:(SEL)actionSelector;
+@end
+
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
new file mode 100644
index 0000000000..1bb5f45a94
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qapplication.h"
+#include "qvarlengtharray.h"
+#import "qcocoamenu.h"
+#import "qcocoamenuloader.h"
+#import "qcocoaapplication.h"
+#include "qcocoahelpers.h"
+#include <private/qapplication_p.h>
+#include <private/qaction_p.h>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QWidget)
+QT_FORWARD_DECLARE_CLASS(QApplication)
+QT_FORWARD_DECLARE_CLASS(QCoreApplication)
+QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
+QT_FORWARD_DECLARE_CLASS(QKeyEvent)
+QT_FORWARD_DECLARE_CLASS(QEvent)
+
+QT_BEGIN_NAMESPACE
+extern void qt_mac_menu_collapseSeparators(NSMenu *menu, bool collapse);
+void qt_mac_clear_status_text(QAction *action);
+extern void qt_mac_emit_menuSignals(QMenu *menu, bool show);
+extern void qt_mac_menu_emit_hovered(QMenu *menu, QAction *action);
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QNativeCocoaMenu)
+
+- (id)initWithQMenu:(QMenu*)menu
+{
+ self = [super init];
+ if (self) {
+ qmenu = menu;
+ previousAction = 0;
+ [self setAutoenablesItems:NO];
+ [self setDelegate:self];
+ }
+ return self;
+}
+
+- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item
+{
+ Q_UNUSED(menu);
+
+ if (!item) {
+ if (previousAction) {
+ qt_mac_clear_status_text(previousAction);
+ previousAction = 0;
+ }
+ return;
+ }
+
+ if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
+ QMenu *qtmenu = static_cast<QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *>(menu)->qmenu;
+ previousAction = action;
+ action->activate(QAction::Hover);
+ qt_mac_menu_emit_hovered(qtmenu, action);
+ action->showStatusText(0); // 0 widget -> action's parent
+ }
+}
+
+- (void)menuWillOpen:(NSMenu*)menu
+{
+ while (QWidget *popup
+ = QApplication::activePopupWidget())
+ popup->close();
+ QMenu *qtmenu = static_cast<QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *>(menu)->qmenu;
+ qt_mac_emit_menuSignals(qtmenu, true);
+ qt_mac_menu_collapseSeparators(menu, qtmenu->separatorsCollapsible());
+}
+
+- (void)menuDidClose:(NSMenu*)menu
+{
+ qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *)menu)->qmenu, false);
+ if (previousAction) {
+ qt_mac_clear_status_text(previousAction);
+ previousAction = 0;
+ }
+}
+
+- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
+ whichItem:(NSMenuItem**)outItem
+{
+ for (NSMenuItem *item in [menu itemArray]) {
+ if (![item isEnabled] || [item isHidden] || [item isSeparatorItem])
+ continue;
+ if ([item hasSubmenu]) {
+ if ([self hasShortcut:[item submenu]
+ forKey:key
+ forModifiers:modifier whichItem:outItem]) {
+ if (outItem)
+ *outItem = item;
+ return YES;
+ }
+ }
+ NSString *menuKey = [item keyEquivalent];
+ if (menuKey && NSOrderedSame == [menuKey compare:key]
+ && (modifier == [item keyEquivalentModifierMask])) {
+ if (outItem)
+ *outItem = item;
+ return YES;
+ }
+ }
+ if (outItem)
+ *outItem = 0;
+ return NO;
+}
+
+NSString *qt_mac_removePrivateUnicode(NSString* string)
+{
+ int len = [string length];
+ if (len) {
+ QVarLengthArray <unichar, 10> characters(len);
+ bool changed = false;
+ for (int i = 0; i<len; i++) {
+ characters[i] = [string characterAtIndex:i];
+ // check if they belong to key codes in private unicode range
+ // currently we need to handle only the NSDeleteFunctionKey
+ if (characters[i] == NSDeleteFunctionKey) {
+ characters[i] = NSDeleteCharacter;
+ changed = true;
+ }
+ }
+ if (changed)
+ return [NSString stringWithCharacters:characters.data() length:len];
+ }
+ return string;
+}
+
+- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
+{
+ // Check if the menu actually has a keysequence defined for this key event.
+ // If it does, then we will first send the key sequence to the QWidget that has focus
+ // since (in Qt's eyes) it needs to a chance at the key event first. If the widget
+ // accepts the key event, we then return YES, but set the target and action to be nil,
+ // which means that the action should not be triggered, and instead dispatch the event ourselves.
+ // In every other case we return NO, which means that Cocoa can do as it pleases
+ // (i.e., fire the menu action).
+ NSMenuItem *whichItem;
+ // Change the private unicode keys to the ones used in setting the "Key Equivalents"
+ NSString *characters = qt_mac_removePrivateUnicode([event characters]);
+ if ([self hasShortcut:menu
+ forKey:characters
+ // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
+ forModifiers:([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask))
+ whichItem:&whichItem]) {
+ QWidget *widget = 0;
+ QAction *qaction = 0;
+ if (whichItem && [whichItem tag]) {
+ qaction = reinterpret_cast<QAction *>([whichItem tag]);
+ }
+ if (qApp->activePopupWidget())
+ widget = (qApp->activePopupWidget()->focusWidget() ?
+ qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
+ else if (QApplicationPrivate::focus_widget)
+ widget = QApplicationPrivate::focus_widget;
+ // If we could not find any receivers, pass it to the active window
+ if (!widget)
+ widget = qApp->activeWindow();
+ if (qaction && widget) {
+ int key = qaction->shortcut();
+ QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
+ Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
+ accel_ev.ignore();
+
+// ### qt_sendSpontaneousEvent(widget, &accel_ev);
+
+ if (accel_ev.isAccepted()) {
+ qWarning("Unimplemented: qt_dispatchKeyEvent");
+#if 0
+ qt_dispatchKeyEvent(event, widget);
+#endif
+ *target = nil;
+ *action = nil;
+ return YES;
+ }
+ }
+ }
+ return NO;
+}
+
+- (NSInteger)indexOfItemWithTarget:(id)anObject andAction:(SEL)actionSelector
+{
+ NSInteger index = [super indexOfItemWithTarget:anObject andAction:actionSelector];
+ static SEL selForOFCP = NSSelectorFromString(@"orderFrontCharacterPalette:");
+ if (index == -1 && selForOFCP == actionSelector) {
+ // Check if the 'orderFrontCharacterPalette' SEL exists for QNativeCocoaMenuLoader object
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
+ return [super indexOfItemWithTarget:loader andAction:actionSelector];
+ }
+ return index;
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+extern int qt_mac_menus_open_count; // qmenu_mac.mm
+
+void qt_mac_emit_menuSignals(QMenu *menu, bool show)
+{
+ if (!menu)
+ return;
+ int delta;
+ if (show) {
+ emit menu->aboutToShow();
+ delta = 1;
+ } else {
+ emit menu->aboutToHide();
+ delta = -1;
+ }
+ qt_mac_menus_open_count += delta;
+}
+
+void qt_mac_clear_status_text(QAction *action)
+{
+ action->d_func()->showStatusText(0, QString());
+}
+
+void qt_mac_menu_emit_hovered(QMenu *menu, QAction *action)
+{
+ emit menu->hovered(action);
+}
+
+
+QT_END_NAMESPACE
+
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h
new file mode 100644
index 0000000000..2fcda512f0
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOAMENULOADER_P_H
+#define QCOCOAMENULOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#import <Cocoa/Cocoa.h>
+#include <QtCore/private/qcore_mac_p.h>
+
+@interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder
+{
+ IBOutlet NSMenu *theMenu;
+ IBOutlet NSMenu *appMenu;
+ IBOutlet NSMenuItem *quitItem;
+ IBOutlet NSMenuItem *preferencesItem;
+ IBOutlet NSMenuItem *aboutItem;
+ IBOutlet NSMenuItem *aboutQtItem;
+ IBOutlet NSMenuItem *hideItem;
+ NSMenuItem *lastAppSpecificItem;
+ NSMenuItem *servicesItem;
+ NSMenuItem *hideAllOthersItem;
+ NSMenuItem *showAllItem;
+}
+- (void)ensureAppMenuInMenu:(NSMenu *)menu;
+- (void)removeActionsFromAppMenu;
+- (NSMenu *)applicationMenu;
+- (NSMenu *)menu;
+- (NSMenuItem *)quitMenuItem;
+- (NSMenuItem *)preferencesMenuItem;
+- (NSMenuItem *)aboutMenuItem;
+- (NSMenuItem *)aboutQtMenuItem;
+- (NSMenuItem *)hideMenuItem;
+- (NSMenuItem *)appSpecificMenuItem;
+- (IBAction)terminate:(id)sender;
+- (IBAction)orderFrontStandardAboutPanel:(id)sender;
+- (IBAction)hideOtherApplications:(id)sender;
+- (IBAction)unhideAllApplications:(id)sender;
+- (IBAction)hide:(id)sender;
+- (IBAction)qtDispatcherToQAction:(id)sender;
+- (void)qtUpdateMenubar;
+- (void)orderFrontCharacterPalette:(id)sender;
+@end
+
+void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader);
+
+#endif // QCOCOAMENULOADER_P_H
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
new file mode 100644
index 0000000000..353808655f
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcocoamenuloader.h"
+
+#include "qmenu_mac.h"
+#include "qcocoahelpers.h"
+
+#include <QtCore/private/qcore_mac_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+#ifndef QT_NO_TRANSLATION
+ QT_BEGIN_NAMESPACE
+ extern QString qt_mac_applicationmenu_string(int type);
+ QT_END_NAMESPACE
+#endif
+
+QT_USE_NAMESPACE
+
+/*
+ Loads and instantiates the main app menu from the menu nib file(s).
+
+ The main app menu contains the Quit, Hide About, Preferences entries, and
+ The reason for having the nib file is that those can not be created
+ programmatically. To ease deployment the nib files are stored in Qt resources
+ and written to QDir::temp() before loading. (Earlier Qt versions used
+ to require having the nib file in the QtGui framework.)
+*/
+void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
+{
+ // Create qt_menu.nib dir in temp.
+ QDir temp = QDir::temp();
+ temp.mkdir("qt_menu.nib");
+ QString nibDir = temp.canonicalPath() + QLatin1String("/") + QLatin1String("qt_menu.nib/");
+ if (!QDir(nibDir).exists()) {
+ qWarning("qt_mac_loadMenuNib: could not create nib directory in temp");
+ return;
+ }
+
+ // Copy nib files from resources to temp.
+ QDir nibResource(":/trolltech/mac/qt_menu.nib/");
+ if (!nibResource.exists()) {
+ qWarning("qt_mac_loadMenuNib: could not load nib from resources");
+ return;
+ }
+ foreach (const QFileInfo &file, nibResource.entryInfoList()) {
+ QFile::copy(file.absoluteFilePath(), nibDir + QLatin1String("/") + file.fileName());
+ }
+
+ // Load and instantiate nib file from temp
+ NSURL *nibUrl = [NSURL fileURLWithPath : const_cast<NSString *>(reinterpret_cast<const NSString *>(QCFString::toCFStringRef(nibDir)))];
+ [nibUrl autorelease];
+ NSNib *nib = [[NSNib alloc] initWithContentsOfURL : nibUrl];
+ [nib autorelease];
+ if(!nib) {
+ qWarning("qt_mac_loadMenuNib: could not load nib from temp");
+ return;
+ }
+ bool ok = [nib instantiateNibWithOwner : qtMenuLoader topLevelObjects : nil];
+ if (!ok) {
+ qWarning("qt_mac_loadMenuNib: could not instantiate nib");
+ }
+}
+
+
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader)
+
+- (void)awakeFromNib
+{
+ servicesItem = [[appMenu itemWithTitle:@"Services"] retain];
+ hideAllOthersItem = [[appMenu itemWithTitle:@"Hide Others"] retain];
+ showAllItem = [[appMenu itemWithTitle:@"Show All"] retain];
+
+ // Get the names in the nib to match the app name set by Qt.
+ const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qt_mac_applicationName()));
+ [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+ withString:const_cast<NSString *>(appName)]];
+ [appName release];
+ // Disable the items that don't do anything. If someone associates a QAction with them
+ // They should get synced back in.
+ [preferencesItem setEnabled:NO];
+ [preferencesItem setHidden:YES];
+ [aboutItem setEnabled:NO];
+ [aboutItem setHidden:YES];
+}
+
+- (void)ensureAppMenuInMenu:(NSMenu *)menu
+{
+ // The application menu is the menu in the menu bar that contains the
+ // 'Quit' item. When changing menu bar (e.g when switching between
+ // windows with different menu bars), we never recreate this menu, but
+ // instead pull it out the current menu bar and place into the new one:
+ NSMenu *mainMenu = [NSApp mainMenu];
+ if ([NSApp mainMenu] == menu)
+ return; // nothing to do (menu is the current menu bar)!
+
+#ifndef QT_NAMESPACE
+ Q_ASSERT(mainMenu);
+#endif
+ // Grab the app menu out of the current menu.
+ int numItems = [mainMenu numberOfItems];
+ NSMenuItem *oldAppMenuItem = 0;
+ for (int i = 0; i < numItems; ++i) {
+ NSMenuItem *item = [mainMenu itemAtIndex:i];
+ if ([item submenu] == appMenu) {
+ oldAppMenuItem = item;
+ [oldAppMenuItem retain];
+ [mainMenu removeItemAtIndex:i];
+ break;
+ }
+ }
+
+ if (oldAppMenuItem) {
+ [oldAppMenuItem setSubmenu:nil];
+ [oldAppMenuItem release];
+ NSMenuItem *appMenuItem = [[NSMenuItem alloc] initWithTitle:@"Apple"
+ action:nil keyEquivalent:@""];
+ [appMenuItem setSubmenu:appMenu];
+ [menu insertItem:appMenuItem atIndex:0];
+ }
+}
+
+- (void)removeActionsFromAppMenu
+{
+ for (NSMenuItem *item in [appMenu itemArray])
+ [item setTag:nil];
+}
+
+- (void)dealloc
+{
+ [servicesItem release];
+ [hideAllOthersItem release];
+ [showAllItem release];
+
+ [lastAppSpecificItem release];
+ [theMenu release];
+ [appMenu release];
+ [super dealloc];
+}
+
+- (NSMenu *)menu
+{
+ return [[theMenu retain] autorelease];
+}
+
+- (NSMenu *)applicationMenu
+{
+ return [[appMenu retain] autorelease];
+}
+
+- (NSMenuItem *)quitMenuItem
+{
+ return [[quitItem retain] autorelease];
+}
+
+- (NSMenuItem *)preferencesMenuItem
+{
+ return [[preferencesItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutMenuItem
+{
+ return [[aboutItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutQtMenuItem
+{
+ return [[aboutQtItem retain] autorelease];
+}
+
+- (NSMenuItem *)hideMenuItem
+{
+ return [[hideItem retain] autorelease];
+}
+
+- (NSMenuItem *)appSpecificMenuItem
+{
+ // Create an App-Specific menu item, insert it into the menu and return
+ // it as an autorelease item.
+ NSMenuItem *item = [[NSMenuItem alloc] init];
+
+ NSInteger location;
+ if (lastAppSpecificItem == nil) {
+ location = [appMenu indexOfItem:aboutQtItem];
+ } else {
+ location = [appMenu indexOfItem:lastAppSpecificItem];
+ [lastAppSpecificItem release];
+ }
+ lastAppSpecificItem = item; // Keep track of this for later (i.e., don't release it)
+ [appMenu insertItem:item atIndex:location + 1];
+
+ return [[item retain] autorelease];
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void)terminate:(id)sender
+{
+ [NSApp terminate:sender];
+}
+
+- (void)orderFrontStandardAboutPanel:(id)sender
+{
+ [NSApp orderFrontStandardAboutPanel:sender];
+}
+
+- (void)hideOtherApplications:(id)sender
+{
+ [NSApp hideOtherApplications:sender];
+}
+
+- (void)unhideAllApplications:(id)sender
+{
+ [NSApp unhideAllApplications:sender];
+}
+
+- (void)hide:(id)sender
+{
+ [NSApp hide:sender];
+}
+
+- (void)qtUpdateMenubar
+{
+ QCocoaMenuBar::macUpdateMenuBarImmediatly();
+}
+
+- (void)qtTranslateApplicationMenu
+{
+
+ qDebug() << "qtTranslateApplicationMenu";
+
+#ifndef QT_NO_TRANSLATION
+ [servicesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(0))];
+ [hideItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(1).arg(qt_mac_applicationName()))];
+ [hideAllOthersItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(2))];
+ [showAllItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(3))];
+ [preferencesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(4))];
+ [quitItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(5).arg(qt_mac_applicationName()))];
+ [aboutItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(6).arg(qt_mac_applicationName()))];
+#endif
+}
+
+- (IBAction)qtDispatcherToQAction:(id)sender
+{
+ //
+ //QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+ NSMenuItem *item = static_cast<NSMenuItem *>(sender);
+ if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
+ action->trigger();
+ } else if (item == quitItem) {
+ // We got here because someone was once the quitItem, but it has been
+ // abandoned (e.g., the menubar was deleted). In the meantime, just do
+ // normal QApplication::quit().
+ qApp->quit();
+ }
+}
+
+ - (void)orderFrontCharacterPalette:(id)sender
+ {
+ [NSApp orderFrontCharacterPalette:sender];
+ }
+@end
diff --git a/src/plugins/platforms/cocoa/qcocoaeventloopintegration.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
index 5765483fc7..f8216d8e61 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventloopintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
@@ -39,27 +39,17 @@
**
****************************************************************************/
-#ifndef QCOCAEVENTLOOPINTEGRATION_H
-#define QCOCAEVENTLOOPINTEGRATION_H
+#ifndef QCOCOANATIVEINTERFACE_H
+#define QCOCOANATIVEINTERFACE_H
-#include <Cocoa/Cocoa.h>
+#include <QtGui/QPlatformNativeInterface>
-#include <QPlatformEventLoopIntegration>
+class QWidget;
-
-class QCocoaEventLoopIntegration : public QPlatformEventLoopIntegration
+class QCocoaNativeInterface : public QPlatformNativeInterface
{
public:
- QCocoaEventLoopIntegration();
- void startEventLoop();
- void quitEventLoop();
- void qtNeedsToProcessEvents();
-
-private:
- CFRunLoopSourceContext m_sourceContext;
- CFRunLoopTimerContext m_timerContext;
- CFRunLoopSourceRef m_source;
+ void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window);
};
-#endif // QCOCAEVENTLOOPINTEGRATION_H
-
+#endif // QCOCOANATIVEINTERFACE_H
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
new file mode 100644
index 0000000000..c6aa0d39e6
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcocoanativeinterface.h"
+#include "qcocoaglcontext.h"
+#include "qcocoawindow.h"
+#include <qbytearray.h>
+#include <qwindow.h>
+#include "qplatformwindow_qpa.h"
+#include "qsurfaceformat.h"
+#include "qplatformopenglcontext_qpa.h"
+#include "qopenglcontext.h"
+#include <qdebug.h>
+
+void *QCocoaNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window)
+{
+ if (resourceString == "nsopenglcontext") {
+ return static_cast<QCocoaWindow *>(window->handle())->currentContext()->nsOpenGLContext();
+ }
+ return 0;
+}
diff --git a/src/plugins/platforms/cocoa/qcocoaresources.qrc b/src/plugins/platforms/cocoa/qcocoaresources.qrc
new file mode 100644
index 0000000000..de50d397c6
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoaresources.qrc
@@ -0,0 +1,17 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/trolltech/mac/cursors">
+<file>images/copyarrowcursor.png</file>
+<file>images/forbiddencursor.png</file>
+<file>images/spincursor.png</file>
+<file>images/waitcursor.png</file>
+<file>images/pluscursor.png</file>
+</qresource>
+<qresource prefix="/trolltech/mac/style">
+<file>images/leopard-unified-toolbar-on.png</file>
+</qresource>
+<qresource prefix="/trolltech/mac/">
+<file>qt_menu.nib/classes.nib</file>
+<file>qt_menu.nib/info.nib</file>
+<file>qt_menu.nib/keyedobjects.nib</file>
+</qresource>
+</RCC>
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index 9e7e68b7d2..ce79d3967f 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -45,28 +45,55 @@
#include <Cocoa/Cocoa.h>
#include <QPlatformWindow>
+#include <QRect>
+
+#include "qcocoaglcontext.h"
+#include "qnsview.h"
QT_BEGIN_NAMESPACE
+@interface QNSWindow : NSWindow {
+
+}
+
+@end
+
class QCocoaWindow : public QPlatformWindow
{
public:
- QCocoaWindow(QWidget *tlw);
+ QCocoaWindow(QWindow *tlw);
~QCocoaWindow();
void setGeometry(const QRect &rect);
-
void setVisible(bool visible);
+ void setWindowTitle(const QString &title);
+ void raise();
+ void lower();
WId winId() const;
-
NSView *contentView() const;
- void setContentView(NSView *contentView);
+ void windowDidMove();
void windowDidResize();
+ void windowWillClose();
+
+ void setCurrentContext(QCocoaGLContext *context);
+ QCocoaGLContext *currentContext() const;
+
+protected:
+ void determineWindowClass();
+ QNSWindow *createWindow();
+ NSRect globalGeometry(const QRect localWindowGeometry) const;
+ QRect windowGeometry() const;
+ QCocoaWindow *parentCocoaWindow() const;
private:
- NSWindow *m_nsWindow;
+ friend class QCocoaBackingStore;
+ QNSWindow *m_nsWindow;
+ QNSView *m_contentView;
+ quint32 m_windowAttributes;
+ quint32 m_windowClass;
+ QCocoaGLContext *m_glContext;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index a2fdce3520..7e4d4217ef 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -41,54 +41,116 @@
#include "qcocoawindow.h"
#include "qnswindowdelegate.h"
#include "qcocoaautoreleasepool.h"
+#include "qcocoaglcontext.h"
+#include "qnsview.h"
+#include <QtCore/private/qcore_mac_p.h>
+#include <qwindow.h>
+#include <QWindowSystemInterface>
+#include <QPlatformScreen>
-#include <QWidget>
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
-#include <QtGui/QApplication>
+#include <QDebug>
-#include <QWindowSystemInterface>
+@implementation QNSWindow
-#include <QDebug>
+- (BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ return YES;
+}
+
+@end
-QCocoaWindow::QCocoaWindow(QWidget *tlw)
+QCocoaWindow::QCocoaWindow(QWindow *tlw)
: QPlatformWindow(tlw)
+ , m_windowAttributes(0)
+ , m_windowClass(0)
+ , m_glContext(0)
{
QCocoaAutoReleasePool pool;
- const QRect geo = tlw->geometry();
- NSRect frame = NSMakeRect(geo.x(), geo.y(), geo.width(), geo.height());
- m_nsWindow = [[NSWindow alloc] initWithContentRect:frame
- styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask
- backing:NSBackingStoreBuffered
- defer:YES];
+ determineWindowClass();
+ m_nsWindow = createWindow();
QNSWindowDelegate *delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
[m_nsWindow setDelegate:delegate];
-
- [m_nsWindow makeKeyAndOrderFront:nil];
[m_nsWindow setAcceptsMouseMovedEvents:YES];
+
+ // Prevent Cocoa from releasing the window on close. Qt
+ // handles the close event asynchronously and we want to
+ // make sure that m_nsWindow stays valid until the
+ // QCocoaWindow is deleted by Qt.
+ [m_nsWindow setReleasedWhenClosed : NO];
+
+ m_contentView = [[QNSView alloc] initWithQWindow:tlw];
+
+ setGeometry(tlw->geometry());
+
+ [m_nsWindow setContentView:m_contentView];
}
QCocoaWindow::~QCocoaWindow()
{
+ [m_nsWindow release];
}
void QCocoaWindow::setGeometry(const QRect &rect)
{
QPlatformWindow::setGeometry(rect);
- NSRect bounds = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
+ NSRect bounds = globalGeometry(rect);
[[m_nsWindow contentView]setFrameSize:bounds.size];
+ [m_nsWindow setContentSize : bounds.size];
+ [m_nsWindow setFrameOrigin : bounds.origin];
+
+ if (m_glContext)
+ m_glContext->update();
}
void QCocoaWindow::setVisible(bool visible)
{
- Q_UNUSED(visible);
+ if (visible) {
+ // The parent window might have moved while this window was hidden,
+ // update the window geometry if there is a parent.
+ if (window()->transientParent())
+ setGeometry(window()->geometry());
+
+ // Make sure the QWindow has a frame ready before we show the NSWindow.
+ QWindowSystemInterface::handleSynchronousExposeEvent(window(), QRect(QPoint(), geometry().size()));
+
+ [m_nsWindow makeKeyAndOrderFront:nil];
+ } else {
+ [m_nsWindow orderOut:nil];
+ }
+}
+
+void QCocoaWindow::setWindowTitle(const QString &title)
+{
+ CFStringRef windowTitle = QCFString::toCFStringRef(title);
+ [m_nsWindow setTitle: const_cast<NSString *>(reinterpret_cast<const NSString *>(windowTitle))];
+ CFRelease(windowTitle);
+}
+
+void QCocoaWindow::raise()
+{
+ // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
+ [m_nsWindow orderFront: m_nsWindow];
+}
+
+void QCocoaWindow::lower()
+{
+ [m_nsWindow orderFront: m_nsWindow];
}
WId QCocoaWindow::winId() const
{
- return WId([m_nsWindow windowNumber]);
+ return WId(m_nsWindow);
}
NSView *QCocoaWindow::contentView() const
@@ -96,15 +158,231 @@ NSView *QCocoaWindow::contentView() const
return [m_nsWindow contentView];
}
-void QCocoaWindow::setContentView(NSView *contentView)
+void QCocoaWindow::windowDidMove()
{
- [m_nsWindow setContentView:contentView];
+ if (m_glContext)
+ m_glContext->update();
}
void QCocoaWindow::windowDidResize()
{
- //jlind: XXX This isn't ideal. Eventdispatcher does not run when resizing...
+ if (m_glContext)
+ m_glContext->update();
+
NSRect rect = [[m_nsWindow contentView]frame];
QRect geo(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);
- QWindowSystemInterface::handleGeometryChange(widget(),geo);
+ QWindowSystemInterface::handleSynchronousGeometryChange(window(), geo);
+}
+
+
+void QCocoaWindow::windowWillClose()
+{
+ QWindowSystemInterface::handleCloseEvent(window());
+}
+
+void QCocoaWindow::setCurrentContext(QCocoaGLContext *context)
+{
+ m_glContext = context;
+}
+
+QCocoaGLContext *QCocoaWindow::currentContext() const
+{
+ return m_glContext;
}
+
+/*
+ Determine the window class based on the window type and
+ window flags, and widget attr Sets m_windowAttributes
+ and m_windowClass.
+*/
+void QCocoaWindow::determineWindowClass()
+{
+ Qt::WindowType type = window()->windowType();
+ Qt::WindowFlags flags = window()->windowFlags();
+
+ const bool popup = (type == Qt::Popup);
+
+ if (type == Qt::ToolTip || type == Qt::SplashScreen || popup)
+ flags |= Qt::FramelessWindowHint;
+
+ m_windowClass = kSheetWindowClass;
+
+ if (popup || type == Qt::SplashScreen)
+ m_windowClass = kModalWindowClass;
+ else if (type == Qt::ToolTip)
+ m_windowClass = kHelpWindowClass;
+ else if (type == Qt::Tool)
+ m_windowClass = kFloatingWindowClass;
+ else
+ m_windowClass = kDocumentWindowClass;
+
+ m_windowAttributes = (kWindowCompositingAttribute | kWindowStandardHandlerAttribute);
+
+// if(qt_mac_is_macsheet(window())) {
+// m_windowClass = kSheetWindowClass;
+// } else
+
+ {
+ // Shift things around a bit to get the correct window class based on the presence
+ // (or lack) of the border.
+
+ bool customize = flags & Qt::CustomizeWindowHint;
+ bool framelessWindow = (flags & Qt::FramelessWindowHint || (customize && !(flags & Qt::WindowTitleHint)));
+ if (framelessWindow) {
+ if (m_windowClass == kDocumentWindowClass) {
+ m_windowAttributes |= kWindowNoTitleBarAttribute;
+ } else if (m_windowClass == kFloatingWindowClass) {
+ m_windowAttributes |= kWindowNoTitleBarAttribute;
+ } else if (m_windowClass == kMovableModalWindowClass) {
+ m_windowClass = kModalWindowClass;
+ }
+ } else {
+ m_windowAttributes |= NSTitledWindowMask;
+ if (m_windowClass != kModalWindowClass)
+ m_windowAttributes |= NSResizableWindowMask;
+ }
+
+ // Only add extra decorations (well, buttons) for widgets that can have them
+ // and have an actual border we can put them on.
+
+ if(m_windowClass != kModalWindowClass && m_windowClass != kMovableModalWindowClass
+ && m_windowClass != kSheetWindowClass && m_windowClass != kPlainWindowClass
+ && !framelessWindow && m_windowClass != kDrawerWindowClass
+ && m_windowClass != kHelpWindowClass) {
+ if (flags & Qt::WindowMinimizeButtonHint)
+ m_windowAttributes |= NSMiniaturizableWindowMask;
+ if (flags & Qt::WindowSystemMenuHint || flags & Qt::WindowCloseButtonHint)
+ m_windowAttributes |= NSClosableWindowMask;
+ } else {
+ // Clear these hints so that we aren't call them on invalid windows
+ flags &= ~(Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
+ }
+
+ }
+
+ if((popup || type == Qt::Tool) && !window()->isModal())
+ m_windowAttributes |= kWindowHideOnSuspendAttribute;
+ m_windowAttributes |= kWindowLiveResizeAttribute;
+}
+
+/*
+
+*/
+QNSWindow * QCocoaWindow::createWindow()
+{
+ // Determine if we need to add in our "custom window" attribute. Cocoa is rather clever
+ // in deciding if we need the maximize button or not (i.e., it's resizeable, so you
+ // must need a maximize button). So, the only buttons we have control over are the
+ // close and minimize buttons. If someone wants to customize and NOT have the maximize
+ // button, then we have to do our hack. We only do it for these cases because otherwise
+ // the window looks different when activated. This "QtMacCustomizeWindow" attribute is
+ // intruding on a public space and WILL BREAK in the future.
+ // One can hope that there is a more public API available by that time.
+/*
+ Qt::WindowFlags flags = widget ? widget->windowFlags() : Qt::WindowFlags(0);
+ if ((flags & Qt::CustomizeWindowHint)) {
+ if ((flags & (Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowTitleHint))
+ && !(flags & Qt::WindowMaximizeButtonHint))
+ wattr |= QtMacCustomizeWindow;
+ }
+*/
+ NSRect frame = globalGeometry(window()->geometry());
+ QCocoaAutoReleasePool pool;
+ QNSWindow *window;
+
+ switch (m_windowClass) {
+ case kMovableModalWindowClass:
+ case kModalWindowClass:
+ case kSheetWindowClass:
+ case kFloatingWindowClass:
+ case kOverlayWindowClass:
+ case kHelpWindowClass: {
+ NSPanel *panel;
+
+ BOOL needFloating = NO;
+ BOOL worksWhenModal = (this->window()->windowType() == Qt::Popup);
+
+ // Add in the extra flags if necessary.
+ switch (m_windowClass) {
+ case kSheetWindowClass:
+ m_windowAttributes |= NSDocModalWindowMask;
+ break;
+ case kFloatingWindowClass:
+ case kHelpWindowClass:
+ needFloating = YES;
+ m_windowAttributes |= NSUtilityWindowMask;
+ break;
+ default:
+ break;
+ }
+
+ panel = [[NSPanel alloc] initWithContentRect:frame
+ styleMask:m_windowAttributes
+ backing:NSBackingStoreBuffered
+ defer:NO]; // see window case below
+// ### crashes
+// [panel setFloatingPanel:needFloating];
+// [panel setWorksWhenModal:worksWhenModal];
+ window = static_cast<NSWindow *>(panel);
+ break;
+ }
+ default:
+ window = [[QNSWindow alloc] initWithContentRect:frame
+ styleMask:(NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask)
+ backing:NSBackingStoreBuffered
+ defer:NO]; // Deferring window creation breaks OpenGL (the GL context is set up
+ // before the window is shown and needs a proper window.).
+ break;
+ }
+
+ //qt_syncCocoaTitleBarButtons(window, widget);
+ return window;
+}
+
+// Calculate the global screen geometry for the given local geometry, which
+// might be in the parent window coordinate system.
+NSRect QCocoaWindow::globalGeometry(const QRect localGeometry) const
+{
+ QRect finalGeometry = localGeometry;
+
+ if (QCocoaWindow *parent = parentCocoaWindow()) {
+ QRect parentGeometry = parent->windowGeometry();
+ finalGeometry.adjust(parentGeometry.x(), parentGeometry.y(), parentGeometry.x(), parentGeometry.y());
+
+ // Qt child window geometry assumes that the origin is at the
+ // top-left of the content area of the parent window. The title
+ // bar is not a part of this contet area, but is still included
+ // in the NSWindow height. Move the child window down to acccount
+ // for this if the parent window has a title bar.
+ const int titlebarHeight = 22;
+ if (!(window()->windowFlags() & Qt::FramelessWindowHint))
+ finalGeometry.adjust(0, titlebarHeight, 0, titlebarHeight);
+ }
+
+ // The final "y invert" to get OS X global geometry:
+ QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window());
+ int flippedY = onScreen->geometry().height() - finalGeometry.y() - finalGeometry.height();
+ return NSMakeRect(finalGeometry.x(), flippedY, finalGeometry.width(), finalGeometry.height());
+}
+
+// Returns the current global screen geometry for the nswindow accociated with this window.
+QRect QCocoaWindow::windowGeometry() const
+{
+ NSRect rect = [m_nsWindow frame];
+ QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window());
+ int flippedY = onScreen->geometry().height() - rect.origin.y - rect.size.height; // account for nswindow inverted y.
+ QRect qRect = QRect(rect.origin.x, flippedY, rect.size.width, rect.size.height);
+ return qRect;
+}
+
+// Returns a pointer to the parent QCocoaWindow for this window, or 0 if there is none.
+QCocoaWindow *QCocoaWindow::parentCocoaWindow() const
+{
+ if (window() && window()->transientParent()) {
+ return static_cast<QCocoaWindow*>(window()->transientParent()->handle());
+ }
+ return 0;
+}
+
diff --git a/src/plugins/platforms/cocoa/qmenu_mac.h b/src/plugins/platforms/cocoa/qmenu_mac.h
new file mode 100644
index 0000000000..f20f82c761
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qmenu_mac.h
@@ -0,0 +1,85 @@
+
+#include <private/qt_mac_p.h>
+#include <QtCore/qpointer.h>
+#include <QtWidgets/qmenu.h>
+#include <QtWidgets/qmenubar.h>
+#include <QtWidgets/qplatformmenu_qpa.h>
+
+@class NSMenuItem;
+class QCocoaMenuAction : public QPlatformMenuAction
+{
+public:
+ QCocoaMenuAction();
+ ~QCocoaMenuAction();
+
+ NSMenuItem *menuItem;
+ uchar ignore_accel : 1;
+ uchar merged : 1;
+ OSMenuRef menu;
+ QPointer<QMenu> qtMenu;
+};
+
+struct QMenuMergeItem
+{
+ inline QMenuMergeItem(NSMenuItem *c, QCocoaMenuAction *a) : menuItem(c), action(a) { }
+ NSMenuItem *menuItem;
+ QCocoaMenuAction *action;
+};
+typedef QList<QMenuMergeItem> QMenuMergeList;
+
+class QCocoaMenu : public QPlatformMenu
+{
+public:
+ QCocoaMenu(QMenu *qtMenu);
+ ~QCocoaMenu();
+
+ OSMenuRef macMenu(OSMenuRef merge = 0);
+ void syncSeparatorsCollapsible(bool collapse);
+ void setMenuEnabled(bool enable);
+
+ void addAction(QAction *action, QAction *before);
+ void syncAction(QAction *action);
+ void removeAction(QAction *action);
+
+ void addAction(QCocoaMenuAction *action, QCocoaMenuAction *before);
+ void syncAction(QCocoaMenuAction *action);
+ void removeAction(QCocoaMenuAction *action);
+ bool merged(const QAction *action) const;
+ QCocoaMenuAction *findAction(QAction *action) const;
+
+ OSMenuRef menu;
+ static QHash<OSMenuRef, OSMenuRef> mergeMenuHash;
+ static QHash<OSMenuRef, QMenuMergeList*> mergeMenuItemsHash;
+ QList<QCocoaMenuAction*> actionItems;
+ QMenu *qtMenu;
+};
+
+class QCocoaMenuBar : public QPlatformMenuBar
+{
+public:
+ QCocoaMenuBar(QMenuBar *qtMenuBar);
+ ~QCocoaMenuBar();
+
+ void handleReparent(QWidget *newParent);
+
+ void addAction(QAction *action, QAction *before);
+ void syncAction(QAction *action);
+ void removeAction(QAction *action);
+
+ void addAction(QCocoaMenuAction *action, QCocoaMenuAction *before);
+ void syncAction(QCocoaMenuAction *action);
+ void removeAction(QCocoaMenuAction *action);
+
+ bool macWidgetHasNativeMenubar(QWidget *widget);
+ void macCreateMenuBar(QWidget *parent);
+ void macDestroyMenuBar();
+ OSMenuRef macMenu();
+ static bool macUpdateMenuBarImmediatly();
+ static void macUpdateMenuBar();
+ QCocoaMenuAction *findAction(QAction *action) const;
+
+ OSMenuRef menu;
+ OSMenuRef apple_menu;
+ QList<QCocoaMenuAction*> actionItems;
+ QMenuBar *qtMenuBar;
+};
diff --git a/src/plugins/platforms/cocoa/qmenu_mac.mm b/src/plugins/platforms/cocoa/qmenu_mac.mm
new file mode 100644
index 0000000000..7ca546dd79
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qmenu_mac.mm
@@ -0,0 +1,1274 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmenu_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "qmenu.h"
+#include "qhash.h"
+#include <qdebug.h>
+#include "qapplication.h"
+#include "qregexp.h"
+#include "qtoolbar.h"
+#include "qevent.h"
+#include "qstyle.h"
+#include "qwidgetaction.h"
+
+#include <private/qmenu_p.h>
+#include <private/qmenubar_p.h>
+#include <private/qguiapplication_p.h>
+
+#include "qcocoahelpers.h"
+#include "qcocoaapplication.h"
+#include "qcocoamenuloader.h"
+#include "qcocoamenu.h"
+#include "qcocoahelpers.h"
+#include "qcocoaautoreleasepool.h"
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ QMenu debug facilities
+ *****************************************************************************/
+
+/*****************************************************************************
+ QMenu globals
+ *****************************************************************************/
+bool qt_mac_no_menubar_merge = false;
+bool qt_mac_quit_menu_item_enabled = true;
+int qt_mac_menus_open_count = 0;
+
+static OSMenuRef qt_mac_create_menu(QWidget *w);
+
+static struct {
+ QPointer<QMenuBar> qmenubar;
+ bool modal;
+} qt_mac_current_menubar = { 0, false };
+
+
+
+
+/*****************************************************************************
+ Externals
+ *****************************************************************************/
+extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
+extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
+extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
+RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
+void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+
+/*****************************************************************************
+ QMenu utility functions
+ *****************************************************************************/
+bool qt_mac_watchingAboutToShow(QMenu *menu)
+{
+ return menu; /* && menu->receivers(SIGNAL(aboutToShow()));*/
+}
+
+static int qt_mac_CountMenuItems(OSMenuRef menu)
+{
+ if (menu) {
+ return [menu numberOfItems];
+ }
+ return 0;
+}
+
+void qt_mac_menu_collapseSeparators(NSMenu * theMenu, bool collapse)
+{
+ QCocoaAutoReleasePool pool;
+ OSMenuRef menu = static_cast<OSMenuRef>(theMenu);
+ if (collapse) {
+ bool previousIsSeparator = true; // setting to true kills all the separators placed at the top.
+ NSMenuItem *previousItem = nil;
+
+ NSArray *itemArray = [menu itemArray];
+ for (unsigned int i = 0; i < [itemArray count]; ++i) {
+ NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
+ if ([item isSeparatorItem]) {
+ [item setHidden:previousIsSeparator];
+ }
+
+ if (![item isHidden]) {
+ previousItem = item;
+ previousIsSeparator = ([previousItem isSeparatorItem]);
+ }
+ }
+
+ // We now need to check the final item since we don't want any separators at the end of the list.
+ if (previousItem && previousIsSeparator)
+ [previousItem setHidden:YES];
+ } else {
+ NSArray *itemArray = [menu itemArray];
+ for (unsigned int i = 0; i < [itemArray count]; ++i) {
+ NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
+ if (QAction *action = reinterpret_cast<QAction *>([item tag]))
+ [item setHidden:!action->isVisible()];
+ }
+ }
+}
+
+#ifndef QT_NO_TRANSLATION
+static const char *application_menu_strings[] = {
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Services"),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide %1"),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide Others"),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Show All"),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Preferences..."),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Quit %1"),
+ QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","About %1")
+ };
+
+QString qt_mac_applicationmenu_string(int type)
+{
+ QString menuString = QString::fromLatin1(application_menu_strings[type]);
+ QString translated = qApp->translate("QMenuBar", application_menu_strings[type]);
+ if (translated != menuString)
+ return translated;
+ else
+ return qApp->translate("MAC_APPLICATION_MENU",
+ application_menu_strings[type]);
+}
+#endif
+
+
+static quint32 constructModifierMask(quint32 accel_key)
+{
+ quint32 ret = 0;
+ const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
+ if ((accel_key & Qt::CTRL) == Qt::CTRL)
+ ret |= (dontSwap ? NSControlKeyMask : NSCommandKeyMask);
+ if ((accel_key & Qt::META) == Qt::META)
+ ret |= (dontSwap ? NSCommandKeyMask : NSControlKeyMask);
+ if ((accel_key & Qt::ALT) == Qt::ALT)
+ ret |= NSAlternateKeyMask;
+ if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
+ ret |= NSShiftKeyMask;
+ return ret;
+}
+
+static void cancelAllMenuTracking()
+{
+ QCocoaAutoReleasePool pool;
+ NSMenu *mainMenu = [NSApp mainMenu];
+ [mainMenu cancelTracking];
+ for (NSMenuItem *item in [mainMenu itemArray]) {
+ if ([item submenu]) {
+ [[item submenu] cancelTracking];
+ }
+ }
+}
+
+static bool actualMenuItemVisibility(const QCocoaMenuBar *mbp,
+ const QCocoaMenuAction *action)
+{
+ bool visible = action->action->isVisible();
+ if (visible && action->action->text() == QString(QChar(0x14)))
+ return false;
+
+ if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
+/* ### !qt_mac_CountMenuItems(cocoaMenu->macMenu(mbp->apple_menu)) &&*/
+ !qt_mac_watchingAboutToShow(action->action->menu())) {
+ return false;
+ }
+ return visible;
+}
+
+static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
+{
+ [menuItem setHidden:NO];
+ [menuItem setHidden:YES];
+ [menuItem setHidden:!actionVisibility];
+}
+
+static inline void syncNSMenuItemEnabled(NSMenuItem *menuItem, bool enabled)
+{
+ [menuItem setEnabled:NO];
+ [menuItem setEnabled:YES];
+ [menuItem setEnabled:enabled];
+}
+
+static inline void syncMenuBarItemsVisiblity(const QCocoaMenuBar *mac_menubar)
+{
+ const QList<QCocoaMenuAction *> &menubarActions = mac_menubar->actionItems;
+ for (int i = 0; i < menubarActions.size(); ++i) {
+ const QCocoaMenuAction *action = menubarActions.at(i);
+ syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action));
+ }
+}
+
+static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
+{
+ return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
+}
+
+static NSMenuItem *createNSMenuItem(const QString &title)
+{
+ NSMenuItem *item = [[NSMenuItem alloc]
+ initWithTitle:qt_mac_QStringToNSString(title)
+ action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
+ [item setTarget:nil];
+ return item;
+}
+
+// helper that recurses into a menu structure and en/dis-ables them
+void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
+{
+ bool modalWindowOnScreen = qApp->activeModalWidget() != 0;
+ for (NSMenuItem *item in [menu itemArray]) {
+ OSMenuRef submenu = [item submenu];
+ if (submenu != merge) {
+ if (submenu)
+ qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
+ if (!on) {
+ // The item should follow what the QAction has.
+ if ([item tag]) {
+ QAction *action = reinterpret_cast<QAction *>([item tag]);
+ syncNSMenuItemEnabled(item, action->isEnabled());
+ } else {
+ syncNSMenuItemEnabled(item, YES);
+ }
+ // We sneak in some extra code here to handle a menu problem:
+ // If there is no window on screen, we cannot set 'nil' as
+ // menu item target, because then cocoa will disable the item
+ // (guess it assumes that there will be no first responder to
+ // catch the trigger anyway?) OTOH, If we have a modal window,
+ // then setting the menu loader as target will make cocoa not
+ // deliver the trigger because the loader is then seen as modally
+ // shaddowed). So either way there are shortcomings. Instead, we
+ // decide the target as late as possible:
+ [item setTarget:modalWindowOnScreen ? nil : getMenuLoader()];
+ } else {
+ syncNSMenuItemEnabled(item, NO);
+ }
+ }
+ }
+}
+
+//toggling of modal state
+static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
+{
+ OSMenuRef merge = QCocoaMenu::mergeMenuHash.value(menu);
+ qt_mac_set_modal_state_helper_recursive(menu, merge, on);
+ // I'm ignoring the special items now, since they should get handled via a syncAction()
+}
+
+bool qt_mac_menubar_is_open()
+{
+ return qt_mac_menus_open_count > 0;
+}
+
+QCocoaMenuAction::~QCocoaMenuAction()
+{
+ [menu release];
+ // Update the menu item if this action still owns it. For some items
+ // (like 'Quit') ownership will be transferred between all menu bars...
+ if (action && action.data() == reinterpret_cast<QAction *>([menuItem tag])) {
+ QAction::MenuRole role = action->menuRole();
+ // Check if the item is owned by Qt, and should be hidden to keep it from causing
+ // problems. Do it for everything but the quit menu item since that should always
+ // be visible.
+ if (role > QAction::ApplicationSpecificRole && role < QAction::QuitRole) {
+ [menuItem setHidden:YES];
+ } else if (role == QAction::TextHeuristicRole
+ && menuItem != [getMenuLoader() quitMenuItem]) {
+ [menuItem setHidden:YES];
+ }
+ [menuItem setTag:nil];
+ }
+ [menuItem release];
+}
+
+static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QCocoaMenuAction *action)
+{
+ if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
+ || action->action->menuRole() == QAction::NoRole)
+ return 0;
+
+ QString t = qt_mac_removeMnemonics(action->action->text().toLower());
+ int st = t.lastIndexOf(QLatin1Char('\t'));
+ if (st != -1)
+ t.remove(st, t.length()-st);
+ t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
+ //now the fun part
+ NSMenuItem *ret = 0;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+
+ switch (action->action->menuRole()) {
+ case QAction::NoRole:
+ ret = 0;
+ break;
+ case QAction::ApplicationSpecificRole:
+ ret = [loader appSpecificMenuItem];
+ break;
+ case QAction::AboutRole:
+ ret = [loader aboutMenuItem];
+ break;
+ case QAction::AboutQtRole:
+ ret = [loader aboutQtMenuItem];
+ break;
+ case QAction::QuitRole:
+ ret = [loader quitMenuItem];
+ break;
+ case QAction::PreferencesRole:
+ ret = [loader preferencesMenuItem];
+ break;
+ case QAction::TextHeuristicRole: {
+ QString aboutString = QMenuBar::tr("About").toLower();
+ if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
+ if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
+ ret = [loader aboutMenuItem];
+ } else {
+ ret = [loader aboutQtMenuItem];
+ }
+ } else if (t.startsWith(QMenuBar::tr("Config").toLower())
+ || t.startsWith(QMenuBar::tr("Preference").toLower())
+ || t.startsWith(QMenuBar::tr("Options").toLower())
+ || t.startsWith(QMenuBar::tr("Setting").toLower())
+ || t.startsWith(QMenuBar::tr("Setup").toLower())) {
+ ret = [loader preferencesMenuItem];
+ } else if (t.startsWith(QMenuBar::tr("Quit").toLower())
+ || t.startsWith(QMenuBar::tr("Exit").toLower())) {
+ ret = [loader quitMenuItem];
+ }
+ }
+ break;
+ }
+
+ if (QMenuMergeList *list = QCocoaMenu::mergeMenuItemsHash.value(merge)) {
+ for(int i = 0; i < list->size(); ++i) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.menuItem == ret && item.action)
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+static QString qt_mac_menu_merge_text(QCocoaMenuAction *action)
+{
+ QString ret;
+ extern QString qt_mac_applicationmenu_string(int type);
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ if (action->action->menuRole() == QAction::ApplicationSpecificRole)
+ ret = action->action->text();
+ else if (action->menuItem == [loader aboutMenuItem]) {
+ ret = qt_mac_applicationmenu_string(6).arg(qt_mac_applicationName());
+ } else if (action->menuItem == [loader aboutQtMenuItem]) {
+ if (action->action->text() == QString("About Qt"))
+ ret = QMenuBar::tr("About Qt");
+ else
+ ret = action->action->text();
+ } else if (action->menuItem == [loader preferencesMenuItem]) {
+ ret = qt_mac_applicationmenu_string(4);
+ } else if (action->menuItem == [loader quitMenuItem]) {
+ ret = qt_mac_applicationmenu_string(5).arg(qt_mac_applicationName());
+ }
+ return ret;
+}
+
+static QKeySequence qt_mac_menu_merge_accel(QCocoaMenuAction *action)
+{
+ QKeySequence ret;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ if (action->action->menuRole() == QAction::ApplicationSpecificRole)
+ ret = action->action->shortcut();
+ else if (action->menuItem == [loader preferencesMenuItem])
+ ret = QKeySequence(QKeySequence::Preferences);
+ else if (action->menuItem == [loader quitMenuItem])
+ ret = QKeySequence(QKeySequence::Quit);
+ return ret;
+}
+
+void Q_WIDGETS_EXPORT qt_mac_set_menubar_icons(bool b)
+{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
+void Q_WIDGETS_EXPORT qt_mac_set_native_menubar(bool b)
+{ QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, !b); }
+void Q_WIDGETS_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }
+
+/*****************************************************************************
+ QMenu bindings
+ *****************************************************************************/
+
+QCocoaMenuAction::QCocoaMenuAction()
+ : menuItem(0)
+ , ignore_accel(0), merged(0), menu(0)
+{
+
+}
+
+QCocoaMenu::QCocoaMenu(QMenu *a_qtMenu) : menu(0), qtMenu(a_qtMenu)
+{
+}
+
+QCocoaMenu::~QCocoaMenu()
+{
+ QCocoaAutoReleasePool pool;
+ while (actionItems.size()) {
+ QCocoaMenuAction *action = static_cast<QCocoaMenuAction *>(actionItems.takeFirst());
+ if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) {
+ int i = 0;
+ while (i < list->size()) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.action == action)
+ list->removeAt(i);
+ else
+ ++i;
+ }
+ }
+ delete action;
+ }
+ mergeMenuHash.remove(menu);
+ mergeMenuItemsHash.remove(menu);
+ [menu release];
+}
+
+
+void QCocoaMenu::addAction(QAction *a, QAction *before)
+{
+ QCocoaMenuAction *action = new QCocoaMenuAction;
+ action->action = a;
+ action->ignore_accel = 0;
+ action->merged = 0;
+ action->menu = 0;
+
+ QCocoaMenuAction *cocoaBefore = findAction(before);
+ addAction(action, cocoaBefore);
+}
+
+
+void QCocoaMenu::addAction(QCocoaMenuAction *action, QCocoaMenuAction *before)
+{
+ QCocoaAutoReleasePool pool;
+ if (!action)
+ return;
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+
+ [menu retain];
+ [action->menu release];
+ action->menu = menu;
+
+ /* When the action is considered a mergable action it
+ will stay that way, until removed.. */
+ if (!qt_mac_no_menubar_merge) {
+ OSMenuRef merge = QCocoaMenu::mergeMenuHash.value(menu);
+ if (merge) {
+ if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) {
+ action->merged = 1;
+ [merge retain];
+ [action->menu release];
+ action->menu = merge;
+ [cmd retain];
+ [cmd setAction:@selector(qtDispatcherToQAction:)];
+ [cmd setTarget:nil];
+ [action->menuItem release];
+ action->menuItem = cmd;
+ QMenuMergeList *list = QCocoaMenu::mergeMenuItemsHash.value(merge);
+ if (!list) {
+ list = new QMenuMergeList;
+ QCocoaMenu::mergeMenuItemsHash.insert(merge, list);
+ }
+ list->append(QMenuMergeItem(cmd, action));
+ }
+ }
+ }
+
+ NSMenuItem *newItem = action->menuItem;
+ if (newItem == 0) {
+ newItem = createNSMenuItem(action->action->text());
+ action->menuItem = newItem;
+ if (before) {
+ [menu insertItem:newItem atIndex:qMax(before_index, 0)];
+ } else {
+ [menu addItem:newItem];
+ }
+ } else {
+ [newItem setEnabled:YES];
+ // ###
+ //[newItem setEnabled:!QApplicationPrivate::modalState()];
+
+ }
+ [newItem setTag:long(static_cast<QAction *>(action->action))];
+ syncAction(action);
+}
+
+void QCocoaMenu::syncAction(QAction *a)
+{
+ syncAction(findAction(a));
+}
+
+void QCocoaMenu::removeAction(QAction *a)
+{
+ removeAction(findAction(a));
+}
+
+QCocoaMenuAction *QCocoaMenu::findAction(QAction *action) const
+{
+ for (int i = 0; i < actionItems.size(); i++) {
+ QCocoaMenuAction *act = actionItems[i];
+ if (action == act->action)
+ return act;
+ }
+ return 0;
+}
+
+
+// return an autoreleased string given a QKeySequence (currently only looks at the first one).
+NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
+{
+ quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL));
+ QChar cocoa_key = qt_mac_qtKey2CocoaKey(Qt::Key(accel_key));
+ if (cocoa_key.isNull())
+ cocoa_key = QChar(accel_key).toLower().unicode();
+ return [NSString stringWithCharacters:&cocoa_key.unicode() length:1];
+}
+
+// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
+NSUInteger keySequenceModifierMask(const QKeySequence &accel)
+{
+ return constructModifierMask(accel[0]);
+}
+
+void QCocoaMenu::syncAction(QCocoaMenuAction *action)
+{
+ if (!action)
+ return;
+
+ NSMenuItem *item = action->menuItem;
+ if (!item)
+ return;
+
+ QCocoaAutoReleasePool pool;
+ NSMenu *menu = [item menu];
+ bool actionVisible = action->action->isVisible();
+ [item setHidden:!actionVisible];
+ if (!actionVisible)
+ return;
+
+ int itemIndex = [menu indexOfItem:item];
+ Q_ASSERT(itemIndex != -1);
+ if (action->action->isSeparator()) {
+ action->menuItem = [NSMenuItem separatorItem];
+ [action->menuItem retain];
+ [menu insertItem: action->menuItem atIndex:itemIndex];
+ [menu removeItem:item];
+ [item release];
+ item = action->menuItem;
+ return;
+ } else if ([item isSeparatorItem]) {
+ // I'm no longer a separator...
+ action->menuItem = createNSMenuItem(action->action->text());
+ [menu insertItem:action->menuItem atIndex:itemIndex];
+ [menu removeItem:item];
+ [item release];
+ item = action->menuItem;
+ }
+
+ //find text (and accel)
+ action->ignore_accel = 0;
+ QString text = action->action->text();
+ QKeySequence accel = action->action->shortcut();
+ {
+ int st = text.lastIndexOf(QLatin1Char('\t'));
+ if (st != -1) {
+ action->ignore_accel = 1;
+ accel = QKeySequence(text.right(text.length()-(st+1)));
+ text.remove(st, text.length()-st);
+ }
+ }
+ {
+ QString cmd_text = qt_mac_menu_merge_text(action);
+ if (!cmd_text.isEmpty()) {
+ text = cmd_text;
+ accel = qt_mac_menu_merge_accel(action);
+ }
+ }
+ // Show multiple key sequences as part of the menu text.
+ if (accel.count() > 1)
+ text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");
+
+#if 0
+ QString finalString = qt_mac_removeMnemonics(text);
+#else
+ QString finalString = qt_mac_removeMnemonics(text);
+#endif
+ // Cocoa Font and title
+ if (action->action->font().resolve()) {
+ const QFont &actionFont = action->action->font();
+ NSFont *customMenuFont = [NSFont fontWithName:qt_mac_QStringToNSString(actionFont.family())
+ size:actionFont.pointSize()];
+ NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil];
+ NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil];
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
+ NSAttributedString *str = [[[NSAttributedString alloc] initWithString:qt_mac_QStringToNSString(finalString)
+ attributes:attributes] autorelease];
+ [item setAttributedTitle: str];
+ } else {
+ [item setTitle: qt_mac_QStringToNSString(finalString)];
+ }
+
+ if (action->action->menuRole() == QAction::AboutRole || action->action->menuRole() == QAction::QuitRole)
+ [item setTitle:qt_mac_QStringToNSString(text)];
+ else
+ [item setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(text))];
+
+ // Cocoa Enabled
+ [item setEnabled: action->action->isEnabled()];
+
+ // Cocoa icon
+ NSImage *nsimage = 0;
+ if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) {
+ nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(16, QIcon::Normal)));
+ }
+ [item setImage:nsimage];
+ [nsimage release];
+
+ if (action->action->menu()) { //submenu
+ QCocoaMenu *cocoaMenu = static_cast<QCocoaMenu *>(action->action->menu()->platformMenu());
+ NSMenu *subMenu = cocoaMenu->macMenu();
+ if ([subMenu supermenu] && [subMenu supermenu] != [item menu]) {
+ // The menu is already a sub-menu of another one. Cocoa will throw an exception,
+ // in such cases. For the time being, a new QMenu with same set of actions is the
+ // only workaround.
+ action->action->setEnabled(false);
+ } else {
+ [item setSubmenu:subMenu];
+ }
+ } else { //respect some other items
+ [item setSubmenu:0];
+ // No key equivalent set for multiple key QKeySequence.
+ if (accel.count() == 1) {
+ [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
+ [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
+ } else {
+ [item setKeyEquivalent:@""];
+ [item setKeyEquivalentModifierMask:NSCommandKeyMask];
+ }
+ }
+ //mark glyph
+ [item setState:action->action->isChecked() ? NSOnState : NSOffState];
+}
+
+void QCocoaMenu::removeAction(QCocoaMenuAction *action)
+{
+ if (!action)
+ return;
+ QCocoaAutoReleasePool pool;
+ if (action->merged) {
+ if (reinterpret_cast<QAction *>([action->menuItem tag]) == action->action) {
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [action->menuItem setEnabled:false];
+ if (action->menuItem != [loader quitMenuItem]
+ && action->menuItem != [loader preferencesMenuItem]) {
+ [[action->menuItem menu] removeItem:action->menuItem];
+ }
+ }
+ } else {
+ [[action->menuItem menu] removeItem:action->menuItem];
+ }
+ actionItems.removeAll(action);
+}
+
+OSMenuRef QCocoaMenu::macMenu(OSMenuRef merge)
+{
+ if (menu)
+ return menu;
+ menu = qt_mac_create_menu(qtMenu);
+ if (merge) {
+ mergeMenuHash.insert(menu, merge);
+ }
+ QList<QAction*> items = qtMenu->actions();
+ for(int i = 0; i < items.count(); i++)
+ addAction(items[i], 0);
+ syncSeparatorsCollapsible(qtMenu->separatorsCollapsible());
+ return menu;
+}
+
+/*!
+ \internal
+*/
+void
+QCocoaMenu::syncSeparatorsCollapsible(bool collapse)
+{
+ qt_mac_menu_collapseSeparators(menu, collapse);
+}
+
+/*!
+ \internal
+*/
+void QCocoaMenu::setMenuEnabled(bool enable)
+{
+ QCocoaAutoReleasePool pool;
+ if (enable) {
+ for (int i = 0; i < actionItems.count(); ++i) {
+ QCocoaMenuAction *menuItem = static_cast<QCocoaMenuAction *>(actionItems.at(i));
+ if (menuItem && menuItem->action && menuItem->action->isEnabled()) {
+ [menuItem->menuItem setEnabled:true];
+ }
+ }
+ } else {
+ NSMenu *menu = menu;
+ for (NSMenuItem *item in [menu itemArray]) {
+ [item setEnabled:false];
+ }
+ }
+}
+
+/*!
+ \internal
+
+ This function will return the OSMenuRef used to create the native menu bar
+ bindings.
+
+ If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used
+ with Carbon's Menu Manager API.
+
+ If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer.
+
+ \warning This function is not portable.
+
+ \sa QMenuBar::macMenu()
+*/
+/// OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); }
+
+/*****************************************************************************
+ QMenuBar bindings
+ *****************************************************************************/
+typedef QHash<QWidget *, QMenuBar *> MenuBarHash;
+Q_GLOBAL_STATIC(MenuBarHash, menubars)
+static QMenuBar *fallback = 0;
+
+QCocoaMenuBar::QCocoaMenuBar(QMenuBar *a_qtMenuBar) : menu(0), apple_menu(0), qtMenuBar(a_qtMenuBar)
+{
+ macCreateMenuBar(qtMenuBar->parentWidget());
+}
+
+QCocoaMenuBar::~QCocoaMenuBar()
+{
+ for(QList<QCocoaMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it)
+ delete (*it);
+ [apple_menu release];
+ [menu release];
+}
+void QCocoaMenuBar::handleReparent(QWidget *newParent)
+{
+ if (macWidgetHasNativeMenubar(newParent)) {
+ // If the new parent got a native menubar from before, keep that
+ // menubar rather than replace it with this one (because a parents
+ // menubar has precedence over children menubars).
+ macDestroyMenuBar();
+ macCreateMenuBar(newParent);
+ }
+
+}
+
+void QCocoaMenuBar::addAction(QAction *action, QAction *beforeAction)
+{
+ if (action->isSeparator() || !menu)
+ return;
+ QCocoaMenuAction *cocoaAction = new QCocoaMenuAction;
+ cocoaAction->action = action;
+ cocoaAction->ignore_accel = 1;
+ QCocoaMenuAction *cocoaBeforeAction = findAction(beforeAction);
+ addAction(cocoaAction, cocoaBeforeAction);
+}
+
+void QCocoaMenuBar::addAction(QCocoaMenuAction *action, QCocoaMenuAction *before)
+{
+ if (!action || !menu)
+ return;
+
+ int before_index = actionItems.indexOf(before);
+ if (before_index < 0) {
+ before = 0;
+ before_index = actionItems.size();
+ }
+ actionItems.insert(before_index, action);
+
+ MenuItemIndex index = actionItems.size()-1;
+
+ action->menu = menu;
+ QCocoaAutoReleasePool pool;
+ [action->menu retain];
+ NSMenuItem *newItem = createNSMenuItem(action->action->text());
+ action->menuItem = newItem;
+
+ if (before) {
+ [menu insertItem:newItem atIndex:qMax(1, before_index + 1)];
+ index = before_index;
+ } else {
+ [menu addItem:newItem];
+ }
+ [newItem setTag:long(static_cast<QAction *>(action->action))];
+ syncAction(action);
+}
+
+
+void QCocoaMenuBar::syncAction(QCocoaMenuAction *action)
+{
+ if (!action || !menu)
+ return;
+
+ QCocoaAutoReleasePool pool;
+ NSMenuItem *item = action->menuItem;
+
+ OSMenuRef submenu = 0;
+ bool release_submenu = false;
+ if (action->action->menu()) {
+ QCocoaMenu *cocoaMenu = static_cast<QCocoaMenu *>(action->action->menu()->platformMenu());
+ if (!cocoaMenu) {
+
+ }
+
+ if ((submenu = cocoaMenu->macMenu(apple_menu))) {
+ if ([submenu supermenu] && [submenu supermenu] != [item menu])
+ return;
+ else
+ [item setSubmenu:submenu];
+ }
+ }
+
+ if (submenu) {
+ bool visible = actualMenuItemVisibility(this, action);
+ [item setSubmenu: submenu];
+ [submenu setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(action->action->text()))];
+ syncNSMenuItemVisiblity(item, visible);
+ if (release_submenu) { //no pointers to it
+ [submenu release];
+ }
+ } else {
+ qWarning("QMenu: No OSMenuRef created for popup menu");
+ }
+}
+
+
+void QCocoaMenuBar::removeAction(QCocoaMenuAction *action)
+{
+ if (!action || !menu)
+ return;
+ QCocoaAutoReleasePool pool;
+ [action->menu removeItem:action->menuItem];
+ actionItems.removeAll(action);
+}
+
+void QCocoaMenuBar::syncAction(QAction *a)
+{
+ syncAction(findAction(a));
+}
+
+void QCocoaMenuBar::removeAction(QAction *a)
+{
+ removeAction(findAction(a));
+}
+
+QCocoaMenuAction *QCocoaMenuBar::findAction(QAction *action) const
+{
+ for (int i = 0; i < actionItems.size(); i++) {
+ QCocoaMenuAction *act = actionItems[i];
+ if (action == act->action)
+ return act;
+ }
+ return 0;
+}
+
+bool QCocoaMenuBar::macWidgetHasNativeMenubar(QWidget *widget)
+{
+ // This function is different from q->isNativeMenuBar(), as
+ // it returns true only if a native menu bar is actually
+ // _created_.
+ if (!widget)
+ return false;
+ return menubars()->contains(widget->window());
+}
+
+void QCocoaMenuBar::macCreateMenuBar(QWidget *parent)
+{
+ static int dontUseNativeMenuBar = -1;
+ // We call the isNativeMenuBar function here
+ // because that will make sure that local overrides
+ // are dealt with correctly. q->isNativeMenuBar() will, if not
+ // overridden, depend on the attribute Qt::AA_DontUseNativeMenuBar:
+ bool qt_mac_no_native_menubar = !qtMenuBar->isNativeMenuBar();
+ if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) {
+ // The menubar is set to be native. Let's check (one time only
+ // for all menubars) if this is OK with the rest of the environment.
+ // As a result, Qt::AA_DontUseNativeMenuBar is set. NB: the application
+ // might still choose to not respect, or change, this flag.
+ bool isPlugin = QApplication::testAttribute(Qt::AA_MacPluginApplication);
+ bool environmentSaysNo = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
+ dontUseNativeMenuBar = isPlugin || environmentSaysNo;
+ QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
+ qt_mac_no_native_menubar = !qtMenuBar->isNativeMenuBar();
+ }
+ if (qt_mac_no_native_menubar == false) {
+ // INVARIANT: Use native menubar.
+ macUpdateMenuBar();
+ if (!parent && !fallback) {
+ fallback = qtMenuBar;
+ } else if (parent && parent->isWindow()) {
+ menubars()->insert(qtMenuBar->window(), qtMenuBar);
+ }
+ }
+}
+
+void QCocoaMenuBar::macDestroyMenuBar()
+{
+ QCocoaAutoReleasePool pool;
+ if (fallback == qtMenuBar)
+ fallback = 0;
+ QWidget *tlw = qtMenuBar->window();
+ menubars()->remove(tlw);
+
+ if (!qt_mac_current_menubar.qmenubar || qt_mac_current_menubar.qmenubar == qtMenuBar) {
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader removeActionsFromAppMenu];
+ QCocoaMenuBar::macUpdateMenuBar();
+ }
+}
+
+OSMenuRef QCocoaMenuBar::macMenu()
+{
+ if (!qtMenuBar->isNativeMenuBar()) {
+ return 0;
+ } else if (!menu) {
+ menu = qt_mac_create_menu(qtMenuBar);
+ ProcessSerialNumber mine, front;
+ if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) {
+ if (!qt_mac_no_menubar_merge && !apple_menu) {
+ apple_menu = qt_mac_create_menu(qtMenuBar);
+ [apple_menu setTitle:qt_mac_QStringToNSString(QString(QChar(0x14)))];
+ NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init];
+ [apple_menuItem setSubmenu:menu];
+ [apple_menu addItem:apple_menuItem];
+ [apple_menuItem release];
+ }
+ if (apple_menu) {
+ QCocoaMenu::mergeMenuHash.insert(menu, apple_menu);
+ }
+ QList<QAction*> items = qtMenuBar->actions();
+ for(int i = 0; i < items.count(); i++)
+ addAction(items[i], 0);
+ }
+ }
+ return menu;
+}
+
+/*!
+ \internal
+
+ This function will return the OSMenuRef used to create the native menu bar
+ bindings. This OSMenuRef is then set as the root menu for the Menu
+ Manager.
+
+ \warning This function is not portable.
+
+ \sa QMenu::macMenu()
+*/
+//OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); }
+
+/* !
+ \internal
+ Ancestor function that crosses windows (QWidget::isAncestorOf
+ only considers widgets within the same window).
+*/
+static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child)
+{
+ if (!possibleAncestor)
+ return false;
+
+ QWidget * current = child->parentWidget();
+ while (current != 0) {
+ if (current == possibleAncestor)
+ return true;
+ current = current->parentWidget();
+ }
+ return false;
+}
+
+/* !
+ \internal
+ Returns true if the entries of menuBar should be disabled,
+ based on the modality type of modalWidget.
+*/
+static bool qt_mac_should_disable_menu(QMenuBar *menuBar)
+{
+ QWidget *modalWidget = qApp->activeModalWidget();
+ if (!modalWidget)
+ return false;
+
+ if (menuBar && menuBar == menubars()->value(modalWidget))
+ // The menu bar is owned by the modal widget.
+ // In that case we should enable it:
+ return false;
+
+ // When there is an application modal window on screen, the entries of
+ // the menubar should be disabled. The exception in Qt is that if the
+ // modal window is the only window on screen, then we enable the menu bar.
+ QWidget *w = modalWidget;
+ QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
+ while (w) {
+ if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) {
+ for (int i=0; i<topLevelWidgets.size(); ++i) {
+ QWidget *top = topLevelWidgets.at(i);
+ if (w != top && top->isVisible()) {
+ // INVARIANT: we found another visible window
+ // on screen other than our modalWidget. We therefore
+ // disable the menu bar to follow normal modality logic:
+ return true;
+ }
+ }
+ // INVARIANT: We have only one window on screen that happends
+ // to be application modal. We choose to enable the menu bar
+ // in that case to e.g. enable the quit menu item.
+ return false;
+ }
+ w = w->parentWidget();
+ }
+
+ // INVARIANT: modalWidget is window modal. Disable menu entries
+ // if the menu bar belongs to an ancestor of modalWidget. If menuBar
+ // is nil, we understand it as the default menu bar set by the nib:
+ return menuBar ? qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget) : false;
+}
+
+static QWidget *findWindowThatShouldDisplayMenubar()
+{
+ QWidget *w = qApp->activeWindow();
+
+ if (!w) {
+ // We have no active window on screen. Try to
+ // find a window from the list of top levels:
+ QWidgetList tlws = QApplication::topLevelWidgets();
+ for(int i = 0; i < tlws.size(); ++i) {
+ QWidget *tlw = tlws.at(i);
+ if ((tlw->isVisible() && tlw->windowType() != Qt::Tool &&
+ tlw->windowType() != Qt::Popup)) {
+ w = tlw;
+ break;
+ }
+ }
+ }
+
+ return w;
+}
+
+static QMenuBar *findMenubarForWindow(QWidget *w)
+{
+ QMenuBar *mb = 0;
+ if (w) {
+ mb = menubars()->value(w);
+
+#if 0
+// ###
+//#ifndef QT_NO_MAINWINDOW
+ QDockWidget *dw = qobject_cast<QDockWidget *>(w);
+ if (!mb && dw) {
+ QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget());
+ if (mw && (mb = menubars()->value(mw)))
+ w = mw;
+ }
+#endif
+ while(w && !mb)
+ mb = menubars()->value((w = w->parentWidget()));
+ }
+
+ if (!mb) {
+ // We could not find a menu bar for the window. Lets
+ // check if we have a global (parentless) menu bar instead:
+ mb = fallback;
+ }
+
+ return mb;
+}
+
+void qt_mac_clear_menubar()
+{
+ if (QApplication::testAttribute(Qt::AA_MacPluginApplication))
+ return;
+
+ QCocoaAutoReleasePool pool;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ NSMenu *menu = [loader menu];
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+ const bool modal = qt_mac_should_disable_menu(0);
+ if (qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal)
+ qt_mac_set_modal_state(menu, modal);
+ qt_mac_current_menubar.qmenubar = 0;
+ qt_mac_current_menubar.modal = modal;
+}
+
+/*!
+ \internal
+
+ This function will update the current menu bar and set it as the
+ active menu bar in the Menu Manager.
+
+ \warning This function is not portable.
+*/
+void QCocoaMenuBar::macUpdateMenuBar()
+{
+ [getMenuLoader() performSelectorOnMainThread: @selector(qtUpdateMenubar) withObject: nil waitUntilDone: NO];
+}
+
+bool QCocoaMenuBar::macUpdateMenuBarImmediatly()
+{
+ bool ret = false;
+ cancelAllMenuTracking();
+ QWidget *w = findWindowThatShouldDisplayMenubar();
+ QMenuBar *mb = findMenubarForWindow(w);
+
+ // ### extern bool qt_mac_app_fullscreen; //qapplication_mac.mm
+ bool qt_mac_app_fullscreen = false;
+ // We need to see if we are in full screen mode, if so we need to
+ // switch the full screen mode to be able to show or hide the menubar.
+ if(w && mb) {
+ // This case means we are creating a menubar, check if full screen
+ if(w->isFullScreen()) {
+ // Ok, switch to showing the menubar when hovering over it.
+ SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
+ qt_mac_app_fullscreen = true;
+ }
+ } else if(w) {
+ // Removing a menubar
+ if(w->isFullScreen()) {
+ // Ok, switch to not showing the menubar when hovering on it
+ SetSystemUIMode(kUIModeAllHidden, 0);
+ qt_mac_app_fullscreen = true;
+ }
+ }
+
+ if (mb && mb->isNativeMenuBar()) {
+
+ // ###
+ bool modal = false;
+ //bool modal = QGuiApplicationPrivate::modalState();
+ QCocoaAutoReleasePool pool;
+ if (OSMenuRef menu = reinterpret_cast<QCocoaMenuBar *>(mb->platformMenuBar())->macMenu()) {
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+ syncMenuBarItemsVisiblity(reinterpret_cast<QCocoaMenuBar *>(mb->platformMenuBar()));
+
+ if (OSMenuRef tmpMerge = QCocoaMenu::mergeMenuHash.value(menu)) {
+ if (QMenuMergeList *mergeList
+ = QCocoaMenu::mergeMenuItemsHash.value(tmpMerge)) {
+ const int mergeListSize = mergeList->size();
+
+ for (int i = 0; i < mergeListSize; ++i) {
+ const QMenuMergeItem &mergeItem = mergeList->at(i);
+ // Ideally we would call QCocoaMenu::syncAction, but that requires finding
+ // the original QMen and likely doing more work than we need.
+ // For example, enabled is handled below.
+ [mergeItem.menuItem setTag:reinterpret_cast<long>(
+ static_cast<QAction *>(mergeItem.action->action))];
+ [mergeItem.menuItem setHidden:!(mergeItem.action->action->isVisible())];
+ }
+ }
+ }
+ // Check if menu is modally shaddowed and should be disabled:
+ modal = qt_mac_should_disable_menu(mb);
+ if (mb != qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal)
+ qt_mac_set_modal_state(menu, modal);
+ }
+ qt_mac_current_menubar.qmenubar = mb;
+ qt_mac_current_menubar.modal = modal;
+ ret = true;
+ } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) {
+ // INVARIANT: The currently active menu bar (if any) is not native. But we do have a
+ // native menu bar from before. So we need to decide whether or not is should be enabled:
+ const bool modal = qt_mac_should_disable_menu(qt_mac_current_menubar.qmenubar);
+ if (modal != qt_mac_current_menubar.modal) {
+ ret = true;
+ if (OSMenuRef menu = reinterpret_cast<QCocoaMenuBar *>(qt_mac_current_menubar.qmenubar->platformMenuBar())->macMenu()) {
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader ensureAppMenuInMenu:menu];
+ [NSApp setMainMenu:menu];
+ syncMenuBarItemsVisiblity(reinterpret_cast<QCocoaMenuBar *>(qt_mac_current_menubar.qmenubar->platformMenuBar()));
+ qt_mac_set_modal_state(menu, modal);
+ }
+ qt_mac_current_menubar.modal = modal;
+ }
+ }
+
+ if (!ret) {
+ qt_mac_clear_menubar();
+ }
+ return ret;
+}
+
+QHash<OSMenuRef, OSMenuRef> QCocoaMenu::mergeMenuHash;
+QHash<OSMenuRef, QMenuMergeList*> QCocoaMenu::mergeMenuItemsHash;
+
+bool QCocoaMenu::merged(const QAction *action) const
+{
+ if (OSMenuRef merge = mergeMenuHash.value(menu)) {
+ if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) {
+ for(int i = 0; i < list->size(); ++i) {
+ const QMenuMergeItem &item = list->at(i);
+ if (item.action->action == action)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//creation of the OSMenuRef
+static OSMenuRef qt_mac_create_menu(QWidget *w)
+{
+ OSMenuRef ret;
+ if (QMenu *qmenu = qobject_cast<QMenu *>(w)){
+ ret = [[QT_MANGLE_NAMESPACE(QNativeCocoaMenu) alloc] initWithQMenu:qmenu];
+ } else {
+ ret = [[NSMenu alloc] init];
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h
index 69a11134bd..0b96928d5b 100644
--- a/src/plugins/platforms/cocoa/qnsview.h
+++ b/src/plugins/platforms/cocoa/qnsview.h
@@ -48,17 +48,18 @@
@interface QNSView : NSView {
CGImageRef m_cgImage;
- QWidget *m_widget;
+ QWindow *m_window;
Qt::MouseButtons m_buttons;
}
- (id)init;
-- (id)initWithWidget:(QWidget *)widget;
+- (id)initWithQWindow:(QWindow *)window;
- (void)setImage:(QImage *)image;
- (void)drawRect:(NSRect)dirtyRect;
- (BOOL)isFlipped;
+- (BOOL)acceptsFirstResponder;
- (void)handleMouseEvent:(NSEvent *)theEvent;
- (void)mouseDown:(NSEvent *)theEvent;
@@ -74,6 +75,12 @@
- (void)otherMouseDragged:(NSEvent *)theEvent;
- (void)otherMouseUp:(NSEvent *)theEvent;
+- (int) convertKeyCode : (QChar)keyCode;
+- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags;
+- (void)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType;
+- (void)keyDown:(NSEvent *)theEvent;
+- (void)keyUp:(NSEvent *)theEvent;
+
@end
#endif //QNSVIEW_H
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 037cbdb5d6..f3c71d9eed 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -39,12 +39,20 @@
**
****************************************************************************/
+#include <Carbon/Carbon.h>
+
#include "qnsview.h"
+#include "qcocoahelpers.h"
#include <QtGui/QWindowSystemInterface>
-
#include <QtCore/QDebug>
+@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
+ - (CGFloat)deviceDeltaX;
+ - (CGFloat)deviceDeltaY;
+ - (CGFloat)deviceDeltaZ;
+@end
+
@implementation QNSView
- (id) init
@@ -52,16 +60,16 @@
self = [super init];
if (self) {
m_cgImage = 0;
- m_widget = 0;
+ m_window = 0;
m_buttons = Qt::NoButton;
}
return self;
}
-- (id)initWithWidget:(QWidget *)widget {
+- (id)initWithQWindow:(QWindow *)widget {
self = [self init];
if (self) {
- m_widget = widget;
+ m_window = widget;
}
return self;
}
@@ -91,7 +99,7 @@
bitDepth,
bytesPrLine,
cgColourSpaceRef,
- kCGImageAlphaNone,
+ kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
cgDataProviderRef,
NULL,
false,
@@ -130,82 +138,185 @@
return YES;
}
+- (BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
- (void)handleMouseEvent:(NSEvent *)theEvent;
{
- NSPoint point = [self convertPoint: [theEvent locationInWindow] fromView: nil];
- QPoint qt_localPoint(point.x,point.y);
+ NSPoint windowPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
+ QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
NSTimeInterval timestamp = [theEvent timestamp];
ulong qt_timestamp = timestamp * 1000;
- QWindowSystemInterface::handleMouseEvent(m_widget,qt_timestamp,qt_localPoint,QPoint(),m_buttons);
+ // ### Should the points be windowPoint and screenPoint?
+ QWindowSystemInterface::handleMouseEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, m_buttons);
+}
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ m_buttons |= Qt::LeftButton;
+ [self handleMouseEvent:theEvent];
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ if (!(m_buttons & Qt::LeftButton))
+ qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
+ [self handleMouseEvent:theEvent];
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ m_buttons &= QFlag(~int(Qt::LeftButton));
+ [self handleMouseEvent:theEvent];
}
- - (void)mouseDown:(NSEvent *)theEvent
- {
- m_buttons |= Qt::LeftButton;
- [self handleMouseEvent:theEvent];
- }
- - (void)mouseDragged:(NSEvent *)theEvent
- {
- if (!(m_buttons & Qt::LeftButton))
- qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
- [self handleMouseEvent:theEvent];
- }
- - (void)mouseUp:(NSEvent *)theEvent
- {
- m_buttons &= QFlag(~int(Qt::LeftButton));
- [self handleMouseEvent:theEvent];
- }
- (void)mouseMoved:(NSEvent *)theEvent
{
- qDebug() << "mouseMove";
[self handleMouseEvent:theEvent];
}
+
- (void)mouseEntered:(NSEvent *)theEvent
{
- Q_UNUSED(theEvent);
- QWindowSystemInterface::handleEnterEvent(m_widget);
+ Q_UNUSED(theEvent);
+ QWindowSystemInterface::handleEnterEvent(m_window);
}
+
- (void)mouseExited:(NSEvent *)theEvent
{
- Q_UNUSED(theEvent);
- QWindowSystemInterface::handleLeaveEvent(m_widget);
+ Q_UNUSED(theEvent);
+ QWindowSystemInterface::handleLeaveEvent(m_window);
}
+
- (void)rightMouseDown:(NSEvent *)theEvent
{
- m_buttons |= Qt::RightButton;
+ m_buttons |= Qt::RightButton;
[self handleMouseEvent:theEvent];
}
+
- (void)rightMouseDragged:(NSEvent *)theEvent
{
- if (!(m_buttons & Qt::LeftButton))
- qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
- [self handleMouseEvent:theEvent];
+ if (!(m_buttons & Qt::LeftButton))
+ qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
+ [self handleMouseEvent:theEvent];
}
+
- (void)rightMouseUp:(NSEvent *)theEvent
{
- m_buttons &= QFlag(~int(Qt::RightButton));
- [self handleMouseEvent:theEvent];
+ m_buttons &= QFlag(~int(Qt::RightButton));
+ [self handleMouseEvent:theEvent];
}
+
- (void)otherMouseDown:(NSEvent *)theEvent
{
- m_buttons |= Qt::RightButton;
+ m_buttons |= Qt::RightButton;
[self handleMouseEvent:theEvent];
}
+
- (void)otherMouseDragged:(NSEvent *)theEvent
{
- if (!(m_buttons & Qt::LeftButton))
- qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
- [self handleMouseEvent:theEvent];
+ if (!(m_buttons & Qt::LeftButton))
+ qWarning("Internal Mousebutton tracking invalid(missing Qt::LeftButton");
+ [self handleMouseEvent:theEvent];
}
+
- (void)otherMouseUp:(NSEvent *)theEvent
{
- m_buttons &= QFlag(~int(Qt::MiddleButton));
- [self handleMouseEvent:theEvent];
+ m_buttons &= QFlag(~int(Qt::MiddleButton));
+ [self handleMouseEvent:theEvent];
}
+#ifndef QT_NO_WHEELEVENT
+- (void)scrollWheel:(NSEvent *)theEvent
+{
+ int deltaX = 0;
+ int deltaY = 0;
+ int deltaZ = 0;
+
+ const EventRef carbonEvent = (EventRef)[theEvent eventRef];
+ const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
+ const bool scrollEvent = carbonEventKind == kEventMouseScroll;
+
+ if (scrollEvent) {
+ // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
+ // Since deviceDelta is delivered as pixels rather than degrees, we need to
+ // convert from pixels to degrees in a sensible manner.
+ // It looks like 1/4 degrees per pixel behaves most native.
+ // (NB: Qt expects the unit for delta to be 8 per degree):
+ const int pixelsToDegrees = 2; // 8 * 1/4
+ deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
+ deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
+ deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
+ } else {
+ // carbonEventKind == kEventMouseWheelMoved
+ // Remove acceleration, and use either -120 or 120 as delta:
+ deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120);
+ deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120);
+ deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120);
+ }
+
+ NSPoint windowPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
+ QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
+ NSTimeInterval timestamp = [theEvent timestamp];
+ ulong qt_timestamp = timestamp * 1000;
+
+ if (deltaX != 0)
+ QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, deltaX, Qt::Horizontal);
+
+ if (deltaY != 0)
+ QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, deltaY, Qt::Vertical);
+ if (deltaZ != 0)
+ // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
+ // try to be ahead of the pack, I'm adding this extra value.
+ QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_windowPoint, deltaY, (Qt::Orientation)3);
+}
+#endif //QT_NO_WHEELEVENT
+
+- (int) convertKeyCode : (QChar)keyChar
+{
+ return qt_mac_cocoaKey2QtKey(keyChar);
+}
+
+- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
+{
+ Qt::KeyboardModifiers qtMods =Qt::NoModifier;
+ if (modifierFlags & NSShiftKeyMask)
+ qtMods |= Qt::ShiftModifier;
+ if (modifierFlags & NSControlKeyMask)
+ qtMods |= Qt::MetaModifier;
+ if (modifierFlags & NSAlternateKeyMask)
+ qtMods |= Qt::AltModifier;
+ if (modifierFlags & NSCommandKeyMask)
+ qtMods |= Qt::ControlModifier;
+ if (modifierFlags & NSNumericPadKeyMask)
+ qtMods |= Qt::KeypadModifier;
+ return qtMods;
+}
+
+- (void)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType
+{
+ NSTimeInterval timestamp = [theEvent timestamp];
+ ulong qt_timestamp = timestamp * 1000;
+ QString characters = QString::fromUtf8([[theEvent characters] UTF8String]);
+ Qt::KeyboardModifiers modifiers = [self convertKeyModifiers : [theEvent modifierFlags]];
+ QChar ch([[theEvent charactersIgnoringModifiers] characterAtIndex:0]);
+ int keyCode = [self convertKeyCode : ch];
+
+ QWindowSystemInterface::handleKeyEvent(m_window, qt_timestamp, QEvent::Type(eventType), keyCode, modifiers, characters);
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ [self handleKeyEvent : theEvent eventType :int(QEvent::KeyPress)];
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ [self handleKeyEvent : theEvent eventType :int(QEvent::KeyRelease)];
+}
@end
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h
index cf296c4a8b..5cd226a71d 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.h
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h
@@ -46,6 +46,26 @@
#include "qcocoawindow.h"
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@protocol NSWindowDelegate <NSObject>
+//- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize;
+//- (void)windowDidMiniaturize:(NSNotification*)notification;
+- (void)windowDidResize:(NSNotification *)notification;
+- (void)windowWillClose:(NSNotification *)notification;
+//- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame;
+- (void)windowDidMove:(NSNotification *)notification;
+//- (BOOL)windowShouldClose:(id)window;
+//- (void)windowDidDeminiaturize:(NSNotification *)notification;
+//- (void)windowDidBecomeMain:(NSNotification*)notification;
+//- (void)windowDidResignMain:(NSNotification*)notification;
+//- (void)windowDidBecomeKey:(NSNotification*)notification;
+//- (void)windowDidResignKey:(NSNotification*)notification;
+//- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu;
+//- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard;
+//- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame;
+@end
+#endif
+
@interface QNSWindowDelegate : NSObject <NSWindowDelegate>
{
QCocoaWindow *m_cocoaWindow;
@@ -54,6 +74,7 @@
- (id)initWithQCocoaWindow: (QCocoaWindow *) cocoaWindow;
- (void)windowDidResize:(NSNotification *)notification;
+- (void)windowDidMove:(NSNotification *)notification;
- (void)windowWillClose:(NSNotification *)notification;
@end
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
index 887b08f6d2..869ef7840b 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
@@ -64,10 +64,20 @@
}
}
+- (void)windowDidMove:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ if (m_cocoaWindow) {
+ m_cocoaWindow->windowDidMove();
+ }
+}
+
- (void)windowWillClose:(NSNotification *)notification
{
Q_UNUSED(notification);
- QWindowSystemInterface::handleCloseEvent(m_cocoaWindow->widget());
+ if (m_cocoaWindow) {
+ m_cocoaWindow->windowWillClose();
+ }
}
@end
diff --git a/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib b/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib
new file mode 100644
index 0000000000..0031e0e4e5
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>ACTIONS</key>
+ <dict>
+ <key>hide</key>
+ <string>id</string>
+ <key>hideOtherApplications</key>
+ <string>id</string>
+ <key>orderFrontStandardAboutPanel</key>
+ <string>id</string>
+ <key>qtDispatcherToQAction</key>
+ <string>id</string>
+ <key>terminate</key>
+ <string>id</string>
+ <key>unhideAllApplications</key>
+ <string>id</string>
+ </dict>
+ <key>CLASS</key>
+ <string>QCocoaMenuLoader</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>aboutItem</key>
+ <string>NSMenuItem</string>
+ <key>aboutQtItem</key>
+ <string>NSMenuItem</string>
+ <key>appMenu</key>
+ <string>NSMenu</string>
+ <key>hideItem</key>
+ <string>NSMenuItem</string>
+ <key>preferencesItem</key>
+ <string>NSMenuItem</string>
+ <key>quitItem</key>
+ <string>NSMenuItem</string>
+ <key>theMenu</key>
+ <string>NSMenu</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>FirstResponder</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ </array>
+ <key>IBVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/plugins/platforms/cocoa/qt_menu.nib/info.nib b/src/plugins/platforms/cocoa/qt_menu.nib/info.nib
new file mode 100644
index 0000000000..02e5cca562
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qt_menu.nib/info.nib
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBFramework Version</key>
+ <string>672</string>
+ <key>IBOldestOS</key>
+ <integer>5</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>57</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>9L31a</string>
+ <key>targetFramework</key>
+ <string>IBCocoaFramework</string>
+</dict>
+</plist>
diff --git a/src/plugins/platforms/cocoa/qt_menu.nib/keyedobjects.nib b/src/plugins/platforms/cocoa/qt_menu.nib/keyedobjects.nib
new file mode 100644
index 0000000000..3edb0ed2eb
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qt_menu.nib/keyedobjects.nib
Binary files differ