File indexing completed on 2025-03-16 08:11:26
0001 /* 0002 This source file is part of Konsole, a terminal emulator. 0003 0004 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0011 GNU General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; if not, write to the Free Software 0015 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0016 02110-1301 USA. 0017 */ 0018 0019 #ifndef KEYBOARDTRANSLATOR_H 0020 #define KEYBOARDTRANSLATOR_H 0021 0022 // Qt 0023 #include <QHash> 0024 #include <QKeySequence> 0025 #include <QList> 0026 #include <QMetaType> 0027 #include <QVarLengthArray> 0028 0029 // Konsole 0030 // #include "konsole_export.h" 0031 #define KONSOLEPRIVATE_EXPORT 0032 0033 class QIODevice; 0034 class QTextStream; 0035 0036 namespace Konsole 0037 { 0038 0039 /** 0040 * A convertor which maps between key sequences pressed by the user and the 0041 * character strings which should be sent to the terminal and commands 0042 * which should be invoked when those character sequences are pressed. 0043 * 0044 * Konsole supports multiple keyboard translators, allowing the user to 0045 * specify the character sequences which are sent to the terminal 0046 * when particular key sequences are pressed. 0047 * 0048 * A key sequence is defined as a key code, associated keyboard modifiers 0049 * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state 0050 * which the terminal must be in for the key sequence to apply. 0051 */ 0052 class KeyboardTranslator 0053 { 0054 public: 0055 /** 0056 * The meaning of a particular key sequence may depend upon the state which 0057 * the terminal emulation is in. Therefore findEntry() may return a different 0058 * Entry depending upon the state flags supplied. 0059 * 0060 * This enum describes the states which may be associated with with a particular 0061 * entry in the keyboard translation entry. 0062 */ 0063 enum State { 0064 /** Indicates that no special state is active */ 0065 NoState = 0, 0066 /** 0067 * TODO More documentation 0068 */ 0069 NewLineState = 1, 0070 /** 0071 * Indicates that the terminal is in 'Ansi' mode. 0072 * TODO: More documentation 0073 */ 0074 AnsiState = 2, 0075 /** 0076 * TODO More documentation 0077 */ 0078 CursorKeysState = 4, 0079 /** 0080 * Indicates that the alternate screen ( typically used by interactive programs 0081 * such as screen or vim ) is active 0082 */ 0083 AlternateScreenState = 8, 0084 /** Indicates that any of the modifier keys is active. */ 0085 AnyModifierState = 16, 0086 /** Indicates that the numpad is in application mode. */ 0087 ApplicationKeypadState = 32 0088 }; 0089 Q_DECLARE_FLAGS(States, State) 0090 0091 /** 0092 * This enum describes commands which are associated with particular key sequences. 0093 */ 0094 enum Command { 0095 /** Indicates that no command is associated with this command sequence */ 0096 NoCommand = 0, 0097 /** TODO Document me */ 0098 SendCommand = 1, 0099 /** Scroll the terminal display up one page */ 0100 ScrollPageUpCommand = 2, 0101 /** Scroll the terminal display down one page */ 0102 ScrollPageDownCommand = 4, 0103 /** Scroll the terminal display up one line */ 0104 ScrollLineUpCommand = 8, 0105 /** Scroll the terminal display down one line */ 0106 ScrollLineDownCommand = 16, 0107 /** Toggles scroll lock mode */ 0108 ScrollLockCommand = 32, 0109 /** Scroll the terminal display up to the start of history */ 0110 ScrollUpToTopCommand = 64, 0111 /** Scroll the terminal display down to the end of history */ 0112 ScrollDownToBottomCommand = 128, 0113 /** Echos the operating system specific erase character. */ 0114 EraseCommand = 256 0115 }; 0116 Q_DECLARE_FLAGS(Commands, Command) 0117 0118 /** 0119 * Represents an association between a key sequence pressed by the user 0120 * and the character sequence and commands associated with it for a particular 0121 * KeyboardTranslator. 0122 */ 0123 class Entry 0124 { 0125 public: 0126 /** 0127 * Constructs a new entry for a keyboard translator. 0128 */ 0129 Entry(); 0130 0131 /** 0132 * Returns true if this entry is null. 0133 * This is true for newly constructed entries which have no properties set. 0134 */ 0135 bool isNull() const; 0136 0137 /** Returns the commands associated with this entry */ 0138 Command command() const; 0139 /** Sets the command associated with this entry. */ 0140 void setCommand(Command command); 0141 0142 /** 0143 * Returns the character sequence associated with this entry, optionally replacing 0144 * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed. 0145 * 0146 * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code. 0147 * Document them. 0148 * 0149 * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in 0150 * the entry should be replaced with a number to indicate the modifier keys being pressed. 0151 * 0152 * @param modifiers The keyboard modifiers being pressed. 0153 */ 0154 QByteArray text(bool expandWildCards = false, Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; 0155 0156 /** Sets the character sequence associated with this entry */ 0157 void setText(const QByteArray &text); 0158 0159 /** 0160 * Returns the character sequence associated with this entry, 0161 * with any non-printable characters replaced with escape sequences. 0162 * 0163 * eg. \\E for Escape, \\t for tab, \\n for new line. 0164 * 0165 * @param expandWildCards See text() 0166 * @param modifiers See text() 0167 */ 0168 QByteArray escapedText(bool expandWildCards = false, Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; 0169 0170 /** Returns the character code ( from the Qt::Key enum ) associated with this entry */ 0171 int keyCode() const; 0172 /** Sets the character code associated with this entry */ 0173 void setKeyCode(int keyCode); 0174 0175 /** 0176 * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry. 0177 * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry 0178 * only matches when that modifier is NOT pressed. 0179 * 0180 * If a modifier is not set in modifierMask() then the entry matches whether the modifier 0181 * is pressed or not. 0182 */ 0183 Qt::KeyboardModifiers modifiers() const; 0184 0185 /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */ 0186 Qt::KeyboardModifiers modifierMask() const; 0187 0188 /** See modifiers() */ 0189 void setModifiers(Qt::KeyboardModifiers modifiers); 0190 /** See modifierMask() and modifiers() */ 0191 void setModifierMask(Qt::KeyboardModifiers modifiers); 0192 0193 /** 0194 * Returns a bitwise-OR of the enabled state flags associated with this entry. 0195 * If flag is set in stateMask() but not in state(), this means that the entry only 0196 * matches when the terminal is NOT in that state. 0197 * 0198 * If a state is not set in stateMask() then the entry matches whether the terminal 0199 * is in that state or not. 0200 */ 0201 States state() const; 0202 0203 /** Returns the state flags which are valid in this entry. See state() */ 0204 States stateMask() const; 0205 0206 /** See state() */ 0207 void setState(States state); 0208 /** See stateMask() */ 0209 void setStateMask(States mask); 0210 0211 /** 0212 * Returns the key code and modifiers associated with this entry 0213 * as a QKeySequence 0214 */ 0215 // QKeySequence keySequence() const; 0216 0217 /** 0218 * Returns this entry's conditions ( ie. its key code, modifier and state criteria ) 0219 * as a string. 0220 */ 0221 QString conditionToString() const; 0222 0223 /** 0224 * Returns this entry's result ( ie. its command or character sequence ) 0225 * as a string. 0226 * 0227 * @param expandWildCards See text() 0228 * @param modifiers See text() 0229 */ 0230 QString resultToString(bool expandWildCards = false, Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; 0231 0232 /** 0233 * Returns true if this entry matches the given key sequence, specified 0234 * as a combination of @p keyCode , @p modifiers and @p state. 0235 */ 0236 bool matches(int keyCode, Qt::KeyboardModifiers modifiers, States flags) const; 0237 0238 bool operator==(const Entry &rhs) const; 0239 0240 private: 0241 void insertModifier(QString &item, int modifier) const; 0242 void insertState(QString &item, int state) const; 0243 QByteArray unescape(const QByteArray &text) const; 0244 0245 int _keyCode; 0246 Qt::KeyboardModifiers _modifiers; 0247 Qt::KeyboardModifiers _modifierMask; 0248 States _state; 0249 States _stateMask; 0250 0251 Command _command; 0252 QByteArray _text; 0253 }; 0254 0255 /** Constructs a new keyboard translator with the given @p name */ 0256 KeyboardTranslator(const QString &name); 0257 0258 // KeyboardTranslator(const KeyboardTranslator& other); 0259 0260 /** Returns the name of this keyboard translator */ 0261 QString name() const; 0262 0263 /** Sets the name of this keyboard translator */ 0264 void setName(const QString &name); 0265 0266 /** Returns the descriptive name of this keyboard translator */ 0267 QString description() const; 0268 0269 /** Sets the descriptive name of this keyboard translator */ 0270 void setDescription(const QString &description); 0271 0272 /** 0273 * Looks for an entry in this keyboard translator which matches the given 0274 * key code, keyboard modifiers and state flags. 0275 * 0276 * Returns the matching entry if found or a null Entry otherwise ( ie. 0277 * entry.isNull() will return true ) 0278 * 0279 * @param keyCode A key code from the Qt::Key enum 0280 * @param modifiers A combination of modifiers 0281 * @param state Optional flags which specify the current state of the terminal 0282 */ 0283 Entry findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state = NoState) const; 0284 0285 /** 0286 * Adds an entry to this keyboard translator's table. Entries can be looked up according 0287 * to their key sequence using findEntry() 0288 */ 0289 void addEntry(const Entry &entry); 0290 0291 /** 0292 * Replaces an entry in the translator. If the @p existing entry is null, 0293 * then this is equivalent to calling addEntry(@p replacement) 0294 */ 0295 void replaceEntry(const Entry &existing, const Entry &replacement); 0296 0297 /** 0298 * Removes an entry from the table. 0299 */ 0300 void removeEntry(const Entry &entry); 0301 0302 /** Returns a list of all entries in the translator. */ 0303 QList<Entry> entries() const; 0304 0305 /** The modifier code for the actual Ctrl key on this OS. */ 0306 static const Qt::KeyboardModifier CTRL_MOD; 0307 0308 private: 0309 QMultiHash<int, Entry> _entries; // entries in this keyboard translation, 0310 // entries are indexed according to 0311 // their keycode 0312 QString _name; 0313 QString _description; 0314 }; 0315 Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States) 0316 Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands) 0317 0318 /** 0319 * Parses the contents of a Keyboard Translator (.keytab) file and 0320 * returns the entries found in it. 0321 * 0322 * Usage example: 0323 * 0324 * @code 0325 * QFile source( "/path/to/keytab" ); 0326 * source.open( QIODevice::ReadOnly ); 0327 * 0328 * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" ); 0329 * 0330 * KeyboardTranslatorReader reader(source); 0331 * while ( reader.hasNextEntry() ) 0332 * translator->addEntry(reader.nextEntry()); 0333 * 0334 * source.close(); 0335 * 0336 * if ( !reader.parseError() ) 0337 * { 0338 * // parsing succeeded, do something with the translator 0339 * } 0340 * else 0341 * { 0342 * // parsing failed 0343 * } 0344 * @endcode 0345 */ 0346 class KeyboardTranslatorReader 0347 { 0348 public: 0349 /** Constructs a new reader which parses the given @p source */ 0350 KeyboardTranslatorReader(QIODevice *source); 0351 0352 /** 0353 * Returns the description text. 0354 * TODO: More documentation 0355 */ 0356 QString description() const; 0357 0358 /** Returns true if there is another entry in the source stream */ 0359 bool hasNextEntry() const; 0360 /** Returns the next entry found in the source stream */ 0361 KeyboardTranslator::Entry nextEntry(); 0362 0363 /** 0364 * Returns true if an error occurred whilst parsing the input or 0365 * false if no error occurred. 0366 */ 0367 bool parseError(); 0368 0369 /** 0370 * Parses a condition and result string for a translator entry 0371 * and produces a keyboard translator entry. 0372 * 0373 * The condition and result strings are in the same format as in 0374 */ 0375 static KeyboardTranslator::Entry createEntry(const QString &condition, const QString &result); 0376 0377 private: 0378 struct Token { 0379 enum Type { TitleKeyword, TitleText, KeyKeyword, KeySequence, Command, OutputText }; 0380 Type type; 0381 QString text; 0382 }; 0383 QList<Token> tokenize(const QString &); 0384 void readNext(); 0385 bool decodeSequence(const QString &, 0386 int &keyCode, 0387 Qt::KeyboardModifiers &modifiers, 0388 Qt::KeyboardModifiers &modifierMask, 0389 KeyboardTranslator::States &state, 0390 KeyboardTranslator::States &stateFlags); 0391 0392 static bool parseAsModifier(const QString &item, Qt::KeyboardModifier &modifier); 0393 static bool parseAsStateFlag(const QString &item, KeyboardTranslator::State &state); 0394 static bool parseAsKeyCode(const QString &item, int &keyCode); 0395 static bool parseAsCommand(const QString &text, KeyboardTranslator::Command &command); 0396 0397 QIODevice *_source; 0398 QString _description; 0399 KeyboardTranslator::Entry _nextEntry; 0400 bool _hasNext; 0401 }; 0402 0403 /** Writes a keyboard translation to disk. */ 0404 class KeyboardTranslatorWriter 0405 { 0406 public: 0407 /** 0408 * Constructs a new writer which saves data into @p destination. 0409 * The caller is responsible for closing the device when writing is complete. 0410 */ 0411 KeyboardTranslatorWriter(QIODevice *destination); 0412 ~KeyboardTranslatorWriter(); 0413 0414 /** 0415 * Writes the header for the keyboard translator. 0416 * @param description Description of the keyboard translator. 0417 */ 0418 void writeHeader(const QString &description); 0419 /** Writes a translator entry. */ 0420 void writeEntry(const KeyboardTranslator::Entry &entry); 0421 0422 private: 0423 QIODevice *_destination; 0424 QTextStream *_writer; 0425 }; 0426 0427 /** 0428 * Manages the keyboard translations available for use by terminal sessions, 0429 * see KeyboardTranslator. 0430 */ 0431 class KONSOLEPRIVATE_EXPORT KeyboardTranslatorManager 0432 { 0433 public: 0434 /** 0435 * Constructs a new KeyboardTranslatorManager and loads the list of 0436 * available keyboard translations. 0437 * 0438 * The keyboard translations themselves are not loaded until they are 0439 * first requested via a call to findTranslator() 0440 */ 0441 KeyboardTranslatorManager(); 0442 ~KeyboardTranslatorManager(); 0443 0444 KeyboardTranslatorManager(const KeyboardTranslatorManager &) = delete; 0445 KeyboardTranslatorManager &operator=(const KeyboardTranslatorManager &) = delete; 0446 0447 /** 0448 * Adds a new translator. If a translator with the same name 0449 * already exists, it will be replaced by the new translator. 0450 * 0451 * TODO: More documentation. 0452 */ 0453 void addTranslator(KeyboardTranslator *translator); 0454 0455 /** 0456 * Deletes a translator. Returns true on successful deletion or false otherwise. 0457 * 0458 * TODO: More documentation 0459 */ 0460 bool deleteTranslator(const QString &name); 0461 0462 /** Returns the default translator for Konsole. */ 0463 const KeyboardTranslator *defaultTranslator(); 0464 0465 /** 0466 * Returns the keyboard translator with the given name or 0 if no translator 0467 * with that name exists. 0468 * 0469 * The first time that a translator with a particular name is requested, 0470 * the on-disk .keyboard file is loaded and parsed. 0471 */ 0472 const KeyboardTranslator *findTranslator(const QString &name); 0473 /** 0474 * Returns a list of the names of available keyboard translators. 0475 * 0476 * The first time this is called, a search for available 0477 * translators is started. 0478 */ 0479 QList<QString> allTranslators(); 0480 0481 /** Returns the global KeyboardTranslatorManager instance. */ 0482 static KeyboardTranslatorManager *instance(); 0483 0484 private: 0485 static const QByteArray defaultTranslatorText; 0486 0487 void findTranslators(); // locate the available translators 0488 KeyboardTranslator *loadTranslator(const QString &name); // loads the translator 0489 // with the given name 0490 KeyboardTranslator *loadTranslator(QIODevice *device, const QString &name); 0491 0492 bool saveTranslator(const KeyboardTranslator *translator); 0493 QString findTranslatorPath(const QString &name); 0494 0495 QHash<QString, KeyboardTranslator *> _translators; // maps translator-name -> KeyboardTranslator 0496 // instance 0497 bool _haveLoadedAll; 0498 }; 0499 0500 inline int KeyboardTranslator::Entry::keyCode() const 0501 { 0502 return _keyCode; 0503 } 0504 inline void KeyboardTranslator::Entry::setKeyCode(int keyCode) 0505 { 0506 _keyCode = keyCode; 0507 } 0508 0509 inline void KeyboardTranslator::Entry::setModifiers(Qt::KeyboardModifiers modifier) 0510 { 0511 _modifiers = modifier; 0512 } 0513 inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const 0514 { 0515 return _modifiers; 0516 } 0517 0518 inline void KeyboardTranslator::Entry::setModifierMask(Qt::KeyboardModifiers mask) 0519 { 0520 _modifierMask = mask; 0521 } 0522 inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const 0523 { 0524 return _modifierMask; 0525 } 0526 0527 inline bool KeyboardTranslator::Entry::isNull() const 0528 { 0529 return (*this == Entry()); 0530 } 0531 0532 inline void KeyboardTranslator::Entry::setCommand(Command command) 0533 { 0534 _command = command; 0535 } 0536 inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const 0537 { 0538 return _command; 0539 } 0540 0541 inline void KeyboardTranslator::Entry::setText(const QByteArray &text) 0542 { 0543 _text = unescape(text); 0544 } 0545 inline int oneOrZero(int value) 0546 { 0547 return value ? 1 : 0; 0548 } 0549 inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards, Qt::KeyboardModifiers modifiers) const 0550 { 0551 QByteArray expandedText = _text; 0552 0553 if (expandWildCards) { 0554 int modifierValue = 1; 0555 modifierValue += oneOrZero(modifiers & Qt::ShiftModifier); 0556 modifierValue += oneOrZero(modifiers & Qt::AltModifier) << 1; 0557 modifierValue += oneOrZero(modifiers & KeyboardTranslator::CTRL_MOD) << 2; 0558 0559 for (int i = 0; i < _text.length(); i++) { 0560 if (expandedText[i] == '*') 0561 expandedText[i] = '0' + modifierValue; 0562 } 0563 } 0564 0565 return expandedText; 0566 } 0567 0568 inline void KeyboardTranslator::Entry::setState(States state) 0569 { 0570 _state = state; 0571 } 0572 inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const 0573 { 0574 return _state; 0575 } 0576 0577 inline void KeyboardTranslator::Entry::setStateMask(States stateMask) 0578 { 0579 _stateMask = stateMask; 0580 } 0581 inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const 0582 { 0583 return _stateMask; 0584 } 0585 } 0586 0587 Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry) 0588 Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator *) 0589 0590 #endif // KEYBOARDTRANSLATOR_H