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