File indexing completed on 2024-12-08 03:39:25

0001 #include <QKeySequence>
0002 
0003 #include "kglobalshortcutinfo_p.h"
0004 #include "sequencehelpers_p.h"
0005 
0006 namespace Utils
0007 {
0008 QKeySequence reverseKey(const QKeySequence &key)
0009 {
0010     int k[maxSequenceLength] = {0, 0, 0, 0};
0011     int count = key.count();
0012     for (int i = 0; i < count; i++) {
0013         k[count - i - 1] = key[i].toCombined();
0014     }
0015 
0016     return QKeySequence(k[0], k[1], k[2], k[3]);
0017 }
0018 
0019 QKeySequence cropKey(const QKeySequence &key, int count)
0020 {
0021     if (count < 1) {
0022         return key;
0023     }
0024 
0025     // Key is shorter than count we want to cut off
0026     if (key.count() < count) {
0027         return QKeySequence();
0028     }
0029 
0030     int k[maxSequenceLength] = {0, 0, 0, 0};
0031     // cut from beginning
0032     for (int i = count; i < key.count(); i++) {
0033         k[i - count] = key[i].toCombined();
0034     }
0035 
0036     return QKeySequence(k[0], k[1], k[2], k[3]);
0037 }
0038 
0039 bool contains(const QKeySequence &key, const QKeySequence &other)
0040 {
0041     int minLength = std::min(key.count(), other.count());
0042 
0043     // There's an empty key, assume it matches nothing
0044     if (!minLength) {
0045         return false;
0046     }
0047 
0048     bool ret = false;
0049     for (int i = 0; i <= other.count() - minLength; i++) {
0050         QKeySequence otherCropped = cropKey(other, i);
0051         if (key.matches(otherCropped) == QKeySequence::PartialMatch || reverseKey(key).matches(reverseKey(otherCropped)) == QKeySequence::PartialMatch) {
0052             ret = true;
0053             break;
0054         }
0055     }
0056 
0057     return ret;
0058 }
0059 
0060 bool matchSequences(const QKeySequence &key, const QList<QKeySequence> &keys)
0061 {
0062     // Since we're testing sequences, we need to check for all possible matches
0063     // between existing and new sequences.
0064 
0065     // Let's assume we have (Alt+B, Alt+F, Alt+G) assigned. Examples of bad shortcuts are:
0066     // 1) Exact matching: (Alt+B, Alt+F, Alt+G)
0067     // 2) Sequence shadowing: (Alt+B, Alt+F)
0068     // 3) Sequence being shadowed: (Alt+B, Alt+F, Alt+G, <any key>)
0069     // 4) Shadowing at the end: (Alt+F, Alt+G)
0070     // 5) Being shadowed from the end: (<any key>, Alt+B, Alt+F, Alt+G)
0071 
0072     for (const QKeySequence &otherKey : keys) {
0073         if (otherKey.isEmpty()) {
0074             continue;
0075         }
0076         if (key.matches(otherKey) == QKeySequence::ExactMatch || contains(key, otherKey) || contains(otherKey, key)) {
0077             return true;
0078         }
0079     }
0080     return false;
0081 }
0082 
0083 QKeySequence mangleKey(const QKeySequence &key)
0084 {
0085     // Qt triggers both shortcuts that include Shift+Backtab and Shift+Tab
0086     // when user presses Shift+Tab. Make no difference here.
0087     int k[maxSequenceLength] = {0, 0, 0, 0};
0088     for (int i = 0; i < key.count(); i++) {
0089         // Qt triggers both shortcuts that include Shift+Backtab and Shift+Tab
0090         // when user presses Shift+Tab. Make no difference here.
0091         int keySym = key[i].toCombined() & ~Qt::KeyboardModifierMask;
0092         int keyMod = key[i].toCombined() & Qt::KeyboardModifierMask;
0093         if ((keyMod & Qt::SHIFT) && (keySym == Qt::Key_Backtab || keySym == Qt::Key_Tab)) {
0094             k[i] = keyMod | Qt::Key_Tab;
0095         } else {
0096             k[i] = key[i].toCombined();
0097         }
0098     }
0099 
0100     return QKeySequence(k[0], k[1], k[2], k[3]);
0101 }
0102 
0103 } // namespace Utils