diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
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 |