summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorMorten Sorvig <morten.sorvig@nokia.com>2011-09-06 14:10:50 +0200
committerMorten Johan Sørvig <morten.sorvig@nokia.com>2011-09-07 11:00:36 +0200
commitcee63e2c17c8e83a4366741da0cbd5fc852e5835 (patch)
tree75ccc2afe762a51984d6651d6ff6eb875b73cad6 /src/plugins/platforms/cocoa
parente2be53891fe218c9a1a63c2d385d149d2b1f982f (diff)
Cocoa: QMenu refactor.
This change prepares for enabling native menus on OS X. Move code from src/widgets to cocoa: - qcocoaapplicaiton - qcocoaapplicaitondelegate - qcocoamenuloader - qcocoamenu - qmenu_mac - misc helpers to qcocoahelpers Create a QNSApplication and QCocoaApplicationDelegate at application startup. New Lighthouse API: - class QPlatformMenu - class QPlatformMenuBar - QPlatformIntegration::createPlatformMenu() - QPlatformIntegration::createPlatformMenuBar() Platforms that wants a native menu integration subclasses QPlatformMenu[|Bar] and implements the create function. The default implementation returns 0, which causes QMenu to use the standard Qt menus. This API is based on the current native menu abstraction that Mac, Wince and S60 uses in Qt 4. The main difference is that the platform classes are proper standalone classes and not #ifdeffed into QMenuPrivate. Change-Id: I3da41f80b0ae903a476937908b1f9b88014b7954 Reviewed-on: http://codereview.qt.nokia.com/4068 Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro11
-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/qcocoaeventdispatcher.h11
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm25
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h17
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm328
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h3
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm45
-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.mm24
-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.mm41
17 files changed, 2942 insertions, 93 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index afc094c4cb..d6801e0bed 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -13,6 +13,10 @@ OBJECTIVE_SOURCES += main.mm \
qcocoanativeinterface.mm \
qcocoaeventdispatcher.mm \
qcocoamenuloader.mm \
+ qcocoaapplicationdelegate.mm \
+ qcocoaapplication.mm \
+ qcocoamenu.mm \
+ qmenu_mac.mm \
qcocoahelpers.mm \
HEADERS += qcocoaintegration.h \
@@ -25,15 +29,18 @@ HEADERS += qcocoaintegration.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 -framework Cocoa
-QT += core-private gui-private platformsupport-private
+QT += core-private gui-private widgets-private platformsupport-private
CONFIG += qpa/basicunixfontdatabase
target.path += $$[QT_INSTALL_PLUGINS]/platforms
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/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 2085a437ac..7184db84fa 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -103,17 +103,6 @@ typedef struct _QCocoaModalSessionInfo {
void *nswindow;
} QCocoaModalSessionInfo;
-class Q_GUI_EXPORT QMacCocoaAutoReleasePool
-{
-private:
- void *pool;
-public:
- QMacCocoaAutoReleasePool();
- ~QMacCocoaAutoReleasePool();
-
- inline void *handle() const { return pool; }
-};
-
class QCocoaEventDispatcherPrivate;
class QCocoaEventDispatcher : public QEventDispatcherUNIX
{
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 22fe8b2e33..9525b47c65 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -74,6 +74,8 @@
****************************************************************************/
#include "qcocoaeventdispatcher.h"
+#include "qcocoaautoreleasepool.h"
+
#include "qguiapplication.h"
#include "qevent.h"
#include "qhash.h"
@@ -84,8 +86,6 @@
#include "private/qguiapplication_p.h"
#include <qdebug.h>
-//#include <private/qcocoaapplication_mac_p.h>
-//#include "private/qt_cocoa_helpers_mac_p.h"
#undef slots
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
@@ -104,17 +104,6 @@ static inline CFRunLoopRef mainRunLoop()
return CFRunLoopGetMain();
}
-QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool()
-{
- pool = (void*)[[NSAutoreleasePool alloc] init];
-}
-
-QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool()
-{
- [(NSAutoreleasePool*)pool release];
-}
-
-
/*****************************************************************************
Timers stuff
*****************************************************************************/
@@ -541,7 +530,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if (d->interrupt)
break;
- QMacCocoaAutoReleasePool pool;
+ QCocoaAutoReleasePool pool;
NSEvent* event = 0;
// First, send all previously excluded input events, if any:
@@ -776,7 +765,7 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession()
// continue;
if (!info.session) {
- QMacCocoaAutoReleasePool pool;
+ QCocoaAutoReleasePool pool;
NSWindow *window = reinterpret_cast<NSWindow *>(info.window->handle()->winId());
if (!window)
continue;
@@ -822,7 +811,7 @@ void QCocoaEventDispatcherPrivate::updateChildrenWorksWhenModal()
// Make the dialog children of the window
// active. And make the dialog children of
// the previous modal dialog unactive again:
- QMacCocoaAutoReleasePool pool;
+ QCocoaAutoReleasePool pool;
int size = cocoaModalSessionStack.size();
if (size > 0){
if (QWindow *prevModal = cocoaModalSessionStack[size-1].window)
@@ -843,7 +832,7 @@ void QCocoaEventDispatcherPrivate::cleanupModalSessions()
// 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.
- QMacCocoaAutoReleasePool pool;
+ QCocoaAutoReleasePool pool;
int stackSize = cocoaModalSessionStack.size();
for (int i=stackSize-1; i>=0; --i) {
@@ -1035,7 +1024,7 @@ void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents()
{
// In case the event dispatcher is waiting for more
// events somewhere, we post a dummy event to wake it up:
- QMacCocoaAutoReleasePool pool;
+ 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];
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 3e978cf858..62930ac076 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -53,9 +53,9 @@
// We mean it.
//
-#include <QtCore/private/qcore_mac_p.h>
-#include <Cocoa/Cocoa.h>
+#include <private/qt_mac_p.h>
+class QPixmap;
class QString;
// Conversion functions
@@ -71,10 +71,19 @@ inline QString qt_mac_NSStringToQString(const NSString *nsstr)
inline NSString *qt_mac_QStringToNSString(const QString &qstr)
{ return [reinterpret_cast<const NSString *>(QCFString::toCFStringRef(qstr)) autorelease]; }
-// Misc
+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);
-void qt_mac_transformProccessToForegroundApplication();
+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
index 335da680ac..03e83f1130 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -41,6 +41,8 @@
#include "qcocoahelpers.h"
+#include "qcocoaautoreleasepool.h"
+
#include <QtCore>
#include <QtGui>
@@ -66,6 +68,199 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
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
@@ -118,3 +313,136 @@ void qt_mac_transformProccessToForegroundApplication()
}
}
+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 0c1b350cef..a253a6bea3 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -81,8 +81,9 @@ public:
QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const;
QAbstractEventDispatcher *guiThreadEventDispatcher() const;
-
QPlatformFontDatabase *fontDatabase() const;
+ QPlatformMenu *createPlatformMenu(QMenu *menu = 0) const;
+ QPlatformMenuBar *createPlatformMenuBar(QMenuBar *menuBar = 0) const;
QPlatformNativeInterface *nativeInterface() const;
private:
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 1d06c182e6..e3e204226f 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -47,6 +47,11 @@
#include "qcocoamenuloader.h"
#include "qcocoaeventdispatcher.h"
#include "qcocoahelpers.h"
+#include "qcocoaapplication.h"
+#include "qcocoaapplicationdelegate.h"
+#include "qmenu_mac.h"
+
+#include <QtCore/qcoreapplication.h>
#include <QtPlatformSupport/private/qbasicunixfontdatabase_p.h>
@@ -78,9 +83,7 @@ QCocoaIntegration::QCocoaIntegration()
{
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
@@ -93,12 +96,24 @@ QCocoaIntegration::QCocoaIntegration()
// 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.)
- [[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
-
- // 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);
- [[NSApplication sharedApplication] setMenu:[qtMenuLoader menu]];
+ [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++) {
@@ -149,6 +164,18 @@ QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const
return mFontDb;
}
+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 QCocoaNativeInterface();
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.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
index d47c4675ca..52aefcb9bf 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
@@ -41,10 +41,14 @@
#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)
@@ -113,7 +117,7 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
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(qAppName()));
+ 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"
@@ -269,28 +273,29 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
- (void)qtUpdateMenubar
{
- // QMenuBarPrivate::macUpdateMenuBarImmediatly();
+ 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(qAppName()))];
+ [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(qAppName()))];
- [aboutItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(6).arg(qAppName()))];
+ [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);
+ //
+ //QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
NSMenuItem *item = static_cast<NSMenuItem *>(sender);
if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
action->trigger();
@@ -300,7 +305,6 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
// normal QApplication::quit().
qApp->quit();
}
-*/
}
- (void)orderFrontCharacterPalette:(id)sender
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.mm b/src/plugins/platforms/cocoa/qnsview.mm
index b2a3d73d48..f3c71d9eed 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -42,6 +42,8 @@
#include <Carbon/Carbon.h>
#include "qnsview.h"
+#include "qcocoahelpers.h"
+
#include <QtGui/QWindowSystemInterface>
#include <QtCore/QDebug>
@@ -276,44 +278,7 @@
- (int) convertKeyCode : (QChar)keyChar
{
- if (keyChar.isLower())
- keyChar = keyChar.toUpper();
- int keyCode = keyChar.unicode();
-
- int qtKeyCode = Qt::Key(keyCode); // default case, overrides below
- switch (keyCode) {
- case NSEnterCharacter: qtKeyCode = Qt::Key_Enter; break;
- case NSBackspaceCharacter: qtKeyCode = Qt::Key_Backspace; break;
- case NSTabCharacter: qtKeyCode = Qt::Key_Tab; break;
- case NSNewlineCharacter: qtKeyCode = Qt::Key_Return; break;
- case NSCarriageReturnCharacter: qtKeyCode = Qt::Key_Return; break;
- case NSBackTabCharacter: qtKeyCode = Qt::Key_Backtab; break;
- case 27 : qtKeyCode = Qt::Key_Escape; break;
- case NSDeleteCharacter : qtKeyCode = Qt::Key_Backspace; break; // Cocoa sends us delete when pressing backspace.
- case NSUpArrowFunctionKey: qtKeyCode = Qt::Key_Up; break;
- case NSDownArrowFunctionKey: qtKeyCode = Qt::Key_Down; break;
- case NSLeftArrowFunctionKey: qtKeyCode = Qt::Key_Left; break;
- case NSRightArrowFunctionKey: qtKeyCode = Qt::Key_Right; break;
- case NSInsertFunctionKey: qtKeyCode = Qt::Key_Insert; break;
- case NSDeleteFunctionKey: qtKeyCode = Qt::Key_Delete; break;
- case NSHomeFunctionKey: qtKeyCode = Qt::Key_Home; break;
- case NSEndFunctionKey: qtKeyCode = Qt::Key_End; break;
- case NSPageUpFunctionKey: qtKeyCode = Qt::Key_PageUp; break;
- case NSPageDownFunctionKey: qtKeyCode = Qt::Key_PageDown; break;
- case NSPrintScreenFunctionKey: qtKeyCode = Qt::Key_Print; break;
- case NSScrollLockFunctionKey: qtKeyCode = Qt::Key_ScrollLock; break;
- case NSPauseFunctionKey: qtKeyCode = Qt::Key_Pause; break;
- case NSSysReqFunctionKey: qtKeyCode = Qt::Key_SysReq; break;
- case NSMenuFunctionKey: qtKeyCode = Qt::Key_Menu; break;
- case NSHelpFunctionKey: qtKeyCode = Qt::Key_Help; break;
- default : break;
- }
-
- // handle all function keys (F1-F35)
- if (keyCode >= NSF1FunctionKey && keyCode <= NSF35FunctionKey)
- qtKeyCode = Qt::Key_F1 + (keyCode - NSF1FunctionKey);
-
- return qtKeyCode;
+ return qt_mac_cocoaKey2QtKey(keyChar);
}
- (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags