changeset 354:4969e7921931 tonioni

Fix single-key menu shortcuts on OS/X (for #890)
author Chris Cannam
date Mon, 02 Jun 2014 17:32:08 +0100
parents 5a66f4e5a3dc
children e7a3fa8f4eec 42a5801da931
files framework/MainWindowBase.cpp framework/MainWindowBase.h
diffstat 2 files changed, 72 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/framework/MainWindowBase.cpp	Thu May 29 16:27:52 2014 +0100
+++ b/framework/MainWindowBase.cpp	Mon Jun 02 17:32:08 2014 +0100
@@ -99,6 +99,7 @@
 #include <QRegExp>
 #include <QScrollArea>
 #include <QDesktopWidget>
+#include <QSignalMapper>
 
 #include <iostream>
 #include <cstdio>
@@ -270,6 +271,70 @@
 }
 
 void
+MainWindowBase::finaliseMenus()
+{
+    QMenuBar *mb = menuBar();
+    QList<QMenu *> menus = mb->findChildren<QMenu *>();
+    foreach (QMenu *menu, menus) {
+        if (menu) finaliseMenu(menu);
+    }
+}
+
+void
+MainWindowBase::finaliseMenu(QMenu *menu)
+{
+#ifdef Q_OS_MAC
+    // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
+    // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
+    // single-key shortcuts that are associated only with a menu
+    // action do not work with Qt 5.x under OS/X.
+    // 
+    // Apparently Cocoa never handled them as a matter of course, but
+    // earlier versions of Qt picked them up as widget shortcuts and
+    // handled them anyway. That behaviour was removed to fix a crash
+    // when invoking a menu while its window was overridden by a modal
+    // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
+    //
+    // This workaround restores the single-key shortcut behaviour by
+    // searching for single-key shortcuts in menus and replacing them
+    // with global application shortcuts that invoke the relevant
+    // actions, testing whether the actions are enabled on invocation.
+    // As it happens, it also replaces some shortcuts that were
+    // working fine (because they were also associated with toolbar
+    // buttons) but that seems to be OK so long as we remove the
+    // shortcuts from the actions as well as adding the new global
+    // shortcuts, to avoid an ambiguous shortcut error.
+    //
+    // If the Qt developers ever fix this in Qt (I couldn't think of
+    // an obvious fix myself) then presumably we can remove this.
+
+    QSignalMapper *mapper = new QSignalMapper(this);
+
+    connect(mapper, SIGNAL(mapped(QObject *)),
+            this, SLOT(menuActionMapperInvoked(QObject *)));
+
+    foreach (QAction *a, menu->actions()) {
+        QKeySequence sc = a->shortcut();
+        if (sc.count() == 1 && !(sc[0] & Qt::KeyboardModifierMask)) {
+            QShortcut *newSc = new QShortcut(sc, a->parentWidget());
+            QObject::connect(newSc, SIGNAL(activated()), mapper, SLOT(map()));
+            mapper->setMapping(newSc, a);
+            a->setShortcut(QKeySequence()); // avoid ambiguous shortcut error
+        }
+    }
+#endif
+}
+
+void
+MainWindowBase::menuActionMapperInvoked(QObject *o)
+{
+    QAction *a = qobject_cast<QAction *>(o);
+    if (a && a->isEnabled()) {
+        a->trigger();
+    }
+}
+
+void
 MainWindowBase::resizeConstrained(QSize size)
 {
     QDesktopWidget *desktop = QApplication::desktop();
--- a/framework/MainWindowBase.h	Thu May 29 16:27:52 2014 +0100
+++ b/framework/MainWindowBase.h	Mon Jun 02 17:32:08 2014 +0100
@@ -284,6 +284,8 @@
 
     virtual void newerVersionAvailable(QString) { }
 
+    virtual void menuActionMapperInvoked(QObject *);
+
 protected:
     QString                  m_sessionFile;
     QString                  m_audioFile;
@@ -412,6 +414,11 @@
     virtual void updateVisibleRangeDisplay(Pane *p) const = 0;
     virtual void updatePositionStatusDisplays() const = 0;
 
+    // Call this after setting up the menu bar, to fix up single-key
+    // shortcuts on OS/X
+    virtual void finaliseMenus();
+    virtual void finaliseMenu(QMenu *);
+
     virtual bool shouldCreateNewSessionForRDFAudio(bool *) { return true; }
 
     virtual void connectLayerEditDialog(ModelDataTableDialog *dialog);