summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro10
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.mm11
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm14
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.h97
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm365
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.h82
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.mm254
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.h117
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.mm358
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.mm80
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm11
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h4
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm32
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm14
-rw-r--r--src/plugins/platforms/cocoa/qt_menu.nib/classes.nib2
-rw-r--r--tests/manual/cocoa/menus/main.cpp201
-rw-r--r--tests/manual/cocoa/menus/menus.pro5
22 files changed, 1423 insertions, 256 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index 313b666793..467ce96e63 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -14,9 +14,12 @@ OBJECTIVE_SOURCES += main.mm \
qcocoaglcontext.mm \
qcocoanativeinterface.mm \
qcocoaeventdispatcher.mm \
- qcocoamenuloader.mm \
qcocoaapplicationdelegate.mm \
qcocoaapplication.mm \
+ qcocoamenu.mm \
+ qcocoamenuitem.mm \
+ qcocoamenubar.mm \
+ qcocoamenuloader.mm \
qcocoahelpers.mm \
qmultitouch_mac.mm \
qcocoaaccessibilityelement.mm \
@@ -46,9 +49,12 @@ HEADERS += qcocoaintegration.h \
qcocoaglcontext.h \
qcocoanativeinterface.h \
qcocoaeventdispatcher.h \
- qcocoamenuloader.h \
qcocoaapplicationdelegate.h \
qcocoaapplication.h \
+ qcocoamenu.h \
+ qcocoamenuitem.h \
+ qcocoamenubar.h \
+ qcocoamenuloader.h \
qcocoahelpers.h \
qmultitouch_mac_p.h \
qcocoaaccessibilityelement.h \
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm
index 2cecb296f4..90f09aa620 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplication.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm
@@ -189,17 +189,6 @@ QT_USE_NAMESPACE
[super sendEvent:event];
}
-- (void)qtDispatcherToQAction:(id)sender
-{
- // Forward actions sent 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
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index ec086fe62c..3855563bab 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -355,7 +355,7 @@ static void cleanupCocoaApplicationDelegate()
- (void)qtDispatcherToQAction:(id)sender
{
Q_UNUSED(sender);
- [qtMenuLoader qtDispatcherToQAction:sender];
+ [qtMenuLoader qtDispatcherToQPAMenuItem:sender];
}
@end
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 5113aca3f7..f8032603e5 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -115,6 +115,11 @@ inline NSPoint qt_mac_flipPoint(const QPointF &p)
NSRect qt_mac_flipRect(const QRect &rect, QWindow *window);
+// strip out '&' characters, and convert "&&" to a single '&', in menu
+// text - since menu text is sometimes decorated with these for Windows
+// accelerators.
+QString qt_mac_removeAmpersandEscapes(QString s);
+
QT_END_NAMESPACE
#endif //QCOCOAHELPERS_H
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 8f8d7b84a6..c0b6f3abb6 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -581,4 +581,18 @@ CGFloat qt_mac_get_scalefactor()
return [[NSScreen mainScreen] userSpaceScaleFactor];
}
+QString qt_mac_removeAmpersandEscapes(QString s)
+{
+ int i = 0;
+ while (i < s.size()) {
+ ++i;
+ if (s.at(i-1) != QLatin1Char('&'))
+ continue;
+ if (i < s.size() && s.at(i) == QLatin1Char('&'))
+ ++i;
+ s.remove(i-1,1);
+ }
+ return s.trimmed();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h
index bf8bba1ddf..479d4b53c2 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.h
+++ b/src/plugins/platforms/cocoa/qcocoamenu.h
@@ -1,9 +1,10 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
** Contact: http://www.qt-project.org/
**
-** This file is part of the QtGui module of the Qt Toolkit.
+** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
@@ -39,42 +40,66 @@
**
****************************************************************************/
-//
-// 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
+#ifndef QCOCOAMENU_H
+#define QCOCOAMENU_H
-#endif
+#include <QtCore/QList>
+#include <qpa/qplatformmenu.h>
+#include "qcocoamenuitem.h"
+
+@class NSMenuItem;
+@class NSMenu;
+@class NSObject;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
-@interface QT_MANGLE_NAMESPACE(QNativeCocoaMenu) : NSMenu <NSMenuDelegate>
+class QCocoaMenu : public QPlatformMenu
{
- 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
+public:
+ QCocoaMenu();
+
+ inline virtual void setTag(quintptr tag)
+ { m_tag = tag; }
+ inline virtual quintptr tag() const
+ { return m_tag; }
+
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before);
+ void removeMenuItem(QPlatformMenuItem *menuItem);
+ void syncMenuItem(QPlatformMenuItem *menuItem);
+ void setEnabled(bool enabled);
+ void syncSeparatorsCollapsible(bool enable);
+
+ void syncModalState(bool modal);
+
+ virtual void setText(const QString &text);
+ void setParentItem(QCocoaMenuItem* item);
+
+ inline NSMenu *nsMenu() const
+ { return m_nativeMenu; }
+ inline NSMenuItem *nsMenuItem() const
+ { return m_nativeItem; }
+
+ virtual QPlatformMenuItem *menuItemAt(int position) const;
+ virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const;
+
+ QList<QCocoaMenuItem *> merged() const;
+private:
+ QCocoaMenuItem *itemOrNull(int index) const;
+ void insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem);
+
+ QList<QCocoaMenuItem *> m_menuItems;
+ NSMenu *m_nativeMenu;
+ NSMenuItem *m_nativeItem;
+ NSObject *m_delegate;
+ bool m_enabled;
+ quintptr m_tag;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index 00bbf49cca..9e466deb9a 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
** Contact: http://www.qt-project.org/
**
-** This file is part of the QtGui module of the Qt Toolkit.
+** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
@@ -39,229 +39,244 @@
**
****************************************************************************/
-#include "qapplication.h"
-#include "qvarlengtharray.h"
-#import "qcocoamenu.h"
-#import "qcocoamenuloader.h"
-#import "qcocoaapplication.h"
+#include "qcocoamenu.h"
+
#include "qcocoahelpers.h"
-#include <private/qapplication_p.h>
-#include <private/qaction_p.h>
+#include "qcocoaautoreleasepool.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)
+@interface QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) : NSObject <NSMenuDelegate> {
+ QCocoaMenu *m_menu;
+}
-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
+- (id) initWithMenu:(QCocoaMenu*) m;
-QT_USE_NAMESPACE
+@end
-@implementation QT_MANGLE_NAMESPACE(QNativeCocoaMenu)
+@implementation QT_MANGLE_NAMESPACE(QCocoaMenuDelegate)
-- (id)initWithQMenu:(QMenu*)menu
+- (id) initWithMenu:(QCocoaMenu*) m
{
- self = [super init];
- if (self) {
- qmenu = menu;
- previousAction = 0;
- [self setAutoenablesItems:NO];
- [self setDelegate:self];
- }
+ if ((self = [super init]))
+ m_menu = m;
+
return self;
}
-- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item
+- (void) menuWillOpen:(NSMenu*)m
{
- Q_UNUSED(menu);
+ Q_UNUSED(m);
+ emit m_menu->aboutToShow();
+}
- if (!item) {
- if (previousAction) {
- qt_mac_clear_status_text(previousAction);
- previousAction = 0;
- }
- return;
- }
+- (void) menuDidClose:(NSMenu*)m
+{
+ Q_UNUSED(m);
+ // wrong, but it's the best we can do
+ emit m_menu->aboutToHide();
+}
- 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) itemFired:(NSMenuItem*) item
+{
+ QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
+ cocoaItem->activated();
}
-- (void)menuWillOpen:(NSMenu*)menu
+- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
- 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());
+ if (![menuItem tag])
+ return YES;
+
+
+ QCocoaMenuItem* cocoaItem = reinterpret_cast<QCocoaMenuItem *>([menuItem tag]);
+ return cocoaItem->isEnabled();
}
-- (void)menuDidClose:(NSMenu*)menu
+@end
+
+QCocoaMenu::QCocoaMenu() :
+ m_enabled(true),
+ m_tag(0)
{
- qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QNativeCocoaMenu) *)menu)->qmenu, false);
- if (previousAction) {
- qt_mac_clear_status_text(previousAction);
- previousAction = 0;
+ m_delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this];
+ m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"];
+ [m_nativeMenu setAutoenablesItems:YES];
+ m_nativeMenu.delegate = (QCocoaMenuDelegate *) m_delegate;
+ [m_nativeItem setSubmenu:m_nativeMenu];
+}
+
+void QCocoaMenu::setText(const QString &text)
+{
+ QString stripped = qt_mac_removeAmpersandEscapes(text);
+ [m_nativeMenu setTitle:QCFString::toNSString(stripped)];
+ [m_nativeItem setTitle:QCFString::toNSString(stripped)];
+}
+
+void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
+{
+ QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
+ QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before);
+
+ cocoaItem->sync();
+ if (beforeItem) {
+ int index = m_menuItems.indexOf(beforeItem);
+ // if a before item is supplied, it should be in the menu
+ Q_ASSERT(index >= 0);
+ m_menuItems.insert(index, cocoaItem);
+ } else {
+ m_menuItems.append(cocoaItem);
}
+
+ insertNative(cocoaItem, beforeItem);
}
-- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
- whichItem:(NSMenuItem**)outItem
+void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem)
{
- 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;
- }
+ [item->nsItem() setTarget:m_delegate];
+ [item->nsItem() setAction:@selector(itemFired:)];
+
+ if (item->isMerged())
+ return;
+
+ // if the item we're inserting before is merged, skip along until
+ // we find a non-merged real item to insert ahead of.
+ while (beforeItem && beforeItem->isMerged()) {
+ beforeItem = itemOrNull(m_menuItems.indexOf(beforeItem) + 1);
+ }
+
+ if (beforeItem) {
+ Q_ASSERT(!beforeItem->isMerged());
+ NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeItem->nsItem()];
+ [m_nativeMenu insertItem: item->nsItem() atIndex: nativeIndex];
+ } else {
+ [m_nativeMenu addItem: item->nsItem()];
}
- if (outItem)
- *outItem = 0;
- return NO;
}
-NSString *qt_mac_removePrivateUnicode(NSString* string)
+void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem)
{
- 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];
+ QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
+ Q_ASSERT(m_menuItems.contains(cocoaItem));
+ m_menuItems.removeOne(cocoaItem);
+ if (!cocoaItem->isMerged()) {
+ [m_nativeMenu removeItem: cocoaItem->nsItem()];
}
- return string;
}
-- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
+QCocoaMenuItem *QCocoaMenu::itemOrNull(int index) const
+{
+ if ((index < 0) || (index >= m_menuItems.size()))
+ return 0;
+
+ return m_menuItems.at(index);
+}
+
+void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem)
{
- // 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]);
+ QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
+ Q_ASSERT(m_menuItems.contains(cocoaItem));
+
+ bool wasMerged = cocoaItem->isMerged();
+ NSMenuItem *oldItem = [m_nativeMenu itemWithTag:(NSInteger) cocoaItem];
+
+ if (cocoaItem->sync() != oldItem) {
+ // native item was changed for some reason
+ if (!wasMerged) {
+ [m_nativeMenu removeItem:oldItem];
}
- 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;
+
+ QCocoaMenuItem* beforeItem = itemOrNull(m_menuItems.indexOf(cocoaItem) + 1);
+ insertNative(cocoaItem, beforeItem);
+ }
+}
+
+void QCocoaMenu::syncSeparatorsCollapsible(bool enable)
+{
+ QCocoaAutoReleasePool pool;
+ if (enable) {
+ bool previousIsSeparator = true; // setting to true kills all the separators placed at the top.
+ NSMenuItem *previousItem = nil;
+
+ NSArray *itemArray = [m_nativeMenu 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 {
+ foreach (QCocoaMenuItem *item, m_menuItems) {
+ if (!item->isSeparator())
+ continue;
+
+ // sync the visiblity directly
+ item->sync();
+ }
}
- return NO;
}
-- (NSInteger)indexOfItemWithTarget:(id)anObject andAction:(SEL)actionSelector
+void QCocoaMenu::setParentItem(QCocoaMenuItem *item)
{
- 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;
+ Q_UNUSED(item);
}
-@end
+void QCocoaMenu::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
-QT_BEGIN_NAMESPACE
-extern int qt_mac_menus_open_count; // qmenu_mac.mm
+QPlatformMenuItem *QCocoaMenu::menuItemAt(int position) const
+{
+ return m_menuItems.at(position);
+}
-void qt_mac_emit_menuSignals(QMenu *menu, bool show)
+QPlatformMenuItem *QCocoaMenu::menuItemForTag(quintptr tag) const
{
- if (!menu)
- return;
- int delta;
- if (show) {
- emit menu->aboutToShow();
- delta = 1;
- } else {
- emit menu->aboutToHide();
- delta = -1;
+ foreach (QCocoaMenuItem *item, m_menuItems) {
+ if (item->tag() == tag)
+ return item;
}
- qt_mac_menus_open_count += delta;
+
+ return 0;
}
-void qt_mac_clear_status_text(QAction *action)
+QList<QCocoaMenuItem *> QCocoaMenu::merged() const
{
- action->d_func()->showStatusText(0, QString());
+ QList<QCocoaMenuItem *> result;
+ foreach (QCocoaMenuItem *item, m_menuItems) {
+ if (item->menu()) { // recurse into submenus
+ result.append(item->menu()->merged());
+ continue;
+ }
+
+ if (item->isMerged())
+ result.append(item);
+ }
+
+ return result;
}
-void qt_mac_menu_emit_hovered(QMenu *menu, QAction *action)
+void QCocoaMenu::syncModalState(bool modal)
{
- emit menu->hovered(action);
-}
+ if (!m_enabled)
+ modal = true;
+ [m_nativeItem setEnabled:!modal];
-QT_END_NAMESPACE
+ foreach (QCocoaMenuItem *item, m_menuItems) {
+ if (item->menu()) { // recurse into submenus
+ item->menu()->syncModalState(modal);
+ continue;
+ }
+ item->syncModalState(modal);
+ }
+}
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h
new file mode 100644
index 0000000000..06e8eb6c9d
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the plugins 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$
+**
+****************************************************************************/
+
+#ifndef QCOCOAMENUBAR_H
+#define QCOCOAMENUBAR_H
+
+#include <QtCore/QList>
+#include <qpa/qplatformmenu.h>
+#include "qcocoamenu.h"
+
+@class NSMenu;
+class QCocoaWindow;
+
+class QCocoaMenuBar : public QPlatformMenuBar
+{
+public:
+ QCocoaMenuBar();
+ virtual ~QCocoaMenuBar();
+
+ virtual void insertMenu(QPlatformMenu *menu, QPlatformMenu* before);
+ virtual void removeMenu(QPlatformMenu *menu);
+ virtual void syncMenu(QPlatformMenuItem *menuItem);
+ virtual void handleReparent(QWindow *newParentWindow);
+ virtual QPlatformMenu *menuForTag(quintptr tag) const;
+
+ inline NSMenu *nsMenu() const
+ { return m_nativeMenu; }
+
+ static void updateMenuBarImmediately();
+
+ QList<QCocoaMenuItem*> merged() const;
+private:
+ static QCocoaWindow *findWindowForMenubar();
+ static QCocoaMenuBar *findGlobalMenubar();
+
+ bool shouldDisable(QCocoaWindow *active) const;
+
+ QList<QCocoaMenu*> m_menus;
+ NSMenu *m_nativeMenu;
+ QCocoaWindow *m_window;
+};
+
+#endif
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm
new file mode 100644
index 0000000000..fc403ba504
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
+** Contact: http://www.qt-project.org/
+**
+** 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 <Cocoa/Cocoa.h>
+
+#include "qcocoamenubar.h"
+#include "qcocoawindow.h"
+#include "qcocoamenuloader.h"
+#include "qcocoaapplication.h" // for custom application category
+#include "qcocoaautoreleasepool.h"
+
+#include <QtGui/QGuiApplication>
+#include <QtCore/QDebug>
+
+static QList<QCocoaMenuBar*> static_menubars;
+
+static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
+{
+ return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
+}
+
+
+QCocoaMenuBar::QCocoaMenuBar() :
+ m_window(0)
+{
+ static_menubars.append(this);
+
+ m_nativeMenu = [[NSMenu alloc] init];
+#ifdef QT_COCOA_ENABLE_MENU_DEBUG
+ qDebug() << "Construct QCocoaMenuBar" << this << m_nativeMenu;
+#endif
+}
+
+QCocoaMenuBar::~QCocoaMenuBar()
+{
+#ifdef QT_COCOA_ENABLE_MENU_DEBUG
+ qDebug() << "~QCocoaMenuBar" << this;
+#endif
+ [m_nativeMenu release];
+ static_menubars.removeOne(this);
+
+ if (m_window)
+ m_window->setMenubar(0);
+}
+
+void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before)
+{
+ QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
+ QCocoaMenu *beforeMenu = static_cast<QCocoaMenu *>(before);
+#ifdef QT_COCOA_ENABLE_MENU_DEBUG
+ qDebug() << "QCocoaMenuBar" << this << "insertMenu" << menu << "before" << before;
+#endif
+
+ Q_ASSERT(!m_menus.contains(menu));
+ if (beforeMenu) {
+ Q_ASSERT(m_menus.contains(beforeMenu));
+ m_menus.insert(m_menus.indexOf(beforeMenu), menu);
+ NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()];
+ [m_nativeMenu insertItem: menu->nsMenuItem() atIndex: nativeIndex];
+ } else {
+ m_menus.append(menu);
+ [m_nativeMenu addItem: menu->nsMenuItem()];
+ }
+
+ [m_nativeMenu setSubmenu: menu->nsMenu() forItem: menu->nsMenuItem()];
+}
+
+void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu)
+{
+ QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
+ Q_ASSERT(m_menus.contains(menu));
+ m_menus.removeOne(menu);
+
+ NSUInteger realIndex = [m_nativeMenu indexOfItem:menu->nsMenuItem()];
+ [m_nativeMenu removeItemAtIndex: realIndex];
+}
+
+void QCocoaMenuBar::syncMenu(QPlatformMenuItem *menuItem)
+{
+ Q_UNUSED(menuItem);
+}
+
+void QCocoaMenuBar::handleReparent(QWindow *newParentWindow)
+{
+#ifdef QT_COCOA_ENABLE_MENU_DEBUG
+ qDebug() << "QCocoaMenuBar" << this << "handleReparent" << newParentWindow;
+#endif
+
+ if (m_window)
+ m_window->setMenubar(NULL);
+
+ if (newParentWindow == NULL) {
+ m_window = NULL;
+ } else {
+ m_window = static_cast<QCocoaWindow*>(newParentWindow->handle());
+ m_window->setMenubar(this);
+ }
+
+ updateMenuBarImmediately();
+}
+
+QCocoaWindow *QCocoaMenuBar::findWindowForMenubar()
+{
+ if (qApp->focusWindow())
+ return static_cast<QCocoaWindow*>(qApp->focusWindow()->handle());
+
+ return NULL;
+}
+
+QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar()
+{
+ foreach (QCocoaMenuBar *mb, static_menubars) {
+ if (mb->m_window == NULL)
+ return mb;
+ }
+
+ return NULL;
+}
+
+void QCocoaMenuBar::updateMenuBarImmediately()
+{
+ QCocoaAutoReleasePool pool;
+ QCocoaMenuBar *mb = findGlobalMenubar();
+ QCocoaWindow *cw = findWindowForMenubar();
+ if (cw && cw->menubar())
+ mb = cw->menubar();
+
+ if (!mb)
+ return;
+
+#ifdef QT_COCOA_ENABLE_MENU_DEBUG
+ qDebug() << "QCocoaMenuBar" << "updateMenuBarImmediately" << cw;
+#endif
+ bool disableForModal = mb->shouldDisable(cw);
+ // force a sync?
+ foreach (QCocoaMenu *m, mb->m_menus) {
+ mb->syncMenu(m);
+ m->syncModalState(disableForModal);
+ }
+
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ [loader ensureAppMenuInMenu:mb->nsMenu()];
+
+ NSMutableSet *mergedItems = [[NSMutableSet setWithCapacity:0] retain];
+ foreach (QCocoaMenuItem *m, mb->merged()) {
+ [mergedItems addObject:m->nsItem()];
+ m->syncMerged();
+ }
+
+ // hide+disable all mergeable items we're not currently using
+ for (NSMenuItem *mergeable in [loader mergeable]) {
+ if (![mergedItems containsObject:mergeable]) {
+ [mergeable setHidden:YES];
+ [mergeable setEnabled:NO];
+ }
+ }
+
+ [mergedItems release];
+ [NSApp setMainMenu:mb->nsMenu()];
+ [loader qtTranslateApplicationMenu];
+}
+
+QList<QCocoaMenuItem*> QCocoaMenuBar::merged() const
+{
+ QList<QCocoaMenuItem*> r;
+ foreach (QCocoaMenu* menu, m_menus)
+ r.append(menu->merged());
+
+ return r;
+}
+
+bool QCocoaMenuBar::shouldDisable(QCocoaWindow *active) const
+{
+ if (active && (active->window()->windowModality() == Qt::NonModal))
+ return false;
+
+ if (m_window == active) {
+ // modal window owns us, we should be enabled!
+ return false;
+ }
+
+ QWindowList topWindows(qApp->topLevelWindows());
+ // 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.
+ foreach (QWindow *w, topWindows) {
+ if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) {
+ // check for other visible windows
+ foreach (QWindow *other, topWindows) {
+ if ((w != other) && (other->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;
+ }
+ }
+
+ return true;
+}
+
+QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const
+{
+ foreach (QCocoaMenu *menu, m_menus) {
+ if (menu->tag() == tag)
+ return menu;
+ }
+
+ return 0;
+}
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h
new file mode 100644
index 0000000000..4a063d5965
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
+** Contact: http://www.qt-project.org/
+**
+** 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 QCOCOAMENUITEM_H
+#define QCOCOAMENUITEM_H
+
+#include <qpa/qplatformmenu.h>
+#include <QtGui/QImage>
+
+//#define QT_COCOA_ENABLE_MENU_DEBUG
+
+@class NSMenuItem;
+@class NSMenu;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QCocoaMenu;
+
+class QCocoaMenuItem : public QPlatformMenuItem
+{
+public:
+ QCocoaMenuItem();
+ virtual ~QCocoaMenuItem();
+
+ inline virtual void setTag(quintptr tag)
+ { m_tag = tag; }
+ inline virtual quintptr tag() const
+ { return m_tag; }
+
+ void setText(const QString &text);
+ void setIcon(const QImage &icon);
+ void setMenu(QPlatformMenu *menu);
+ void setVisible(bool isVisible);
+ void setIsSeparator(bool isSeparator);
+ void setFont(const QFont &font);
+ void setRole(MenuRole role);
+ void setShortcut(const QKeySequence& shortcut);
+ void setChecked(bool isChecked);
+ void setEnabled(bool isEnabled);
+
+ inline QString text() const { return m_text; }
+ inline NSMenuItem * nsItem() { return m_native; }
+ NSMenuItem *sync();
+
+ void syncMerged();
+ void syncModalState(bool modal);
+
+ inline bool isMerged() const { return m_merged; }
+ inline bool isEnabled() const { return m_enabled; }
+ inline bool isSeparator() const { return m_isSeparator; }
+
+ QCocoaMenu *menu() const { return m_menu; }
+private:
+ QString mergeText();
+ QKeySequence mergeAccel();
+
+ NSMenuItem *m_native;
+ QString m_text;
+ QImage m_icon;
+ QCocoaMenu *m_menu;
+ bool m_isVisible;
+ bool m_enabled;
+ bool m_isSeparator;
+ QFont m_font;
+ MenuRole m_role;
+ QKeySequence m_shortcut;
+ bool m_checked;
+ bool m_merged;
+ quintptr m_tag;
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
new file mode 100644
index 0000000000..150e94ab07
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
@@ -0,0 +1,358 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
+** Contact: http://www.qt-project.org/
+**
+** 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 "qcocoamenuitem.h"
+
+#include "qcocoamenu.h"
+#include "qcocoahelpers.h"
+#include "qcocoaautoreleasepool.h"
+#include "qt_mac_p.h"
+#include "qcocoaapplication.h" // for custom application category
+#include "qcocoamenuloader.h"
+
+#include <QtCore/QDebug>
+
+static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
+{
+ return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
+}
+
+
+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;
+}
+
+// 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]);
+}
+
+QCocoaMenuItem::QCocoaMenuItem() :
+ m_native(NULL),
+ m_menu(NULL),
+ m_isVisible(true),
+ m_enabled(true),
+ m_isSeparator(false),
+ m_role(NoRole),
+ m_checked(false),
+ m_merged(false),
+ m_tag(0)
+{
+}
+
+QCocoaMenuItem::~QCocoaMenuItem()
+{
+ if (m_merged) {
+ [m_native setHidden:YES];
+ }
+
+ [m_native release];
+}
+
+void QCocoaMenuItem::setText(const QString &text)
+{
+ m_text = qt_mac_removeAmpersandEscapes(text);
+}
+
+void QCocoaMenuItem::setIcon(const QImage &icon)
+{
+ m_icon = icon;
+}
+
+void QCocoaMenuItem::setMenu(QPlatformMenu *menu)
+{
+ if (menu == m_menu)
+ return;
+
+ QCocoaAutoReleasePool pool;
+ m_menu = static_cast<QCocoaMenu *>(menu);
+ if (m_menu) {
+ m_menu->setParentItem(this);
+ } else {
+ // we previously had a menu, but no longer
+ // clear out our item so the nexy sync() call builds a new one
+ [m_native release];
+ m_native = nil;
+ }
+}
+
+void QCocoaMenuItem::setVisible(bool isVisible)
+{
+ m_isVisible = isVisible;
+}
+
+void QCocoaMenuItem::setIsSeparator(bool isSeparator)
+{
+ m_isSeparator = isSeparator;
+}
+
+void QCocoaMenuItem::setFont(const QFont &font)
+{
+ m_font = font;
+}
+
+void QCocoaMenuItem::setRole(MenuRole role)
+{
+ m_role = role;
+}
+
+void QCocoaMenuItem::setShortcut(const QKeySequence& shortcut)
+{
+ m_shortcut = shortcut;
+}
+
+void QCocoaMenuItem::setChecked(bool isChecked)
+{
+ m_checked = isChecked;
+}
+
+void QCocoaMenuItem::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+NSMenuItem *QCocoaMenuItem::sync()
+{
+ if (m_isSeparator != [m_native isSeparatorItem]) {
+ [m_native release];
+ if (m_isSeparator) {
+ m_native = [[NSMenuItem separatorItem] retain];
+ [m_native setTag:reinterpret_cast<NSInteger>(this)];
+ } else
+ m_native = nil;
+ }
+
+ if (m_menu) {
+ if (m_native != m_menu->nsMenuItem()) {
+ [m_native release];
+ m_native = [m_menu->nsMenuItem() retain];
+ [m_native setTag:reinterpret_cast<NSInteger>(this)];
+ }
+ }
+
+ if ((m_role != NoRole) || m_merged) {
+ NSMenuItem *mergeItem = nil;
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ switch (m_role) {
+ case ApplicationSpecificRole:
+ mergeItem = [loader appSpecificMenuItem];
+ break;
+ case AboutRole:
+ mergeItem = [loader aboutMenuItem];
+ break;
+ case AboutQtRole:
+ mergeItem = [loader aboutQtMenuItem];
+ break;
+ case QuitRole:
+ mergeItem = [loader quitMenuItem];
+ break;
+ case PreferencesRole:
+ mergeItem = [loader preferencesMenuItem];
+ break;
+ case TextHeuristicRole: {
+ QString aboutString = tr("About").toLower();
+
+ if (m_text.startsWith(aboutString) || m_text.endsWith(aboutString)) {
+ if (m_text.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1)
+ mergeItem = [loader aboutMenuItem];
+ else
+ mergeItem = [loader aboutQtMenuItem];
+
+ m_merged = true;
+ } else if (m_text.startsWith(tr("Config").toLower())
+ || m_text.startsWith(tr("Preference").toLower())
+ || m_text.startsWith(tr("Options").toLower())
+ || m_text.startsWith(tr("Setting").toLower())
+ || m_text.startsWith(tr("Setup").toLower())) {
+ mergeItem = [loader preferencesMenuItem];
+ } else if (m_text.startsWith(tr("Quit").toLower())
+ || m_text.startsWith(tr("Exit").toLower())) {
+ mergeItem = [loader quitMenuItem];
+ }
+ break;
+ }
+
+ default:
+ qWarning() << Q_FUNC_INFO << "unsupported role" << (int) m_role;
+ }
+
+ if (mergeItem) {
+ m_merged = true;
+ [m_native release];
+ m_native = mergeItem;
+ [m_native retain]; // balance out release!
+ [m_native setTag:reinterpret_cast<NSInteger>(this)];
+ } else if (m_merged) {
+ // was previously merged, but no longer
+ [m_native release];
+ m_native = nil; // create item below
+ m_merged = false;
+ }
+ }
+
+ if (!m_native) {
+ m_native = [[NSMenuItem alloc] initWithTitle:QCFString::toNSString(m_text)
+ action:nil
+ keyEquivalent:@""];
+ [m_native retain];
+ [m_native setTag:reinterpret_cast<NSInteger>(this)];
+ }
+
+// [m_native setHidden:YES];
+// [m_native setHidden:NO];
+ [m_native setHidden: !m_isVisible];
+
+ QString text = m_text;
+ QKeySequence accel = m_shortcut;
+
+ {
+ int st = text.lastIndexOf(QLatin1Char('\t'));
+ if (st != -1) {
+ accel = QKeySequence(text.right(text.length()-(st+1)));
+ text.remove(st, text.length()-st);
+ }
+ }
+
+ text = mergeText();
+ accel = mergeAccel();
+
+ // Show multiple key sequences as part of the menu text.
+ if (accel.count() > 1)
+ text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");
+
+ QString finalString = qt_mac_removeMnemonics(text);
+ // Cocoa Font and title
+ if (m_font.resolve()) {
+ NSFont *customMenuFont = [NSFont fontWithName:QCFString::toNSString(m_font.family())
+ size:m_font.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:QCFString::toNSString(finalString)
+ attributes:attributes] autorelease];
+ [m_native setAttributedTitle: str];
+ } else {
+ [m_native setTitle: QCFString::toNSString(finalString)];
+ }
+
+ if (accel.count() == 1) {
+ [m_native setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
+ [m_native setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
+ } else {
+ [m_native setKeyEquivalent:@""];
+ [m_native setKeyEquivalentModifierMask:NSCommandKeyMask];
+ }
+
+ if (!m_icon.isNull()) {
+ NSImage *img = qt_mac_cgimage_to_nsimage(qt_mac_image_to_cgimage(m_icon));
+ [m_native setImage: img];
+ }
+
+ [m_native setState:m_checked ? NSOnState : NSOffState];
+ return m_native;
+}
+
+QString QCocoaMenuItem::mergeText()
+{
+ extern QString qt_mac_applicationmenu_string(int type);
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ if (m_native == [loader aboutMenuItem]) {
+ return qt_mac_applicationmenu_string(6).arg(qt_mac_applicationName());
+ } else if (m_native== [loader aboutQtMenuItem]) {
+ if (m_text == QString("About Qt"))
+ return tr("About Qt");
+ else
+ return m_text;
+ } else if (m_native == [loader preferencesMenuItem]) {
+ return qt_mac_applicationmenu_string(4);
+ } else if (m_native == [loader quitMenuItem]) {
+ return qt_mac_applicationmenu_string(5).arg(qt_mac_applicationName());
+ }
+ return m_text;
+}
+
+QKeySequence QCocoaMenuItem::mergeAccel()
+{
+ QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
+ if (m_native == [loader preferencesMenuItem])
+ return QKeySequence(QKeySequence::Preferences);
+ else if (m_native == [loader quitMenuItem])
+ return QKeySequence(QKeySequence::Quit);
+
+ return m_shortcut;
+}
+
+void QCocoaMenuItem::syncMerged()
+{
+ Q_ASSERT(m_merged);
+ [m_native setTag:reinterpret_cast<NSInteger>(this)];
+ [m_native setHidden: !m_isVisible];
+}
+
+void QCocoaMenuItem::syncModalState(bool modal)
+{
+ if (modal)
+ [m_native setEnabled:NO];
+ else
+ [m_native setEnabled:m_enabled];
+}
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h
index b0d61fa541..fe9d0e0af5 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuloader.h
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.h
@@ -85,10 +85,11 @@
- (IBAction)hideOtherApplications:(id)sender;
- (IBAction)unhideAllApplications:(id)sender;
- (IBAction)hide:(id)sender;
-- (IBAction)qtDispatcherToQAction:(id)sender;
-- (void)qtUpdateMenubar;
+- (IBAction)qtDispatcherToQPAMenuItem:(id)sender;
- (void)orderFrontCharacterPalette:(id)sender;
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem;
+- (void)qtTranslateApplicationMenu;
+- (NSArray *)mergeable;
@end
void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader);
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
index 7854a83253..45ac878ea4 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
@@ -42,6 +42,8 @@
#include "qcocoamenuloader.h"
#include "qcocoahelpers.h"
+#include "qcocoamenubar.h"
+#include "qcocoamenuitem.h"
#include <QtCore/private/qcore_mac_p.h>
#include <QtCore/qcoreapplication.h>
@@ -52,14 +54,33 @@
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
+#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
+
/*
Loads and instantiates the main app menu from the menu nib file(s).
@@ -127,6 +148,11 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
// They should get synced back in.
[preferencesItem setEnabled:NO];
[preferencesItem setHidden:YES];
+
+ // should set this in the NIB
+ [preferencesItem setTarget: self];
+ [preferencesItem setAction: @selector(qtDispatcherToQPAMenuItem:)];
+
[aboutItem setEnabled:NO];
[aboutItem setHidden:YES];
}
@@ -269,19 +295,10 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
[NSApp hide:sender];
}
-- (void)qtUpdateMenubar
-{
-#if 0
- QCocoaMenuBar::macUpdateMenuBarImmediatly();
-#endif
-}
-
- (void)qtTranslateApplicationMenu
{
- qDebug() << "qtTranslateApplicationMenu";
-#if 0
- //#ifndef QT_NO_TRANSLATION
+#ifndef QT_NO_TRANSLATION
[servicesItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(0))];
[hideItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(1).arg(qt_mac_applicationName()))];
[hideAllOthersItem setTitle: QCFString::toNSString(qt_mac_applicationmenu_string(2))];
@@ -292,27 +309,27 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
#endif
}
-- (IBAction)qtDispatcherToQAction:(id)sender
+- (IBAction)qtDispatcherToQPAMenuItem:(id)sender
{
-#if 0
- //
- //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) {
+ 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();
+ return;
+ }
+
+ if ([item tag]) {
+ QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
+ cocoaItem->activated();
}
-#endif
}
- - (void)orderFrontCharacterPalette:(id)sender
- {
- [NSApp orderFrontCharacterPalette:sender];
- }
+- (void)orderFrontCharacterPalette:(id)sender
+{
+ [NSApp orderFrontCharacterPalette:sender];
+}
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
@@ -320,9 +337,18 @@ void qt_mac_loadMenuNib(QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader)
|| [menuItem action] == @selector(hideOtherApplications:)
|| [menuItem action] == @selector(unhideAllApplications:)) {
return [NSApp validateMenuItem:menuItem];
+ } else if ([menuItem tag]) {
+ QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([menuItem tag]);
+ return cocoaItem->isEnabled();
} else {
return [menuItem isEnabled];
}
}
+- (NSArray*) mergeable
+{
+ // don't include the quitItem here, since we want it always visible and enabled regardless
+ return [NSArray arrayWithObjects:preferencesItem, aboutItem, aboutQtItem, lastAppSpecificItem, nil];
+}
+
@end
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
index 0346eafec4..fd0b10358f 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
@@ -53,8 +53,13 @@ class QCocoaNativeInterface : public QPlatformNativeInterface
{
Q_OBJECT
public:
+ QCocoaNativeInterface();
+
void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window);
+public Q_SLOTS:
+ void onAppFocusWindowChanged(QWindow *window);
+
private:
/*
"Virtual" function to create the platform printer support
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index df9ae23606..06d1f9ee0f 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -43,6 +43,7 @@
#include "qcocoaglcontext.h"
#include "qcocoawindow.h"
#include "qcocoaprintersupport.h"
+#include "qcocoamenubar.h"
#include <qbytearray.h>
#include <qwindow.h>
@@ -50,12 +51,17 @@
#include "qsurfaceformat.h"
#include <qpa/qplatformopenglcontext.h>
#include "qopenglcontext.h"
+#include "qguiapplication.h"
#include <qdebug.h>
#include "qprintengine_mac_p.h"
QT_BEGIN_NAMESPACE
+QCocoaNativeInterface::QCocoaNativeInterface()
+{
+}
+
void *QCocoaNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window)
{
if (!window->handle()) {
@@ -84,4 +90,9 @@ void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine
return macPrintEngine->d_func()->printInfo;
}
+void QCocoaNativeInterface::onAppFocusWindowChanged(QWindow *window)
+{
+ QCocoaMenuBar::updateMenuBarImmediately();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index d126050f98..3c071d44c3 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -54,6 +54,10 @@ public:
QCocoaTheme();
~QCocoaTheme();
+ virtual QPlatformMenuItem* createPlatformMenuItem() const;
+ virtual QPlatformMenu* createPlatformMenu() const;
+ virtual QPlatformMenuBar* createPlatformMenuBar() const;
+
bool usePlatformNativeDialog(DialogType dialogType) const;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const;
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index cfb146ad45..2f630c7763 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -41,12 +41,19 @@
#include "qcocoatheme.h"
-#include <QVariant>
+#include <QtCore/QVariant>
#include "qcocoacolordialoghelper.h"
#include "qcocoafiledialoghelper.h"
#include "qcocoafontdialoghelper.h"
#include "qcocoasystemsettings.h"
+#include "qcocoamenuitem.h"
+#include "qcocoamenu.h"
+#include "qcocoamenubar.h"
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/QPlatformIntegration>
+#include <QtGui/QPlatformNativeInterface>
QT_BEGIN_NAMESPACE
@@ -133,4 +140,27 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
return QPlatformTheme::themeHint(hint);
}
+QPlatformMenuItem *QCocoaTheme::createPlatformMenuItem() const
+{
+ return new QCocoaMenuItem();
+}
+
+QPlatformMenu *QCocoaTheme::createPlatformMenu() const
+{
+ return new QCocoaMenu();
+}
+
+QPlatformMenuBar *QCocoaTheme::createPlatformMenuBar() const
+{
+ static bool haveMenubar = false;
+ if (!haveMenubar) {
+ haveMenubar = true;
+ QObject::connect(qGuiApp, SIGNAL(focusWindowChanged(QWindow*)),
+ QGuiApplicationPrivate::platformIntegration()->nativeInterface(),
+ SLOT(onAppFocusWindowChanged(QWindow*)));
+ }
+
+ return new QCocoaMenuBar();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index f2d6ac67bb..debc8c42a2 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -85,6 +85,8 @@ QT_BEGIN_NAMESPACE
// See the qt_on_cocoa manual tests for a working example, located
// in tests/manual/cocoa at the time of writing.
+class QCocoaMenuBar;
+
class QCocoaWindow : public QPlatformWindow
{
public:
@@ -120,6 +122,8 @@ public:
bool setWindowModified(bool modified) Q_DECL_OVERRIDE;
+ void setMenubar(QCocoaMenuBar *mb);
+ QCocoaMenuBar *menubar() const;
protected:
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed
// in an existing NSWindow or in one created by Qt.
@@ -145,6 +149,7 @@ public: // for QNSView
bool m_inConstructor;
QCocoaGLContext *m_glContext;
+ QCocoaMenuBar *m_menubar;
bool m_hasModalSession;
};
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 5480b32083..880292b24a 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -100,8 +100,12 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
, m_synchedWindowState(Qt::WindowActive)
, m_inConstructor(true)
, m_glContext(0)
+ , m_menubar(0)
, m_hasModalSession(false)
{
+#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
+ qDebug() << "QCocoaWindow::QCocoaWindow" << this;
+#endif
QCocoaAutoReleasePool pool;
m_contentView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this];
@@ -577,3 +581,13 @@ bool QCocoaWindow::setWindowModified(bool modified)
[m_nsWindow setDocumentEdited:(modified?YES:NO)];
return true;
}
+
+void QCocoaWindow::setMenubar(QCocoaMenuBar *mb)
+{
+ m_menubar = mb;
+}
+
+QCocoaMenuBar *QCocoaWindow::menubar() const
+{
+ return m_menubar;
+}
diff --git a/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib b/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib
index 0031e0e4e5..78941153c2 100644
--- a/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib
+++ b/src/plugins/platforms/cocoa/qt_menu.nib/classes.nib
@@ -13,7 +13,7 @@
<string>id</string>
<key>orderFrontStandardAboutPanel</key>
<string>id</string>
- <key>qtDispatcherToQAction</key>
+ <key>qtDispatcherToQPAMenuItem</key>
<string>id</string>
<key>terminate</key>
<string>id</string>
diff --git a/tests/manual/cocoa/menus/main.cpp b/tests/manual/cocoa/menus/main.cpp
new file mode 100644
index 0000000000..d695c6c138
--- /dev/null
+++ b/tests/manual/cocoa/menus/main.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 KDAB
+** Contact: http://www.qt-project.org/
+**
+** 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 <QtGui>
+#include <QtWidgets>
+
+
+class Responder : public QObject
+{
+ Q_OBJECT
+
+public:
+ Responder(QObject *pr) :
+ QObject(pr)
+ {
+ }
+
+public slots:
+
+
+ void toggleChecked(bool b)
+ {
+ QAction *a = qobject_cast<QAction *>(sender());
+ }
+
+ void showModalDialog()
+ {
+ QMessageBox::information(NULL, "Something", "Something happened. Modally.");
+ }
+
+ void doPreferences()
+ {
+ qDebug() << "show preferences";
+ }
+
+ void aboutToShowSubmenu()
+ {
+ QMenu* m = (QMenu*) sender();
+ qDebug() << "will show" << m;
+
+ m->clear();
+
+ for (int i=0; i<10; ++i) {
+ m->addAction(QString("Recent File %1").arg(i + 1));
+ }
+ }
+};
+
+void createWindow1()
+{
+
+ QMainWindow *window = new QMainWindow;
+ QMenu *menu = new QMenu("TestMenu", window);
+
+ window->menuBar()->addMenu(menu);
+
+ Responder *r = new Responder(window);
+
+ QAction *a = menu->addAction("TestMenuItem1");
+ a->setShortcut( Qt::Key_A | Qt::SHIFT | Qt::CTRL );
+ QObject::connect(a, SIGNAL(triggered()),
+ r, SLOT(showModalDialog()));
+
+
+ menu->addAction("T&estMenuItem2");
+ a = menu->addAction("Preferences");
+ a->setMenuRole(QAction::PreferencesRole);
+ QObject::connect(a, SIGNAL(triggered()),
+ r, SLOT(doPreferences()));
+
+ a = menu->addAction("TestMenuItem4");
+ a->setShortcut( Qt::Key_W | Qt::CTRL);
+
+ QMenu *menu2 = new QMenu("SecondMenu", window);
+ window->menuBar()->addMenu(menu2);
+
+ menu2->addAction("Yellow");
+ a = menu2->addAction("Mau&ve");
+
+ QFont f;
+ f.setPointSize(9);
+ a->setFont(f);
+
+ menu2->addAction("Taupe");
+
+ QMenu *submenu1 = new QMenu("Submenu", window);
+ submenu1->addAction("Sub Item 1");
+ submenu1->addAction("Sub Item 2");
+ submenu1->addAction("Sub Item 3");
+ menu2->addMenu(submenu1);
+
+ QMenu *submenu2 = new QMenu("Deeper", window);
+ submenu2->addAction("Sub Sub Item 1");
+ submenu2->addAction("Sub Sub Item 2");
+ submenu2->addAction("Sub Sub Item 3");
+ submenu1->addMenu(submenu2);
+
+ QMenu *menu3 = new QMenu("A Third Menu", window);
+
+ menu3->addAction("Eins");
+
+ QMenu *submenu3 = new QMenu("Dynamic", window);
+ QObject::connect(submenu3, SIGNAL(aboutToShow()), r, SLOT(aboutToShowSubmenu()));
+ menu3->addMenu(submenu3);
+
+ a = menu3->addAction("Zwei");
+ a->setShortcut( Qt::Key_3 | Qt::ALT);
+ a = menu3->addAction("About Drei...");
+ a->setMenuRole(QAction::AboutRole);
+
+ window->menuBar()->addMenu(menu3);
+
+ QAction *checkableAction = new QAction("Thing Enabled", window);
+ checkableAction->setCheckable(true);
+ checkableAction->setChecked(true);
+ QObject::connect(checkableAction, SIGNAL(triggered(bool)),
+ r, SLOT(toggleChecked(bool)));
+
+ menu2->addAction(checkableAction);
+
+ window->show();
+
+}
+
+void createWindow2()
+{
+ QMainWindow *window = new QMainWindow;
+ QMenu *menu = new QMenu("Nuts", window);
+
+ window->menuBar()->addMenu(menu);
+
+ menu->addAction("Peanuts");
+ menu->addAction("Walnuts");
+
+ QMenu *menu2 = new QMenu("Colours", window);
+ window->menuBar()->addMenu(menu2);
+
+ menu2->addAction("Pink");
+ menu2->addAction("Yellow");
+ menu2->addAction("Grape");
+
+ QMenu *menu3 = new QMenu("Edit", window);
+ menu3->addAction("Cut");
+ menu3->addAction("Copy boring way");
+ menu3->addAction("Copy awesomely");
+ menu3->addAction("Paste");
+
+ window->menuBar()->addMenu(menu3);
+ window->show();
+}
+
+int main(int argc, char **argv) {
+ QApplication app(argc, argv);
+
+ app.setApplicationName("Banana");
+
+ createWindow1();
+ createWindow2();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/cocoa/menus/menus.pro b/tests/manual/cocoa/menus/menus.pro
new file mode 100644
index 0000000000..0ccb25feb9
--- /dev/null
+++ b/tests/manual/cocoa/menus/menus.pro
@@ -0,0 +1,5 @@
+TEMPLATE = app
+
+SOURCES += main.cpp
+QT += gui widgets
+CONFIG -=app_bundle