cui/source/dialogs/SpellDialog.cxx | 74 +++++++++++++++++++++++++++++++++++-- cui/source/inc/SpellDialog.hxx | 9 ++++ 2 files changed, 78 insertions(+), 5 deletions(-)
New commits: commit 506012898d0a41acbdefbe0a9b5a735df21c89ef Author: Justin Luth <[email protected]> AuthorDate: Thu Jul 10 16:21:41 2025 -0400 Commit: Justin Luth <[email protected]> CommitDate: Mon Sep 29 13:53:51 2025 +0200 tdf#126814 cui SpellDialog: don't clear ChangeAll dict unless ADD_ENTRY Coding was inspired/copy/pasted/adapted from mk-reviewed https://gerrit.libreoffice.org/c/core/+/160152 When the spell checker loses focus, that might mean that a spell-check is run on another document. In that case, the session's ChangeAll dictionary might have added new words (which must not be allowed to affect our document) and thus the resume function was clearing the ChangeAll dict again. However, unnecessarily killing the CorrectAll words is VERY annoying to the user doing the spell checking. So try to avoid clearing upon resume if possible. One way is to listen for any ADD_ENTRY events to the dictionary, and if there are none, then it is safe to continue using it. NOTE: it is already possible for two simultaneous spell checks to affect each other's results. Not every focus change results in calling SpellDialog::InvalidateDialog... Change-Id: Id8d80f61501c1750121b58df75b3796ff391ca59 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187667 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> diff --git a/cui/source/dialogs/SpellDialog.cxx b/cui/source/dialogs/SpellDialog.cxx index 6071bf6abb74..1e915376aff1 100644 --- a/cui/source/dialogs/SpellDialog.cxx +++ b/cui/source/dialogs/SpellDialog.cxx @@ -36,6 +36,7 @@ #include <linguistic/misc.hxx> #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/linguistic2/DictionaryEventFlags.hpp> #include <com/sun/star/linguistic2/XDictionary.hpp> #include <com/sun/star/linguistic2/XSpellAlternatives.hpp> #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> @@ -89,6 +90,52 @@ namespace namespace svx{ + +class DictionaryEventListener : public cppu::WeakImplHelper<XDictionaryEventListener> +{ +public: + DictionaryEventListener(uno::Reference<linguistic2::XDictionary> xDict, SpellDialog& rDialog); + void stopListening(); + + // XEventListener + void SAL_CALL disposing(const css::lang::EventObject& rSource) override; + + // XDictionaryEventListener + void SAL_CALL processDictionaryEvent(const DictionaryEvent& rDicEvt) override; + +private: + uno::Reference<linguistic2::XDictionary> m_xDict; + SpellDialog& m_rDialog; +}; + +DictionaryEventListener::DictionaryEventListener(uno::Reference<linguistic2::XDictionary> xDict, + SpellDialog& rDialog) + : m_xDict(std::move(xDict)) + , m_rDialog(rDialog) +{ + if (m_xDict) + m_xDict->addDictionaryEventListener(this); +} + +void DictionaryEventListener::stopListening() +{ + if (m_xDict) + m_xDict->removeDictionaryEventListener(this); +} + +void DictionaryEventListener::disposing(const css::lang::EventObject& rSource) +{ + if (rSource.Source == m_xDict) + m_xDict->removeDictionaryEventListener(this); +} + +void DictionaryEventListener::processDictionaryEvent(const DictionaryEvent& rDicEvt) +{ + m_rDialog.processDictionaryEvent(rDicEvt); +} + +// class SpellUndoAction_Impl + class SpellUndoAction_Impl : public SfxUndoAction { SpellUndoAction m_nId; @@ -243,6 +290,8 @@ SpellDialog::SpellDialog(SpellDialogChildWindow* pChildWindow, // disable controls if service is missing m_xDialog->set_sensitive(xSpell.is()); + m_xChangeAllDictListener.set(new DictionaryEventListener(LinguMgr::GetChangeAllList(), *this)); + // further initialization happens in Initialize() to prevent virtual // calls from the constructor } @@ -264,6 +313,9 @@ SpellDialog::~SpellDialog() pImpl.reset(); } + + m_xChangeAllDictListener->stopListening(); + m_xChangeAllDictListener.clear(); } void SpellDialog::UpdateBoxes_Impl(bool bCallFromSelectHdl) @@ -440,7 +492,7 @@ IMPL_LINK( SpellDialog, ExtClickHdl, weld::Button&, rBtn, void ) IMPL_LINK_NOARG(SpellDialog, CheckGrammarHdl, weld::Toggleable&, void) { rParent.SetGrammarChecking(m_xCheckGrammarCB->get_active()); - Impl_Restore(true); + Impl_Restore(/*UseSavedSentence=*/true, /*ClearChangeAll=*/true); } void SpellDialog::StartSpellOptDlg_Impl() @@ -495,6 +547,18 @@ OUString SpellDialog::getReplacementString() const return getDotReplacementString(sOrigString, sReplacement); } +void SpellDialog::processDictionaryEvent(const DictionaryEvent& rDicEvt) +{ + if (rDicEvt.nEvent & DictionaryEventFlags::ADD_ENTRY) + { + // The ChangeAll dictionary is shared by all documents. + // If something changed it while this dialog did not have the focus, + // when spell checking resumes, it must be cleared and start adding words all over again. + if (!m_xDialog->has_toplevel_focus() && rDicEvt.Source == LinguMgr::GetChangeAllList()) + m_bResumeClearsChangeAllDict = true; + } +} + IMPL_LINK_NOARG(SpellDialog, DoubleClickChangeHdl, weld::TreeView&, bool) { ChangeHdl(*m_xChangePB); @@ -669,10 +733,11 @@ IMPL_LINK( SpellDialog, DialogUndoHdl, SpellUndoAction_Impl&, rAction, void ) } } -void SpellDialog::Impl_Restore(bool bUseSavedSentence) +void SpellDialog::Impl_Restore(bool bUseSavedSentence, bool bClearChangeAll) { //clear the "ChangeAllList" - LinguMgr::GetChangeAllList()->clear(); + if (bClearChangeAll) + LinguMgr::GetChangeAllList()->clear(); //get a new sentence m_xSentenceED->SetText(OUString()); m_xSentenceED->ResetModified(); @@ -685,7 +750,8 @@ IMPL_LINK_NOARG(SpellDialog, IgnoreHdl, weld::Button&, void) { if (m_sResumeST == m_xIgnorePB->get_label()) { - Impl_Restore(false); + Impl_Restore(/*UseSavedSentence=*/false, m_bResumeClearsChangeAllDict); + m_bResumeClearsChangeAllDict = false; } else { diff --git a/cui/source/inc/SpellDialog.hxx b/cui/source/inc/SpellDialog.hxx index fae28d10ceac..c7f8c0ce722a 100644 --- a/cui/source/inc/SpellDialog.hxx +++ b/cui/source/inc/SpellDialog.hxx @@ -19,6 +19,7 @@ #pragma once #include <sfx2/basedlgs.hxx> +#include <com/sun/star/linguistic2/DictionaryEvent.hpp> #include <com/sun/star/uno/Reference.hxx> @@ -127,10 +128,12 @@ public: }; class SpellDialogChildWindow; +class DictionaryEventListener; class SpellDialog : public SfxModelessDialogController { friend class SentenceEditWindow_Impl; + friend class DictionaryEventListener; private: OUString m_sResumeST; OUString m_sIgnoreOnceST; @@ -149,6 +152,9 @@ private: css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpell; + rtl::Reference<DictionaryEventListener> m_xChangeAllDictListener; + bool m_bResumeClearsChangeAllDict = false; // clear if another instance changed this word list + std::unordered_map<OUString, OUString> m_aDictIdToName; std::unique_ptr<weld::Label> m_xAltTitle; @@ -200,7 +206,7 @@ private: void SpellContinue_Impl(std::unique_ptr<UndoChangeGroupGuard>* pGuard = nullptr, bool UseSavedSentence = false, bool bIgnoreCurrentError = false ); void LockFocusChanges( bool bLock ) {bFocusLocked = bLock;} void ToplevelFocusChanged(); - void Impl_Restore(bool bUseSavedSentence); + void Impl_Restore(bool bUseSavedSentence, bool bClearChangeAll); LanguageType GetSelectedLang_Impl() const; @@ -215,6 +221,7 @@ private: protected: OUString getReplacementString() const; + void processDictionaryEvent(const css::linguistic2::DictionaryEvent& rDicEvt); public: SpellDialog(
