diff options
Diffstat (limited to 'sw/source/core/unocore/unotextmarkup.cxx')
-rw-r--r-- | sw/source/core/unocore/unotextmarkup.cxx | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/sw/source/core/unocore/unotextmarkup.cxx b/sw/source/core/unocore/unotextmarkup.cxx new file mode 100644 index 0000000000..4bbc860160 --- /dev/null +++ b/sw/source/core/unocore/unotextmarkup.cxx @@ -0,0 +1,514 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unotextmarkup.hxx> + +#include <comphelper/servicehelper.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <svl/listener.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <SwSmartTagMgr.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/text/TextMarkupType.hpp> +#include <com/sun/star/text/TextMarkupDescriptor.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/container/XStringKeyMap.hpp> +#include <ndtxt.hxx> +#include <SwGrammarMarkUp.hxx> +#include <TextCursorHelper.hxx> +#include <GrammarContact.hxx> + +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +#include <pam.hxx> + +#include <unotextrange.hxx> +#include <modeltoviewhelper.hxx> + +using namespace ::com::sun::star; + +struct SwXTextMarkup::Impl + : public SvtListener +{ + SwTextNode* m_pTextNode; + ModelToViewHelper const m_ConversionMap; + + Impl(SwTextNode* const pTextNode, ModelToViewHelper aMap) + : m_pTextNode(pTextNode) + , m_ConversionMap(std::move(aMap)) + { + if(m_pTextNode) + StartListening(pTextNode->GetNotifier()); + } + + virtual void Notify(const SfxHint& rHint) override; +}; + +SwXTextMarkup::SwXTextMarkup( + SwTextNode *const pTextNode, const ModelToViewHelper& rMap) + : m_pImpl(new Impl(pTextNode, rMap)) +{ +} + +SwXTextMarkup::~SwXTextMarkup() +{ +} + +SwTextNode* SwXTextMarkup::GetTextNode() +{ + return m_pImpl->m_pTextNode; +} + +void SwXTextMarkup::ClearTextNode() +{ + m_pImpl->m_pTextNode = nullptr; + m_pImpl->EndListeningAll(); +} + +const ModelToViewHelper& SwXTextMarkup::GetConversionMap() const +{ + return m_pImpl->m_ConversionMap; +} + +uno::Reference< container::XStringKeyMap > SAL_CALL SwXTextMarkup::getMarkupInfoContainer() +{ + return new SwXStringKeyMap; +} + +void SAL_CALL SwXTextMarkup::commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange, + const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) +{ + SolarMutexGuard aGuard; + + if (auto pRange = dynamic_cast<SwXTextRange*>(xRange.get())) + { + SwDoc& rDoc = pRange->GetDoc(); + + SwUnoInternalPaM aPam(rDoc); + + ::sw::XTextRangeToSwPaM(aPam, xRange); + + auto [startPos, endPos] = aPam.StartEnd(); // SwPosition* + + commitStringMarkup (nType, aIdentifier, startPos->GetContentIndex(), endPos->GetContentIndex() - startPos->GetContentIndex(), xMarkupInfoContainer); + } + else if (auto pCursor = dynamic_cast<OTextCursorHelper*>(xRange.get())) + { + SwPaM & rPam(*pCursor->GetPaM()); + + auto [startPos, endPos] = rPam.StartEnd(); // SwPosition* + + commitStringMarkup (nType, aIdentifier, startPos->GetContentIndex(), endPos->GetContentIndex() - startPos->GetContentIndex(), xMarkupInfoContainer); + } +} + +void SAL_CALL SwXTextMarkup::commitStringMarkup( + ::sal_Int32 nType, + const OUString & rIdentifier, + ::sal_Int32 nStart, + ::sal_Int32 nLength, + const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) +{ + SolarMutexGuard aGuard; + + // paragraph already dead or modified? + if (!m_pImpl->m_pTextNode || nLength <= 0) + return; + + if ( nType == text::TextMarkupType::SMARTTAG && + !SwSmartTagMgr::Get().IsSmartTagTypeEnabled( rIdentifier ) ) + return; + + // get appropriate list to use... + SwWrongList* pWList = nullptr; + bool bRepaint = false; + if ( nType == text::TextMarkupType::SPELLCHECK ) + { + pWList = m_pImpl->m_pTextNode->GetWrong(); + if ( !pWList ) + { + pWList = new SwWrongList( WRONGLIST_SPELL ); + m_pImpl->m_pTextNode->SetWrong( std::unique_ptr<SwWrongList>(pWList) ); + } + } + else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) + { + sw::GrammarContact* pGrammarContact = sw::getGrammarContactFor(*m_pImpl->m_pTextNode); + if( pGrammarContact ) + { + pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true); + OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" ); + } + else + { + pWList = m_pImpl->m_pTextNode->GetGrammarCheck(); + if ( !pWList ) + { + m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() ); + pWList = m_pImpl->m_pTextNode->GetGrammarCheck(); + } + } + bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck(); + if( pWList->GetBeginInv() < COMPLETE_STRING ) + static_cast<SwGrammarMarkUp*>(pWList)->ClearGrammarList(); + } + else if ( nType == text::TextMarkupType::SMARTTAG ) + { + pWList = m_pImpl->m_pTextNode->GetSmartTags(); + if ( !pWList ) + { + pWList = new SwWrongList( WRONGLIST_SMARTTAG ); + m_pImpl->m_pTextNode->SetSmartTags( std::unique_ptr<SwWrongList>(pWList) ); + } + } + else + { + OSL_FAIL( "Unknown mark-up type" ); + return; + } + + const ModelToViewHelper::ModelPosition aStartPos = + m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart ); + const ModelToViewHelper::ModelPosition aEndPos = + m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart + nLength - 1); + + const bool bStartInField = aStartPos.mbIsField; + const bool bEndInField = aEndPos.mbIsField; + bool bCommit = false; + + if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos ) + { + nStart = aStartPos.mnSubPos; + const sal_Int32 nFieldPosModel = aStartPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + + SwWrongList* pSubList = pWList->SubList( nInsertPos ); + if ( !pSubList ) + { + if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) + pSubList = new SwGrammarMarkUp(); + else + pSubList = new SwWrongList( pWList->GetWrongListType() ); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + + pWList = pSubList; + bCommit = true; + } + else if ( !bStartInField && !bEndInField ) + { + nStart = aStartPos.mnPos; + bCommit = true; + nLength = aEndPos.mnPos + 1 - aStartPos.mnPos; + } + else if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) + { + bCommit = true; + nStart = aStartPos.mnPos; + sal_Int32 nEnd = aEndPos.mnPos; + if( bStartInField && nType != text::TextMarkupType::SENTENCE ) + { + const sal_Int32 nFieldPosModel = aStartPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + SwWrongList* pSubList = pWList->SubList( nInsertPos ); + if ( !pSubList ) + { + pSubList = new SwGrammarMarkUp(); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + const sal_Int32 nTmpStart = + m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos); + const sal_Int32 nTmpLen = + m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos + 1) + - nTmpStart - aStartPos.mnSubPos; + if( nTmpLen > 0 ) + { + pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen ); + } + ++nStart; + } + if( bEndInField && nType != text::TextMarkupType::SENTENCE ) + { + const sal_Int32 nFieldPosModel = aEndPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + SwWrongList* pSubList = pWList->SubList( nInsertPos ); + if ( !pSubList ) + { + pSubList = new SwGrammarMarkUp(); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1; + pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen ); + } + else + ++nEnd; + if( nEnd > nStart ) + nLength = nEnd - nStart; + else + bCommit = false; + } + + if ( bCommit ) + { + if( nType == text::TextMarkupType::SENTENCE ) + static_cast<SwGrammarMarkUp*>(pWList)->setSentence( nStart ); + else + pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength ); + } + + if( bRepaint ) + sw::finishGrammarCheckFor(*m_pImpl->m_pTextNode); +} + +static void lcl_commitGrammarMarkUp( + const ModelToViewHelper& rConversionMap, + SwGrammarMarkUp* pWList, + ::sal_Int32 nType, + const OUString & rIdentifier, + ::sal_Int32 nStart, + ::sal_Int32 nLength, + const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) +{ + OSL_ENSURE( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE, "Wrong mark-up type" ); + const ModelToViewHelper::ModelPosition aStartPos = + rConversionMap.ConvertToModelPosition( nStart ); + const ModelToViewHelper::ModelPosition aEndPos = + rConversionMap.ConvertToModelPosition( nStart + nLength - 1); + + const bool bStartInField = aStartPos.mbIsField; + const bool bEndInField = aEndPos.mbIsField; + bool bCommit = false; + + if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos ) + { + nStart = aStartPos.mnSubPos; + const sal_Int32 nFieldPosModel = aStartPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + + SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos )); + if ( !pSubList ) + { + pSubList = new SwGrammarMarkUp(); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + + pWList = pSubList; + bCommit = true; + } + else if ( !bStartInField && !bEndInField ) + { + nStart = aStartPos.mnPos; + bCommit = true; + nLength = aEndPos.mnPos + 1 - aStartPos.mnPos; + } + else + { + bCommit = true; + nStart = aStartPos.mnPos; + sal_Int32 nEnd = aEndPos.mnPos; + if( bStartInField && nType != text::TextMarkupType::SENTENCE ) + { + const sal_Int32 nFieldPosModel = aStartPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos )); + if ( !pSubList ) + { + pSubList = new SwGrammarMarkUp(); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + const sal_Int32 nTmpStart = rConversionMap.ConvertToViewPosition( aStartPos.mnPos ); + const sal_Int32 nTmpLen = rConversionMap.ConvertToViewPosition( aStartPos.mnPos + 1 ) + - nTmpStart - aStartPos.mnSubPos; + if( nTmpLen > 0 ) + pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen ); + ++nStart; + } + if( bEndInField && nType != text::TextMarkupType::SENTENCE ) + { + const sal_Int32 nFieldPosModel = aEndPos.mnPos; + const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); + SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos )); + if ( !pSubList ) + { + pSubList = new SwGrammarMarkUp(); + pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); + } + const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1; + pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen ); + } + else + ++nEnd; + if( nEnd > nStart ) + nLength = nEnd - nStart; + else + bCommit = false; + } + + if ( bCommit ) + { + if( nType == text::TextMarkupType::SENTENCE ) + pWList->setSentence( nStart+nLength ); + else + pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength ); + } +} + +void SAL_CALL SwXTextMarkup::commitMultiTextMarkup( + const uno::Sequence< text::TextMarkupDescriptor > &rMarkups ) +{ + SolarMutexGuard aGuard; + + // paragraph already dead or modified? + if (!m_pImpl->m_pTextNode) + return; + + // for grammar checking there should be exactly one sentence markup + // and 0..n grammar markups. + // Different markups are not expected but may be applied anyway since + // that should be no problem... + // but it has to be implemented, at the moment only this function is for + // grammar markups and sentence markup only! + const text::TextMarkupDescriptor *pSentenceMarkUp = nullptr; + for( const text::TextMarkupDescriptor &rDesc : rMarkups ) + { + if (rDesc.nType == text::TextMarkupType::SENTENCE) + { + if (pSentenceMarkUp != nullptr) + throw lang::IllegalArgumentException(); // there is already one sentence markup + pSentenceMarkUp = &rDesc; + } + else if( rDesc.nType != text::TextMarkupType::PROOFREADING ) + return; + } + + if( pSentenceMarkUp == nullptr ) + return; + + // get appropriate list to use... + SwGrammarMarkUp* pWList = nullptr; + bool bRepaint = false; + sw::GrammarContact* pGrammarContact = sw::getGrammarContactFor(*m_pImpl->m_pTextNode); + if( pGrammarContact ) + { + pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true); + OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" ); + } + else + { + pWList = m_pImpl->m_pTextNode->GetGrammarCheck(); + if ( !pWList ) + { + m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() ); + pWList = m_pImpl->m_pTextNode->GetGrammarCheck(); + pWList->SetInvalid( 0, COMPLETE_STRING ); + } + } + bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck(); + + bool bAcceptGrammarError = false; + if( pWList->GetBeginInv() < COMPLETE_STRING ) + { + const ModelToViewHelper::ModelPosition aSentenceEnd = + m_pImpl->m_ConversionMap.ConvertToModelPosition( + pSentenceMarkUp->nOffset + pSentenceMarkUp->nLength ); + bAcceptGrammarError = aSentenceEnd.mnPos > pWList->GetBeginInv(); + pWList->ClearGrammarList( aSentenceEnd.mnPos ); + } + + if( bAcceptGrammarError ) + { + for( const text::TextMarkupDescriptor &rDesc : rMarkups ) + { + lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType, + rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer ); + } + } + else + { + bRepaint = false; + const text::TextMarkupDescriptor &rDesc = *pSentenceMarkUp; + lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType, + rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer ); + } + + if( bRepaint ) + sw::finishGrammarCheckFor(*m_pImpl->m_pTextNode); +} + +void SwXTextMarkup::Impl::Notify(const SfxHint& rHint) +{ + DBG_TESTSOLARMUTEX(); + if(rHint.GetId() == SfxHintId::Dying) + { + m_pTextNode = nullptr; + } +} + +SwXStringKeyMap::SwXStringKeyMap() +{ +} + +uno::Any SAL_CALL SwXStringKeyMap::getValue(const OUString & aKey) +{ + std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey ); + if ( aIter == maMap.end() ) + throw container::NoSuchElementException(); + + return (*aIter).second; +} + +sal_Bool SAL_CALL SwXStringKeyMap::hasValue(const OUString & aKey) +{ + return maMap.find( aKey ) != maMap.end(); +} + +void SAL_CALL SwXStringKeyMap::insertValue(const OUString & aKey, const uno::Any & aValue) +{ + std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey ); + if ( aIter != maMap.end() ) + throw container::ElementExistException(); + + maMap[ aKey ] = aValue; +} + +::sal_Int32 SAL_CALL SwXStringKeyMap::getCount() +{ + return maMap.size(); +} + +OUString SAL_CALL SwXStringKeyMap::getKeyByIndex(::sal_Int32 nIndex) +{ + if ( o3tl::make_unsigned(nIndex) >= maMap.size() ) + throw lang::IndexOutOfBoundsException(); + + return OUString(); +} + +uno::Any SAL_CALL SwXStringKeyMap::getValueByIndex(::sal_Int32 nIndex) +{ + if ( o3tl::make_unsigned(nIndex) >= maMap.size() ) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |