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