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

jenkins
Last change on this file 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
Line 
1/*
2    This source file is part of Konsole, a terminal emulator.
3
4    Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
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
26#include <cctype>
27#include <cstdio>
28
29// Qt
30#include <QBuffer>
31#include <QFile>
32#include <QFileInfo>
33#include <QTextStream>
34#include <QKeySequence>
35#include <QDir>
36#include <QtDebug>
37#if QT_VERSION >= 0x060000
38#include <QtCore5Compat/QRegExp>
39#include <QtCore5Compat/QStringRef>
40#endif
41#include "tools.h"
42
43// KDE
44//#include <KDebug>
45//#include <KLocale>
46//#include <KStandardDirs>
47
48using namespace Konsole;
49
50
51const QByteArray KeyboardTranslatorManager::defaultTranslatorText(
52"keyboard \"Fallback Key Translator\"\n"
53"key Tab : \"\\t\""
54);
55
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
62
63KeyboardTranslatorManager::KeyboardTranslatorManager()
64    : _haveLoadedAll(false)
65{
66}
67KeyboardTranslatorManager::~KeyboardTranslatorManager()
68{
69    qDeleteAll(_translators);
70}
71QString KeyboardTranslatorManager::findTranslatorPath(const QString& name)
72{
73    return QString(get_kb_layout_dir() + name + QLatin1String(".keytab"));
74    //return KGlobal::dirs()->findResource("data","konsole/"+name+".keytab");
75}
76
77void KeyboardTranslatorManager::findTranslators()
78{
79    QDir dir(get_kb_layout_dir());
80    QStringList filters;
81    filters << QLatin1String("*.keytab");
82    dir.setNameFilters(filters);
83    QStringList list = dir.entryList(filters);
84//    QStringList list = KGlobal::dirs()->findAllResources("data",
85//                                                         "konsole/*.keytab",
86//                                                        KStandardDirs::NoDuplicates);
87
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();
97
98        if ( !_translators.contains(name) )
99            _translators.insert(name,0);
100    }
101
102    _haveLoadedAll = true;
103}
104
105const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name)
106{
107    if ( name.isEmpty() )
108        return defaultTranslator();
109
110    if ( _translators.contains(name) && _translators[name] != 0 )
111        return _translators[name];
112
113    KeyboardTranslator* translator = loadTranslator(name);
114
115    if ( translator != nullptr )
116        _translators[name] = translator;
117    else if ( !name.isEmpty() )
118        qDebug() << "Unable to load translator" << name;
119
120    return translator;
121}
122
123bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator)
124{
125qDebug() << "KeyboardTranslatorManager::saveTranslator" << "unimplemented";
126Q_UNUSED(translator);
127#if 0
128    const QString path = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name()
129           +".keytab";
130
131    //kDebug() << "Saving translator to" << path;
132
133    QFile destination(path);
134    if (!destination.open(QIODevice::WriteOnly | QIODevice::Text))
135    {
136        qDebug() << "Unable to save keyboard translation:"
137                   << destination.errorString();
138        return false;
139    }
140
141    {
142        KeyboardTranslatorWriter writer(&destination);
143        writer.writeHeader(translator->description());
144
145        QListIterator<KeyboardTranslator::Entry> iter(translator->entries());
146        while ( iter.hasNext() )
147            writer.writeEntry(iter.next());
148    }
149
150    destination.close();
151#endif
152    return true;
153}
154
155KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name)
156{
157    const QString& path = findTranslatorPath(name);
158
159    QFile source(path);
160    if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text))
161        return nullptr;
162
163    return loadTranslator(&source,name);
164}
165
166const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator()
167{
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;
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() );
186    while ( reader.hasNextEntry() )
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;
198        return nullptr;
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
223        result = QLatin1Char('\"') + entry.resultToString() + QLatin1Char('\"');
224
225    *_writer << QLatin1String("key ") << entry.conditionToString() << QLatin1String(" : ") << result << QLatin1Char('\n');
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
238// and flags are optional, if a particular modifier or state is not specified it is
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   {
255        QList<Token> tokens = tokenize( QString::fromUtf8(source->readLine()) );
256        if ( !tokens.isEmpty() && tokens.first().type == Token::TitleKeyword )
257            _description = tokens[1].text;
258   }
259   // read first entry (if any)
260   readNext();
261}
262void KeyboardTranslatorReader::readNext()
263{
264    // find next entry
265    while ( !_source->atEnd() )
266    {
267        const QList<Token>& tokens = tokenize( QString::fromUtf8(_source->readLine()) );
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,
282                           flagMask);
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
295                if (!parseAsCommand(tokens[2].text,command))
296                    qDebug() << "Command" << tokens[2].text << "not understood.";
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        }
314    }
315
316    _hasNext = false;
317}
318
319bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTranslator::Command& command)
320{
321    if ( text.compare(QLatin1String("erase"),Qt::CaseInsensitive) == 0 )
322        command = KeyboardTranslator::EraseCommand;
323    else if ( text.compare(QLatin1String("scrollpageup"),Qt::CaseInsensitive) == 0 )
324        command = KeyboardTranslator::ScrollPageUpCommand;
325    else if ( text.compare(QLatin1String("scrollpagedown"),Qt::CaseInsensitive) == 0 )
326        command = KeyboardTranslator::ScrollPageDownCommand;
327    else if ( text.compare(QLatin1String("scrolllineup"),Qt::CaseInsensitive) == 0 )
328        command = KeyboardTranslator::ScrollLineUpCommand;
329    else if ( text.compare(QLatin1String("scrolllinedown"),Qt::CaseInsensitive) == 0 )
330        command = KeyboardTranslator::ScrollLineDownCommand;
331    else if ( text.compare(QLatin1String("scrolllock"),Qt::CaseInsensitive) == 0 )
332        command = KeyboardTranslator::ScrollLockCommand;
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;
337    else
338        return false;
339
340    return true;
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{
350    bool isWanted = true;
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
359    for ( int i = 0 ; i < text.length() ; i++ )
360    {
361        const QChar& ch = text[i];
362        bool isFirstLetter = i == 0;
363        bool isLastLetter = ( i == text.length()-1 );
364        endOfItem = true;
365        if ( ch.isLetterOrNumber() )
366        {
367            endOfItem = false;
368            buffer.append(ch);
369        } else if ( isFirstLetter )
370        {
371            buffer.append(ch);
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
402        // check if this is a wanted / not-wanted flag and update the
403        // state ready for the next item
404        if ( ch == QLatin1Char('+') )
405           isWanted = true;
406        else if ( ch == QLatin1Char('-') )
407           isWanted = false;
408    }
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{
420    if ( item == QLatin1String("shift") )
421        modifier = Qt::ShiftModifier;
422    else if ( item == QLatin1String("ctrl") || item == QLatin1String("control") )
423        modifier = Qt::ControlModifier;
424    else if ( item == QLatin1String("alt") )
425        modifier = Qt::AltModifier;
426    else if ( item == QLatin1String("meta") )
427        modifier = Qt::MetaModifier;
428    else if ( item == QLatin1String("keypad") )
429        modifier = Qt::KeypadModifier;
430    else
431        return false;
432
433    return true;
434}
435bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag)
436{
437    if ( item == QLatin1String("appcukeys") || item == QLatin1String("appcursorkeys") )
438        flag = KeyboardTranslator::CursorKeysState;
439    else if ( item == QLatin1String("ansi") )
440        flag = KeyboardTranslator::AnsiState;
441    else if ( item == QLatin1String("newline") )
442        flag = KeyboardTranslator::NewLineState;
443    else if ( item == QLatin1String("appscreen") )
444        flag = KeyboardTranslator::AlternateScreenState;
445    else if ( item == QLatin1String("anymod") || item == QLatin1String("anymodifier") )
446        flag = KeyboardTranslator::AnyModifierState;
447    else if ( item == QLatin1String("appkeypad") )
448        flag = KeyboardTranslator::ApplicationKeypadState;
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    {
459#if QT_VERSION >= 0x060000
460        keyCode = sequence[0].toCombined();
461#else
462        keyCode = sequence[0];
463#endif
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
470    else if ( item == QLatin1String("prior") )
471        keyCode = Qt::Key_PageUp;
472    else if ( item == QLatin1String("next") )
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}
484bool KeyboardTranslatorReader::hasNextEntry() const
485{
486    return _hasNext;
487}
488KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry( const QString& condition ,
489                                                                 const QString& result )
490{
491    QString entryString = QString::fromLatin1("keyboard \"temporary\"\nkey ");
492    entryString.append(condition);
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('\"'));
503
504    QByteArray array = entryString.toUtf8();
505    QBuffer buffer(&array);
506    buffer.open(QIODevice::ReadOnly);
507    KeyboardTranslatorReader reader(&buffer);
508
509    KeyboardTranslator::Entry entry;
510    if ( reader.hasNextEntry() )
511        entry = reader.nextEntry();
512
513    return entry;
514}
515
516KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry()
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{
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();
546
547    // title line: keyboard "title"
548    static QRegExp title(QLatin1String("keyboard\\s+\"(.*)\""));
549    // key line: key KeySequence : "output"
550    // key line: key KeySequence : command
551    static QRegExp key(QLatin1String("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)"));
552
553    QList<Token> list;
554    if ( text.isEmpty() )
555    {
556        return list;
557    }
558
559    if ( title.exactMatch(text) )
560    {
561        Token titleToken = { Token::TitleKeyword , QString() };
562        Token textToken = { Token::TitleText , title.capturedTexts().at(1) };
563
564        list << titleToken << textToken;
565    }
566    else if  ( key.exactMatch(text) )
567    {
568        Token keyToken = { Token::KeyKeyword , QString() };
569        Token sequenceToken = { Token::KeySequence , key.capturedTexts().value(1).remove(QLatin1Char(' ')) };
570
571        list << keyToken << sequenceToken;
572
573        if ( key.capturedTexts().at(3).isEmpty() )
574        {
575            // capturedTexts()[2] is a command
576            Token commandToken = { Token::Command , key.capturedTexts().at(2) };
577            list << commandToken;
578        }
579        else
580        {
581            // capturedTexts()[3] is the output string
582           Token outputToken = { Token::OutputText , key.capturedTexts().at(3) };
583           list << outputToken;
584        }
585    }
586    else
587    {
588        qDebug() << "Line in keyboard translator file could not be understood:" << text;
589    }
590
591    return list;
592}
593
594QList<QString> KeyboardTranslatorManager::allTranslators()
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
625bool KeyboardTranslator::Entry::matches(int keyCode ,
626                                        Qt::KeyboardModifiers modifiers,
627                                        States testState) const
628{
629#ifdef Q_OS_MAC
630    // On Mac, arrow keys are considered part of keypad. Ignore that.
631    modifiers &= ~Qt::KeypadModifier;
632#endif
633
634    if ( _keyCode != keyCode )
635        return false;
636
637    if ( (modifiers & _modifierMask) != (_modifiers & _modifierMask) )
638        return false;
639
640    // if modifiers is non-zero, the 'any modifier' state is implicit
641    if ( (modifiers & ~Qt::KeypadModifier) != 0 )
642        testState |= AnyModifierState;
643
644    if ( (testState & _stateMask) != (_state & _stateMask) )
645        return false;
646
647    // special handling for the 'Any Modifier' state, which checks for the presence of
648    // any or no modifiers.  In this context, the 'keypad' modifier does not count.
649    bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier;
650    bool wantAnyModifier = _state & KeyboardTranslator::AnyModifierState;
651    if ( _stateMask & KeyboardTranslator::AnyModifierState )
652    {
653        if ( wantAnyModifier != anyModifiersSet )
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
663    for ( int i = 0 ; i < result.length() ; i++ )
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)
679                if ( !QChar(QLatin1Char(ch)).isPrint() )
680                    replacement = 'x';
681        }
682
683        if ( replacement == 'x' )
684        {
685            QByteArray data = "\\x"+QByteArray(1,ch).toHex();
686            result.replace(i,1,data);
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
701    for ( int i = 0 ; i < result.length()-1 ; i++ )
702    {
703
704        auto ch = result[i];
705        if ( ch == '\\' )
706        {
707           char replacement[2] = {0,0};
708           int charsToRemove = 2;
709           bool escapedChar = true;
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' :
720                 {
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
726                    if ( (i < result.length()-2) && isxdigit(result[i+2]) )
727                            hexDigits[0] = result[i+2];
728                    if ( (i < result.length()-3) && isxdigit(result[i+3]) )
729                            hexDigits[1] = result[i+3];
730
731                    unsigned charValue = 0;
732                    sscanf(hexDigits,"%x",&charValue);
733
734                    replacement[0] = (char)charValue;
735                    charsToRemove = 2 + strlen(hexDigits);
736                  }
737              break;
738              default:
739                  escapedChar = false;
740           }
741
742           if ( escapedChar )
743               result.replace(i,charsToRemove,replacement);
744        }
745    }
746
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 )
756        item += QLatin1Char('+');
757    else
758        item += QLatin1Char('-');
759
760    if ( modifier == Qt::ShiftModifier )
761        item += QLatin1String("Shift");
762    else if ( modifier == Qt::ControlModifier )
763        item += QLatin1String("Ctrl");
764    else if ( modifier == Qt::AltModifier )
765        item += QLatin1String("Alt");
766    else if ( modifier == Qt::MetaModifier )
767        item += QLatin1String("Meta");
768    else if ( modifier == Qt::KeypadModifier )
769        item += QLatin1String("KeyPad");
770}
771void KeyboardTranslator::Entry::insertState( QString& item , int state ) const
772{
773    if ( !(state & _stateMask) )
774        return;
775
776    if ( state & _state )
777        item += QLatin1Char('+') ;
778    else
779        item += QLatin1Char('-') ;
780
781    if ( state == KeyboardTranslator::AlternateScreenState )
782        item += QLatin1String("AppScreen");
783    else if ( state == KeyboardTranslator::NewLineState )
784        item += QLatin1String("NewLine");
785    else if ( state == KeyboardTranslator::AnsiState )
786        item += QLatin1String("Ansi");
787    else if ( state == KeyboardTranslator::CursorKeysState )
788        item += QLatin1String("AppCursorKeys");
789    else if ( state == KeyboardTranslator::AnyModifierState )
790        item += QLatin1String("AnyModifier");
791    else if ( state == KeyboardTranslator::ApplicationKeypadState )
792        item += QLatin1String("AppKeypad");
793}
794QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
795{
796    if ( !_text.isEmpty() )
797        return QString::fromLatin1(escapedText(expandWildCards,modifiers));
798    else if ( _command == EraseCommand )
799        return QLatin1String("Erase");
800    else if ( _command == ScrollPageUpCommand )
801        return QLatin1String("ScrollPageUp");
802    else if ( _command == ScrollPageDownCommand )
803        return QLatin1String("ScrollPageDown");
804    else if ( _command == ScrollLineUpCommand )
805        return QLatin1String("ScrollLineUp");
806    else if ( _command == ScrollLineDownCommand )
807        return QLatin1String("ScrollLineDown");
808    else if ( _command == ScrollLockCommand )
809        return QLatin1String("ScrollLock");
810    else if (_command == ScrollUpToTopCommand)
811        return QLatin1String("ScrollUpToTop");
812    else if (_command == ScrollDownToBottomCommand)
813        return QLatin1String("ScrollDownToBottom");
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 );
824    insertModifier( result , Qt::MetaModifier );
825    insertModifier( result , Qt::KeypadModifier );
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 );
832    insertState( result , KeyboardTranslator::ApplicationKeypadState );
833
834    return result;
835}
836
837KeyboardTranslator::KeyboardTranslator(const QString& name)
838: _name(name)
839{
840}
841
842void KeyboardTranslator::setDescription(const QString& description)
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();
867    _entries.insert(keyCode,entry);
868}
869void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement)
870{
871    if ( !existing.isNull() )
872        _entries.remove(existing.keyCode(),existing);
873    _entries.insert(replacement.keyCode(),replacement);
874}
875void KeyboardTranslator::removeEntry(const Entry& entry)
876{
877    _entries.remove(entry.keyCode(),entry);
878}
879KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const
880{
881    for (auto it = _entries.cbegin(), end = _entries.cend(); it != end; ++it)
882    {
883        if (it.key() == keyCode)
884            if ( it.value().matches(keyCode,modifiers,state) )
885                return *it;
886    }
887    return Entry(); // entry not found
888}
889void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator)
890{
891    _translators.insert(translator->name(),translator);
892
893    if ( !saveTranslator(translator) )
894        qDebug() << "Unable to save translator" << translator->name()
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);
906        return true;
907    }
908    else
909    {
910        qDebug() << "Failed to remove translator - " << path;
911        return false;
912    }
913}
914Q_GLOBAL_STATIC( KeyboardTranslatorManager , theKeyboardTranslatorManager )
915KeyboardTranslatorManager* KeyboardTranslatorManager::instance()
916{
917    return theKeyboardTranslatorManager;
918}
Note: See TracBrowser for help on using the repository browser.