summaryrefslogtreecommitdiffstats
path: root/sw/source/core/doc/docedt.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/doc/docedt.cxx')
-rw-r--r--sw/source/core/doc/docedt.cxx886
1 files changed, 886 insertions, 0 deletions
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
new file mode 100644
index 000000000..07902efe3
--- /dev/null
+++ b/sw/source/core/doc/docedt.cxx
@@ -0,0 +1,886 @@
+/* -*- 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 <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <acorrect.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docsh.hxx>
+#include <docary.hxx>
+#include <mdiexp.hxx>
+#include <mvsave.hxx>
+#include <redline.hxx>
+#include <rootfrm.hxx>
+#include <splargs.hxx>
+#include <swcrsr.hxx>
+#include <txtfrm.hxx>
+#include <unoflatpara.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <docedt.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <frameformats.hxx>
+
+#include <vector>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::i18n;
+
+
+void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos,
+ const SwNodeIndex* pInsertPos )
+{
+ SwPosition aPos(rStartPos);
+ for(const SaveFly & rSave : rArr)
+ {
+ // create new anchor
+ SwFrameFormat* pFormat = rSave.pFrameFormat;
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+
+ if (rSave.isAtInsertNode)
+ {
+ if( pInsertPos != nullptr )
+ {
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ aPos.nNode = *pInsertPos;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
+ rSave.nContentIndex);
+ }
+ else
+ {
+ assert(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR);
+ aPos = rStartPos;
+ }
+ }
+ else
+ {
+ aPos.nNode = rStartPos.nNode;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()), 0);
+ }
+ }
+ else
+ {
+ aPos.nNode = rStartPos.nNode.GetIndex() + rSave.nNdDiff;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
+ rSave.nNdDiff == 0
+ ? rStartPos.nContent.GetIndex() + rSave.nContentIndex
+ : rSave.nContentIndex);
+ }
+
+ aAnchor.SetAnchor( &aPos );
+ pFormat->GetDoc()->GetSpzFrameFormats()->push_back( pFormat );
+ // SetFormatAttr should call Modify() and add it to the node
+ pFormat->SetFormatAttr( aAnchor );
+ SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode();
+ if (pCNd && pCNd->getLayoutFrame(pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
+ pFormat->MakeFrames();
+ }
+ sw::CheckAnchoredFlyConsistency(*rStartPos.nNode.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
+{
+ SwFrameFormats& rFormats = *rRg.aStart.GetNode().GetDoc()->GetSpzFrameFormats();
+ for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ SwFrameFormat *const pFormat = rFormats[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
+ {
+ SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? pAPos->nContent.GetIndex()
+ : 0,
+ pFormat, false );
+ rArr.push_back( aSave );
+ pFormat->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pFormat->SetFormatAttr(aAnchor);
+ rFormats.erase( rFormats.begin() + n-- );
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(*rRg.aStart.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
+ SaveFlyArr& rArr, bool bMoveAllFlys )
+{
+ SwFrameFormats& rFormats = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrameFormats();
+ SwFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+
+ const SwPosition* pPos = rPam.Start();
+ const SwNodeIndex& rSttNdIdx = pPos->nNode;
+
+ SwPosition atParaEnd(*rPam.End());
+ if (bMoveAllFlys)
+ {
+ assert(!rPam.End()->nNode.GetNode().IsTextNode() // can be table end-node
+ || rPam.End()->nContent.GetIndex() == rPam.End()->nNode.GetNode().GetTextNode()->Len());
+ ++atParaEnd.nNode;
+ atParaEnd.nContent.Assign(atParaEnd.nNode.GetNode().GetContentNode(), 0);
+ }
+
+ for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ pFormat = rFormats[n];
+ pAnchor = &pFormat->GetAnchor();
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ const SwNodeIndex* pContentIdx;
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ // do not move if the InsPos is in the ContentArea of the Fly
+ ( nullptr == ( pContentIdx = pFormat->GetContent().GetContentIdx() ) ||
+ !(*pContentIdx < rInsPos.nNode &&
+ rInsPos.nNode < pContentIdx->GetNode().EndOfSectionIndex())))
+ {
+ bool bInsPos = false;
+
+ if ( (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && IsSelectFrameAnchoredAtPara(*pAPos, *rPam.Start(), atParaEnd,
+ bMoveAllFlys
+ ? DelContentType::CheckNoCntnt|DelContentType::AllMask
+ : DelContentType::AllMask))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos.nNode == pAPos->nNode)))
+ || (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos == *pAPos))))
+ {
+ SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? (pAPos->nNode == rSttNdIdx)
+ ? pAPos->nContent.GetIndex() - rPam.Start()->nContent.GetIndex()
+ : pAPos->nContent.GetIndex()
+ : 0,
+ pFormat, bInsPos );
+ rArr.push_back( aSave );
+ pFormat->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pFormat->SetFormatAttr(aAnchor);
+ rFormats.erase( rFormats.begin() + n-- );
+ }
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(*rPam.GetPoint()->nNode.GetNode().GetDoc());
+}
+
+/// Delete and move all Flys at the paragraph, that are within the selection.
+/// If there is a Fly at the SPoint, it is moved onto the Mark.
+void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
+ const SwNodeIndex& rPtNdIdx,
+ SwIndex const*const pMkIdx, SwIndex const*const pPtIdx)
+{
+ assert((pMkIdx == nullptr) == (pPtIdx == nullptr));
+ SwPosition const point(pPtIdx
+ ? SwPosition(rPtNdIdx, *pPtIdx)
+ : SwPosition(rPtNdIdx));
+ SwPosition const mark(pPtIdx
+ ? SwPosition(rMkNdIdx, *pMkIdx)
+ : SwPosition(rMkNdIdx));
+ SwPosition const& rStart = mark <= point ? mark : point;
+ SwPosition const& rEnd = mark <= point ? point : mark;
+
+ SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
+ SwFrameFormats& rTable = *pDoc->GetSpzFrameFormats();
+ for ( auto i = rTable.size(); i; )
+ {
+ SwFrameFormat *pFormat = rTable[--i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnch.GetContentAnchor();
+ if (pAPos &&
+ (((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ && IsSelectFrameAnchoredAtPara(*pAPos, rStart, rEnd, pPtIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))
+ || ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ && IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, pPtIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))))
+ {
+ // If the Fly is deleted, all Flys in its content have to be deleted too.
+ const SwFormatContent &rContent = pFormat->GetContent();
+ // But only fly formats own their content, not draw formats.
+ if (rContent.GetContentIdx() && pFormat->Which() == RES_FLYFRMFMT)
+ {
+ DelFlyInRange( *rContent.GetContentIdx(),
+ SwNodeIndex( *rContent.GetContentIdx()->
+ GetNode().EndOfSectionNode() ));
+ // Position could have been moved!
+ if (i > rTable.size())
+ i = rTable.size();
+ else if (pFormat != rTable[i])
+ i = std::distance(rTable.begin(), rTable.find( pFormat ));
+ }
+
+ pDoc->getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+
+ // DelLayoutFormat can also trigger the deletion of objects.
+ if (i > rTable.size())
+ i = rTable.size();
+ }
+ }
+}
+
+// #i59534: Redo of insertion of multiple text nodes runs into trouble
+// because of unnecessary expanded redlines
+// From now on this class saves the redline positions of all redlines which ends exact at the
+// insert position (node _and_ content index)
+SaveRedlEndPosForRestore::SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, sal_Int32 nCnt )
+ : mnSaveContent( nCnt )
+{
+ SwNode& rNd = rInsIdx.GetNode();
+ SwDoc* pDest = rNd.GetDoc();
+ if( !pDest->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwRedlineTable::size_type nFndPos;
+ const SwPosition* pEnd;
+ SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetContentNode(), nCnt ));
+ pDest->getIDocumentRedlineAccess().GetRedline( aSrcPos, &nFndPos );
+ const SwRangeRedline* pRedl;
+ while( nFndPos--
+ && *( pEnd = ( pRedl = pDest->getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos ] )->End() ) == aSrcPos
+ && *pRedl->Start() < aSrcPos )
+ {
+ if( !mpSaveIndex )
+ {
+ mpSaveIndex.reset(new SwNodeIndex( rInsIdx, -1 ));
+ }
+ mvSavArr.push_back( const_cast<SwPosition*>(pEnd) );
+ }
+ }
+}
+
+SaveRedlEndPosForRestore::~SaveRedlEndPosForRestore()
+{
+ mpSaveIndex.reset();
+}
+
+void SaveRedlEndPosForRestore::Restore()
+{
+ if (mvSavArr.empty())
+ return;
+ ++(*mpSaveIndex);
+ SwContentNode* pNode = mpSaveIndex->GetNode().GetContentNode();
+ // If there's no content node at the remembered position, we will not restore the old position
+ // This may happen if a table (or section?) will be inserted.
+ if( pNode )
+ {
+ SwPosition aPos( *mpSaveIndex, SwIndex( pNode, mnSaveContent ));
+ for( auto n = mvSavArr.size(); n; )
+ *mvSavArr[ --n ] = aPos;
+ }
+}
+
+/// Convert list of ranges of whichIds to a corresponding list of whichIds
+static std::vector<sal_uInt16> lcl_RangesToVector(const sal_uInt16 * pRanges)
+{
+ std::vector<sal_uInt16> aResult;
+
+ int i = 0;
+ while (pRanges[i] != 0)
+ {
+ OSL_ENSURE(pRanges[i+1] != 0, "malformed ranges");
+
+ for (sal_uInt16 j = pRanges[i]; j <= pRanges[i+1]; j++)
+ aResult.push_back(j);
+
+ i += 2;
+ }
+
+ return aResult;
+}
+
+void sw_GetJoinFlags( SwPaM& rPam, bool& rJoinText, bool& rJoinPrev )
+{
+ rJoinText = false;
+ rJoinPrev = false;
+ if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
+ {
+ const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
+ SwTextNode *pSttNd = pStt->nNode.GetNode().GetTextNode();
+ if( pSttNd )
+ {
+ SwTextNode *pEndNd = pEnd->nNode.GetNode().GetTextNode();
+ rJoinText = nullptr != pEndNd;
+ if( rJoinText )
+ {
+ bool bExchange = pStt == rPam.GetPoint();
+ if( !pStt->nContent.GetIndex() &&
+ pEndNd->GetText().getLength() != pEnd->nContent.GetIndex())
+ bExchange = !bExchange;
+ if( bExchange )
+ rPam.Exchange();
+ rJoinPrev = rPam.GetPoint() == pStt;
+ OSL_ENSURE( !pStt->nContent.GetIndex() &&
+ pEndNd->GetText().getLength() != pEnd->nContent.GetIndex()
+ ? (rPam.GetPoint()->nNode < rPam.GetMark()->nNode)
+ : (rPam.GetPoint()->nNode > rPam.GetMark()->nNode),
+ "sw_GetJoinFlags");
+ }
+ }
+ }
+}
+
+bool sw_JoinText( SwPaM& rPam, bool bJoinPrev )
+{
+ SwNodeIndex aIdx( rPam.GetPoint()->nNode );
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ SwNodeIndex aOldIdx( aIdx );
+ SwTextNode *pOldTextNd = pTextNd;
+
+ if( pTextNd && pTextNd->CanJoinNext( &aIdx ) )
+ {
+ SwDoc* pDoc = rPam.GetDoc();
+ if( bJoinPrev )
+ {
+ // We do not need to handle xmlids in this case, because
+ // it is only invoked if one paragraph is/becomes completely empty
+ // (see sw_GetJoinFlags)
+ {
+ // If PageBreaks are deleted/set, it must not be added to the Undo history!
+ // Also, deleting the Node is not added to the Undo history!
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ /* PageBreaks, PageDesc, ColumnBreaks */
+ // If we need to change something about the logic to copy the PageBreaks,
+ // PageDesc, etc. we also have to change SwUndoDelete.
+ // There, we copy the AUTO PageBreak from the GetMarkNode!
+
+ /* The MarkNode */
+ pTextNd = aIdx.GetNode().GetTextNode();
+ if (pTextNd->HasSwAttrSet())
+ {
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState(
+ RES_BREAK, false, &pItem ) )
+ pTextNd->ResetAttr( RES_BREAK );
+ if( pTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState(
+ RES_PAGEDESC, false, &pItem ) )
+ pTextNd->ResetAttr( RES_PAGEDESC );
+ }
+
+ /* The PointNode */
+ if( pOldTextNd->HasSwAttrSet() )
+ {
+ const SfxPoolItem* pItem;
+ SfxItemSet aSet( pDoc->GetAttrPool(), aBreakSetRange );
+ const SfxItemSet* pSet = pOldTextNd->GetpSwAttrSet();
+ if( SfxItemState::SET == pSet->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( aSet.Count() )
+ pTextNd->SetAttr( aSet );
+ }
+ pOldTextNd->FormatToTextAttr( pTextNd );
+
+ const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(pDoc, aOldIdx.GetIndex(), SAL_MAX_INT32);
+
+ SwIndex aAlphaIdx(pTextNd);
+ pOldTextNd->CutText( pTextNd, aAlphaIdx, SwIndex(pOldTextNd),
+ pOldTextNd->Len() );
+ SwPosition aAlphaPos( aIdx, aAlphaIdx );
+ pDoc->CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, true );
+
+ // move all Bookmarks/TOXMarks
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( pDoc, aIdx.GetIndex() );
+
+ // If the passed PaM is not in the Cursor ring,
+ // treat it separately (e.g. when it's being called from AutoFormat)
+ if( pOldTextNd == rPam.GetBound().nContent.GetIdxReg() )
+ rPam.GetBound() = aAlphaPos;
+ if( pOldTextNd == rPam.GetBound( false ).nContent.GetIdxReg() )
+ rPam.GetBound( false ) = aAlphaPos;
+ }
+ // delete the Node, at last!
+ SwNode::Merge const eOldMergeFlag(pOldTextNd->GetRedlineMergeFlag());
+ if (eOldMergeFlag == SwNode::Merge::First
+ && !pTextNd->IsCreateFrameWhenHidingRedlines())
+ {
+ sw::MoveDeletedPrevFrames(*pOldTextNd, *pTextNd);
+ }
+ pDoc->GetNodes().Delete( aOldIdx );
+ sw::CheckResetRedlineMergeFlag(*pTextNd,
+ eOldMergeFlag == SwNode::Merge::NonFirst
+ ? sw::Recreate::Predecessor
+ : sw::Recreate::No);
+ }
+ else
+ {
+ SwTextNode* pDelNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd->Len() )
+ pDelNd->FormatToTextAttr( pTextNd );
+ else
+ {
+ /* This case was missed:
+
+ <something></something> <-- pTextNd
+ <other>ccc</other> <-- pDelNd
+
+ <something> and <other> are paragraph
+ attributes. The attribute <something> stayed if not
+ overwritten by an attribute in "ccc". Fixed by
+ first resetting all character attributes in first
+ paragraph (pTextNd).
+ */
+ std::vector<sal_uInt16> aShorts =
+ lcl_RangesToVector(aCharFormatSetRange);
+ pTextNd->ResetAttr(aShorts);
+
+ if( pDelNd->HasSwAttrSet() )
+ {
+ // only copy the character attributes
+ SfxItemSet aTmpSet( pDoc->GetAttrPool(), aCharFormatSetRange );
+ aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
+ pTextNd->SetAttr( aTmpSet );
+ }
+ }
+
+ pDoc->CorrRel( aIdx, *rPam.GetPoint(), 0, true );
+ // #i100466# adjust given <rPam>, if it does not belong to the cursors
+ if ( pDelNd == rPam.GetBound().nContent.GetIdxReg() )
+ {
+ rPam.GetBound() = SwPosition( SwNodeIndex( *pTextNd ), SwIndex( pTextNd ) );
+ }
+ if( pDelNd == rPam.GetBound( false ).nContent.GetIdxReg() )
+ {
+ rPam.GetBound( false ) = SwPosition( SwNodeIndex( *pTextNd ), SwIndex( pTextNd ) );
+ }
+ pTextNd->JoinNext();
+ }
+ return true;
+ }
+ else return false;
+}
+
+static void lcl_syncGrammarError( SwTextNode &rTextNode, linguistic2::ProofreadingResult& rResult,
+ const ModelToViewHelper &rConversionMap )
+{
+ if( rTextNode.IsGrammarCheckDirty() )
+ return;
+ SwGrammarMarkUp* pWrong = rTextNode.GetGrammarCheck();
+ linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
+ sal_uInt16 j = 0;
+ if( pWrong )
+ {
+ for( sal_Int32 i = 0; i < rResult.aErrors.getLength(); ++i )
+ {
+ const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
+ const sal_Int32 nStart = rConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos;
+ const sal_Int32 nEnd = rConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos;
+ if( i != j )
+ pArray[j] = pArray[i];
+ if( pWrong->LookForEntry( nStart, nEnd ) )
+ ++j;
+ }
+ }
+ if( rResult.aErrors.getLength() > j )
+ rResult.aErrors.realloc( j );
+}
+
+uno::Any SwDoc::Spell( SwPaM& rPaM,
+ uno::Reference< XSpellChecker1 > const &xSpeller,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
+ bool bGrammarCheck,
+ SwRootFrame const*const pLayout,
+ SwConversionArgs *pConvArgs ) const
+{
+ SwPosition* pSttPos = rPaM.Start(), *pEndPos = rPaM.End();
+
+ std::unique_ptr<SwSpellArgs> pSpellArgs;
+ if (pConvArgs)
+ {
+ pConvArgs->SetStart(pSttPos->nNode.GetNode().GetTextNode(), pSttPos->nContent);
+ pConvArgs->SetEnd( pEndPos->nNode.GetNode().GetTextNode(), pEndPos->nContent );
+ }
+ else
+ pSpellArgs.reset(new SwSpellArgs( xSpeller,
+ pSttPos->nNode.GetNode().GetTextNode(), pSttPos->nContent,
+ pEndPos->nNode.GetNode().GetTextNode(), pEndPos->nContent,
+ bGrammarCheck ));
+
+ sal_uLong nCurrNd = pSttPos->nNode.GetIndex();
+ sal_uLong nEndNd = pEndPos->nNode.GetIndex();
+
+ uno::Any aRet;
+ if( nCurrNd <= nEndNd )
+ {
+ SwContentFrame* pContentFrame;
+ bool bGoOn = true;
+ while( bGoOn )
+ {
+ SwNode* pNd = GetNodes()[ nCurrNd ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ if( nullptr != ( pContentFrame = pNd->GetTextNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout() )) )
+ {
+ // skip protected and hidden Cells and Flys
+ if( pContentFrame->IsProtected() )
+ {
+ nCurrNd = pNd->EndOfSectionIndex();
+ }
+ else if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ long nStat;
+ if( nPageNr >= *pPageSt )
+ nStat = nPageNr - *pPageSt + 1;
+ else
+ nStat = nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, GetDocShell() );
+ }
+ //Spell() changes the pSpellArgs in case an error is found
+ sal_Int32 nBeginGrammarCheck = 0;
+ sal_Int32 nEndGrammarCheck = 0;
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
+ {
+ nBeginGrammarCheck = pSpellArgs->pStartNode == pNd ? pSpellArgs->pStartIdx->GetIndex() : 0;
+ // if grammar checking starts inside of a sentence the start position has to be adjusted
+ if( nBeginGrammarCheck )
+ {
+ SwIndex aStartIndex( dynamic_cast< SwTextNode* >( pNd ), nBeginGrammarCheck );
+ SwPosition aStart( *pNd, aStartIndex );
+ SwCursor aCursor(aStart, nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence( SwCursor::START_SENT );
+ if( aOrigPos != *aCursor.GetPoint() )
+ {
+ nBeginGrammarCheck = aCursor.GetPoint()->nContent.GetIndex();
+ }
+ }
+ nEndGrammarCheck = (pSpellArgs->pEndNode == pNd)
+ ? pSpellArgs->pEndIdx->GetIndex()
+ : pNd->GetTextNode()
+ ->GetText().getLength();
+ }
+
+ sal_Int32 nSpellErrorPosition = pNd->GetTextNode()->GetText().getLength();
+ if( (!pConvArgs && pNd->GetTextNode()->Spell( pSpellArgs.get() )) ||
+ ( pConvArgs && pNd->GetTextNode()->Convert( *pConvArgs )))
+ {
+ // Cancel and remember position
+ pSttPos->nNode = nCurrNd;
+ pEndPos->nNode = nCurrNd;
+ nCurrNd = nEndNd;
+ if( pSpellArgs )
+ nSpellErrorPosition = pSpellArgs->pStartIdx->GetIndex() > pSpellArgs->pEndIdx->GetIndex() ?
+ pSpellArgs->pEndIdx->GetIndex() :
+ pSpellArgs->pStartIdx->GetIndex();
+ }
+
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
+ {
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
+ if (xGCIterator.is())
+ {
+ uno::Reference< lang::XComponent > xDoc = GetDocShell()->GetBaseModel();
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNd->GetTextNode(), pLayout);
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ // get XFlatParagraph to use...
+ uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *pNd->GetTextNode(), aExpandText, aConversionMap );
+
+ // get error position of cursor in XFlatParagraph
+ linguistic2::ProofreadingResult aResult;
+ bool bGrammarErrors;
+ do
+ {
+ aConversionMap.ConvertToViewPosition( nBeginGrammarCheck );
+ aResult = xGCIterator->checkSentenceAtPosition(
+ xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
+
+ lcl_syncGrammarError( *pNd->GetTextNode(), aResult, aConversionMap );
+
+ // get suggestions to use for the specific error position
+ bGrammarErrors = aResult.aErrors.hasElements();
+ // if grammar checking doesn't have any progress then quit
+ if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
+ break;
+ // prepare next iteration
+ nBeginGrammarCheck = aResult.nStartOfNextSentencePosition;
+ }
+ while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !bGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
+
+ if( bGrammarErrors && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
+ {
+ aRet <<= aResult;
+ //put the cursor to the current error
+ const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
+ nCurrNd = pNd->GetIndex();
+ pSttPos->nNode = nCurrNd;
+ pEndPos->nNode = nCurrNd;
+ pSpellArgs->pStartNode = pNd->GetTextNode();
+ pSpellArgs->pEndNode = pNd->GetTextNode();
+ pSpellArgs->pStartIdx->Assign(pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos );
+ pSpellArgs->pEndIdx->Assign(pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos );
+ nCurrNd = nEndNd;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case SwNodeType::Section:
+ if( static_cast<SwSectionNode*>(pNd)->GetSection().IsProtect() ||
+ static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() )
+ nCurrNd = pNd->EndOfSectionIndex();
+ break;
+ case SwNodeType::End:
+ {
+ break;
+ }
+ default: break;
+ }
+
+ bGoOn = nCurrNd < nEndNd;
+ ++nCurrNd;
+ }
+ }
+
+ if( !aRet.hasValue() )
+ {
+ if (pConvArgs)
+ aRet <<= pConvArgs->aConvText;
+ else
+ aRet <<= pSpellArgs->xSpellAlt;
+ }
+
+ return aRet;
+}
+
+namespace {
+
+class SwHyphArgs : public SwInterHyphInfo
+{
+ const SwNode *m_pStart;
+ const SwNode *m_pEnd;
+ SwNode *m_pNode;
+ sal_uInt16 *m_pPageCnt;
+ sal_uInt16 *m_pPageSt;
+
+ sal_uInt32 m_nNode;
+ sal_Int32 m_nPamStart;
+ sal_Int32 m_nPamLen;
+
+public:
+ SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart );
+ void SetPam( SwPaM *pPam ) const;
+ void SetNode( SwNode *pNew ) { m_pNode = pNew; }
+ inline void SetRange( const SwNode *pNew );
+ void NextNode() { ++m_nNode; }
+ sal_uInt16 *GetPageCnt() { return m_pPageCnt; }
+ sal_uInt16 *GetPageSt() { return m_pPageSt; }
+};
+
+}
+
+SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart )
+ : SwInterHyphInfo( rCursorPos ), m_pNode(nullptr),
+ m_pPageCnt( pPageCount ), m_pPageSt( pPageStart )
+{
+ // The following constraints have to be met:
+ // 1) there is at least one Selection
+ // 2) SPoint() == Start()
+ OSL_ENSURE( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
+ OSL_ENSURE( *pPam->GetPoint() <= *pPam->GetMark(),
+ "SwDoc::Hyphenate: New York, New York");
+
+ const SwPosition *pPoint = pPam->GetPoint();
+ m_nNode = pPoint->nNode.GetIndex();
+
+ // Set start
+ m_pStart = pPoint->nNode.GetNode().GetTextNode();
+ m_nPamStart = pPoint->nContent.GetIndex();
+
+ // Set End and Length
+ const SwPosition *pMark = pPam->GetMark();
+ m_pEnd = pMark->nNode.GetNode().GetTextNode();
+ m_nPamLen = pMark->nContent.GetIndex();
+ if( pPoint->nNode == pMark->nNode )
+ m_nPamLen = m_nPamLen - pPoint->nContent.GetIndex();
+}
+
+inline void SwHyphArgs::SetRange( const SwNode *pNew )
+{
+ m_nStart = m_pStart == pNew ? m_nPamStart : 0;
+ m_nEnd = m_pEnd == pNew ? m_nPamStart + m_nPamLen : SAL_MAX_INT32;
+}
+
+void SwHyphArgs::SetPam( SwPaM *pPam ) const
+{
+ if( !m_pNode )
+ *pPam->GetPoint() = *pPam->GetMark();
+ else
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+ pPam->GetPoint()->nContent.Assign( m_pNode->GetContentNode(), m_nWordStart );
+ pPam->GetMark()->nNode = m_nNode;
+ pPam->GetMark()->nContent.Assign( m_pNode->GetContentNode(),
+ m_nWordStart + m_nWordLen );
+ OSL_ENSURE( m_nNode == m_pNode->GetIndex(),
+ "SwHyphArgs::SetPam: Pam disaster" );
+ }
+}
+
+// Returns true if we can proceed.
+static bool lcl_HyphenateNode( const SwNodePtr& rpNd, void* pArgs )
+{
+ // Hyphenate returns true if there is a hyphenation point and sets pPam
+ SwTextNode *pNode = rpNd->GetTextNode();
+ SwHyphArgs *pHyphArgs = static_cast<SwHyphArgs*>(pArgs);
+ if( pNode )
+ {
+ // sw_redlinehide: this will be called once per node for merged nodes;
+ // the fully deleted ones won't have frames so are skipped.
+ SwContentFrame* pContentFrame = pNode->getLayoutFrame( pNode->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pContentFrame && !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
+ sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
+ : nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, pNode->GetDoc()->GetDocShell() );
+ }
+ pHyphArgs->SetRange( rpNd );
+ if( pNode->Hyphenate( *pHyphArgs ) )
+ {
+ pHyphArgs->SetNode( rpNd );
+ return false;
+ }
+ }
+ }
+ pHyphArgs->NextNode();
+ return true;
+}
+
+uno::Reference< XHyphenatedWord > SwDoc::Hyphenate(
+ SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ OSL_ENSURE(this == pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
+
+ if( *pPam->GetPoint() > *pPam->GetMark() )
+ pPam->Exchange();
+
+ SwHyphArgs aHyphArg( pPam, rCursorPos, pPageCnt, pPageSt );
+ SwNodeIndex aTmpIdx( pPam->GetMark()->nNode, 1 );
+ GetNodes().ForEach( pPam->GetPoint()->nNode, aTmpIdx,
+ lcl_HyphenateNode, &aHyphArg );
+ aHyphArg.SetPam( pPam );
+ return aHyphArg.GetHyphWord(); // will be set by lcl_HyphenateNode
+}
+
+// Save the current values to add them as automatic entries to AutoCorrect.
+void SwDoc::SetAutoCorrExceptWord( std::unique_ptr<SwAutoCorrExceptWord> pNew )
+{
+ mpACEWord = std::move(pNew);
+}
+
+void SwDoc::DeleteAutoCorrExceptWord()
+{
+ mpACEWord.reset();
+}
+
+void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat )
+{
+ // This is a modified version of SwDoc::TransliterateText
+ const SwPosition* pStt = rPaM.Start();
+ const SwPosition* pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
+ : rPaM.GetPoint();
+
+ const sal_uLong nSttNd = pStt->nNode.GetIndex();
+ const sal_uLong nEndNd = pEnd->nNode.GetIndex();
+
+ const sal_Int32 nSttCnt = pStt->nContent.GetIndex();
+ const sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
+
+ const SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( pStt == pEnd && pTNd ) // no region ?
+ {
+ // do nothing
+ return;
+ }
+
+ if( nSttNd != nEndNd )
+ {
+ SwNodeIndex aIdx( pStt->nNode );
+ if( nSttCnt )
+ {
+ ++aIdx;
+ if( pTNd )
+ pTNd->CountWords( rStat, nSttCnt, pTNd->GetText().getLength() );
+ }
+
+ for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
+ if( nullptr != ( pTNd = aIdx.GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, pTNd->GetText().getLength() );
+
+ if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, nEndCnt );
+ }
+ else if( pTNd && nSttCnt < nEndCnt )
+ pTNd->CountWords( rStat, nSttCnt, nEndCnt );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */