File indexing completed on 2024-05-19 05:01:19

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2021 Stefano Crocco <posta@stefanocrocco.it>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "spellcheckermanager.h"
0010 #include "webenginepage.h"
0011 
0012 #include <QDir>
0013 #include <QStringList>
0014 #include <QDebug>
0015 #include <QWebEngineProfile>
0016 #include <QMenu>
0017 #include <QAction>
0018 #include <QLibraryInfo>
0019 #include <QApplication>
0020 
0021 #include <KActionCollection>
0022 #include <KLocalizedString>
0023 #include <KSharedConfig>
0024 #include <KConfigGroup>
0025 
0026 #include <konq_spellcheckingconfigurationdispatcher.h>
0027 
0028 QString SpellCheckerManager::dictionaryDir()
0029 {
0030     static const char *varName = "QTWEBENGINE_DICTIONARIES_PATH";
0031     static QString s_dir;
0032     if (s_dir.isNull()) {
0033         if (qEnvironmentVariableIsSet(varName)) {
0034             s_dir = qEnvironmentVariable(varName);
0035         } else {
0036 #ifdef WEBENGINEPART_OWN_DICTIONARY_DIR
0037             s_dir = QString(WEBENGINEPART_OWN_DICTIONARY_DIR);
0038 #else
0039             QLatin1String suffix("/qtwebengine_dictionaries");
0040             s_dir = QApplication::applicationDirPath() + suffix;
0041             if (!QDir(s_dir).exists()) {
0042                 s_dir = QLibraryInfo::location(QLibraryInfo::DataPath) + suffix;
0043             }
0044 #endif
0045         }
0046     }
0047     return s_dir;
0048 }
0049 
0050 SpellCheckerManager::SpellCheckerManager(QWebEngineProfile *profile, QObject *parent): QObject(parent), m_profile(profile)
0051 {
0052     m_dictionaryDir = dictionaryDir();
0053     connect(KonqSpellCheckingConfigurationDispatcher::self(), &KonqSpellCheckingConfigurationDispatcher::spellCheckingConfigurationChanged,
0054             this, &SpellCheckerManager::updateConfiguration);
0055     KSharedConfigPtr cfg = KSharedConfig::openConfig();
0056     KConfigGroup grp = cfg->group("General");
0057     updateConfiguration(grp.readEntry("SpellCheckingEnabled", false));
0058 }
0059 
0060 SpellCheckerManager::~SpellCheckerManager()
0061 {
0062 }
0063 
0064 void SpellCheckerManager::detectDictionaries()
0065 {
0066     if (m_dictionaryDir.isEmpty()) {
0067         m_dicts.clear();
0068         m_enabledDicts.clear();
0069         return;
0070     }
0071     QStringList files = QDir(m_dictionaryDir).entryList({"*.bdic"});
0072     QStringList languages;
0073     std::transform(files.constBegin(), files.constEnd(), std::back_inserter(languages), [](const QString &f){return f.chopped(5);});
0074     QMap<QString, QString> dicts = m_speller.availableDictionaries();
0075     for (auto it = dicts.constBegin(); it != dicts.constEnd(); ++it) {
0076         if (languages.contains(it.value())) {
0077             m_dicts[it.value()] = it.key();
0078         }
0079     }
0080     QMap<QString, QString> preferred = m_speller.preferredDictionaries();
0081     for (auto it = preferred.constBegin(); it != preferred.constEnd(); ++it) {
0082         if (m_dicts.contains(it.value())) {
0083             m_enabledDicts << it.value();
0084         }
0085     }
0086 }
0087 
0088 void SpellCheckerManager::updateConfiguration(bool spellCheckingEnabled)
0089 {
0090     detectDictionaries();
0091     m_profile->setSpellCheckEnabled(spellCheckingEnabled);
0092     m_profile->setSpellCheckLanguages(m_enabledDicts);
0093 }
0094 
0095 QMenu * SpellCheckerManager::spellCheckingMenu(const QStringList &suggestions, KActionCollection* coll, WebEnginePage* page)
0096 {
0097     QMenu *menu = new QMenu();
0098     menu->setTitle(i18n("Spelling"));
0099 
0100     bool spellingEnabled = m_profile->isSpellCheckEnabled();
0101 
0102     QAction *a = new QAction(i18n("Spell Checking Enabled"), coll);
0103     a->setCheckable(true);
0104     a->setChecked(spellingEnabled);
0105     connect(a, &QAction::toggled, this, &SpellCheckerManager::spellCheckingToggled);
0106     menu->addAction(a);
0107 
0108     if (spellingEnabled) {
0109         if (!suggestions.isEmpty()) {
0110             menu->addSeparator();
0111             for (const QString &s : suggestions) {
0112                 a = new QAction(s, menu);
0113                 menu->addAction(a);
0114                 connect(a, &QAction::triggered, page, [page, s](){page->replaceMisspelledWord(s);});
0115             }
0116         }
0117 
0118         menu->addSeparator();
0119         QMenu *langs = new QMenu(menu);
0120         langs->setTitle(i18n("&Languages"));
0121         menu->addMenu(langs);
0122         QStringList enabledLangs = m_profile->spellCheckLanguages();
0123         for (auto it = m_dicts.constBegin(); it != m_dicts.constEnd(); ++it) {
0124             a = new QAction(it.value(), coll);
0125             a->setCheckable(true);
0126             const QString lang = it.key();
0127             a->setChecked(enabledLangs.contains(lang));
0128             connect(a, &QAction::toggled, this, [this, lang](bool on){on ? addLanguage(lang) : removeLanguage(lang);});
0129             langs->addAction(a);
0130         }
0131 
0132     }
0133     return menu;
0134 }
0135 
0136 void SpellCheckerManager::addLanguage(const QString& lang)
0137 {
0138     QStringList langs = m_profile->spellCheckLanguages();
0139     if (!langs.contains(lang)) {
0140         langs << lang;
0141         m_profile->setSpellCheckLanguages(langs);
0142     }
0143 }
0144 
0145 void SpellCheckerManager::removeLanguage(const QString& lang)
0146 {
0147     QStringList langs = m_profile->spellCheckLanguages();
0148     langs.removeAll(lang);
0149     m_profile->setSpellCheckLanguages(langs);
0150 }
0151 
0152 void SpellCheckerManager::spellCheckingToggled(bool on)
0153 {
0154     m_profile->setSpellCheckEnabled(on);
0155 }