~verzegnassi-stefano/+junk/ubuntu-terminal-app-uitk13

« back to all changes in this revision

Viewing changes to src/plugin/qmltermwidget/qtermwidget/lib/KeyboardTranslator.cpp

  • Committer: Filippo Scognamiglio
  • Date: 2014-10-25 04:42:31 UTC
  • Revision ID: flscogna@gmail.com-20141025044231-javjhusbqa171127
Initial reboot commit.

Show diffs side-by-side

added added

removed removed

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