File indexing completed on 2024-05-19 05:29:57

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