cui/source/dialogs/SpellDialog.cxx | 23 ++++++++++++++++++++--- linguistic/source/dicimp.cxx | 13 ++++++++++++- sw/source/uibase/lingu/olmenu.cxx | 18 +++++++++++++++++- sw/source/uibase/shells/textsh1.cxx | 21 +++++++++++++++++---- 4 files changed, 66 insertions(+), 9 deletions(-)
New commits: commit 80dcb39edbffbe53d6833160afb7ad11cc51409d Author: László Németh <[email protected]> AuthorDate: Sun Dec 28 05:04:17 2025 +0100 Commit: László Németh <[email protected]> CommitDate: Sun Dec 28 23:48:55 2025 +0100 tdf#130695 sw linguistic: fix custom abbreviations linguistic: Abbreviations in the custom dictionary were accepted without the terminating dot, too (because bSimilarOnly comparison used by getEntry() removes all terminating dots during the dictionary look up.) Because "Add to [custom] dictionary" always removed the terminating dot, and direct editing of the custom dictionaries is still not so comfortable, the following user-friendly options were implemented for the abbreviations: – spell checking dialog: words with multiple dots – e.g. F.A.C.S. –, are always added to the custom dictionary with the terminating dot to reject the bad abbreviations: "e.g", "F.A.C.S" etc.; – context menu: add optional menu items to the context menu of the abbreviations to put the word with the terminating dot into the custom dictionaries, e.g. for the word "Corp.": * standard.dic (Corp) * standard.dic (Corp.) as menu items of the the Add to Dictionary submenu. Change-Id: I54532c0a63af8effe9f103e75e853703d7d57563 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196263 Reviewed-by: László Németh <[email protected]> Tested-by: Jenkins diff --git a/cui/source/dialogs/SpellDialog.cxx b/cui/source/dialogs/SpellDialog.cxx index 56c089c347f7..357a8fb2830e 100644 --- a/cui/source/dialogs/SpellDialog.cxx +++ b/cui/source/dialogs/SpellDialog.cxx @@ -603,6 +603,23 @@ IMPL_LINK_NOARG(SpellDialog, ChangeHdl, weld::Button&, void) m_xIgnorePB->grab_focus(); } +// words with multiple dots are added to the custom +// dictionary with the terminating dot +static bool lcl_IsStripDot( const OUString & rWord ) +{ + // strip the terminating dot, if there is no + // an extra dot inside the word, e.g. + // put "dots." as "dots" into the dictionary, + // but put "F.A.C.S." as "F.A.C.S.", not "F.A.C.S" + if ( rWord.endsWith(".") ) { + sal_Int32 nDotPos = rWord.indexOf("."); + if ( nDotPos == rWord.getLength() - 1 ) + return true; + } + + return false; +} + IMPL_LINK_NOARG(SpellDialog, ChangeAllHdl, weld::Button&, void) { auto xGuard(std::make_unique<UndoChangeGroupGuard>(*m_xSentenceED)); @@ -615,7 +632,7 @@ IMPL_LINK_NOARG(SpellDialog, ChangeAllHdl, weld::Button&, void) Reference<XDictionary> aXDictionary = LinguMgr::GetChangeAllList(); DictionaryError nAdded = AddEntryToDic( aXDictionary, aOldWord, true, - aString ); + aString, lcl_IsStripDot(aOldWord) ); if(nAdded == DictionaryError::NONE) { @@ -661,7 +678,7 @@ IMPL_LINK( SpellDialog, IgnoreAllHdl, weld::Button&, rButton, void ) OUString sErrorText(m_xSentenceED->GetErrorText()); DictionaryError nAdded = AddEntryToDic( aXDictionary, sErrorText, false, - OUString() ); + OUString(), lcl_IsStripDot(sErrorText) ); if (nAdded == DictionaryError::NONE) { std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl( @@ -914,7 +931,7 @@ void SpellDialog::AddToDictionaryExecute(const OUString& rItemId) DictionaryError nAddRes = DictionaryError::UNKNOWN; if (xDic.is()) { - nAddRes = AddEntryToDic( xDic, aNewWord, false, OUString() ); + nAddRes = AddEntryToDic( xDic, aNewWord, false, OUString(), lcl_IsStripDot(aNewWord) ); // save modified user-dictionary if it is persistent uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY ); if (xSavDic.is()) diff --git a/linguistic/source/dicimp.cxx b/linguistic/source/dicimp.cxx index 9c243c794933..24f6384cab3e 100644 --- a/linguistic/source/dicimp.cxx +++ b/linguistic/source/dicimp.cxx @@ -834,8 +834,19 @@ uno::Reference< XDictionaryEntry > SAL_CALL DictionaryNeo::getEntry( if (bNeedEntries) loadEntries( aMainURL ); + // first search: ignore the dots at the end of the + // dictionary words and at the end of the query word sal_Int32 nPos; - bool bFound = seekEntry( aWord, &nPos, true ); + bool bFound = seekEntry( aWord, &nPos,/*bSimilarOnly=*/true ); + // don't accept an abbreviation without the dot, i.e. when the + // searched word is without dot, but the dictionary word is there + // in the dictionary only with dot + if ( bFound && !aWord.endsWith(".") && aEntries[nPos]->getDictionaryWord().endsWith(".") ) + { + sal_Int32 nPos2; + if ( !seekEntry( aWord, &nPos2, /*bSimilarOnly=*/false ) ) + bFound = false; + } DBG_ASSERT(!bFound || nPos < static_cast<sal_Int32>(aEntries.size()), "lng : index out of range"); return bFound ? aEntries[ nPos ] diff --git a/sw/source/uibase/lingu/olmenu.cxx b/sw/source/uibase/lingu/olmenu.cxx index 15f49c8a9a20..85cc646ba7a2 100644 --- a/sw/source/uibase/lingu/olmenu.cxx +++ b/sw/source/uibase/lingu/olmenu.cxx @@ -324,6 +324,13 @@ SwSpellPopup::SwSpellPopup( m_aDics = xDicList->getDictionaries(); + OUString sWord; + if ( m_xSpellAlt.is() ) + sWord = m_xSpellAlt->getWord(); + // allow to put the word with terminating dot to the custom dictionaries + // using optional menu items + bool bPossibleAbbreviation = sWord.endsWith("."); + for (const uno::Reference<linguistic2::XDictionary>& rDic : m_aDics) { uno::Reference< linguistic2::XDictionary > xDicTmp = rDic; @@ -339,7 +346,9 @@ SwSpellPopup::SwSpellPopup( { // the extra 1 is because of the (possible) external // linguistic entry above - pMenu->InsertItem( nItemId, xDicTmp->getName() ); + pMenu->InsertItem( nItemId, bPossibleAbbreviation + ? xDicTmp->getName() + " (" + sWord.subView(0, sWord.getLength()-1) + ")" + : xDicTmp->getName() ); m_aDicNameSingle = xDicTmp->getName(); if (bUseImagesInMenus) @@ -358,6 +367,13 @@ SwSpellPopup::SwSpellPopup( } ++nItemId; + + // add an optional menu item for adding the word with dot to this dictionary + if ( bPossibleAbbreviation ) + { + pMenu->InsertItem( nItemId, xDicTmp->getName() + " (" + sWord + ")"); + ++nItemId; + } } } } diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index 981ab67b0709..e00d90ab7be6 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -926,7 +926,7 @@ bool lcl_DeleteChartColumns(const uno::Reference<chart2::XChartDocument>& xChart } } -static bool AddWordToWordbook(const uno::Reference<linguistic2::XDictionary>& xDictionary, SwWrtShell &rWrtSh) +static bool AddWordToWordbook(const uno::Reference<linguistic2::XDictionary>& xDictionary, SwWrtShell &rWrtSh, bool bAddWithDot = false) { if (!xDictionary) return false; @@ -937,7 +937,7 @@ static bool AddWordToWordbook(const uno::Reference<linguistic2::XDictionary>& xD return false; OUString sWord = xSpellAlt->getWord(); - linguistic::DictionaryError nAddRes = linguistic::AddEntryToDic(xDictionary, sWord, false, OUString()); + linguistic::DictionaryError nAddRes = linguistic::AddEntryToDic(xDictionary, sWord, false, OUString(), !bAddWithDot); if (linguistic::DictionaryError::NONE != nAddRes && xDictionary.is() && !xDictionary->getEntry(sWord).is()) { SvxDicError(rWrtSh.GetView().GetFrameWeld(), nAddRes); @@ -2363,7 +2363,7 @@ void SwTextShell::Execute(SfxRequest &rReq) } else if (sApplyText == "Spelling") { - AddWordToWordbook(LinguMgr::GetIgnoreAllList(), rWrtSh); + AddWordToWordbook(LinguMgr::GetIgnoreAllList(), rWrtSh ); } } break; @@ -2373,9 +2373,22 @@ void SwTextShell::Execute(SfxRequest &rReq) if (const SfxStringItem* pItem1 = rReq.GetArg<SfxStringItem>(FN_PARAM_1)) aDicName = pItem1->GetValue(); + // strip dot, if the extended dictionary name ends with ")", but not ".)", e.g. + // "standard.dic (F.A.C.S)" -> strip dot + // "standard.dic (F.A.C.S.)" -> with dot + bool bAddWithDot = false; + if ( aDicName.endsWith(")") ) + { + bAddWithDot = aDicName.endsWith(".)"); + // restore the dictionary name by stripping the parenthesized extension + sal_Int32 nHintPos = aDicName.indexOf(" ("); + if ( nHintPos > 0 ) + aDicName = aDicName.copy(0, nHintPos); + } + uno::Reference<linguistic2::XSearchableDictionaryList> xDicList(LinguMgr::GetDictionaryList()); uno::Reference<linguistic2::XDictionary> xDic = xDicList.is() ? xDicList->getDictionaryByName(aDicName) : nullptr; - if (AddWordToWordbook(xDic, rWrtSh)) + if (AddWordToWordbook(xDic, rWrtSh, bAddWithDot)) { // save modified user-dictionary if it is persistent uno::Reference<frame::XStorable> xSavDic(xDic, uno::UNO_QUERY);
