MauiKit Terminal

KeyboardTranslator.cpp
1/*
2 This source file is part of Konsole, a terminal emulator.
3
4 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 02110-1301 USA.
17*/
18
19// Own
20#include "KeyboardTranslator.h"
21
22// System
23#include <cctype>
24#include <cstdio>
25
26// Qt
27#include <QBuffer>
28#include <QDir>
29#include <QFile>
30#include <QFileInfo>
31#include <QKeySequence>
32#include <QRegExp>
33#include <QTextStream>
34#include <QtDebug>
35
36#include "tools.h"
37
38// KDE
39// #include <KDebug>
40// #include <KLocale>
41// #include <KStandardDirs>
42
43using namespace Konsole;
44
45const QByteArray KeyboardTranslatorManager::defaultTranslatorText(
46 "keyboard \"Fallback Key Translator\"\n"
47 "key Tab : \"\\t\"");
48
49#ifdef Q_OS_MAC
50// On Mac, Qt::ControlModifier means Cmd, and MetaModifier means Ctrl
52#else
54#endif
55
57 : _haveLoadedAll(false)
58{
59}
60
61KeyboardTranslatorManager::~KeyboardTranslatorManager()
62{
63 qDeleteAll(_translators);
64}
65
66QString KeyboardTranslatorManager::findTranslatorPath(const QString &name)
67{
68 return QString(kbLayoutDir() + name + QLatin1String(".keytab"));
69 // return KGlobal::dirs()->findResource("data","konsole/"+name+".keytab");
70}
71
72void KeyboardTranslatorManager::findTranslators()
73{
74 QDir dir(kbLayoutDir());
75 QStringList filters;
76 filters << QLatin1String("*.keytab");
77 dir.setNameFilters(filters);
78 QStringList list = dir.entryList(filters);
79 // QStringList list = KGlobal::dirs()->findAllResources("data",
80 // "konsole/*.keytab",
81 // KStandardDirs::NoDuplicates);
82
83 // add the name of each translator to the list and associated
84 // the name with a null pointer to indicate that the translator
85 // has not yet been loaded from disk
86 QStringListIterator listIter(list);
87 while (listIter.hasNext()) {
88 QString translatorPath = listIter.next();
89
90 QString name = QFileInfo(translatorPath).baseName();
91
92 if (!_translators.contains(name))
93 _translators.insert(name, nullptr);
94 }
95
96 _haveLoadedAll = true;
97}
98
100{
101 if (name.isEmpty())
102 return defaultTranslator();
103
104 if (_translators.contains(name) && _translators[name] != nullptr)
105 return _translators[name];
106
107 KeyboardTranslator *translator = loadTranslator(name);
108
109 if (translator != nullptr)
110 _translators[name] = translator;
111 else if (!name.isEmpty())
112 qDebug() << "Unable to load translator" << name;
113
114 return translator;
115}
116
117bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator *translator)
118{
119 qDebug() << "KeyboardTranslatorManager::saveTranslator"
120 << "unimplemented";
121 Q_UNUSED(translator);
122#if 0
123 const QString path = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name()
124 +".keytab";
125
126 //kDebug() << "Saving translator to" << path;
127
128 QFile destination(path);
129 if (!destination.open(QIODevice::WriteOnly | QIODevice::Text))
130 {
131 qDebug() << "Unable to save keyboard translation:"
132 << destination.errorString();
133 return false;
134 }
135
136 {
137 KeyboardTranslatorWriter writer(&destination);
138 writer.writeHeader(translator->description());
139
141 while ( iter.hasNext() )
142 writer.writeEntry(iter.next());
143 }
144
145 destination.close();
146#endif
147 return true;
148}
149
150KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(const QString &name)
151{
152 const QString &path = findTranslatorPath(name);
153
154 QFile source(path);
155 if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text))
156 return nullptr;
157
158 return loadTranslator(&source, name);
159}
160
162{
163 // Try to find the default.keytab file if it exists, otherwise
164 // fall back to the hard-coded one
165 const KeyboardTranslator *translator = findTranslator(QLatin1String("default"));
166 if (!translator) {
167 QBuffer textBuffer;
168 textBuffer.setData(defaultTranslatorText);
169 textBuffer.open(QIODevice::ReadOnly);
170 translator = loadTranslator(&textBuffer, QLatin1String("fallback"));
171 }
172 return translator;
173}
174
175KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(QIODevice *source, const QString &name)
176{
177 KeyboardTranslator *translator = new KeyboardTranslator(name);
178 KeyboardTranslatorReader reader(source);
179 translator->setDescription(reader.description());
180 while (reader.hasNextEntry())
181 translator->addEntry(reader.nextEntry());
182
183 source->close();
184
185 if (!reader.parseError()) {
186 return translator;
187 } else {
188 delete translator;
189 return nullptr;
190 }
191}
192
194 : _destination(destination)
195{
196 Q_ASSERT(destination && destination->isWritable());
197
198 _writer = new QTextStream(_destination);
199}
200KeyboardTranslatorWriter::~KeyboardTranslatorWriter()
201{
202 delete _writer;
203}
205{
206 *_writer << "keyboard \"" << description << '\"' << '\n';
207}
209{
210 QString result;
212 result = entry.resultToString();
213 else
214 result = QLatin1Char('\"') + entry.resultToString() + QLatin1Char('\"');
215
216 *_writer << QLatin1String("key ") << entry.conditionToString() << QLatin1String(" : ") << result << QLatin1Char('\n');
217}
218
219// each line of the keyboard translation file is one of:
220//
221// - keyboard "name"
222// - key KeySequence : "characters"
223// - key KeySequence : CommandName
224//
225// KeySequence begins with the name of the key ( taken from the Qt::Key enum )
226// and is followed by the keyboard modifiers and state flags ( with + or - in front
227// of each modifier or flag to indicate whether it is required ). All keyboard modifiers
228// and flags are optional, if a particular modifier or state is not specified it is
229// assumed not to be a part of the sequence. The key sequence may contain whitespace
230//
231// eg: "key Up+Shift : scrollLineUp"
232// "key Next-Shift : "\E[6~"
233//
234// (lines containing only whitespace are ignored, parseLine assumes that comments have
235// already been removed)
236//
237
239 : _source(source)
240 , _hasNext(false)
241{
242 // read input until we find the description
243 while (_description.isEmpty() && !source->atEnd()) {
244 QList<Token> tokens = tokenize(QString::fromUtf8(source->readLine()));
245 if (!tokens.isEmpty() && tokens.first().type == Token::TitleKeyword)
246 _description = tokens[1].text;
247 }
248 // read first entry (if any)
249 readNext();
250}
251void KeyboardTranslatorReader::readNext()
252{
253 // find next entry
254 while (!_source->atEnd()) {
255 const QList<Token> &tokens = tokenize(QString::fromUtf8(_source->readLine()));
256 if (!tokens.isEmpty() && tokens.first().type == Token::KeyKeyword) {
261
262 int keyCode = Qt::Key_unknown;
263
264 decodeSequence(tokens[1].text.toLower(), keyCode, modifiers, modifierMask, flags, flagMask);
265
267 QByteArray text;
268
269 // get text or command
270 if (tokens[2].type == Token::OutputText) {
271 text = tokens[2].text.toLocal8Bit();
272 } else if (tokens[2].type == Token::Command) {
273 // identify command
274 if (!parseAsCommand(tokens[2].text, command))
275 qDebug() << "Command" << tokens[2].text << "not understood.";
276 }
277
279 newEntry.setKeyCode(keyCode);
280 newEntry.setState(flags);
281 newEntry.setStateMask(flagMask);
282 newEntry.setModifiers(modifiers);
283 newEntry.setModifierMask(modifierMask);
284 newEntry.setText(text);
285 newEntry.setCommand(command);
286
287 _nextEntry = newEntry;
288
289 _hasNext = true;
290
291 return;
292 }
293 }
294
295 _hasNext = false;
296}
297
298bool KeyboardTranslatorReader::parseAsCommand(const QString &text, KeyboardTranslator::Command &command)
299{
300 if (text.compare(QLatin1String("erase"), Qt::CaseInsensitive) == 0)
302 else if (text.compare(QLatin1String("scrollpageup"), Qt::CaseInsensitive) == 0)
304 else if (text.compare(QLatin1String("scrollpagedown"), Qt::CaseInsensitive) == 0)
306 else if (text.compare(QLatin1String("scrolllineup"), Qt::CaseInsensitive) == 0)
308 else if (text.compare(QLatin1String("scrolllinedown"), Qt::CaseInsensitive) == 0)
310 else if (text.compare(QLatin1String("scrolllock"), Qt::CaseInsensitive) == 0)
312 else if (text.compare(QLatin1String("scrolluptotop"), Qt::CaseInsensitive) == 0)
314 else if (text.compare(QLatin1String("scrolldowntobottom"), Qt::CaseInsensitive) == 0)
316 else
317 return false;
318
319 return true;
320}
321
322bool KeyboardTranslatorReader::decodeSequence(const QString &text,
323 int &keyCode,
324 Qt::KeyboardModifiers &modifiers,
325 Qt::KeyboardModifiers &modifierMask,
328{
329 bool isWanted = true;
330 bool endOfItem = false;
331 QString buffer;
332
333 Qt::KeyboardModifiers tempModifiers = modifiers;
334 Qt::KeyboardModifiers tempModifierMask = modifierMask;
335 KeyboardTranslator::States tempFlags = flags;
336 KeyboardTranslator::States tempFlagMask = flagMask;
337
338 for (int i = 0; i < text.size(); i++) {
339 const QChar &ch = text[i];
340 bool isFirstLetter = i == 0;
341 bool isLastLetter = (i == text.size() - 1);
342 endOfItem = true;
343 if (ch.isLetterOrNumber()) {
344 endOfItem = false;
345 buffer.append(ch);
346 } else if (isFirstLetter) {
347 buffer.append(ch);
348 }
349
350 if ((endOfItem || isLastLetter) && !buffer.isEmpty()) {
352 int itemKeyCode = 0;
354
355 if (parseAsModifier(buffer, itemModifier)) {
356 tempModifierMask |= itemModifier;
357
358 if (isWanted)
359 tempModifiers |= itemModifier;
360 } else if (parseAsStateFlag(buffer, itemFlag)) {
361 tempFlagMask |= itemFlag;
362
363 if (isWanted)
364 tempFlags |= itemFlag;
365 } else if (parseAsKeyCode(buffer, itemKeyCode))
366 keyCode = itemKeyCode;
367 else
368 qDebug() << "Unable to parse key binding item:" << buffer;
369
370 buffer.clear();
371 }
372
373 // check if this is a wanted / not-wanted flag and update the
374 // state ready for the next item
375 if (ch == QLatin1Char('+'))
376 isWanted = true;
377 else if (ch == QLatin1Char('-'))
378 isWanted = false;
379 }
380
381 modifiers = tempModifiers;
382 modifierMask = tempModifierMask;
383 flags = tempFlags;
384 flagMask = tempFlagMask;
385
386 return true;
387}
388
389bool KeyboardTranslatorReader::parseAsModifier(const QString &item, Qt::KeyboardModifier &modifier)
390{
391 if (item == QLatin1String("shift"))
392 modifier = Qt::ShiftModifier;
393 else if (item == QLatin1String("ctrl") || item == QLatin1String("control"))
394 modifier = Qt::ControlModifier;
395 else if (item == QLatin1String("alt"))
396 modifier = Qt::AltModifier;
397 else if (item == QLatin1String("meta"))
398 modifier = Qt::MetaModifier;
399 else if (item == QLatin1String("keypad"))
400 modifier = Qt::KeypadModifier;
401 else
402 return false;
403
404 return true;
405}
406bool KeyboardTranslatorReader::parseAsStateFlag(const QString &item, KeyboardTranslator::State &flag)
407{
408 if (item == QLatin1String("appcukeys") || item == QLatin1String("appcursorkeys"))
410 else if (item == QLatin1String("ansi"))
412 else if (item == QLatin1String("newline"))
414 else if (item == QLatin1String("appscreen"))
416 else if (item == QLatin1String("anymod") || item == QLatin1String("anymodifier"))
418 else if (item == QLatin1String("appkeypad"))
420 else
421 return false;
422
423 return true;
424}
425bool KeyboardTranslatorReader::parseAsKeyCode(const QString &item, int &keyCode)
426{
427 QKeySequence sequence = QKeySequence::fromString(item);
428 if (!sequence.isEmpty()) {
429#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
430 keyCode = sequence[0];
431#else
432 keyCode = sequence[0].toCombined();
433#endif
434
435 if (sequence.count() > 1) {
436 qDebug() << "Unhandled key codes in sequence: " << item;
437 }
438 }
439 // additional cases implemented for backwards compatibility with KDE 3
440 else if (item == QLatin1String("prior"))
441 keyCode = Qt::Key_PageUp;
442 else if (item == QLatin1String("next"))
443 keyCode = Qt::Key_PageDown;
444 else
445 return false;
446
447 return true;
448}
449
451{
452 return _description;
453}
455{
456 return _hasNext;
457}
459{
460 QString entryString = QString::fromLatin1("keyboard \"temporary\"\nkey ");
461 entryString.append(condition);
462 entryString.append(QLatin1String(" : "));
463
464 // if 'result' is the name of a command then the entry result will be that command,
465 // otherwise the result will be treated as a string to echo when the key sequence
466 // specified by 'condition' is pressed
468 if (parseAsCommand(result, command))
469 entryString.append(result);
470 else
471 entryString.append(QLatin1Char('\"') + result + QLatin1Char('\"'));
472
473 QByteArray array = entryString.toUtf8();
474 QBuffer buffer(&array);
476 KeyboardTranslatorReader reader(&buffer);
477
479 if (reader.hasNextEntry())
480 entry = reader.nextEntry();
481
482 return entry;
483}
484
486{
487 Q_ASSERT(_hasNext);
488 KeyboardTranslator::Entry entry = _nextEntry;
489 readNext();
490 return entry;
491}
493{
494 return false;
495}
496QList<KeyboardTranslatorReader::Token> KeyboardTranslatorReader::tokenize(const QString &line)
497{
498 QString text = line;
499
500 // remove comments
501 bool inQuotes = false;
502 int commentPos = -1;
503 for (int i = text.length() - 1; i >= 0; i--) {
504 QChar ch = text[i];
505 if (ch == QLatin1Char('\"'))
506 inQuotes = !inQuotes;
507 else if (ch == QLatin1Char('#') && !inQuotes)
508 commentPos = i;
509 }
510 if (commentPos != -1)
511 text.remove(commentPos, text.length());
512
513 text = text.simplified();
514
515 // title line: keyboard "title"
516 static QRegExp title(QLatin1String("keyboard\\s+\"(.*)\""));
517 // key line: key KeySequence : "output"
518 // key line: key KeySequence : command
519 static QRegExp key(QLatin1String("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)"));
520
521 QList<Token> list;
522 if (text.isEmpty()) {
523 return list;
524 }
525
526 if (title.exactMatch(text)) {
527 Token titleToken = {Token::TitleKeyword, QString()};
528 Token textToken = {Token::TitleText, title.capturedTexts().at(1)};
529
530 list << titleToken << textToken;
531 } else if (key.exactMatch(text)) {
532 Token keyToken = {Token::KeyKeyword, QString()};
533 Token sequenceToken = {Token::KeySequence, key.capturedTexts().value(1).remove(QLatin1Char(' '))};
534
535 list << keyToken << sequenceToken;
536
537 if (key.capturedTexts().at(3).isEmpty()) {
538 // capturedTexts()[2] is a command
539 Token commandToken = {Token::Command, key.capturedTexts().at(2)};
540 list << commandToken;
541 } else {
542 // capturedTexts()[3] is the output string
543 Token outputToken = {Token::OutputText, key.capturedTexts().at(3)};
544 list << outputToken;
545 }
546 } else {
547 qDebug() << "Line in keyboard translator file could not be understood:" << text;
548 }
549
550 return list;
551}
552
554{
555 if (!_haveLoadedAll) {
556 findTranslators();
557 }
558
559 return _translators.keys();
560}
561
563 : _keyCode(0)
564 , _modifiers(Qt::NoModifier)
565 , _modifierMask(Qt::NoModifier)
566 , _state(NoState)
567 , _stateMask(NoState)
568 , _command(NoCommand)
569{
570}
571
572bool KeyboardTranslator::Entry::operator==(const Entry &rhs) const
573{
574 return _keyCode == rhs._keyCode && _modifiers == rhs._modifiers && _modifierMask == rhs._modifierMask && _state == rhs._state
575 && _stateMask == rhs._stateMask && _command == rhs._command && _text == rhs._text;
576}
577
578bool KeyboardTranslator::Entry::matches(int keyCode, Qt::KeyboardModifiers modifiers, States testState) const
579{
580#ifdef Q_OS_MAC
581 // On Mac, arrow keys are considered part of keypad. Ignore that.
582 modifiers &= ~Qt::KeypadModifier;
583#endif
584
585 if (_keyCode != keyCode)
586 return false;
587
588 if ((modifiers & _modifierMask) != (_modifiers & _modifierMask))
589 return false;
590
591 // if modifiers is non-zero, the 'any modifier' state is implicit
592 if ((modifiers & ~Qt::KeypadModifier) != 0)
593 testState |= AnyModifierState;
594
595 if ((testState & _stateMask) != (_state & _stateMask))
596 return false;
597
598 // special handling for the 'Any Modifier' state, which checks for the presence of
599 // any or no modifiers. In this context, the 'keypad' modifier does not count.
600 bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier;
601 bool wantAnyModifier = _state & KeyboardTranslator::AnyModifierState;
602 if (_stateMask & KeyboardTranslator::AnyModifierState) {
603 if (wantAnyModifier != anyModifiersSet)
604 return false;
605 }
606
607 return true;
608}
610{
611 QByteArray result(text(expandWildCards, modifiers));
612
613 for (int i = 0; i < result.size(); i++) {
614 char ch = result[i];
615 char replacement = 0;
616
617 switch (ch) {
618 case 27:
619 replacement = 'E';
620 break;
621 case 8:
622 replacement = 'b';
623 break;
624 case 12:
625 replacement = 'f';
626 break;
627 case 9:
628 replacement = 't';
629 break;
630 case 13:
631 replacement = 'r';
632 break;
633 case 10:
634 replacement = 'n';
635 break;
636 default:
637 // any character which is not printable is replaced by an equivalent
638 // \xhh escape sequence (where 'hh' are the corresponding hex digits)
639 if (!QChar(QLatin1Char(ch)).isPrint())
640 replacement = 'x';
641 }
642
643 if (replacement == 'x') {
644 result.replace(i, 1, QByteArray("\\x" + QByteArray(1, ch).toHex()));
645 } else if (replacement != 0) {
646 result.remove(i, 1);
647 result.insert(i, '\\');
648 result.insert(i + 1, replacement);
649 }
650 }
651
652 return result;
653}
654QByteArray KeyboardTranslator::Entry::unescape(const QByteArray &input) const
655{
656 QByteArray result(input);
657
658 for (int i = 0; i < result.size() - 1; i++) {
659 auto ch = result[i];
660 if (ch == '\\') {
661 char replacement[2] = {0, 0};
662 int charsToRemove = 2;
663 bool escapedChar = true;
664
665 switch (result[i + 1]) {
666 case 'E':
667 replacement[0] = 27;
668 break;
669 case 'b':
670 replacement[0] = 8;
671 break;
672 case 'f':
673 replacement[0] = 12;
674 break;
675 case 't':
676 replacement[0] = 9;
677 break;
678 case 'r':
679 replacement[0] = 13;
680 break;
681 case 'n':
682 replacement[0] = 10;
683 break;
684 case 'x': {
685 // format is \xh or \xhh where 'h' is a hexadecimal
686 // digit from 0-9 or A-F which should be replaced
687 // with the corresponding character value
688 char hexDigits[3] = {0};
689
690 if ((i < result.size() - 2) && isxdigit(result[i + 2]))
691 hexDigits[0] = result[i + 2];
692 if ((i < result.size() - 3) && isxdigit(result[i + 3]))
693 hexDigits[1] = result[i + 3];
694
695 unsigned charValue = 0;
696 sscanf(hexDigits, "%x", &charValue);
697
698 replacement[0] = (char)charValue;
699 charsToRemove = 2 + strlen(hexDigits);
700 } break;
701 default:
702 escapedChar = false;
703 }
704
705 if (escapedChar)
706 result.replace(i, charsToRemove, replacement, 1);
707 }
708 }
709
710 return result;
711}
712
713void KeyboardTranslator::Entry::insertModifier(QString &item, int modifier) const
714{
715 if (!(modifier & _modifierMask))
716 return;
717
718 if (modifier & _modifiers)
719 item += QLatin1Char('+');
720 else
721 item += QLatin1Char('-');
722
723 if (modifier == Qt::ShiftModifier)
724 item += QLatin1String("Shift");
725 else if (modifier == Qt::ControlModifier)
726 item += QLatin1String("Ctrl");
727 else if (modifier == Qt::AltModifier)
728 item += QLatin1String("Alt");
729 else if (modifier == Qt::MetaModifier)
730 item += QLatin1String("Meta");
731 else if (modifier == Qt::KeypadModifier)
732 item += QLatin1String("KeyPad");
733}
734void KeyboardTranslator::Entry::insertState(QString &item, int state) const
735{
736 if (!(state & _stateMask))
737 return;
738
739 if (state & _state)
740 item += QLatin1Char('+');
741 else
742 item += QLatin1Char('-');
743
745 item += QLatin1String("AppScreen");
746 else if (state == KeyboardTranslator::NewLineState)
747 item += QLatin1String("NewLine");
748 else if (state == KeyboardTranslator::AnsiState)
749 item += QLatin1String("Ansi");
750 else if (state == KeyboardTranslator::CursorKeysState)
751 item += QLatin1String("AppCursorKeys");
752 else if (state == KeyboardTranslator::AnyModifierState)
753 item += QLatin1String("AnyModifier");
755 item += QLatin1String("AppKeypad");
756}
758{
759 if (!_text.isEmpty())
760 return QString::fromLatin1(escapedText(expandWildCards, modifiers));
761 else if (_command == EraseCommand)
762 return QLatin1String("Erase");
763 else if (_command == ScrollPageUpCommand)
764 return QLatin1String("ScrollPageUp");
765 else if (_command == ScrollPageDownCommand)
766 return QLatin1String("ScrollPageDown");
767 else if (_command == ScrollLineUpCommand)
768 return QLatin1String("ScrollLineUp");
769 else if (_command == ScrollLineDownCommand)
770 return QLatin1String("ScrollLineDown");
771 else if (_command == ScrollLockCommand)
772 return QLatin1String("ScrollLock");
773 else if (_command == ScrollUpToTopCommand)
774 return QLatin1String("ScrollUpToTop");
775 else if (_command == ScrollDownToBottomCommand)
776 return QLatin1String("ScrollDownToBottom");
777
778 return QString();
779}
781{
782 QString result = QKeySequence(_keyCode).toString();
783
784 insertModifier(result, Qt::ShiftModifier);
785 insertModifier(result, Qt::ControlModifier);
786 insertModifier(result, Qt::AltModifier);
787 insertModifier(result, Qt::MetaModifier);
788 insertModifier(result, Qt::KeypadModifier);
789
790 insertState(result, KeyboardTranslator::AlternateScreenState);
791 insertState(result, KeyboardTranslator::NewLineState);
792 insertState(result, KeyboardTranslator::AnsiState);
793 insertState(result, KeyboardTranslator::CursorKeysState);
794 insertState(result, KeyboardTranslator::AnyModifierState);
796
797 return result;
798}
799
804
806{
807 _description = description;
808}
810{
811 return _description;
812}
814{
815 _name = name;
816}
818{
819 return _name;
820}
821
823{
824 return _entries.values();
825}
826
828{
829 const int keyCode = entry.keyCode();
830 _entries.insert(keyCode, entry);
831}
832void KeyboardTranslator::replaceEntry(const Entry &existing, const Entry &replacement)
833{
834 if (!existing.isNull())
835 _entries.remove(existing.keyCode(), existing);
836 _entries.insert(replacement.keyCode(), replacement);
837}
839{
840 _entries.remove(entry.keyCode(), entry);
841}
843{
844 for (auto it = _entries.cbegin(), end = _entries.cend(); it != end; ++it) {
845 if (it.key() == keyCode)
846 if (it.value().matches(keyCode, modifiers, state))
847 return *it;
848 }
849 return Entry(); // entry not found
850}
852{
853 _translators.insert(translator->name(), translator);
854
855 if (!saveTranslator(translator))
856 qDebug() << "Unable to save translator" << translator->name() << "to disk.";
857}
859{
860 Q_ASSERT(_translators.contains(name));
861
862 // locate and delete
863 QString path = findTranslatorPath(name);
864 if (QFile::remove(path)) {
865 _translators.remove(name);
866 return true;
867 } else {
868 qDebug() << "Failed to remove translator - " << path;
869 return false;
870 }
871}
872Q_GLOBAL_STATIC(KeyboardTranslatorManager, theKeyboardTranslatorManager)
874{
875 return theKeyboardTranslatorManager;
876}
Manages the keyboard translations available for use by terminal sessions, see KeyboardTranslator.
KeyboardTranslatorManager()
Constructs a new KeyboardTranslatorManager and loads the list of available keyboard translations.
const KeyboardTranslator * defaultTranslator()
Returns the default translator for Konsole.
QList< QString > allTranslators()
Returns a list of the names of available keyboard translators.
const KeyboardTranslator * findTranslator(const QString &name)
Returns the keyboard translator with the given name or 0 if no translator with that name exists.
void addTranslator(KeyboardTranslator *translator)
Adds a new translator.
bool deleteTranslator(const QString &name)
Deletes a translator.
Parses the contents of a Keyboard Translator (.keytab) file and returns the entries found in it.
static KeyboardTranslator::Entry createEntry(const QString &condition, const QString &result)
Parses a condition and result string for a translator entry and produces a keyboard translator entry.
KeyboardTranslatorReader(QIODevice *source)
Constructs a new reader which parses the given source.
bool parseError()
Returns true if an error occurred whilst parsing the input or false if no error occurred.
bool hasNextEntry() const
Returns true if there is another entry in the source stream.
KeyboardTranslator::Entry nextEntry()
Returns the next entry found in the source stream.
QString description() const
Returns the description text.
Writes a keyboard translation to disk.
void writeHeader(const QString &description)
Writes the header for the keyboard translator.
void writeEntry(const KeyboardTranslator::Entry &entry)
Writes a translator entry.
KeyboardTranslatorWriter(QIODevice *destination)
Constructs a new writer which saves data into destination.
Represents an association between a key sequence pressed by the user and the character sequence and c...
Command command() const
Returns the commands associated with this entry.
void setModifierMask(Qt::KeyboardModifiers modifiers)
See modifierMask() and modifiers()
void setModifiers(Qt::KeyboardModifiers modifiers)
See modifiers()
int keyCode() const
Returns the character code ( from the Qt::Key enum ) associated with this entry.
QString resultToString(bool expandWildCards=false, Qt::KeyboardModifiers modifiers=Qt::NoModifier) const
Returns this entry's result ( ie.
QString conditionToString() const
Returns the key code and modifiers associated with this entry as a QKeySequence.
void setKeyCode(int keyCode)
Sets the character code associated with this entry.
void setCommand(Command command)
Sets the command associated with this entry.
void setStateMask(States mask)
See stateMask()
bool matches(int keyCode, Qt::KeyboardModifiers modifiers, States flags) const
Returns true if this entry matches the given key sequence, specified as a combination of keyCode ,...
QByteArray escapedText(bool expandWildCards=false, Qt::KeyboardModifiers modifiers=Qt::NoModifier) const
Returns the character sequence associated with this entry, with any non-printable characters replaced...
void setState(States state)
See state()
void setText(const QByteArray &text)
Sets the character sequence associated with this entry.
Entry()
Constructs a new entry for a keyboard translator.
bool isNull() const
Returns true if this entry is null.
A convertor which maps between key sequences pressed by the user and the character strings which shou...
QString name() const
Returns the name of this keyboard translator.
Entry findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state=NoState) const
Looks for an entry in this keyboard translator which matches the given key code, keyboard modifiers a...
QString description() const
Returns the descriptive name of this keyboard translator.
static const Qt::KeyboardModifier CTRL_MOD
The modifier code for the actual Ctrl key on this OS.
void replaceEntry(const Entry &existing, const Entry &replacement)
Replaces an entry in the translator.
void setName(const QString &name)
Sets the name of this keyboard translator.
State
The meaning of a particular key sequence may depend upon the state which the terminal emulation is in...
@ AnsiState
Indicates that the terminal is in 'Ansi' mode.
@ NoState
Indicates that no special state is active.
@ AlternateScreenState
Indicates that the alternate screen ( typically used by interactive programs such as screen or vim ) ...
@ AnyModifierState
Indicates that any of the modifier keys is active.
@ NewLineState
TODO More documentation.
@ CursorKeysState
TODO More documentation.
@ ApplicationKeypadState
Indicates that the numpad is in application mode.
QList< Entry > entries() const
Returns a list of all entries in the translator.
Command
This enum describes commands which are associated with particular key sequences.
@ NoCommand
Indicates that no command is associated with this command sequence.
@ EraseCommand
Echos the operating system specific erase character.
@ ScrollPageDownCommand
Scroll the terminal display down one page.
@ ScrollDownToBottomCommand
Scroll the terminal display down to the end of history.
@ ScrollUpToTopCommand
Scroll the terminal display up to the start of history.
@ ScrollLineUpCommand
Scroll the terminal display up one line.
@ ScrollLockCommand
Toggles scroll lock mode.
@ ScrollPageUpCommand
Scroll the terminal display up one page.
@ ScrollLineDownCommand
Scroll the terminal display down one line.
KeyboardTranslator(const QString &name)
Constructs a new keyboard translator with the given name.
void setDescription(const QString &description)
Sets the descriptive name of this keyboard translator.
void addEntry(const Entry &entry)
Adds an entry to this keyboard translator's table.
void removeEntry(const Entry &entry)
Removes an entry from the table.
QString name(GameStandardAction id)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
virtual bool open(OpenMode flags) override
void setData(const QByteArray &data)
QByteArray & insert(qsizetype i, QByteArrayView data)
QByteArray & remove(qsizetype pos, qsizetype len)
QByteArray & replace(QByteArrayView before, QByteArrayView after)
qsizetype size() const const
bool isLetterOrNumber(char32_t ucs4)
bool remove()
QString baseName() const const
virtual bool atEnd() const const
virtual void close()
bool isWritable() const const
QByteArray readLine(qint64 maxSize)
int count() const const
QKeySequence fromString(const QString &str, SequenceFormat format)
bool isEmpty() const const
QString toString(SequenceFormat format) const const
T & first()
bool isEmpty() const const
QString & append(QChar ch)
void clear()
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString simplified() const const
qsizetype size() const const
QByteArray toUtf8() const const
CaseInsensitive
Key_unknown
KeyboardModifier
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 29 2024 11:55:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.