source: ogBrowser-Git/qtermwidget/lib/KeyboardTranslator.cpp @ c0cec9d

jenkinsmain
Last change on this file since c0cec9d was fedf2a2, checked in by Vadim Troshchinskiy Shmelev <vtroshchinskiy@…>, 18 months ago

Update Qtermwidget to Qt6 version
Remove build files

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