diff --git a/src/plugins/ispellchecker/ispellcheckerclient.h b/src/plugins/ispellchecker/ispellcheckerclient.h --- a/src/plugins/ispellchecker/ispellcheckerclient.h +++ b/src/plugins/ispellchecker/ispellcheckerclient.h @@ -12,6 +12,8 @@ #include #include +#include + namespace Sonnet { class SpellerPlugin; } @@ -41,9 +43,8 @@ } 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/ispellcheckerclient.cpp b/src/plugins/ispellchecker/ispellcheckerclient.cpp --- a/src/plugins/ispellchecker/ispellcheckerclient.cpp +++ b/src/plugins/ispellchecker/ispellcheckerclient.cpp @@ -15,52 +15,50 @@ { 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/ispellcheckerdict.h b/src/plugins/ispellchecker/ispellcheckerdict.h --- a/src/plugins/ispellchecker/ispellcheckerdict.h +++ b/src/plugins/ispellchecker/ispellcheckerdict.h @@ -14,7 +14,7 @@ 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; @@ -26,8 +26,8 @@ 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 diff --git a/src/plugins/ispellchecker/ispellcheckerdict.cpp b/src/plugins/ispellchecker/ispellcheckerdict.cpp --- a/src/plugins/ispellchecker/ispellcheckerdict.cpp +++ b/src/plugins/ispellchecker/ispellcheckerdict.cpp @@ -9,29 +9,24 @@ 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; @@ -47,7 +42,7 @@ // 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; @@ -73,11 +68,11 @@ 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())); }