Cocoa: Load the standard app menu.
authorMorten Sorvig <morten.sorvig@nokia.com>
Mon, 22 Aug 2011 11:43:42 +0000 (13:43 +0200)
committerRichard Moe Gustavsen <richard.gustavsen@nokia.com>
Mon, 29 Aug 2011 11:14:26 +0000 (13:14 +0200)
It's now possible to quit and hide the app window
using the keyboard shortcuts.

The menu nib files are now stored in Qt resources,
written to QDir::temp at app startup and loaded
from there. This will simplify deployment compared
with Qt 4 where the nibs had to be copied into the
QtGui framework when deploying the app.

This change also moves the mac resources
(qt_menu.nib, cursors) from widgets to the cocoa
plugin.

Change-Id: If1d6fd353369ea4e89a9e35579906a2de7298fa2
Reviewed-on: http://codereview.qt.nokia.com/3314
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@nokia.com>
16 files changed:
src/plugins/platforms/cocoa/cocoa.pro
src/plugins/platforms/cocoa/images/copyarrowcursor.png [moved from src/widgets/mac/images/copyarrowcursor.png with 100% similarity]
src/plugins/platforms/cocoa/images/forbiddencursor.png [moved from src/widgets/mac/images/forbiddencursor.png with 100% similarity]
src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png [moved from src/widgets/mac/images/leopard-unified-toolbar-on.png with 100% similarity]
src/plugins/platforms/cocoa/images/pluscursor.png [moved from src/widgets/mac/images/pluscursor.png with 100% similarity]
src/plugins/platforms/cocoa/images/spincursor.png [moved from src/widgets/mac/images/spincursor.png with 100% similarity]
src/plugins/platforms/cocoa/images/waitcursor.png [moved from src/widgets/mac/images/waitcursor.png with 100% similarity]
src/plugins/platforms/cocoa/qcocoaintegration.mm
src/plugins/platforms/cocoa/qcocoamenuloader.h [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoamenuloader.mm [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoaresources.qrc [moved from src/widgets/mac/macresources.qrc with 70% similarity]
src/plugins/platforms/cocoa/qt_menu.nib/classes.nib [moved from src/widgets/mac/qt_menu.nib/classes.nib with 100% similarity]
src/plugins/platforms/cocoa/qt_menu.nib/info.nib [moved from src/widgets/mac/qt_menu.nib/info.nib with 100% similarity]
src/plugins/platforms/cocoa/qt_menu.nib/keyedobjects.nib [moved from src/widgets/mac/qt_menu.nib/keyedobjects.nib with 100% similarity]
src/widgets/kernel/kernel.pri
src/widgets/widgets/widgets.pri

index 53bd477..8f74ccc 100644 (file)
@@ -11,7 +11,8 @@ OBJECTIVE_SOURCES += main.mm \
     qnswindowdelegate.mm \
     qcocoaglcontext.mm \
     qcocoanativeinterface.mm \
-    qcocoaeventdispatcher.mm
+    qcocoaeventdispatcher.mm \
+    qcocoamenuloader.mm
 
 HEADERS += qcocoaintegration.h \
     qcocoabackingstore.h \
@@ -21,7 +22,10 @@ HEADERS += qcocoaintegration.h \
     qnswindowdelegate.h \
     qcocoaglcontext.h \
     qcocoanativeinterface.h \
-    qcocoaeventdispatcher.h
+    qcocoaeventdispatcher.h \
+    qcocoamenuloader.h
+
+RESOURCES += qcocoaresources.qrc
 
 #add libz for freetype.
 LIBS += -lz -framework Cocoa
index ace4527..4a798e4 100644 (file)
@@ -44,8 +44,9 @@
 #include "qcocoawindow.h"
 #include "qcocoabackingstore.h"
 #include "qcocoanativeinterface.h"
-
+#include "qcocoamenuloader.h"
 #include "qcocoaeventdispatcher.h"
+
 #include <QtPlatformSupport/private/qbasicunixfontdatabase_p.h>
 
 QT_BEGIN_NAMESPACE
@@ -86,6 +87,10 @@ QCocoaIntegration::QCocoaIntegration()
     // (See the activateIgnoringOtherApps docs.)
     [[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
 
+    QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) alloc] init];
+    qt_mac_loadMenuNib(qtMenuLoader);
+    [[NSApplication sharedApplication] setMenu:[qtMenuLoader menu]];
+
     NSArray *screens = [NSScreen screens];
     for (uint i = 0; i < [screens count]; i++) {
         QCocoaScreen *screen = new QCocoaScreen(i);
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h
new file mode 100644 (file)
index 0000000..9f3a31e
--- /dev/null
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOAMENULOADER_P_H
+#define QCOCOAMENULOADER_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp.  This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qcore_mac_p.h>
+#import <Cocoa/Cocoa.h>
+
+@interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder
+{
+    IBOutlet NSMenu *theMenu;
+    IBOutlet NSMenu *appMenu;
+    IBOutlet NSMenuItem *quitItem;
+    IBOutlet NSMenuItem *preferencesItem;
+    IBOutlet NSMenuItem *aboutItem;
+    IBOutlet NSMenuItem *aboutQtItem;
+    IBOutlet NSMenuItem *hideItem;
+    NSMenuItem *lastAppSpecificItem;
+    NSMenuItem *servicesItem;
+    NSMenuItem *hideAllOthersItem;
+    NSMenuItem *showAllItem;
+}
+- (void)ensureAppMenuInMenu:(NSMenu *)menu;
+- (void)removeActionsFromAppMenu;
+- (NSMenu *)applicationMenu;
+- (NSMenu *)menu;
+- (NSMenuItem *)quitMenuItem;
+- (NSMenuItem *)preferencesMenuItem;
+- (NSMenuItem *)aboutMenuItem;
+- (NSMenuItem *)aboutQtMenuItem;
+- (NSMenuItem *)hideMenuItem;
+- (NSMenuItem *)appSpecificMenuItem;
+- (IBAction)terminate:(id)sender;
+- (IBAction)orderFrontStandardAboutPanel:(id)sender;
+- (IBAction)hideOtherApplications:(id)sender;
+- (IBAction)unhideAllApplications:(id)sender;
+- (IBAction)hide:(id)sender;
+- (IBAction)qtDispatcherToQAction:(id)sender;
+- (void)qtUpdateMenubar;
+- (void)orderFrontCharacterPalette:(id)sender;
+@end
+
+void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader);
+
+#endif // QCOCOAMENULOADER_P_H
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
new file mode 100644 (file)
index 0000000..d47c467
--- /dev/null
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcocoamenuloader.h"
+
+#include <QtCore/private/qcore_mac_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qstring.h>
+
+QT_FORWARD_DECLARE_CLASS(QCFString)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+#ifndef QT_NO_TRANSLATION
+    QT_BEGIN_NAMESPACE
+    extern QString qt_mac_applicationmenu_string(int type);
+    QT_END_NAMESPACE
+#endif
+
+QT_USE_NAMESPACE
+
+/*
+    Loads and instantiates the main app menu from the menu nib file(s).
+
+    The main app menu contains the Quit, Hide  About, Preferences entries, and
+    The reason for having the nib file is that those can not be created
+    programmatically. To ease deployment the nib files are stored in Qt resources
+    and written to QDir::temp() before loading. (Earlier Qt versions used
+    to require having the nib file in the QtGui framework.)
+*/
+void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
+{
+    // Create qt_menu.nib dir in temp.
+    QDir temp = QDir::temp();
+    temp.mkdir("qt_menu.nib");
+    QString nibDir = temp.canonicalPath() + QLatin1String("/") + QLatin1String("qt_menu.nib/");
+    if (!QDir(nibDir).exists()) {
+        qWarning("qt_mac_loadMenuNib: could not create nib directory in temp");
+        return;
+    }
+
+    // Copy nib files from resources to temp.
+    QDir nibResource(":/trolltech/mac/qt_menu.nib/");
+    if (!nibResource.exists()) {
+        qWarning("qt_mac_loadMenuNib: could not load nib from resources");
+        return;
+    }
+    foreach (const QFileInfo &file, nibResource.entryInfoList()) {
+        QFile::copy(file.absoluteFilePath(), nibDir + QLatin1String("/") + file.fileName());
+    }
+
+    // Load and instantiate nib file from temp
+    NSURL *nibUrl = [NSURL fileURLWithPath : reinterpret_cast<const NSString *>(QCFString::toCFStringRef(nibDir))];
+    [nibUrl autorelease];
+    NSNib *nib = [[NSNib alloc] initWithContentsOfURL : nibUrl];
+    [nib autorelease];
+    if(!nib) {
+        qWarning("qt_mac_loadMenuNib: could not load nib from  temp");
+        return;
+    }
+    bool ok = [nib instantiateNibWithOwner : qtMenuLoader topLevelObjects : nil];
+    if (!ok) {
+        qWarning("qt_mac_loadMenuNib: could not instantiate nib");
+    }
+}
+
+
+
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader)
+
+- (void)awakeFromNib
+{
+    servicesItem = [[appMenu itemWithTitle:@"Services"] retain];
+    hideAllOthersItem = [[appMenu itemWithTitle:@"Hide Others"] retain];
+    showAllItem = [[appMenu itemWithTitle:@"Show All"] retain];
+
+    // Get the names in the nib to match the app name set by Qt.
+    const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName()));
+    [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+                                                                   withString:const_cast<NSString *>(appName)]];
+    [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+                                                                   withString:const_cast<NSString *>(appName)]];
+    [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication"
+                                                                   withString:const_cast<NSString *>(appName)]];
+    [appName release];
+    // Disable the items that don't do anything. If someone associates a QAction with them
+    // They should get synced back in.
+    [preferencesItem setEnabled:NO];
+    [preferencesItem setHidden:YES];
+    [aboutItem setEnabled:NO];
+    [aboutItem setHidden:YES];
+}
+
+- (void)ensureAppMenuInMenu:(NSMenu *)menu
+{
+    // The application menu is the menu in the menu bar that contains the
+    // 'Quit' item. When changing menu bar (e.g when switching between
+    // windows with different menu bars), we never recreate this menu, but
+    // instead pull it out the current menu bar and place into the new one:
+    NSMenu *mainMenu = [NSApp mainMenu];
+    if ([NSApp mainMenu] == menu)
+        return; // nothing to do (menu is the current menu bar)!
+
+#ifndef QT_NAMESPACE
+    Q_ASSERT(mainMenu);
+#endif
+    // Grab the app menu out of the current menu.
+    int numItems = [mainMenu numberOfItems];
+    NSMenuItem *oldAppMenuItem = 0;
+    for (int i = 0; i < numItems; ++i) {
+        NSMenuItem *item = [mainMenu itemAtIndex:i];
+        if ([item submenu] == appMenu) {
+            oldAppMenuItem = item;
+            [oldAppMenuItem retain];
+            [mainMenu removeItemAtIndex:i];
+            break;
+        }
+    }
+
+    if (oldAppMenuItem) {
+        [oldAppMenuItem setSubmenu:nil];
+        [oldAppMenuItem release];
+        NSMenuItem *appMenuItem = [[NSMenuItem alloc] initWithTitle:@"Apple"
+            action:nil keyEquivalent:@""];
+        [appMenuItem setSubmenu:appMenu];
+        [menu insertItem:appMenuItem atIndex:0];
+    }
+}
+
+- (void)removeActionsFromAppMenu
+{
+    for (NSMenuItem *item in [appMenu itemArray])
+        [item setTag:nil];
+}
+
+- (void)dealloc
+{
+    [servicesItem release];
+    [hideAllOthersItem release];
+    [showAllItem release];
+
+    [lastAppSpecificItem release];
+    [theMenu release];
+    [appMenu release];
+    [super dealloc];
+}
+
+- (NSMenu *)menu
+{
+    return [[theMenu retain] autorelease];
+}
+
+- (NSMenu *)applicationMenu
+{
+    return [[appMenu retain] autorelease];
+}
+
+- (NSMenuItem *)quitMenuItem
+{
+    return [[quitItem retain] autorelease];
+}
+
+- (NSMenuItem *)preferencesMenuItem
+{
+    return [[preferencesItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutMenuItem
+{
+    return [[aboutItem retain] autorelease];
+}
+
+- (NSMenuItem *)aboutQtMenuItem
+{
+    return [[aboutQtItem retain] autorelease];
+}
+
+- (NSMenuItem *)hideMenuItem
+{
+    return [[hideItem retain] autorelease];
+}
+
+- (NSMenuItem *)appSpecificMenuItem
+{
+    // Create an App-Specific menu item, insert it into the menu and return
+    // it as an autorelease item.
+    NSMenuItem *item = [[NSMenuItem alloc] init];
+
+    NSInteger location;
+    if (lastAppSpecificItem == nil) {
+        location = [appMenu indexOfItem:aboutQtItem];
+    } else {
+        location = [appMenu indexOfItem:lastAppSpecificItem];
+        [lastAppSpecificItem release];
+    }
+    lastAppSpecificItem = item;  // Keep track of this for later (i.e., don't release it)
+    [appMenu insertItem:item atIndex:location + 1];
+
+    return [[item retain] autorelease];
+}
+
+- (BOOL) acceptsFirstResponder
+{
+    return YES;
+}
+
+- (void)terminate:(id)sender
+{
+    [NSApp terminate:sender];
+}
+
+- (void)orderFrontStandardAboutPanel:(id)sender
+{
+    [NSApp orderFrontStandardAboutPanel:sender];
+}
+
+- (void)hideOtherApplications:(id)sender
+{
+    [NSApp hideOtherApplications:sender];
+}
+
+- (void)unhideAllApplications:(id)sender
+{
+    [NSApp unhideAllApplications:sender];
+}
+
+- (void)hide:(id)sender
+{
+    [NSApp hide:sender];
+}
+
+- (void)qtUpdateMenubar
+{
+    //    QMenuBarPrivate::macUpdateMenuBarImmediatly();
+}
+
+- (void)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()))];
+    [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()))];
+#endif
+*/
+}
+
+- (IBAction)qtDispatcherToQAction:(id)sender
+{
+    /*
+    QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+    NSMenuItem *item = static_cast<NSMenuItem *>(sender);
+    if (QAction *action = reinterpret_cast<QAction *>([item tag])) {
+        action->trigger();
+    } else if (item == quitItem) {
+        // We got here because someone was once the quitItem, but it has been
+        // abandoned (e.g., the menubar was deleted). In the meantime, just do
+        // normal QApplication::quit().
+        qApp->quit();
+    }
+*/
+}
+
+ - (void)orderFrontCharacterPalette:(id)sender
+ {
+     [NSApp orderFrontCharacterPalette:sender];
+ }
+@end
similarity index 70%
rename from src/widgets/mac/macresources.qrc
rename to src/plugins/platforms/cocoa/qcocoaresources.qrc
index 9696002..de50d39 100644 (file)
@@ -9,4 +9,9 @@
 <qresource prefix="/trolltech/mac/style">
 <file>images/leopard-unified-toolbar-on.png</file>
 </qresource>
+<qresource prefix="/trolltech/mac/">
+<file>qt_menu.nib/classes.nib</file>
+<file>qt_menu.nib/info.nib</file>
+<file>qt_menu.nib/keyedobjects.nib</file>
+</qresource>
 </RCC>
index 08ec6ac..cebd106 100644 (file)
@@ -211,7 +211,6 @@ qpa {
                 kernel/qcocoaview_mac.mm \
                 kernel/qcocoawindow_mac.mm \
                 kernel/qcocoawindowdelegate_mac.mm \
-                kernel/qcocoamenuloader_mac.mm \
                 kernel/qcocoaapplication_mac.mm \
                 kernel/qcocoaapplicationdelegate_mac.mm \
                 kernel/qt_cocoa_helpers_mac.mm \
index e4b5478..a272540 100644 (file)
@@ -158,8 +158,7 @@ SOURCES += \
 mac {
     OBJECTIVE_SOURCES += widgets/qmenu_mac.mm \
                          widgets/qcocoamenu_mac.mm \
-                         platforms/mac/qt_widget_helpers_mac.mm \
-                         platforms/mac/qcocoamenuloader_mac.mm
+                         platforms/mac/qt_widget_helpers_mac.mm
 }
 
 wince*: {