diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/messages.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenubar.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenubar.mm | 63 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenuitem.h | 3 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenuitem.mm | 15 |
6 files changed, 97 insertions, 2 deletions
diff --git a/src/plugins/platforms/cocoa/messages.cpp b/src/plugins/platforms/cocoa/messages.cpp index 1fe80b28b1..8fc8b312f5 100644 --- a/src/plugins/platforms/cocoa/messages.cpp +++ b/src/plugins/platforms/cocoa/messages.cpp @@ -90,6 +90,14 @@ QPlatformMenuItem::MenuRole detectMenuRole(const QString &caption) || caption.startsWith(QCoreApplication::translate("QCocoaMenuItem", "Exit"), Qt::CaseInsensitive)) { return QPlatformMenuItem::QuitRole; } + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Cut"), Qt::CaseInsensitive)) + return QPlatformMenuItem::CutRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Copy"), Qt::CaseInsensitive)) + return QPlatformMenuItem::CopyRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Paste"), Qt::CaseInsensitive)) + return QPlatformMenuItem::PasteRole; + if (!caption.compare(QCoreApplication::translate("QCocoaMenuItem", "Select All"), Qt::CaseInsensitive)) + return QPlatformMenuItem::SelectAllRole; return QPlatformMenuItem::NoRole; } diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 8728ab8764..2b7b8109ad 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -52,6 +52,7 @@ #include <private/qguiapplication_p.h> #include "qt_mac_p.h" #include "qcocoahelpers.h" +#include "qcocoamenubar.h" #include <qregexp.h> #include <qbuffer.h> #include <qdebug.h> @@ -225,6 +226,7 @@ static QString strippedText(QString s) || [self panel:nil shouldShowFilename:filepath]; [self updateProperties]; + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); [mOpenPanel setAllowedFileTypes:nil]; [mSavePanel setNameFieldStringValue:selectable ? QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.fileName()) : @""]; @@ -250,7 +252,9 @@ static QString strippedText(QString s) // cleanup of modal sessions. Do this before showing the native dialog, otherwise it will // close down during the cleanup. qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); mReturnCode = [mSavePanel runModal]; + QCocoaMenuBar::resetKnownMenuItemsToQt(); QAbstractEventDispatcher::instance()->interrupt(); return (mReturnCode == NSOKButton); @@ -269,6 +273,7 @@ static QString strippedText(QString s) || [self panel:nil shouldShowFilename:filepath]; [self updateProperties]; + QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); [mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]]; [mSavePanel setNameFieldStringValue:selectable ? QCFString::toNSString(info.fileName()) : @""]; @@ -583,6 +588,7 @@ void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_selectionChanged(const QSt void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_panelClosed(bool accepted) { + QCocoaMenuBar::resetKnownMenuItemsToQt(); if (accepted) { emit accept(); } else { diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index 7a1bda74a4..fa02a7870b 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -67,9 +67,13 @@ public: inline NSMenu *nsMenu() const { return m_nativeMenu; } + static void redirectKnownMenuItemsToFirstResponder(); + static void resetKnownMenuItemsToQt(); static void updateMenuBarImmediately(); QList<QCocoaMenuItem*> merged() const; + NSMenuItem *itemForRole(QPlatformMenuItem::MenuRole r); + private: static QCocoaWindow *findWindowForMenubar(); static QCocoaMenuBar *findGlobalMenubar(); diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 7335deb800..ffc0fabdce 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -205,6 +205,59 @@ QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar() return NULL; } +void QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder() +{ + // QTBUG-17291: http://forums.macrumors.com/showthread.php?t=1249452 + // When a dialog is opened, shortcuts for actions inside the dialog (cut, paste, ...) + // continue to go through the same menu items which claimed those shortcuts. + // They are not keystrokes which we can intercept in any other way; the OS intercepts them. + // The menu items had to be created by the application. That's why we need roles + // to identify those "special" menu items which can be useful even when non-Qt + // native widgets are in focus. When the native widget is focused it will be the + // first responder, so the menu item needs to have its target be the first responder; + // this is done by setting it to nil. + + // This function will find all menu items on all menus which have + // "special" roles, set the target and also set the standard actions which + // apply to those roles. But afterwards it is necessary to call + // resetKnownMenuItemsToQt() to put back the target and action so that + // those menu items will go back to invoking their associated QActions. + foreach (QCocoaMenuBar *mb, static_menubars) + foreach (QCocoaMenu *m, mb->m_menus) + foreach (QCocoaMenuItem *i, m->items()) { + bool known = true; + switch (i->effectiveRole()) { + case QPlatformMenuItem::CutRole: + [i->nsItem() setAction:@selector(cut:)]; + break; + case QPlatformMenuItem::CopyRole: + [i->nsItem() setAction:@selector(copy:)]; + break; + case QPlatformMenuItem::PasteRole: + [i->nsItem() setAction:@selector(paste:)]; + break; + case QPlatformMenuItem::SelectAllRole: + [i->nsItem() setAction:@selector(selectAll:)]; + break; + // We may discover later that there are other roles/actions which + // are meaningful to standard native widgets; they can be added. + default: + known = false; + break; + } + if (known) + [i->nsItem() setTarget:nil]; + } +} + +void QCocoaMenuBar::resetKnownMenuItemsToQt() +{ + // Undo the effect of redirectKnownMenuItemsToFirstResponder(): + // set the menu items' actions to itemFired and their targets to + // the QCocoaMenuDelegate. + updateMenuBarImmediately(); +} + void QCocoaMenuBar::updateMenuBarImmediately() { QCocoaAutoReleasePool pool; @@ -321,3 +374,13 @@ QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const return 0; } + +NSMenuItem *QCocoaMenuBar::itemForRole(QPlatformMenuItem::MenuRole r) +{ + foreach (QCocoaMenu *m, m_menus) + foreach (QCocoaMenuItem *i, m->items()) + if (i->effectiveRole() == r) + return i->nsItem(); + return Q_NULLPTR; +} + diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index b0169b9746..61706c19bc 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -98,6 +98,8 @@ public: inline bool isSeparator() const { return m_isSeparator; } QCocoaMenu *menu() const { return m_menu; } + MenuRole effectiveRole() const; + private: QString mergeText(); QKeySequence mergeAccel(); @@ -112,6 +114,7 @@ private: bool m_isSeparator; QFont m_font; MenuRole m_role; + MenuRole m_detectedRole; QKeySequence m_shortcut; bool m_checked; bool m_merged; diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 2246d2ce46..58fe07bc62 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -227,7 +227,8 @@ NSMenuItem *QCocoaMenuItem::sync() if (depth == 3 || !menubar) break; // Menu item too deep in the hierarchy, or not connected to any menubar - switch (detectMenuRole(m_text)) { + m_detectedRole = detectMenuRole(m_text); + switch (m_detectedRole) { case QPlatformMenuItem::AboutRole: if (m_text.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) mergeItem = [loader aboutMenuItem]; @@ -241,6 +242,8 @@ NSMenuItem *QCocoaMenuItem::sync() mergeItem = [loader quitMenuItem]; break; default: + if (m_detectedRole >= CutRole && m_detectedRole < RoleCount && menubar) + mergeItem = menubar->itemForRole(m_detectedRole); if (!m_text.isEmpty()) m_textSynced = true; break; @@ -249,7 +252,7 @@ NSMenuItem *QCocoaMenuItem::sync() } default: - qWarning() << Q_FUNC_INFO << "unsupported role" << (int) m_role; + qWarning() << Q_FUNC_INFO << "menu item" << m_text << "has unsupported role" << (int)m_role; } if (mergeItem) { @@ -374,3 +377,11 @@ void QCocoaMenuItem::syncModalState(bool modal) else [m_native setEnabled:m_enabled]; } + +QPlatformMenuItem::MenuRole QCocoaMenuItem::effectiveRole() const +{ + if (m_role > TextHeuristicRole) + return m_role; + else + return m_detectedRole; +} |