diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoamenuitem.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenuitem.mm | 358 |
1 files changed, 358 insertions, 0 deletions
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]; +} |