File indexing completed on 2024-04-28 05:50:42

0001 /*
0002     This source file is part of Konsole, a terminal emulator.
0003 
0004     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 // Own
0010 #include "KeyboardTranslatorManager.h"
0011 
0012 #include "FallbackKeyboardTranslator.h"
0013 #include "KeyboardTranslatorReader.h"
0014 #include "KeyboardTranslatorWriter.h"
0015 
0016 // Qt
0017 #include <QDir>
0018 #include <QFile>
0019 #include <QFileInfo>
0020 #include <QStandardPaths>
0021 
0022 using namespace Konsole;
0023 
0024 KeyboardTranslatorManager::KeyboardTranslatorManager()
0025     : _haveLoadedAll(false)
0026     , _fallbackTranslator(nullptr)
0027     , _translators(QHash<QString, KeyboardTranslator *>())
0028 {
0029     _fallbackTranslator = new FallbackKeyboardTranslator();
0030 }
0031 
0032 KeyboardTranslatorManager::~KeyboardTranslatorManager()
0033 {
0034     qDeleteAll(_translators);
0035     delete _fallbackTranslator;
0036 }
0037 
0038 Q_GLOBAL_STATIC(KeyboardTranslatorManager, theKeyboardTranslatorManager)
0039 KeyboardTranslatorManager *KeyboardTranslatorManager::instance()
0040 {
0041     return theKeyboardTranslatorManager;
0042 }
0043 
0044 void KeyboardTranslatorManager::addTranslator(KeyboardTranslator *translator)
0045 {
0046     _translators.insert(translator->name(), translator);
0047 
0048     if (!saveTranslator(translator)) {
0049         qCDebug(KonsoleKeyTrDebug) << "Unable to save translator" << translator->name() << "to disk.";
0050     }
0051 }
0052 
0053 bool KeyboardTranslatorManager::deleteTranslator(const QString &name)
0054 {
0055     Q_ASSERT(_translators.contains(name));
0056 
0057     // locate and delete
0058     QString path = findTranslatorPath(name);
0059     if (QFile::remove(path)) {
0060         _translators.remove(name);
0061         return true;
0062     }
0063     qCDebug(KonsoleKeyTrDebug) << "Failed to remove translator - " << path;
0064     return false;
0065 }
0066 
0067 bool KeyboardTranslatorManager::isTranslatorDeletable(const QString &name) const
0068 {
0069     const QString &dir = QFileInfo(findTranslatorPath(name)).path();
0070     return QFileInfo(dir).isWritable();
0071 }
0072 
0073 bool KeyboardTranslatorManager::isTranslatorResettable(const QString &name) const
0074 {
0075     const QStringList &paths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + name + QStringLiteral(".keytab"));
0076 
0077     return (paths.count() > 1);
0078 }
0079 
0080 const QString KeyboardTranslatorManager::findTranslatorPath(const QString &name) const
0081 {
0082     return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + name + QStringLiteral(".keytab"));
0083 }
0084 
0085 void KeyboardTranslatorManager::findTranslators()
0086 {
0087     QStringList list;
0088     const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("konsole"), QStandardPaths::LocateDirectory);
0089     list.reserve(dirs.size());
0090 
0091     for (const QString &dir : dirs) {
0092         const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.keytab"));
0093         for (const QString &file : fileNames) {
0094             list.append(dir + QLatin1Char('/') + file);
0095         }
0096     }
0097 
0098     // add the name of each translator to the list and associated
0099     // the name with a null pointer to indicate that the translator
0100     // has not yet been loaded from disk
0101     for (const QString &translatorPath : std::as_const(list)) {
0102         QString name = QFileInfo(translatorPath).completeBaseName();
0103 
0104         if (!_translators.contains(name)) {
0105             _translators.insert(name, nullptr);
0106         }
0107     }
0108 
0109     _haveLoadedAll = true;
0110 }
0111 
0112 const KeyboardTranslator *KeyboardTranslatorManager::findTranslator(const QString &name)
0113 {
0114     if (name.isEmpty()) {
0115         return defaultTranslator();
0116     }
0117 
0118     if (_translators.contains(name) && _translators[name] != nullptr) {
0119         return _translators[name];
0120     }
0121 
0122     KeyboardTranslator *translator = loadTranslator(name);
0123 
0124     if (translator != nullptr) {
0125         _translators[name] = translator;
0126     } else if (!name.isEmpty()) {
0127         qCDebug(KonsoleKeyTrDebug) << "Unable to load translator" << name;
0128     }
0129 
0130     return translator;
0131 }
0132 
0133 bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator *translator)
0134 {
0135     const QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/konsole/");
0136     QDir().mkpath(dir);
0137     const QString path = dir + translator->name() + QStringLiteral(".keytab");
0138 
0139     ////qDebug() << "Saving translator to" << path;
0140 
0141     QFile destination(path);
0142     if (!destination.open(QIODevice::WriteOnly | QIODevice::Text)) {
0143         qCDebug(KonsoleKeyTrDebug) << "Unable to save keyboard translation:" << destination.errorString();
0144         return false;
0145     }
0146 
0147     {
0148         KeyboardTranslatorWriter writer(&destination);
0149         writer.writeHeader(translator->description());
0150         const QList<KeyboardTranslator::Entry> entriesList = translator->entries();
0151         for (const KeyboardTranslator::Entry &entry : entriesList) {
0152             writer.writeEntry(entry);
0153         }
0154     }
0155 
0156     destination.close();
0157 
0158     return true;
0159 }
0160 
0161 KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(const QString &name)
0162 {
0163     const QString &path = findTranslatorPath(name);
0164 
0165     QFile source(path);
0166     if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text)) {
0167         return nullptr;
0168     }
0169 
0170     return loadTranslator(&source, name);
0171 }
0172 
0173 KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(QIODevice *source, const QString &name)
0174 {
0175     auto translator = new KeyboardTranslator(name);
0176     KeyboardTranslatorReader reader(source);
0177     translator->setDescription(reader.description());
0178     while (reader.hasNextEntry()) {
0179         translator->addEntry(reader.nextEntry());
0180     }
0181 
0182     source->close();
0183 
0184     if (!reader.parseError()) {
0185         return translator;
0186     }
0187     delete translator;
0188     return nullptr;
0189 }
0190 
0191 const KeyboardTranslator *KeyboardTranslatorManager::defaultTranslator()
0192 {
0193     // Try to find the default.keytab file if it exists, otherwise
0194     // fall back to the internal hard-coded fallback translator
0195     const KeyboardTranslator *translator = findTranslator(QStringLiteral("default"));
0196     if (translator == nullptr) {
0197         translator = _fallbackTranslator;
0198     }
0199     return translator;
0200 }
0201 
0202 const QStringList KeyboardTranslatorManager::allTranslators()
0203 {
0204     if (!_haveLoadedAll) {
0205         findTranslators();
0206     }
0207 
0208     return _translators.keys();
0209 }