2 This source file is part of Konsole, a terminal emulator.
4 Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
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.
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.
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
23 #include "KeyboardTranslator.h"
30 #include <QtCore/QBuffer>
31 #include <QtCore/QFile>
32 #include <QtCore/QFileInfo>
33 #include <QtCore/QTextStream>
34 #include <QtGui/QKeySequence>
39 #include <KStandardDirs>
41 using namespace Konsole
;
44 const char* KeyboardTranslatorManager::defaultTranslatorText
=
45 #include <DefaultTranslatorText.h>
48 KeyboardTranslatorManager::KeyboardTranslatorManager()
49 : _haveLoadedAll(false)
52 KeyboardTranslatorManager::~KeyboardTranslatorManager()
54 qDeleteAll(_translators
);
56 QString
KeyboardTranslatorManager::findTranslatorPath(const QString
& name
)
58 return KGlobal::dirs()->findResource("data","konsole/"+name
+".keytab");
60 void KeyboardTranslatorManager::findTranslators()
62 QStringList list
= KGlobal::dirs()->findAllResources("data",
64 KStandardDirs::NoDuplicates
);
66 // add the name of each translator to the list and associated
67 // the name with a null pointer to indicate that the translator
68 // has not yet been loaded from disk
69 QStringListIterator
listIter(list
);
70 while (listIter
.hasNext())
72 QString translatorPath
= listIter
.next();
74 QString name
= QFileInfo(translatorPath
).baseName();
76 if ( !_translators
.contains(name
) )
77 _translators
.insert(name
,0);
80 _haveLoadedAll
= true;
83 const KeyboardTranslator
* KeyboardTranslatorManager::findTranslator(const QString
& name
)
86 return defaultTranslator();
88 if ( _translators
.contains(name
) && _translators
[name
] != 0 )
89 return _translators
[name
];
91 KeyboardTranslator
* translator
= loadTranslator(name
);
93 if ( translator
!= 0 )
94 _translators
[name
] = translator
;
95 else if ( !name
.isEmpty() )
96 kWarning() << "Unable to load translator" << name
;
101 bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator
* translator
)
103 const QString path
= KGlobal::dirs()->saveLocation("data","konsole/")+translator
->name()
106 kDebug() << "Saving translator to" << path
;
108 QFile
destination(path
);
109 if (!destination
.open(QIODevice::WriteOnly
| QIODevice::Text
))
111 kWarning() << "Unable to save keyboard translation:"
112 << destination
.errorString();
117 KeyboardTranslatorWriter
writer(&destination
);
118 writer
.writeHeader(translator
->description());
120 QListIterator
<KeyboardTranslator::Entry
> iter(translator
->entries());
121 while ( iter
.hasNext() )
122 writer
.writeEntry(iter
.next());
130 KeyboardTranslator
* KeyboardTranslatorManager::loadTranslator(const QString
& name
)
132 const QString
& path
= findTranslatorPath(name
);
135 if (name
.isEmpty() || !source
.open(QIODevice::ReadOnly
| QIODevice::Text
))
138 return loadTranslator(&source
,name
);
141 const KeyboardTranslator
* KeyboardTranslatorManager::defaultTranslator()
143 // Try to find the default.keytab file if it exists, otherwise
144 // fall back to the hard-coded one
145 const KeyboardTranslator
* translator
= findTranslator("default");
149 textBuffer
.setData(defaultTranslatorText
,strlen(defaultTranslatorText
));
150 translator
= loadTranslator(&textBuffer
,"fallback");
155 KeyboardTranslator
* KeyboardTranslatorManager::loadTranslator(QIODevice
* source
,const QString
& name
)
157 KeyboardTranslator
* translator
= new KeyboardTranslator(name
);
158 KeyboardTranslatorReader
reader(source
);
159 translator
->setDescription( reader
.description() );
160 while ( reader
.hasNextEntry() )
161 translator
->addEntry(reader
.nextEntry());
165 if ( !reader
.parseError() )
176 KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice
* destination
)
177 : _destination(destination
)
179 Q_ASSERT( destination
&& destination
->isWritable() );
181 _writer
= new QTextStream(_destination
);
183 KeyboardTranslatorWriter::~KeyboardTranslatorWriter()
187 void KeyboardTranslatorWriter::writeHeader( const QString
& description
)
189 *_writer
<< "keyboard \"" << description
<< '\"' << '\n';
191 void KeyboardTranslatorWriter::writeEntry( const KeyboardTranslator::Entry
& entry
)
194 if ( entry
.command() != KeyboardTranslator::NoCommand
)
195 result
= entry
.resultToString();
197 result
= '\"' + entry
.resultToString() + '\"';
199 *_writer
<< "key " << entry
.conditionToString() << " : " << result
<< '\n';
203 // each line of the keyboard translation file is one of:
206 // - key KeySequence : "characters"
207 // - key KeySequence : CommandName
209 // KeySequence begins with the name of the key ( taken from the Qt::Key enum )
210 // and is followed by the keyboard modifiers and state flags ( with + or - in front
211 // of each modifier or flag to indicate whether it is required ). All keyboard modifiers
212 // and flags are optional, if a particular modifier or state is not specified it is
213 // assumed not to be a part of the sequence. The key sequence may contain whitespace
215 // eg: "key Up+Shift : scrollLineUp"
216 // "key Next-Shift : "\E[6~"
218 // (lines containing only whitespace are ignored, parseLine assumes that comments have
219 // already been removed)
222 KeyboardTranslatorReader::KeyboardTranslatorReader( QIODevice
* source
)
226 // read input until we find the description
227 while ( _description
.isEmpty() && !source
->atEnd() )
229 QList
<Token
> tokens
= tokenize( QString(source
->readLine()) );
230 if ( !tokens
.isEmpty() && tokens
.first().type
== Token::TitleKeyword
)
231 _description
= i18n(tokens
[1].text
.toUtf8());
233 // read first entry (if any)
236 void KeyboardTranslatorReader::readNext()
239 while ( !_source
->atEnd() )
241 const QList
<Token
>& tokens
= tokenize( QString(_source
->readLine()) );
242 if ( !tokens
.isEmpty() && tokens
.first().type
== Token::KeyKeyword
)
244 KeyboardTranslator::States flags
= KeyboardTranslator::NoState
;
245 KeyboardTranslator::States flagMask
= KeyboardTranslator::NoState
;
246 Qt::KeyboardModifiers modifiers
= Qt::NoModifier
;
247 Qt::KeyboardModifiers modifierMask
= Qt::NoModifier
;
249 int keyCode
= Qt::Key_unknown
;
251 decodeSequence(tokens
[1].text
.toLower(),
258 KeyboardTranslator::Command command
= KeyboardTranslator::NoCommand
;
261 // get text or command
262 if ( tokens
[2].type
== Token::OutputText
)
264 text
= tokens
[2].text
.toLocal8Bit();
266 else if ( tokens
[2].type
== Token::Command
)
269 if (!parseAsCommand(tokens
[2].text
,command
))
270 kWarning() << "Command" << tokens
[2].text
<< "not understood.";
273 KeyboardTranslator::Entry newEntry
;
274 newEntry
.setKeyCode( keyCode
);
275 newEntry
.setState( flags
);
276 newEntry
.setStateMask( flagMask
);
277 newEntry
.setModifiers( modifiers
);
278 newEntry
.setModifierMask( modifierMask
);
279 newEntry
.setText( text
);
280 newEntry
.setCommand( command
);
282 _nextEntry
= newEntry
;
293 bool KeyboardTranslatorReader::parseAsCommand(const QString
& text
,KeyboardTranslator::Command
& command
)
295 if ( text
.compare("erase",Qt::CaseInsensitive
) == 0 )
296 command
= KeyboardTranslator::EraseCommand
;
297 else if ( text
.compare("scrollpageup",Qt::CaseInsensitive
) == 0 )
298 command
= KeyboardTranslator::ScrollPageUpCommand
;
299 else if ( text
.compare("scrollpagedown",Qt::CaseInsensitive
) == 0 )
300 command
= KeyboardTranslator::ScrollPageDownCommand
;
301 else if ( text
.compare("scrolllineup",Qt::CaseInsensitive
) == 0 )
302 command
= KeyboardTranslator::ScrollLineUpCommand
;
303 else if ( text
.compare("scrolllinedown",Qt::CaseInsensitive
) == 0 )
304 command
= KeyboardTranslator::ScrollLineDownCommand
;
305 else if ( text
.compare("scrolllock",Qt::CaseInsensitive
) == 0 )
306 command
= KeyboardTranslator::ScrollLockCommand
;
313 bool KeyboardTranslatorReader::decodeSequence(const QString
& text
,
315 Qt::KeyboardModifiers
& modifiers
,
316 Qt::KeyboardModifiers
& modifierMask
,
317 KeyboardTranslator::States
& flags
,
318 KeyboardTranslator::States
& flagMask
)
320 bool isWanted
= true;
321 bool endOfItem
= false;
324 Qt::KeyboardModifiers tempModifiers
= modifiers
;
325 Qt::KeyboardModifiers tempModifierMask
= modifierMask
;
326 KeyboardTranslator::States tempFlags
= flags
;
327 KeyboardTranslator::States tempFlagMask
= flagMask
;
329 for ( int i
= 0 ; i
< text
.count() ; i
++ )
331 const QChar
& ch
= text
[i
];
332 bool isFirstLetter
= i
== 0;
333 bool isLastLetter
= ( i
== text
.count()-1 );
335 if ( ch
.isLetterOrNumber() )
339 } else if ( isFirstLetter
)
344 if ( (endOfItem
|| isLastLetter
) && !buffer
.isEmpty() )
346 Qt::KeyboardModifier itemModifier
= Qt::NoModifier
;
348 KeyboardTranslator::State itemFlag
= KeyboardTranslator::NoState
;
350 if ( parseAsModifier(buffer
,itemModifier
) )
352 tempModifierMask
|= itemModifier
;
355 tempModifiers
|= itemModifier
;
357 else if ( parseAsStateFlag(buffer
,itemFlag
) )
359 tempFlagMask
|= itemFlag
;
362 tempFlags
|= itemFlag
;
364 else if ( parseAsKeyCode(buffer
,itemKeyCode
) )
365 keyCode
= itemKeyCode
;
367 kDebug() << "Unable to parse key binding item:" << buffer
;
372 // check if this is a wanted / not-wanted flag and update the
373 // state ready for the next item
376 else if ( ch
== '-' )
380 modifiers
= tempModifiers
;
381 modifierMask
= tempModifierMask
;
383 flagMask
= tempFlagMask
;
388 bool KeyboardTranslatorReader::parseAsModifier(const QString
& item
, Qt::KeyboardModifier
& modifier
)
390 if ( item
== "shift" )
391 modifier
= Qt::ShiftModifier
;
392 else if ( item
== "ctrl" || item
== "control" )
393 modifier
= Qt::ControlModifier
;
394 else if ( item
== "alt" )
395 modifier
= Qt::AltModifier
;
396 else if ( item
== "meta" )
397 modifier
= Qt::MetaModifier
;
398 else if ( item
== "keypad" )
399 modifier
= Qt::KeypadModifier
;
405 bool KeyboardTranslatorReader::parseAsStateFlag(const QString
& item
, KeyboardTranslator::State
& flag
)
407 if ( item
== "appcukeys" || item
== "appcursorkeys" )
408 flag
= KeyboardTranslator::CursorKeysState
;
409 else if ( item
== "ansi" )
410 flag
= KeyboardTranslator::AnsiState
;
411 else if ( item
== "newline" )
412 flag
= KeyboardTranslator::NewLineState
;
413 else if ( item
== "appscreen" )
414 flag
= KeyboardTranslator::AlternateScreenState
;
415 else if ( item
== "anymod" || item
== "anymodifier" )
416 flag
= KeyboardTranslator::AnyModifierState
;
417 else if ( item
== "appkeypad" )
418 flag
= KeyboardTranslator::ApplicationKeypadState
;
424 bool KeyboardTranslatorReader::parseAsKeyCode(const QString
& item
, int& keyCode
)
426 QKeySequence sequence
= QKeySequence::fromString(item
);
427 if ( !sequence
.isEmpty() )
429 keyCode
= sequence
[0];
431 if ( sequence
.count() > 1 )
433 kDebug() << "Unhandled key codes in sequence: " << item
;
436 // additional cases implemented for backwards compatibility with KDE 3
437 else if ( item
== "prior" )
438 keyCode
= Qt::Key_PageUp
;
439 else if ( item
== "next" )
440 keyCode
= Qt::Key_PageDown
;
447 QString
KeyboardTranslatorReader::description() const
451 bool KeyboardTranslatorReader::hasNextEntry()
455 KeyboardTranslator::Entry
KeyboardTranslatorReader::createEntry( const QString
& condition
,
456 const QString
& result
)
458 QString
entryString("keyboard \"temporary\"\nkey ");
459 entryString
.append(condition
);
460 entryString
.append(" : ");
462 // if 'result' is the name of a command then the entry result will be that command,
463 // otherwise the result will be treated as a string to echo when the key sequence
464 // specified by 'condition' is pressed
465 KeyboardTranslator::Command command
;
466 if (parseAsCommand(result
,command
))
467 entryString
.append(result
);
469 entryString
.append('\"' + result
+ '\"');
471 QByteArray array
= entryString
.toUtf8();
472 QBuffer
buffer(&array
);
473 buffer
.open(QIODevice::ReadOnly
);
474 KeyboardTranslatorReader
reader(&buffer
);
476 KeyboardTranslator::Entry entry
;
477 if ( reader
.hasNextEntry() )
478 entry
= reader
.nextEntry();
483 KeyboardTranslator::Entry
KeyboardTranslatorReader::nextEntry()
485 Q_ASSERT( _hasNext
);
486 KeyboardTranslator::Entry entry
= _nextEntry
;
490 bool KeyboardTranslatorReader::parseError()
494 QList
<KeyboardTranslatorReader::Token
> KeyboardTranslatorReader::tokenize(const QString
& line
)
499 bool inQuotes
= false;
501 for (int i
=text
.length()-1;i
>=0;i
--)
505 inQuotes
= !inQuotes
;
506 else if (ch
== '#' && !inQuotes
)
509 if (commentPos
!= -1)
510 text
.remove(commentPos
,text
.length());
512 text
= text
.simplified();
514 // title line: keyboard "title"
515 static QRegExp
title("keyboard\\s+\"(.*)\"");
516 // key line: key KeySequence : "output"
517 // key line: key KeySequence : command
518 static QRegExp
key("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)");
521 if ( text
.isEmpty() )
526 if ( title
.exactMatch(text
) )
528 Token titleToken
= { Token::TitleKeyword
, QString() };
529 Token textToken
= { Token::TitleText
, title
.capturedTexts()[1] };
531 list
<< titleToken
<< textToken
;
533 else if ( key
.exactMatch(text
) )
535 Token keyToken
= { Token::KeyKeyword
, QString() };
536 Token sequenceToken
= { Token::KeySequence
, key
.capturedTexts()[1].remove(' ') };
538 list
<< keyToken
<< sequenceToken
;
540 if ( key
.capturedTexts()[3].isEmpty() )
542 // capturedTexts()[2] is a command
543 Token commandToken
= { Token::Command
, key
.capturedTexts()[2] };
544 list
<< commandToken
;
548 // capturedTexts()[3] is the output string
549 Token outputToken
= { Token::OutputText
, key
.capturedTexts()[3] };
555 kWarning() << "Line in keyboard translator file could not be understood:" << text
;
561 QList
<QString
> KeyboardTranslatorManager::allTranslators()
563 if ( !_haveLoadedAll
)
568 return _translators
.keys();
571 KeyboardTranslator::Entry::Entry()
573 , _modifiers(Qt::NoModifier
)
574 , _modifierMask(Qt::NoModifier
)
576 , _stateMask(NoState
)
577 , _command(NoCommand
)
581 bool KeyboardTranslator::Entry::operator==(const Entry
& rhs
) const
583 return _keyCode
== rhs
._keyCode
&&
584 _modifiers
== rhs
._modifiers
&&
585 _modifierMask
== rhs
._modifierMask
&&
586 _state
== rhs
._state
&&
587 _stateMask
== rhs
._stateMask
&&
588 _command
== rhs
._command
&&
592 bool KeyboardTranslator::Entry::matches(int keyCode
,
593 Qt::KeyboardModifiers modifiers
,
594 States testState
) const
596 if ( _keyCode
!= keyCode
)
599 if ( (modifiers
& _modifierMask
) != (_modifiers
& _modifierMask
) )
602 // if modifiers is non-zero, the 'any modifier' state is implicit
603 if ( modifiers
!= 0 )
604 testState
|= AnyModifierState
;
606 if ( (testState
& _stateMask
) != (_state
& _stateMask
) )
609 // special handling for the 'Any Modifier' state, which checks for the presence of
610 // any or no modifiers. In this context, the 'keypad' modifier does not count.
611 bool anyModifiersSet
= modifiers
!= 0 && modifiers
!= Qt::KeypadModifier
;
612 bool wantAnyModifier
= _state
& KeyboardTranslator::AnyModifierState
;
613 if ( _stateMask
& KeyboardTranslator::AnyModifierState
)
615 if ( wantAnyModifier
!= anyModifiersSet
)
621 QByteArray
KeyboardTranslator::Entry::escapedText(bool expandWildCards
,Qt::KeyboardModifiers modifiers
) const
623 QByteArray
result(text(expandWildCards
,modifiers
));
625 for ( int i
= 0 ; i
< result
.count() ; i
++ )
628 char replacement
= 0;
632 case 27 : replacement
= 'E'; break;
633 case 8 : replacement
= 'b'; break;
634 case 12 : replacement
= 'f'; break;
635 case 9 : replacement
= 't'; break;
636 case 13 : replacement
= 'r'; break;
637 case 10 : replacement
= 'n'; break;
639 // any character which is not printable is replaced by an equivalent
640 // \xhh escape sequence (where 'hh' are the corresponding hex digits)
641 if ( !QChar(ch
).isPrint() )
645 if ( replacement
== 'x' )
647 result
.replace(i
,1,"\\x"+QByteArray(1,ch
).toHex());
648 } else if ( replacement
!= 0 )
651 result
.insert(i
,'\\');
652 result
.insert(i
+1,replacement
);
658 QByteArray
KeyboardTranslator::Entry::unescape(const QByteArray
& input
) const
660 QByteArray
result(input
);
662 for ( int i
= 0 ; i
< result
.count()-1 ; i
++ )
665 QByteRef ch
= result
[i
];
668 char replacement
[2] = {0,0};
669 int charsToRemove
= 2;
670 bool escapedChar
= true;
672 switch ( result
[i
+1] )
674 case 'E' : replacement
[0] = 27; break;
675 case 'b' : replacement
[0] = 8 ; break;
676 case 'f' : replacement
[0] = 12; break;
677 case 't' : replacement
[0] = 9 ; break;
678 case 'r' : replacement
[0] = 13; break;
679 case 'n' : replacement
[0] = 10; break;
682 // format is \xh or \xhh where 'h' is a hexadecimal
683 // digit from 0-9 or A-F which should be replaced
684 // with the corresponding character value
685 char hexDigits
[3] = {0};
687 if ( (i
< result
.count()-2) && isxdigit(result
[i
+2]) )
688 hexDigits
[0] = result
[i
+2];
689 if ( (i
< result
.count()-3) && isxdigit(result
[i
+3]) )
690 hexDigits
[1] = result
[i
+3];
692 unsigned charValue
= 0;
693 sscanf(hexDigits
,"%x",&charValue
);
695 replacement
[0] = (char)charValue
;
696 charsToRemove
= 2 + strlen(hexDigits
);
704 result
.replace(i
,charsToRemove
,replacement
);
711 void KeyboardTranslator::Entry::insertModifier( QString
& item
, int modifier
) const
713 if ( !(modifier
& _modifierMask
) )
716 if ( modifier
& _modifiers
)
721 if ( modifier
== Qt::ShiftModifier
)
723 else if ( modifier
== Qt::ControlModifier
)
725 else if ( modifier
== Qt::AltModifier
)
727 else if ( modifier
== Qt::MetaModifier
)
729 else if ( modifier
== Qt::KeypadModifier
)
732 void KeyboardTranslator::Entry::insertState( QString
& item
, int state
) const
734 if ( !(state
& _stateMask
) )
737 if ( state
& _state
)
742 if ( state
== KeyboardTranslator::AlternateScreenState
)
744 else if ( state
== KeyboardTranslator::NewLineState
)
746 else if ( state
== KeyboardTranslator::AnsiState
)
748 else if ( state
== KeyboardTranslator::CursorKeysState
)
749 item
+= "AppCursorKeys";
750 else if ( state
== KeyboardTranslator::AnyModifierState
)
751 item
+= "AnyModifier";
752 else if ( state
== KeyboardTranslator::ApplicationKeypadState
)
755 QString
KeyboardTranslator::Entry::resultToString(bool expandWildCards
,Qt::KeyboardModifiers modifiers
) const
757 if ( !_text
.isEmpty() )
758 return escapedText(expandWildCards
,modifiers
);
759 else if ( _command
== EraseCommand
)
761 else if ( _command
== ScrollPageUpCommand
)
762 return "ScrollPageUp";
763 else if ( _command
== ScrollPageDownCommand
)
764 return "ScrollPageDown";
765 else if ( _command
== ScrollLineUpCommand
)
766 return "ScrollLineUp";
767 else if ( _command
== ScrollLineDownCommand
)
768 return "ScrollLineDown";
769 else if ( _command
== ScrollLockCommand
)
774 QString
KeyboardTranslator::Entry::conditionToString() const
776 QString result
= QKeySequence(_keyCode
).toString();
778 insertModifier( result
, Qt::ShiftModifier
);
779 insertModifier( result
, Qt::ControlModifier
);
780 insertModifier( result
, Qt::AltModifier
);
781 insertModifier( result
, Qt::MetaModifier
);
782 insertModifier( result
, Qt::KeypadModifier
);
784 insertState( result
, KeyboardTranslator::AlternateScreenState
);
785 insertState( result
, KeyboardTranslator::NewLineState
);
786 insertState( result
, KeyboardTranslator::AnsiState
);
787 insertState( result
, KeyboardTranslator::CursorKeysState
);
788 insertState( result
, KeyboardTranslator::AnyModifierState
);
789 insertState( result
, KeyboardTranslator::ApplicationKeypadState
);
794 KeyboardTranslator::KeyboardTranslator(const QString
& name
)
799 void KeyboardTranslator::setDescription(const QString
& description
)
801 _description
= description
;
803 QString
KeyboardTranslator::description() const
807 void KeyboardTranslator::setName(const QString
& name
)
811 QString
KeyboardTranslator::name() const
816 QList
<KeyboardTranslator::Entry
> KeyboardTranslator::entries() const
818 return _entries
.values();
821 void KeyboardTranslator::addEntry(const Entry
& entry
)
823 const int keyCode
= entry
.keyCode();
824 _entries
.insert(keyCode
,entry
);
826 void KeyboardTranslator::replaceEntry(const Entry
& existing
, const Entry
& replacement
)
828 if ( !existing
.isNull() )
829 _entries
.remove(existing
.keyCode(),existing
);
830 _entries
.insert(replacement
.keyCode(),replacement
);
832 void KeyboardTranslator::removeEntry(const Entry
& entry
)
834 _entries
.remove(entry
.keyCode(),entry
);
836 KeyboardTranslator::Entry
KeyboardTranslator::findEntry(int keyCode
, Qt::KeyboardModifiers modifiers
, States state
) const
838 foreach(const Entry
& entry
, _entries
.values(keyCode
))
840 if ( entry
.matches(keyCode
,modifiers
,state
) )
843 return Entry(); // entry not found
845 void KeyboardTranslatorManager::addTranslator(KeyboardTranslator
* translator
)
847 _translators
.insert(translator
->name(),translator
);
849 if ( !saveTranslator(translator
) )
850 kWarning() << "Unable to save translator" << translator
->name()
853 bool KeyboardTranslatorManager::deleteTranslator(const QString
& name
)
855 Q_ASSERT( _translators
.contains(name
) );
858 QString path
= findTranslatorPath(name
);
859 if ( QFile::remove(path
) )
861 _translators
.remove(name
);
866 kWarning() << "Failed to remove translator - " << path
;
870 K_GLOBAL_STATIC( KeyboardTranslatorManager
, theKeyboardTranslatorManager
)
871 KeyboardTranslatorManager
* KeyboardTranslatorManager::instance()
873 return theKeyboardTranslatorManager
;