diff --git a/src/plugins/ispellchecker/ispellcheckerclient.cpp b/src/plugins/ispellchecker/ispellcheckerclient.cpp index a32c998..ce41616 100644 --- a/src/plugins/ispellchecker/ispellcheckerclient.cpp +++ b/src/plugins/ispellchecker/ispellcheckerclient.cpp @@ -1,66 +1,64 @@ /* SPDX-FileCopyrightText: 2019 Christoph Cullmann SPDX-License-Identifier: LGPL-2.0-or-later */ #include "ispellcheckerclient.h" #include "ispellcheckerdict.h" #include "ispellcheckerdebug.h" using namespace Sonnet; ISpellCheckerClient::ISpellCheckerClient(QObject *parent) : Client(parent) { qCDebug(SONNET_ISPELLCHECKER) << " ISpellCheckerClient::ISpellCheckerClient"; - // init com if needed - m_wasCOMInitialized = SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); + // init com if needed, use same variant as e.g. Qt in qtbase/src/corelib/io/qfilesystemengine_win.cpp + CoInitialize(nullptr); - // get factory - if (SUCCEEDED(CoCreateInstance(__uuidof(SpellCheckerFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_spellCheckerFactory)))) { + // get factory & collect all known languages + instantiate the spell checkers for them + ISpellCheckerFactory *spellCheckerFactory = nullptr; + if (SUCCEEDED(CoCreateInstance(__uuidof(SpellCheckerFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spellCheckerFactory))) && spellCheckerFactory) { // if we have a factory, cache the language names IEnumString* enumLanguages = nullptr; - if (SUCCEEDED(m_spellCheckerFactory->get_SupportedLanguages(&enumLanguages))) { + if (SUCCEEDED(spellCheckerFactory->get_SupportedLanguages(&enumLanguages))) { HRESULT hr = S_OK; while (S_OK == hr) { LPOLESTR string = nullptr; hr = enumLanguages->Next(1, &string, nullptr); if (S_OK == hr) { - m_languages.push_back(QString::fromWCharArray(string)); + ISpellChecker *spellChecker = nullptr; + if (SUCCEEDED(spellCheckerFactory->CreateSpellChecker(string, &spellChecker)) && spellChecker) { + m_languages.insert(QString::fromWCharArray(string), spellChecker); + } CoTaskMemFree(string); } } enumLanguages->Release(); } - } else { - m_spellCheckerFactory = nullptr; + spellCheckerFactory->Release(); } } ISpellCheckerClient::~ISpellCheckerClient() { - // de-init com if needed - if (m_wasCOMInitialized) { - CoUninitialize(); - } - - // release factory - if (m_spellCheckerFactory) { - m_spellCheckerFactory->Release(); - } + // FIXME: we at the moment leak all checkers as sonnet does the cleanup to late for proper com cleanup :/ } SpellerPlugin *ISpellCheckerClient::createSpeller(const QString &language) { - // create requested spellchecker, might internally fail to create instance + // create requested spellchecker if we know the language qCDebug(SONNET_ISPELLCHECKER) << " SpellerPlugin *ISpellCheckerClient::createSpeller(const QString &language) ;" << language; - ISpellCheckerDict *ad = new ISpellCheckerDict(m_spellCheckerFactory, language); - return ad; + const auto it = m_languages.find(language); + if (it != m_languages.end()) { + return new ISpellCheckerDict(it.value(), language); + } + return nullptr; } QStringList ISpellCheckerClient::languages() const { - return m_languages; + return m_languages.keys(); } diff --git a/src/plugins/ispellchecker/ispellcheckerclient.h b/src/plugins/ispellchecker/ispellcheckerclient.h index 0680211..b86a3cf 100644 --- a/src/plugins/ispellchecker/ispellcheckerclient.h +++ b/src/plugins/ispellchecker/ispellcheckerclient.h @@ -1,49 +1,50 @@ /* SPDX-FileCopyrightText: 2019 Christoph Cullmann SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KSPELL_ISPELLCHECKCLIENT_H #define KSPELL_ISPELLCHECKCLIENT_H #include "client_p.h" #include #include +#include + namespace Sonnet { class SpellerPlugin; } using Sonnet::SpellerPlugin; class ISpellCheckerClient : public Sonnet::Client { Q_OBJECT Q_INTERFACES(Sonnet::Client) Q_PLUGIN_METADATA(IID "org.kde.Sonnet.ISpellCheckerClient") public: explicit ISpellCheckerClient(QObject *parent = nullptr); ~ISpellCheckerClient() override; int reliability() const override { return 40; } SpellerPlugin *createSpeller(const QString &language) override; QStringList languages() const override; QString name() const override { return QStringLiteral("ISpellChecker"); } private: - bool m_wasCOMInitialized = false; - ISpellCheckerFactory* m_spellCheckerFactory = nullptr; - QStringList m_languages; + // we internally keep all spell checker interfaces alive + QMap m_languages; }; #endif diff --git a/src/plugins/ispellchecker/ispellcheckerdict.cpp b/src/plugins/ispellchecker/ispellcheckerdict.cpp index 80f32f1..1e34e1e 100644 --- a/src/plugins/ispellchecker/ispellcheckerdict.cpp +++ b/src/plugins/ispellchecker/ispellcheckerdict.cpp @@ -1,83 +1,78 @@ /* SPDX-FileCopyrightText: 2019 Christoph Cullmann SPDX-License-Identifier: LGPL-2.0-or-later */ #include "ispellcheckerdict.h" #include "ispellcheckerdebug.h" using namespace Sonnet; -ISpellCheckerDict::ISpellCheckerDict(ISpellCheckerFactory *spellCheckerFactory, const QString &language) +ISpellCheckerDict::ISpellCheckerDict(ISpellChecker *spellChecker, const QString &language) : SpellerPlugin(language) + , m_spellChecker(spellChecker) { - // try to init checker - if (!SUCCEEDED(spellCheckerFactory->CreateSpellChecker(language.toStdWString().c_str(), &m_spellChecker))) { - m_spellChecker = nullptr; - } + Q_ASSERT(m_spellChecker); } ISpellCheckerDict::~ISpellCheckerDict() { - // release com if needed - if (m_spellChecker) { - m_spellChecker->Release(); - } + // we don't own m_spellChecker! } bool ISpellCheckerDict::isCorrect(const QString &word) const { // check if we are incorrect, we only need to check one enum entry for that, only empty enum means OK bool ok = true; IEnumSpellingError* enumSpellingError = nullptr; - if (m_spellChecker && SUCCEEDED(m_spellChecker->Check(word.toStdWString().c_str(), &enumSpellingError))) { + if (SUCCEEDED(m_spellChecker->Check(word.toStdWString().c_str(), &enumSpellingError))) { ISpellingError *spellingError = nullptr; if (S_OK == enumSpellingError->Next(&spellingError)) { ok = false; spellingError->Release(); } enumSpellingError->Release(); } return ok; } QStringList ISpellCheckerDict::suggest(const QString &word) const { // query suggestions QStringList replacements; IEnumString* words = nullptr; - if (m_spellChecker && SUCCEEDED(m_spellChecker->Suggest(word.toStdWString().c_str(), &words))) { + if (SUCCEEDED(m_spellChecker->Suggest(word.toStdWString().c_str(), &words))) { HRESULT hr = S_OK; while (S_OK == hr) { LPOLESTR string = nullptr; hr = words->Next(1, &string, nullptr); if (S_OK == hr) { replacements.push_back(QString::fromWCharArray(string)); CoTaskMemFree(string); } } words->Release(); } return replacements; } bool ISpellCheckerDict::storeReplacement(const QString &bad, const QString &good) { Q_UNUSED(bad); Q_UNUSED(good); qCDebug(SONNET_ISPELLCHECKER) << "ISpellCheckerDict::storeReplacement not implemented"; return false; } bool ISpellCheckerDict::addToPersonal(const QString &word) { // add word "permanently" to the dictionary - return m_spellChecker && SUCCEEDED(m_spellChecker->Add(word.toStdWString().c_str())); + return SUCCEEDED(m_spellChecker->Add(word.toStdWString().c_str())); } bool ISpellCheckerDict::addToSession(const QString &word) { // ignore word for this session - return m_spellChecker && SUCCEEDED(m_spellChecker->Ignore(word.toStdWString().c_str())); + return SUCCEEDED(m_spellChecker->Ignore(word.toStdWString().c_str())); } diff --git a/src/plugins/ispellchecker/ispellcheckerdict.h b/src/plugins/ispellchecker/ispellcheckerdict.h index a6581e4..883b399 100644 --- a/src/plugins/ispellchecker/ispellcheckerdict.h +++ b/src/plugins/ispellchecker/ispellcheckerdict.h @@ -1,33 +1,33 @@ /* SPDX-FileCopyrightText: 2019 Christoph Cullmann SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KSPELL_ISPELLCHECKDICT_H #define KSPELL_ISPELLCHECKDICT_H #include "spellerplugin_p.h" #include "ispellcheckerclient.h" class ISpellCheckerDict : public Sonnet::SpellerPlugin { public: - explicit ISpellCheckerDict(ISpellCheckerFactory *spellCheckerFactory, const QString &language); + explicit ISpellCheckerDict(ISpellChecker *spellChecker, const QString &language); ~ISpellCheckerDict() override; bool isCorrect(const QString &word) const override; QStringList suggest(const QString &word) const override; bool storeReplacement(const QString &bad, const QString &good) override; bool addToPersonal(const QString &word) override; bool addToSession(const QString &word) override; private: - // spell checker com object - ISpellChecker *m_spellChecker = nullptr; + // spell checker com object, we don't own this + ISpellChecker * const m_spellChecker; }; #endif