File indexing completed on 2024-04-28 15:31:18
0001 /* 0002 SPDX-FileCopyrightText: 2008-2009 Erlend Hamberg <ehamberg@gmail.com> 0003 SPDX-FileCopyrightText: 2013 Simon St James <kdedevel@etotheipiplusone.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "globalstate.h" 0009 #include "katedocument.h" 0010 #include "kateglobal.h" 0011 #include "macrorecorder.h" 0012 #include "mappings.h" 0013 #include <vimode/inputmodemanager.h> 0014 #include <vimode/keymapper.h> 0015 #include <vimode/keyparser.h> 0016 0017 #include <QTimer> 0018 0019 using namespace KateVi; 0020 0021 KeyMapper::KeyMapper(InputModeManager *kateViInputModeManager, KTextEditor::DocumentPrivate *doc, KTextEditor::ViewPrivate *view) 0022 : m_viInputModeManager(kateViInputModeManager) 0023 , m_doc(doc) 0024 , m_view(view) 0025 { 0026 m_mappingTimer = new QTimer(this); 0027 m_doNotExpandFurtherMappings = false; 0028 m_timeoutlen = 1000; // FIXME: make configurable 0029 m_doNotMapNextKeypress = false; 0030 m_numMappingsBeingExecuted = 0; 0031 m_isPlayingBackRejectedKeys = false; 0032 connect(m_mappingTimer, &QTimer::timeout, this, &KeyMapper::mappingTimerTimeOut); 0033 } 0034 0035 void KeyMapper::executeMapping() 0036 { 0037 m_mappingKeys.clear(); 0038 m_mappingTimer->stop(); 0039 m_numMappingsBeingExecuted++; 0040 const QString mappedKeypresses = 0041 m_viInputModeManager->globalState()->mappings()->get(Mappings::mappingModeForCurrentViMode(m_viInputModeManager->inputAdapter()), 0042 m_fullMappingMatch, 0043 false, 0044 true); 0045 if (!m_viInputModeManager->globalState()->mappings()->isRecursive(Mappings::mappingModeForCurrentViMode(m_viInputModeManager->inputAdapter()), 0046 m_fullMappingMatch)) { 0047 m_doNotExpandFurtherMappings = true; 0048 } 0049 m_doc->editBegin(); 0050 m_viInputModeManager->feedKeyPresses(mappedKeypresses); 0051 m_doNotExpandFurtherMappings = false; 0052 m_doc->editEnd(); 0053 m_numMappingsBeingExecuted--; 0054 } 0055 0056 void KeyMapper::playBackRejectedKeys() 0057 { 0058 m_isPlayingBackRejectedKeys = true; 0059 const QString mappingKeys = m_mappingKeys; 0060 m_mappingKeys.clear(); 0061 m_viInputModeManager->feedKeyPresses(mappingKeys); 0062 m_isPlayingBackRejectedKeys = false; 0063 } 0064 0065 void KeyMapper::setMappingTimeout(int timeoutMS) 0066 { 0067 m_timeoutlen = timeoutMS; 0068 } 0069 0070 void KeyMapper::mappingTimerTimeOut() 0071 { 0072 if (!m_fullMappingMatch.isNull()) { 0073 executeMapping(); 0074 } else { 0075 playBackRejectedKeys(); 0076 } 0077 m_mappingKeys.clear(); 0078 } 0079 0080 bool KeyMapper::handleKeypress(QChar key) 0081 { 0082 if (!m_doNotExpandFurtherMappings && !m_doNotMapNextKeypress && !m_isPlayingBackRejectedKeys) { 0083 m_mappingKeys.append(key); 0084 0085 bool isPartialMapping = false; 0086 bool isFullMapping = false; 0087 m_fullMappingMatch.clear(); 0088 const auto mappingMode = Mappings::mappingModeForCurrentViMode(m_viInputModeManager->inputAdapter()); 0089 const auto mappings = m_viInputModeManager->globalState()->mappings()->getAll(mappingMode, false, true); 0090 for (const QString &mapping : mappings) { 0091 if (mapping.startsWith(m_mappingKeys)) { 0092 if (mapping == m_mappingKeys) { 0093 isFullMapping = true; 0094 m_fullMappingMatch = mapping; 0095 } else { 0096 isPartialMapping = true; 0097 } 0098 } 0099 } 0100 if (isFullMapping && !isPartialMapping) { 0101 // Great - m_mappingKeys is a mapping, and one that can't be extended to 0102 // a longer one - execute it immediately. 0103 executeMapping(); 0104 return true; 0105 } 0106 if (isPartialMapping) { 0107 // Need to wait for more characters (or a timeout) before we decide what to 0108 // do with this. 0109 m_mappingTimer->start(m_timeoutlen); 0110 m_mappingTimer->setSingleShot(true); 0111 return true; 0112 } 0113 // We've been swallowing all the keypresses meant for m_keys for our mapping keys; now that we know 0114 // this cannot be a mapping, restore them. 0115 Q_ASSERT(!isPartialMapping && !isFullMapping); 0116 const bool isUserKeypress = !m_viInputModeManager->macroRecorder()->isReplaying() && !isExecutingMapping(); 0117 if (isUserKeypress && m_mappingKeys.size() == 1) { 0118 // Ugh - unpleasant complication starting with Qt 5.5-ish - it is no 0119 // longer possible to replay QKeyEvents in such a way that shortcuts 0120 // are triggered, so if we want to correctly handle a shortcut (e.g. 0121 // ctrl+s for e.g. Save), we can no longer pop it into m_mappingKeys 0122 // then immediately playBackRejectedKeys() (as this will not trigger 0123 // the e.g. Save shortcut) - the best we can do is, if e.g. ctrl+s is 0124 // not part of any mapping, immediately return false, *not* calling 0125 // playBackRejectedKeys() and clearing m_mappingKeys ourselves. 0126 // If e.g. ctrl+s *is* part of a mapping, then if the mapping is 0127 // rejected, the played back e.g. ctrl+s does not trigger the e.g. 0128 // Save shortcut. Likewise, we can no longer have e.g. ctrl+s inside 0129 // mappings or macros - the e.g. Save shortcut will not be triggered! 0130 // Altogether, a pretty disastrous change from Qt's old behaviour - 0131 // either they "fix" it (although it could be argued that being able 0132 // to trigger shortcuts from QKeyEvents was never the desired behaviour) 0133 // or we try to emulate Shortcut-handling ourselves :( 0134 m_mappingKeys.clear(); 0135 return false; 0136 } else { 0137 playBackRejectedKeys(); 0138 return true; 0139 } 0140 } 0141 m_doNotMapNextKeypress = false; 0142 return false; 0143 } 0144 0145 void KeyMapper::setDoNotMapNextKeypress() 0146 { 0147 m_doNotMapNextKeypress = true; 0148 } 0149 0150 bool KeyMapper::isExecutingMapping() const 0151 { 0152 return m_numMappingsBeingExecuted > 0; 0153 } 0154 0155 bool KeyMapper::isPlayingBackRejectedKeys() const 0156 { 0157 return m_isPlayingBackRejectedKeys; 0158 } 0159 0160 #include "moc_keymapper.cpp"