diff --git a/src/qmlcontrols/kquickcontrols/private/keysequencehelper.cpp b/src/qmlcontrols/kquickcontrols/private/keysequencehelper.cpp index 66eb3e5..fd46eb4 100644 --- a/src/qmlcontrols/kquickcontrols/private/keysequencehelper.cpp +++ b/src/qmlcontrols/kquickcontrols/private/keysequencehelper.cpp @@ -1,528 +1,529 @@ /* * * Copyright (C) 2014 David Edmundson * Copyright (C) 1998 Mark Donohoe * Copyright (C) 2001 Ellis Whitehead * Copyright (C) 2007 Andreas Hartmetz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "keysequencehelper.h" #include +#include #include #include #include #include #include #include #include #include #include uint qHash(const QKeySequence &seq) { return qHash(seq.toString()); } class KeySequenceHelperPrivate { public: KeySequenceHelperPrivate(KeySequenceHelper *q); void init(); static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt); static bool isOkWhenModifierless(int keyQt); void updateShortcutDisplay(); void startRecording(); /** * Conflicts the key sequence @a seq with a current standard * shortcut? */ bool conflictWithStandardShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @a seq with a current global * shortcut? */ bool conflictWithGlobalShortcuts(const QKeySequence &seq); /** * Get permission to steal the shortcut @seq from the standard shortcut @a std. */ bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq); bool checkAgainstStandardShortcuts() const { return checkAgainstShortcutTypes & KeySequenceHelper::StandardShortcuts; } bool checkAgainstGlobalShortcuts() const { return checkAgainstShortcutTypes & KeySequenceHelper::GlobalShortcuts; } void controlModifierlessTimout() { if (nKey != 0 && !modifierKeys) { // No modifier key pressed currently. Start the timout modifierlessTimeout.start(600); } else { // A modifier is pressed. Stop the timeout modifierlessTimeout.stop(); } } void cancelRecording() { keySequence = oldKeySequence; q->doneRecording(); } //members KeySequenceHelper *const q; QToolButton *clearButton; QKeySequence keySequence; QKeySequence oldKeySequence; QTimer modifierlessTimeout; bool allowModifierless; uint nKey; uint modifierKeys; bool isRecording; bool multiKeyShortcutsAllowed; QString componentName; QString shortcutDisplay; //! Check the key sequence against KStandardShortcut::find() KeySequenceHelper::ShortcutTypes checkAgainstShortcutTypes; /** * The list of action to check against for conflict shortcut */ QList checkList; // deprecated /** * The list of action collections to check against for conflict shortcut */ // QList checkActionCollections; /** * The action to steal the shortcut from. */ QList stealActions; bool stealShortcuts(const QList &actions, const QKeySequence &seq); void wontStealShortcut(QAction *item, const QKeySequence &seq); }; KeySequenceHelperPrivate::KeySequenceHelperPrivate(KeySequenceHelper *q) : q(q) , allowModifierless(false) , nKey(0) , modifierKeys(0) , isRecording(false) , multiKeyShortcutsAllowed(true) , componentName() , checkAgainstShortcutTypes(KeySequenceHelper::StandardShortcuts | KeySequenceHelper::GlobalShortcuts) , stealActions() { } -KeySequenceHelper::KeySequenceHelper(QObject* parent): - QObject(), +KeySequenceHelper::KeySequenceHelper(QQuickItem* parent): + QQuickItem(parent), d(new KeySequenceHelperPrivate(this)) { Q_UNUSED(parent); connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording())); d->updateShortcutDisplay(); } KeySequenceHelper::~KeySequenceHelper() { delete d; } bool KeySequenceHelper::multiKeyShortcutsAllowed() const { return d->multiKeyShortcutsAllowed; } void KeySequenceHelper::setMultiKeyShortcutsAllowed(bool allowed) { d->multiKeyShortcutsAllowed = allowed; } void KeySequenceHelper::setModifierlessAllowed(bool allow) { d->allowModifierless = allow; } bool KeySequenceHelper::isModifierlessAllowed() { return d->allowModifierless; } bool KeySequenceHelper::isKeySequenceAvailable(const QKeySequence& keySequence) const { if (keySequence.isEmpty()) { return true; } return !(d->conflictWithGlobalShortcuts(keySequence) || d->conflictWithStandardShortcuts(keySequence)); } // // void KeySequenceHelper::setCheckActionCollections(const QList &actionCollections) // { // d->checkActionCollections = actionCollections; // } // //slot void KeySequenceHelper::captureKeySequence() { d->startRecording(); } QKeySequence KeySequenceHelper::keySequence() const { return d->keySequence; } void KeySequenceHelper::setKeySequence(const QKeySequence& sequence) { if (!d->isRecording) { d->oldKeySequence = d->keySequence; } d->keySequence = sequence; d->updateShortcutDisplay(); emit keySequenceChanged(d->keySequence); } void KeySequenceHelper::clearKeySequence() { setKeySequence(QKeySequence()); } void KeySequenceHelperPrivate::startRecording() { nKey = 0; modifierKeys = 0; oldKeySequence = keySequence; keySequence = QKeySequence(); isRecording = true; - + q->window()->setKeyboardGrabEnabled(true); updateShortcutDisplay(); } // void KeySequenceHelper::doneRecording() { d->modifierlessTimeout.stop(); d->isRecording = false; d->stealActions.clear(); - + window()->setKeyboardGrabEnabled(false); if (d->keySequence == d->oldKeySequence) { // The sequence hasn't changed d->updateShortcutDisplay(); return; } if (! isKeySequenceAvailable(d->keySequence)) { // The sequence had conflicts and the user said no to stealing it d->keySequence = d->oldKeySequence; } else { emit keySequenceChanged(d->keySequence); } Q_EMIT captureFinished(); d->updateShortcutDisplay(); } bool KeySequenceHelperPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence) { #ifdef Q_OS_WIN //on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut if (KeySequenceHelper::GlobalShortcuts && keySequence.toString().contains(QLatin1String("F12"))) { QString title = i18n("Reserved Shortcut"); QString message = i18n("The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n" "Please choose another one."); KMessageBox::sorry(0, message, title); return false; } #endif if (!(checkAgainstShortcutTypes & KeySequenceHelper::GlobalShortcuts)) { return false; } // Global shortcuts are on key+modifier shortcuts. They can clash with // each of the keys of a multi key shortcut. QList others; for (int i = 0; i < keySequence.count(); ++i) { QKeySequence tmp(keySequence[i]); if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) { others << KGlobalAccel::getGlobalShortcutsByKey(tmp); } } if (!others.isEmpty() && !KGlobalAccel::promptStealShortcutSystemwide(nullptr, others, keySequence)) { return true; } // The user approved stealing the shortcut. We have to steal // it immediately because KAction::setGlobalShortcut() refuses // to set a global shortcut that is already used. There is no // error it just silently fails. So be nice because this is // most likely the first action that is done in the slot // listening to keySequenceChanged(). for (int i = 0; i < keySequence.count(); ++i) { KGlobalAccel::stealShortcutSystemwide(keySequence[i]); } return false; } bool KeySequenceHelperPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence) { if (!checkAgainstStandardShortcuts()) { return false; } KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence); if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) { qDebug() << "!!!!!!!!!!!!!!"; return true; } return false; } bool KeySequenceHelperPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq) { QString title = i18n("Conflict with Standard Application Shortcut"); QString message = i18n("The '%1' key combination is also used for the standard action " "\"%2\" that some applications use.\n" "Do you really want to use it as a global shortcut as well?", seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std)); if (KMessageBox::warningContinueCancel(nullptr, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KeySequenceHelperPrivate::updateShortcutDisplay() { //empty string if no non-modifier was pressed QString s = keySequence.toString(QKeySequence::NativeText); s.replace(QLatin1Char('&'), QStringLiteral("&&")); if (isRecording) { if (modifierKeys) { if (!s.isEmpty()) { s.append(QLatin1Char(',')); } if (modifierKeys & Qt::MetaModifier) { s += QKeySequence(Qt::MetaModifier).toString(QKeySequence::NativeText); } #if defined(Q_OS_MAC) if (modifierKeys & Qt::AltModifier) { s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::ControlModifier) { s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText); } #else if (modifierKeys & Qt::ControlModifier) { s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::AltModifier) { s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText); } #endif if (modifierKeys & Qt::ShiftModifier) { s += QKeySequence(Qt::ShiftModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::KeypadModifier) { s += QKeySequence(Qt::KeypadModifier).toString(QKeySequence::NativeText); } } else if (nKey == 0) { s = i18nc("What the user inputs now will be taken as the new shortcut", "Input"); } //make it clear that input is still going on s.append(QStringLiteral(" ...")); } if (s.isEmpty()) { s = i18nc("No shortcut defined", "None"); } s.prepend(QLatin1Char(' ')); s.append(QLatin1Char(' ')); shortcutDisplay = s; q->shortcutDisplayChanged(s); } QString KeySequenceHelper::shortcutDisplay() const { return d->shortcutDisplay; } void KeySequenceHelper::keyPressed(int key, int modifiers) { if (key == -1) { // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key. // We cannot do anything useful with those (several keys have -1, indistinguishable) // and QKeySequence.toString() will also yield a garbage string. KMessageBox::sorry(nullptr, i18n("The key you just pressed is not supported by Qt."), i18n("Unsupported Key")); return d->cancelRecording(); } //don't have the return or space key appear as first key of the sequence when they //were pressed to start editing - catch and them and imitate their effect if (!d->isRecording && ((key == Qt::Key_Return || key == Qt::Key_Space))) { d->startRecording(); d->modifierKeys = modifiers; d->updateShortcutDisplay(); return; } d->modifierKeys = modifiers; switch (key) { case Qt::Key_AltGr: //or else we get unicode salad return; case Qt::Key_Shift: case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Meta: case Qt::Key_Super_L: case Qt::Key_Super_R: case Qt::Key_Menu: //unused (yes, but why?) d->controlModifierlessTimout(); d->updateShortcutDisplay(); break; default: if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) { // It's the first key and no modifier pressed. Check if this is // allowed if (!(KeySequenceHelperPrivate::isOkWhenModifierless(key) || d->allowModifierless)) { // No it's not return; } } // We now have a valid key press. if (key) { if ((key == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) { key = Qt::Key_Tab | d->modifierKeys; } else { key |= (d->modifierKeys); } if (d->nKey == 0) { d->keySequence = QKeySequence(key); } else { d->keySequence = KeySequenceHelperPrivate::appendToSequence(d->keySequence, key); } d->nKey++; if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) { doneRecording(); return; } d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } } // void KeySequenceHelper::keyReleased(int key, int modifiers) { if (key == -1) { // ignore garbage, see keyPressEvent() return; } //if a modifier that belongs to the shortcut was released... if ((modifiers & d->modifierKeys) < d->modifierKeys) { d->modifierKeys = modifiers; d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } // //static QKeySequence KeySequenceHelperPrivate::appendToSequence(const QKeySequence &seq, int keyQt) { if (seq.matches(keyQt) != QKeySequence::NoMatch) { return seq; } switch (seq.count()) { case 0: return QKeySequence(keyQt); case 1: return QKeySequence(seq[0], keyQt); case 2: return QKeySequence(seq[0], seq[1], keyQt); case 3: return QKeySequence(seq[0], seq[1], seq[2], keyQt); default: return seq; } } // //static bool KeySequenceHelperPrivate::isOkWhenModifierless(int keyQt) { //this whole function is a hack, but especially the first line of code if (QKeySequence(keyQt).toString().length() == 1) { return false; } switch (keyQt) { case Qt::Key_Return: case Qt::Key_Space: case Qt::Key_Tab: case Qt::Key_Backtab: //does this ever happen? case Qt::Key_Backspace: case Qt::Key_Delete: return false; default: return true; } } diff --git a/src/qmlcontrols/kquickcontrols/private/keysequencehelper.h b/src/qmlcontrols/kquickcontrols/private/keysequencehelper.h index 54c9049..f7f8bc2 100644 --- a/src/qmlcontrols/kquickcontrols/private/keysequencehelper.h +++ b/src/qmlcontrols/kquickcontrols/private/keysequencehelper.h @@ -1,147 +1,147 @@ /* * * Copyright (C) 2014 David Edmundson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef KEYSEQUENCEHELPER_H #define KEYSEQUENCEHELPER_H -#include +#include #include /** * This class is a clone of Key from XMLGUI * It performs only the logic of building shortcuts * It is a private class to be used by KeySequenceItem * */ class KeySequenceHelperPrivate; -class KeySequenceHelper : public QObject +class KeySequenceHelper : public QQuickItem { Q_OBJECT Q_PROPERTY( QKeySequence keySequence READ keySequence WRITE setKeySequence NOTIFY keySequenceChanged) Q_PROPERTY( bool multiKeyShortcutsAllowed READ multiKeyShortcutsAllowed WRITE setMultiKeyShortcutsAllowed) Q_PROPERTY( QString shortcutDisplay READ shortcutDisplay NOTIFY shortcutDisplayChanged) Q_PROPERTY( bool modifierlessAllowed READ isModifierlessAllowed WRITE setModifierlessAllowed) public: enum ShortcutType { None = 0x00, //!< No checking for conflicts StandardShortcuts = 0x01, //!< Check against standard shortcuts. @see KStandardShortcut GlobalShortcuts = 0x02 //!< Check against global shortcuts. @see KGlobalAccel }; Q_DECLARE_FLAGS(ShortcutTypes, ShortcutType) Q_FLAG(ShortcutTypes) /** * Constructor. */ - explicit KeySequenceHelper(QObject* parent = nullptr); + explicit KeySequenceHelper(QQuickItem* parent = nullptr); /** * Destructs the widget. */ virtual ~KeySequenceHelper(); /** * Allow multikey shortcuts? */ void setMultiKeyShortcutsAllowed(bool); bool multiKeyShortcutsAllowed() const; /** * This only applies to user input, not to setShortcut(). * Set whether to accept "plain" keys without modifiers (like Ctrl, Alt, Meta). * Plain keys by our definition include letter and symbol keys and * text editing keys (Return, Space, Tab, Backspace, Delete). * "Special" keys like F1, Cursor keys, Insert, PageDown will always work. */ void setModifierlessAllowed(bool allow); /** * @see setModifierlessAllowed() */ bool isModifierlessAllowed(); bool isRecording() const; void setShortcut(bool recording); /** * Set the default key sequence from a string */ void setKeySequence(const QKeySequence &sequence); /** * Return the currently selected key sequence as a string */ QKeySequence keySequence() const; QString shortcutDisplay() const; bool isKeySequenceAvailable(const QKeySequence &keySequence) const; Q_SIGNALS: void keySequenceChanged(const QKeySequence &seq); void shortcutDisplayChanged(const QString &string); void captureFinished(); public Q_SLOTS: void captureKeySequence(); void keyPressed(int key, int modifiers); void keyReleased(int key, int modifiers); /** * Clear the key sequence. */ void clearKeySequence(); private Q_SLOTS: void doneRecording(); private: friend class KeySequenceHelperPrivate; KeySequenceHelperPrivate *const d; Q_DISABLE_COPY(KeySequenceHelper) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KeySequenceHelper::ShortcutTypes) #endif // KEYSEQUENCEHELPER_H