summaryrefslogtreecommitdiffstats
path: root/sw/source/core/doc/DocumentRedlineManager.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/doc/DocumentRedlineManager.cxx')
-rw-r--r--sw/source/core/doc/DocumentRedlineManager.cxx3233
1 files changed, 3233 insertions, 0 deletions
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx
new file mode 100644
index 000000000..f3aaa13a6
--- /dev/null
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -0,0 +1,3233 @@
+/* -*- 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 <DocumentRedlineManager.hxx>
+#include <frmfmt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <fmtfld.hxx>
+#include <frmtool.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <redline.hxx>
+#include <UndoRedline.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <ftnidx.hxx>
+#include <authfld.hxx>
+#include <strings.hrc>
+#include <swmodule.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ #define ERROR_PREFIX "redline table corrupted: "
+
+ namespace
+ {
+ // helper function for lcl_CheckRedline
+ // 1. make sure that pPos->nContent points into pPos->nNode
+ // 2. check that position is valid and doesn't point after text
+ void lcl_CheckPosition( const SwPosition* pPos )
+ {
+ assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
+ == pPos->nContent.GetIdxReg());
+
+ SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ assert(pPos->nContent == 0);
+ }
+ else
+ {
+ assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
+ }
+ }
+
+ void lcl_CheckPam( const SwPaM* pPam )
+ {
+ assert(pPam);
+ lcl_CheckPosition( pPam->GetPoint() );
+ lcl_CheckPosition( pPam->GetMark() );
+ }
+
+ // check validity of the redline table. Checks redline bounds, and make
+ // sure the redlines are sorted and non-overlapping.
+ void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess )
+ {
+ const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
+
+ // verify valid redline positions
+ for(SwRangeRedline* i : rTable)
+ lcl_CheckPam( i );
+
+ for(SwRangeRedline* j : rTable)
+ {
+ // check for empty redlines
+ // note: these can destroy sorting in SwTextNode::Update()
+ // if there's another one without mark on the same pos.
+ OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
+ ( j->GetContentIdx() != nullptr ),
+ ERROR_PREFIX "empty redline" );
+ }
+
+ // verify proper redline sorting
+ for( size_t n = 1; n < rTable.size(); ++n )
+ {
+ const SwRangeRedline* pPrev = rTable[ n-1 ];
+ const SwRangeRedline* pCurrent = rTable[ n ];
+
+ // check redline sorting
+ SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "not sorted correctly" );
+
+ // check for overlapping redlines
+ SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "overlapping redlines" );
+ }
+
+ assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
+ }
+ }
+
+ #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
+
+#else
+
+ #define CHECK_REDLINE( pDoc )
+
+#endif
+
+namespace sw {
+
+static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
+{
+ auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
+ SwFieldIds::TableOfAuthorities, OUString(), false)));
+ if (pAuthType) // created on demand...
+ {
+ pAuthType->DelSequenceArray();
+ }
+ rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
+ rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
+ rIDFA.UpdateExpFields(nullptr, false);
+ rIDFA.UpdateRefFields();
+}
+
+void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
+ // the AppendFootnote/RemoveFootnote will do it by itself!
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ SwStartNode *const pTableOrSectionNode(
+ currentStart.nNode.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
+ assert(pTableOrSectionNode); // known pathology
+ for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ if (pTableOrSectionNode->IsTableNode())
+ {
+ static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ else
+ {
+ static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ }
+ }
+ currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
+ currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
+ pStartNode = currentStart.nNode.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ frames.push_back(pFrame);
+ }
+ }
+ if (frames.empty())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ break;
+ }
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ SwTextNode & rFirstNode(pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pFirstNode
+ : *pNode);
+ assert(pNode == pStartNode
+ ? rFirstNode.GetIndex() <= pNode->GetIndex()
+ : &rFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // the first node of the new redline is not necessarily the first
+ // node of the merged frame, there could be another redline nearby
+ sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
+ // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
+ if (pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
+ }
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
+}
+
+void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ bool isAppendObjsCalled(false);
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ SwStartNode const*const pTableOrSectionNode(
+ currentStart.nNode.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
+ assert(pTableOrSectionNode); // known pathology
+ for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines())
+ {
+ // note: this will also create frames for all currently hidden flys
+ // because it calls AppendAllObjs
+ SwNodeIndex const end(*pTableOrSectionNode->EndOfSectionNode());
+ ::MakeFrames(&rDoc, currentStart.nNode, end);
+ isAppendObjsCalled = true;
+ }
+ currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
+ currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
+ pStartNode = currentStart.nNode.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ frames.push_back(pFrame);
+ }
+ }
+ if (frames.empty())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ break;
+ }
+
+ // first, call CheckParaRedlineMerge on the first paragraph,
+ // to init flag on new merge range (if any) + 1st node post the merge
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ if (auto const pMergedPara = pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
+ assert(pNode == pStartNode
+ ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
+ : pMergedPara->pFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ }
+ }
+ if (pLast != pNode)
+ {
+ // now start node until end of merge + 1 has proper flags; MakeFrames
+ // should pick up from the next node in need of frames by checking flags
+ SwNodeIndex const start(*pNode, +1);
+ SwNodeIndex const end(*pLast, +1); // end is exclusive
+ // note: this will also create frames for all currently hidden flys
+ // both on first and non-first nodes because it calls AppendAllObjs
+ ::MakeFrames(&rDoc, start, end);
+ isAppendObjsCalled = true;
+ // re-use this to move flys that are now on the wrong frame, with end
+ // of redline as "second" node; the nodes between start and end should
+ // be complete with MakeFrames already
+ sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
+ }
+
+ if (!isAppendObjsCalled)
+ { // recreate flys in the one node the hard way...
+ for (auto const& pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
+ break;
+ }
+ }
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
+}
+
+} // namespace sw
+
+namespace
+{
+ bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
+ {
+ const SwContentNode* pCNd;
+ if( 0 != rPos2.nContent.GetIndex() )
+ return false;
+ if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() )
+ return false;
+ pCNd = rPos1.nNode.GetNode().GetContentNode();
+ return pCNd && rPos1.nContent.GetIndex() == pCNd->Len();
+ }
+
+ // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
+ SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
+ {
+ SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
+ SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
+ if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
+ {
+ const SwPaM aPam(*pToNode);
+ SwDoc* pDoc = aPam.GetDoc();
+ // using Undo, copy paragraph style
+ SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
+ SwTextFormatColl* pToColl = pToNode->GetTextColl();
+ if (bCopy && pFromColl != pToColl)
+ pDoc->SetTextFormatColl(aPam, pFromColl);
+
+ // using Undo, remove direct paragraph formatting of the "To" paragraph,
+ // and apply here direct paragraph formatting of the "From" paragraph
+ SfxItemSet aTmp(
+ pDoc->GetAttrPool(),
+ svl::Items<
+ RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
+ RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
+ RES_CNTNT, RES_FRMATR_END - 1>{});
+ SfxItemSet aTmp2(aTmp);
+
+ pToNode->GetParaAttr(aTmp, 0, 0);
+ pFromNode->GetParaAttr(aTmp2, 0, 0);
+
+ bool bSameSet = aTmp == aTmp2;
+
+ if (!bSameSet)
+ {
+ for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
+ {
+ sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
+ if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
+ SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
+ aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
+ }
+ }
+
+ if (bCopy && !bSameSet)
+ pDoc->getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
+ else if (!bCopy && (!bSameSet || pFromColl != pToColl))
+ return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
+ }
+ return nullptr;
+ }
+
+ bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Format:
+ {
+ bool bCheck = false, bReplace = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( *pSttRng == *pRStt )
+ pRedl->SetStart( *pEndRng, pRStt );
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEndRng );
+ rArr.Insert( pNew ); ++rPos;
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwDoc& rDoc = *pRedl->GetDoc();
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+ default:
+ bRet = false;
+ }
+
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+ SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+
+ // keep style of the empty paragraph after deletion of wholly paragraphs
+ if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->nContent == 0 )
+ lcl_CopyStyle(*pREnd, *pRStt);
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ case RedlineType::ParagraphFormat:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwDoc& rDoc = *pRedl->GetDoc();
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ {
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // delete the range again
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+
+ SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ if (aPam.End()->nNode.GetNode().IsStartNode())
+ { // end node will be deleted too! see nNodeDiff+1
+ --aPam.End()->nNode;
+ }
+ assert(!aPam.End()->nNode.GetNode().IsStartNode());
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwRangeRedline* pNew = nullptr;
+ bool bCheck = false, bReplace = false;
+ SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
+ pEndRng ? *pEndRng : *pRedl->End());
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ {
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ if( *pSttRng == *pRStt )
+ {
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEndRng );
+ rArr.Insert( pCpy ); ++rPos;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ if( !pRedl->PopData() )
+ // deleting the RedlineObject is enough
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( pNew )
+ {
+ rArr.Insert( pNew ); ++rPos;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
+ }
+ break;
+
+ case RedlineType::Format:
+ case RedlineType::FmtColl:
+ case RedlineType::ParagraphFormat:
+ {
+ // tdf#52391 instead of hidden acception at the requested
+ // rejection, remove direct text formatting to get the potential
+ // original state of the text (FIXME if the original text
+ // has already contained direct text formatting: unfortunately
+ // ODF 1.2 doesn't support rejection of format-only changes)
+ if ( pRedl->GetType() == RedlineType::Format )
+ {
+ SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
+ rDoc.ResetAttrs(aPam);
+ }
+ else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
+ {
+ // handle paragraph formatting changes
+ // (range is only a full paragraph or a part of it)
+ const SwPosition* pStt = pRedl->Start();
+ SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // expand range to the whole paragraph
+ // and reset only the paragraph attributes
+ SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
+ std::set<sal_uInt16> aResetAttrsArray;
+
+ sal_uInt16 aResetableSetRange[] = {
+ RES_PARATR_BEGIN, RES_PARATR_END - 1,
+ RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1,
+ 0
+ };
+
+ const sal_uInt16 *pUShorts = aResetableSetRange;
+ while (*pUShorts)
+ {
+ for (sal_uInt16 i = pUShorts[0]; i <= pUShorts[1]; ++i)
+ aResetAttrsArray.insert( aResetAttrsArray.end(), i );
+ pUShorts += 2;
+ }
+
+ rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
+
+ // remove numbering
+ if ( pTNd->GetNumRule() )
+ rDoc.DelNumRules(aPam);
+ }
+ }
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ rArr.DeleteAndDestroy( rPos-- );
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng,
+ const SwPosition* pEndRng);
+
+
+ int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
+ SwRedlineTable& rArr, bool bCallDelete,
+ const SwPaM& rPam)
+ {
+ SwRedlineTable::size_type n = 0;
+ int nCount = 0;
+
+ const SwPosition* pStt = rPam.Start(),
+ * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
+ : rPam.GetPoint();
+ const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
+ if( pFnd && // Is new a part of it?
+ ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
+ {
+ // Only revoke the partial selection
+ if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
+ nCount++;
+ ++n;
+ }
+
+ // tdf#119824 first we will accept only overlapping paragraph format changes
+ // in the first loop to avoid potential content changes during Redo
+ bool bHasParagraphFormatChange = false;
+ for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
+ {
+ for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
+ {
+ SwRangeRedline* pTmp = rArr[ o ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if( *pTmp->End() <= *pEnd )
+ {
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ else
+ {
+ if( *pTmp->Start() < *pEnd )
+ {
+ // Only revoke the partial selection
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return nCount;
+ }
+
+ void lcl_AdjustRedlineRange( SwPaM& rPam )
+ {
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPosition* pStt = rPam.Start(),
+ * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
+ : rPam.GetPoint();
+ SwDoc* pDoc = rPam.GetDoc();
+ if( !pStt->nContent.GetIndex() &&
+ !pDoc->GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
+ {
+ const SwRangeRedline* pRedl = pDoc->getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pRStt = pRedl->Start();
+ if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
+ pStt->nNode.GetIndex() - 1 )
+ *pStt = *pRStt;
+ }
+ }
+ if( pEnd->nNode.GetNode().IsContentNode() &&
+ !pDoc->GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
+ pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() )
+ {
+ const SwRangeRedline* pRedl = pDoc->getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pREnd = pRedl->End();
+ if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
+ pEnd->nNode.GetIndex() + 1 )
+ *pEnd = *pREnd;
+ }
+ }
+ }
+
+ /// in case some text is deleted, ensure that the not-yet-inserted
+ /// SwRangeRedline has its positions corrected not to point to deleted node
+ class TemporaryRedlineUpdater
+ {
+ private:
+ SwRangeRedline & m_rRedline;
+ std::shared_ptr<SwUnoCursor> m_pCursor;
+ public:
+ TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
+ : m_rRedline(rRedline)
+ , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
+ {
+ if (m_rRedline.HasMark())
+ {
+ m_pCursor->SetMark();
+ *m_pCursor->GetMark() = *m_rRedline.GetMark();
+ *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
+ }
+ *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
+ }
+ ~TemporaryRedlineUpdater()
+ {
+ static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
+ }
+ };
+}
+
+namespace sw
+{
+
+DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
+ , mpRedlineTable(new SwRedlineTable)
+ , mpExtraRedlineTable(new SwExtraRedlineTable)
+ , mbIsRedlineMove(false)
+ , mnAutoFormatRedlnCommentNo(0)
+{
+}
+
+RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
+{
+ return meRedlineFlags;
+}
+
+void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
+{
+ if( meRedlineFlags != eMode )
+ {
+ if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
+ || !(RedlineFlags::ShowMask & eMode) )
+ {
+ bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
+ m_rDoc.SetInXMLImport( false );
+ // and then hide/display everything
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t); // Allow compiler warn if use of
+ // uninitialized ptr is possible
+
+ RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShowMode == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+ else if (eShowMode == RedlineFlags::ShowDelete)
+ pFnc = &SwRangeRedline::ShowOriginal;
+ else
+ {
+ pFnc = &SwRangeRedline::Hide;
+ eMode |= RedlineFlags::ShowInsert;
+ }
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ {
+ // sw_redlinehide: the problem here is that MoveFromSection
+ // creates the frames wrongly (non-merged), because its own
+ // SwRangeRedline has wrong positions until after the nodes
+ // are all moved, so fix things up by force by re-creating
+ // all merged frames from scratch.
+ o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
+ for (SwRootFrame *const pLayout : layouts)
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ pLayout->SetHideRedlines(false);
+ hiddenLayouts.insert(pLayout);
+ }
+ }
+ }
+
+ for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
+ for (size_t i = 0; i < mpRedlineTable->size(); ++i)
+ {
+ SwRangeRedline *const pRedline((*mpRedlineTable)[i]);
+ (pRedline->*pFnc)(nLoop, i);
+ while (mpRedlineTable->size() <= i
+ || (*mpRedlineTable)[i] != pRedline)
+ { // ensure current position
+ --i; // a previous redline may have been deleted
+ }
+ }
+
+ //SwRangeRedline::MoveFromSection routinely changes
+ //the keys that mpRedlineTable is sorted by
+ mpRedlineTable->Resort();
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ for (SwRootFrame *const pLayout : hiddenLayouts)
+ {
+ pLayout->SetHideRedlines(true);
+ }
+
+ m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
+ }
+ meRedlineFlags = eMode;
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::IsRedlineOn() const
+{
+ return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
+}
+
+bool DocumentRedlineManager::IsIgnoreRedline() const
+{
+ return bool(RedlineFlags::Ignore & meRedlineFlags);
+}
+
+void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
+{
+ meRedlineFlags = eMode;
+}
+
+const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
+{
+ return *mpRedlineTable;
+}
+
+SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
+{
+ return *mpRedlineTable;
+}
+
+const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
+{
+ return *mpExtraRedlineTable;
+}
+
+SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
+{
+ return *mpExtraRedlineTable;
+}
+
+bool DocumentRedlineManager::HasExtraRedlineTable() const
+{
+ return mpExtraRedlineTable != nullptr;
+}
+
+bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
+{
+ SwPosition aPos(rNode);
+ SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
+ SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
+ SwPosition(rEndOfRedlines));
+
+ return aPam.ContainsPosition(aPos);
+}
+
+bool DocumentRedlineManager::IsRedlineMove() const
+{
+ return mbIsRedlineMove;
+}
+
+void DocumentRedlineManager::SetRedlineMove(bool bFlag)
+{
+ mbIsRedlineMove = bFlag;
+}
+
+/*
+Text means Text not "polluted" by Redlines.
+
+Behaviour of Insert-Redline:
+ - in the Text - insert Redline Object
+ - in InsertRedline (own) - ignore, existing is extended
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - in DeleteRedline - split up DeleteRedline or
+ move at the end/beginning
+
+Behaviour of Delete-Redline:
+ - in the Text - insert Redline Object
+ - in DeleteRedline (own/others) - ignore
+ - in InsertRedline (own) - ignore, but delete character
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - Text and own Insert overlap - delete Text in the own Insert,
+ extend in the other Text
+ (up to the Insert!)
+ - Text and other Insert overlap - insert Redline Object, the
+ other Insert is overlapped by
+ the Delete
+*/
+IDocumentRedlineAccess::AppendResult
+DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
+{
+ bool bMerged = false;
+ CHECK_REDLINE( *this )
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+
+ if( m_rDoc.IsAutoFormatRedline() )
+ {
+ pNewRedl->SetAutoFormat();
+ if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() )
+ {
+ pNewRedl->SetComment( *mpAutoFormatRedlnComment );
+ pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
+ }
+ }
+
+ SwPosition* pStt = pNewRedl->Start(),
+ * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark()
+ : pNewRedl->GetPoint();
+ {
+ SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pStt->nContent > 0 )
+ {
+ OSL_ENSURE( false, "Redline start: non-text-node with content" );
+ pStt->nContent = 0;
+ }
+ }
+ else
+ {
+ if( pStt->nContent > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline start: index after text" );
+ pStt->nContent = pTextNode->Len();
+ }
+ }
+ pTextNode = pEnd->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pEnd->nContent > 0 )
+ {
+ OSL_ENSURE( false, "Redline end: non-text-node with content" );
+ pEnd->nContent = 0;
+ }
+ }
+ else
+ {
+ if( pEnd->nContent > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline end: index after text" );
+ pEnd->nContent = pTextNode->Len();
+ }
+ }
+ }
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ return AppendResult::IGNORED;
+ }
+ bool bCompress = false;
+ SwRedlineTable::size_type n = 0;
+ // look up the first Redline for the starting position
+ if( !GetRedline( *pStt, &n ) && n )
+ --n;
+ bool bDec = false;
+
+ for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n )
+ {
+ bDec = false;
+
+ SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
+ : pRedl->GetPoint();
+
+ // #i8518# remove empty redlines while we're at it
+ if( ( *pRStt == *pREnd ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ mpRedlineTable->DeleteAndDestroy(n);
+ continue;
+ }
+
+ SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
+
+ switch( pNewRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ if( pRedl->IsOwnRedline( *pNewRedl ) )
+ {
+ bool bDelete = false;
+
+ // Merge if applicable?
+ if( (( SwComparePosition::Behind == eCmpPos &&
+ IsPrevPos( *pREnd, *pStt ) ) ||
+ ( SwComparePosition::CollideStart == eCmpPos ) ||
+ ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( n+1 >= mpRedlineTable->size() ||
+ ( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd &&
+ *(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) )
+ {
+ pRedl->SetEnd( *pEnd, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ }
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if( (( SwComparePosition::Before == eCmpPos &&
+ IsPrevPos( *pEnd, *pRStt ) ) ||
+ ( SwComparePosition::CollideEnd == eCmpPos ) ||
+ ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( !n ||
+ *(*mpRedlineTable)[ n-1 ]->End() != *pRStt ))
+ {
+ pRedl->SetStart( *pStt, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // own insert-over-insert redlines:
+ // just scrap the inside ones
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ *pStt = *pREnd;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = true;
+ }
+ else if( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = true;
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ bDelete = true;
+ bMerged = true;
+ }
+ else if( SwComparePosition::Equal == eCmpPos )
+ bDelete = true;
+
+ if( bDelete )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ mpRedlineTable->Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ mpRedlineTable->Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ pNewRedl->SetStart( *pREnd );
+ }
+ else if ( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ mpRedlineTable->Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ mpRedlineTable->Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::Equal == eCmpPos )
+ {
+ // handle identical redlines in broken documents
+ // delete old (delete) redline
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ { // Another workaround for broken redlines
+ pNewRedl->SetStart( *pREnd );
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ {
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ mpRedlineTable->DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Delete:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ {
+ // Overlaps the current one completely,
+ // split the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ case SwComparePosition::Equal:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::CollideEnd:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) )
+ {
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ // Before we can merge, we make it visible!
+ // We insert temporarily so that pNew is
+ // also dealt with when moving the indices.
+ mpRedlineTable->Insert(pNewRedl);
+ pRedl->Show(0, mpRedlineTable->GetPos(pRedl));
+ mpRedlineTable->Remove( pNewRedl );
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ }
+
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::CollideStart == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+
+ // delete current (below), and restart process with
+ // previous
+ SwRedlineTable::size_type nToBeDeleted = n;
+ bDec = true;
+
+ if( *(pNewRedl->Start()) <= *pREnd )
+ {
+ // Whoooah, we just extended the new 'redline'
+ // beyond previous redlines, so better start
+ // again. Of course this is not supposed to
+ // happen, and in an ideal world it doesn't,
+ // but unfortunately this code is buggy and
+ // totally rotten so it does happen and we
+ // better fix it.
+ n = 0;
+ bDec = true;
+ }
+
+ mpRedlineTable->DeleteAndDestroy( nToBeDeleted );
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Insert:
+ {
+ // b62341295: Do not throw away redlines
+ // even if they are not allowed to be combined
+ RedlineFlags eOld = meRedlineFlags;
+ if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
+ pRedl->IsOwnRedline( *pNewRedl ) )
+ {
+
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ bCompress = true;
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ [[fallthrough]];
+
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ // DeleteAndJoin does not yield the
+ // desired result if there is no paragraph to
+ // join with, i.e. at the end of the document.
+ // For this case, we completely delete the
+ // paragraphs (if, of course, we also start on
+ // a paragraph boundary).
+ if( (pStt->nContent == 0) &&
+ pEnd->nNode.GetNode().IsEndNode() )
+ {
+ pEnd->nNode--;
+ pEnd->nContent.Assign(
+ pEnd->nNode.GetNode().GetTextNode(), 0);
+ m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
+ }
+ else
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+
+ bCompress = true;
+ }
+ if( !bCallDelete && !bDec && *pEnd == *pREnd )
+ {
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+ bCompress = true;
+ }
+ else if ( bCallDelete || !bDec )
+ {
+ // delete new redline, except in some cases of fallthrough from previous
+ // case ::Equal (eg. same portion w:del in w:ins in OOXML import)
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ mpRedlineTable->Remove( n );
+ bDec = true;
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
+ n = 0; // re-initialize
+ }
+ delete pRedl;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ SwPaM aPam( *pRStt, *pEnd );
+
+ if( *pEnd == *pREnd )
+ mpRedlineTable->DeleteAndDestroy( n );
+ else
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ }
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ }
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ SwPaM aPam( *pStt, *pREnd );
+
+ if( *pStt == *pRStt )
+ {
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ meRedlineFlags = eOld;
+ }
+ else
+ {
+ // it may be necessary to split the existing redline in
+ // two. In this case, pRedl will be changed to cover
+ // only part of its former range, and pNew will cover
+ // the remainder.
+ SwRangeRedline* pNew = nullptr;
+
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ {
+ pRedl->PushData( *pNewRedl );
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
+ }
+ bCompress = true;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ if( *pRStt == *pStt )
+ {
+ // #i97421#
+ // redline w/out extent loops
+ if (*pStt != *pEnd)
+ {
+ pNewRedl->PushData( *pRedl, false );
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ bDec = true;
+ }
+ }
+ else
+ {
+ pNewRedl->PushData( *pRedl, false );
+ if( *pREnd != *pEnd )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ }
+ }
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ pRedl->PushData( *pNewRedl );
+ if( *pEnd == *pREnd )
+ {
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ }
+ bCompress = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ if( *pEnd == *pREnd )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ mpRedlineTable->Insert(pNewRedl);
+ pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
+ mpRedlineTable->Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetEnd( *pEnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ pRedl->SetStart( *pNew->End(), pRStt ) ;
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ if( *pStt == *pRStt )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetStart( *pREnd, pStt );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ mpRedlineTable->Insert( pNewRedl );
+ pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
+ mpRedlineTable->Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetStart( *pStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ pRedl->SetEnd( *pNew->Start(), pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // insert the pNew part (if it exists)
+ if( pNew )
+ {
+ mpRedlineTable->Insert( pNew );
+
+ // pNew must be deleted if Insert() wasn't
+ // successful. But that can't happen, since pNew is
+ // part of the original pRedl redline.
+ // OSL_ENSURE( bRet, "Can't insert existing redline?" );
+
+ // restart (now with pRedl being split up)
+ n = 0;
+ bDec = true;
+ }
+ }
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ mpRedlineTable->DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pNewRedl->SetStart( *pREnd, pStt );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Inside:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ break;
+
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // own one can be ignored completely
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ else if( *pREnd == *pEnd )
+ // or else only shorten the current one
+ pRedl->SetEnd( *pStt, pREnd );
+ else if( *pRStt == *pStt )
+ {
+ // or else only shorten the current one
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl, n );
+ bDec = true;
+ }
+ else
+ {
+ // If it lies completely within the current one
+ // we need to split it
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = false;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideEnd:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) && n &&
+ *(*mpRedlineTable)[ n-1 ]->End() < *pStt )
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ case SwComparePosition::CollideStart:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ n+1 < mpRedlineTable->size() &&
+ *(*mpRedlineTable)[ n+1 ]->Start() < *pEnd )
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetStart( *pRStt, pStt );
+ mpRedlineTable->DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ // How should we behave here?
+ // insert as is
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( pNewRedl )
+ {
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ else
+ {
+ if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ if ( pStt->nContent != 0 )
+ {
+ // tdf#119571 update the style of the joined paragraph
+ // after a partially deleted paragraph to show its correct style
+ // in "Show changes" mode, too. All removed paragraphs
+ // get the style of the first (partially deleted) paragraph
+ // to avoid text insertion with bad style in the deleted
+ // area later.
+
+ SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
+ SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
+ SwTextNode* pTextNode;
+ SwNodeIndex aIdx( pEnd->nNode.GetNode() );
+ bool bFirst = true;
+
+ while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
+ {
+ pTextNode = pTextNd->GetTextNode();
+ if (pTextNode && pDelNode != pTextNode )
+ {
+ SwPosition aPos(aIdx);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ bCompress = true;
+
+ // split redline to store ExtraData per paragraphs
+ SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
+ pPar->SetStart( aPos );
+ pNewRedl->SetEnd( aPos );
+
+ // get extradata for reset formatting of the modified paragraph
+ SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
+ if (pExtraData)
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
+ if (!bFirst)
+ pExtraData->SetFormatAll(false);
+ xRedlineExtraData.reset(pExtraData);
+ pPar->SetExtraData( xRedlineExtraData.get() );
+ }
+ mpRedlineTable->Insert( pPar );
+ }
+
+ // modify paragraph formatting
+ lcl_CopyStyle(*pStt, aPos);
+ }
+ pTextNd = SwNodes::GoPrevious( &aIdx );
+
+ if (bFirst)
+ bFirst = false;
+ }
+ }
+ }
+ bool const ret = mpRedlineTable->Insert( pNewRedl );
+ assert(ret || !pNewRedl);
+ if (ret && !pNewRedl)
+ {
+ bMerged = true; // treat InsertWithValidRanges as "merge"
+ }
+ }
+ }
+
+ if( bCompress )
+ CompressRedlines();
+ }
+ else
+ {
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ CHECK_REDLINE( *this )
+
+ return (nullptr != pNewRedl)
+ ? AppendResult::APPENDED
+ : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
+}
+
+bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ mpExtraRedlineTable->Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableRowRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ mpExtraRedlineTable->Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableCellRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+void DocumentRedlineManager::CompressRedlines()
+{
+ CHECK_REDLINE( *this )
+
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t) = nullptr;
+ RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
+ if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShow == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+
+ // Try to merge identical ones
+ for( SwRedlineTable::size_type n = 1; n < mpRedlineTable->size(); ++n )
+ {
+ SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ],
+ * pCur = (*mpRedlineTable)[ n ];
+ const SwPosition* pPrevStt = pPrev->Start(),
+ * pPrevEnd = pPrevStt == pPrev->GetPoint()
+ ? pPrev->GetMark() : pPrev->GetPoint();
+ const SwPosition* pCurStt = pCur->Start(),
+ * pCurEnd = pCurStt == pCur->GetPoint()
+ ? pCur->GetMark() : pCur->GetPoint();
+ if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
+ pPrevStt->nNode.GetNode().StartOfSectionNode() ==
+ pCurEnd->nNode.GetNode().StartOfSectionNode() &&
+ !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
+ {
+ // we then can merge them
+ SwRedlineTable::size_type nPrevIndex = n-1;
+ pPrev->Show(0, nPrevIndex);
+ pCur->Show(0, n);
+
+ pPrev->SetEnd( *pCur->End() );
+ mpRedlineTable->DeleteAndDestroy( n );
+ --n;
+ if( pFnc )
+ (pPrev->*pFnc)(0, nPrevIndex);
+ }
+ }
+ CHECK_REDLINE( *this )
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
+{
+ bool bChg = false;
+ SwRedlineTable::size_type n = 0;
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+ GetRedline( *pStt, &n );
+ for ( ; n < mpRedlineTable->size(); ++n)
+ {
+ SwRangeRedline * pRedline = (*mpRedlineTable)[ n ];
+ SwPosition *const pRedlineStart = pRedline->Start();
+ SwPosition *const pRedlineEnd = pRedline->End();
+ if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
+ *pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
+ {
+ bChg = true;
+ int nn = 0;
+ if (*pStt == *pRedlineStart)
+ nn += 1;
+ if (*pEnd == *pRedlineEnd)
+ nn += 2;
+
+ SwRangeRedline* pNew = nullptr;
+ switch( nn )
+ {
+ case 0:
+ pNew = new SwRangeRedline( *pRedline );
+ pRedline->SetEnd( *pStt, pRedlineEnd );
+ pNew->SetStart( *pEnd );
+ break;
+
+ case 1:
+ *pRedlineStart = *pEnd;
+ break;
+
+ case 2:
+ *pRedlineEnd = *pStt;
+ break;
+
+ case 3:
+ pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ mpRedlineTable->DeleteAndDestroy( n-- );
+ pRedline = nullptr;
+ break;
+ }
+ if (pRedline && !pRedline->HasValidRange())
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedline, n );
+ }
+ if( pNew )
+ mpRedlineTable->Insert( pNew, n );
+ }
+ else if (*pEnd < *pRedlineStart)
+ break;
+ }
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
+ return false;
+
+ bool bChg = false;
+
+ if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
+ if( pUndo->GetRedlSaveCount() )
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ const SwPosition* pStt = rRange.Start(),
+ * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark()
+ : rRange.GetPoint();
+ SwRedlineTable::size_type n = 0;
+ GetRedline( *pStt, &n );
+ for( ; n < mpRedlineTable->size() ; ++n )
+ {
+ SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
+ if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
+ continue;
+
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
+ : pRedl->GetPoint();
+ switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
+ {
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ mpRedlineTable->DeleteAndDestroy( n-- );
+ bChg = true;
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ --n;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ --n;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ // this one needs to be split
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ if( *pRStt == *pStt )
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ --n;
+ }
+ else
+ {
+ SwRangeRedline* pCpy;
+ if( *pREnd != *pEnd )
+ {
+ pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ else
+ pCpy = nullptr;
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ mpRedlineTable->Remove( n );
+ mpRedlineTable->Insert( pRedl );
+ --n;
+ }
+ if( pCpy )
+ mpRedlineTable->Insert( pCpy );
+ }
+ }
+ break;
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before:
+ n = mpRedlineTable->size();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( bChg )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
+ return DeleteRedline(aTemp, bSaveInUndo, nDelType);
+}
+
+SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
+{
+ const sal_uLong nNdIdx = rNd.GetIndex();
+ for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
+ sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
+ nMk = pTmp->GetMark()->nNode.GetIndex();
+ if( nPt < nMk ) { long nTmp = nMk; nMk = nPt; nPt = nTmp; }
+
+ if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
+ nMk <= nNdIdx && nNdIdx <= nPt )
+ return n;
+
+ if( nMk > nNdIdx )
+ break;
+ }
+ return SwRedlineTable::npos;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
+ SwRedlineTable::size_type* pFndPos ) const
+{
+ SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ nM = nU + ( nO - nU ) / 2;
+ const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ];
+ const SwPosition* pStt = pRedl->Start();
+ const SwPosition* pEnd = pStt == pRedl->GetPoint()
+ ? pRedl->GetMark()
+ : pRedl->GetPoint();
+ if( pEnd == pStt
+ ? *pStt == rPos
+ : ( *pStt <= rPos && rPos < *pEnd ) )
+ {
+ while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() &&
+ rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() )
+ {
+ --nM;
+ pRedl = (*mpRedlineTable)[ nM ];
+ }
+ // if there are format and insert changes in the same position
+ // show insert change first.
+ // since the redlines are sorted by position, only check the redline
+ // before and after the current redline
+ if( RedlineType::Format == pRedl->GetType() )
+ {
+ if( nM && rPos >= *(*mpRedlineTable)[ nM - 1 ]->Start() &&
+ rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() &&
+ ( RedlineType::Insert == (*mpRedlineTable)[ nM - 1 ]->GetType() ) )
+ {
+ --nM;
+ pRedl = (*mpRedlineTable)[ nM ];
+ }
+ else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() &&
+ rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() &&
+ ( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) )
+ {
+ ++nM;
+ pRedl = (*mpRedlineTable)[ nM ];
+ }
+ }
+
+ if( pFndPos )
+ *pFndPos = nM;
+ return pRedl;
+ }
+ else if( *pEnd <= rPos )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp) );
+ }
+
+ bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
+ : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = (*mpRedlineTable)[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while( nLoopCnt );
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ lcl_AdjustRedlineRange( aPam );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam ));
+ }
+
+ int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable,
+ bCallDelete, aPam );
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
+ }
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
+{
+ const SwPosition* pStt = rPam.Start(),
+ * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
+ : rPam.GetPoint();
+
+ const sal_uLong nSttIdx = pStt->nNode.GetIndex();
+ const sal_uLong nEndIdx = pEnd->nNode.GetIndex();
+
+ for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
+ sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
+ nMk = pTmp->GetMark()->nNode.GetIndex();
+ if( nPt < nMk ) { long nTmp = nMk; nMk = nPt; nPt = nTmp; }
+
+ if( RedlineType::ParagraphFormat == pTmp->GetType() &&
+ ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
+ AcceptRedline( n, false );
+
+ if( nMk > nEndIdx )
+ break;
+ }
+}
+
+bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRejectRedline>( *pTmp ) );
+ }
+
+ bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
+ : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = (*mpRedlineTable)[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while( nLoopCnt );
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ lcl_AdjustRedlineRange( aPam );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
+ }
+
+ int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable,
+ bCallDelete, aPam );
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
+ }
+
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
+{
+ bool bSuccess = true;
+ OUString sUndoStr;
+ IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
+
+ if (mpRedlineTable->size() > 1)
+ {
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size()));
+ sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, sUndoStr);
+ rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ while (!mpRedlineTable->empty() && bSuccess)
+ {
+ if (bAccept)
+ bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true);
+ else
+ bSuccess = RejectRedline(mpRedlineTable->size() - 1, true);
+ }
+
+ if (!sUndoStr.isEmpty())
+ {
+ rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the next Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
+ if( pFnd )
+ {
+ const SwPosition* pEnd = pFnd->End();
+ if( !pEnd->nNode.GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pEnd->nNode );
+ SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.nNode &&
+ pCNd->Len() == rSttPos.nContent.GetIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->End();
+ }
+
+ do {
+ bRestart = false;
+
+ for( ; !pFnd && n < mpRedlineTable->size(); ++n )
+ {
+ pFnd = (*mpRedlineTable)[ n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->Start();
+ rSttPos = *pFnd->End();
+ break;
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pPrevEnd = pFnd->End();
+ while( ++n < mpRedlineTable->size() )
+ {
+ const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pRStt;
+ if( pFnd->GetType() != pTmp->GetType() ||
+ pFnd->GetAuthor() != pTmp->GetAuthor() )
+ break;
+ pRStt = pTmp->Start();
+ if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
+ {
+ pPrevEnd = pTmp->End();
+ rSttPos = *pPrevEnd;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx <= rPam.GetPoint()->nNode )
+ rPam.GetMark()->nContent.Assign( pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pIdx = &rPam.GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx >= rPam.GetMark()->nNode )
+ rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n < mpRedlineTable->size() )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->End();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the previous Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
+ if( pFnd )
+ {
+ const SwPosition* pStt = pFnd->Start();
+ if( !pStt->nNode.GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pStt->nNode );
+ SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.nNode &&
+ !rSttPos.nContent.GetIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->Start();
+ }
+
+ do {
+ bRestart = false;
+
+ while( !pFnd && 0 < n )
+ {
+ pFnd = (*mpRedlineTable)[ --n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->End();
+ rSttPos = *pFnd->Start();
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pNextStt = pFnd->Start();
+ while( 0 < n )
+ {
+ const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pREnd;
+ if( pFnd->GetType() == pTmp->GetType() &&
+ pFnd->GetAuthor() == pTmp->GetAuthor() &&
+ ( *pNextStt == *( pREnd = pTmp->End() ) ||
+ IsPrevPos( *pREnd, *pNextStt )) )
+ {
+ pNextStt = pTmp->Start();
+ rSttPos = *pNextStt;
+ }
+ else
+ {
+ ++n;
+ break;
+ }
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx >= rPam.GetPoint()->nNode )
+ rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pIdx = &rPam.GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx <= rPam.GetMark()->nNode )
+ rPam.GetPoint()->nContent.Assign( pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->Start();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Set comment at the Redline
+bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
+{
+ bool bRet = false;
+ const SwPosition* pStt = rPaM.Start(),
+ * pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
+ : rPaM.GetPoint();
+ SwRedlineTable::size_type n = 0;
+ if( GetRedlineTable().FindAtPosition( *pStt, n ) )
+ {
+ for( ; n < mpRedlineTable->size(); ++n )
+ {
+ bRet = true;
+ SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
+ if( pStt != pEnd && *pTmp->Start() > *pEnd )
+ break;
+
+ pTmp->SetComment( rS );
+ if( *pTmp->End() >= *pEnd )
+ break;
+ }
+ }
+ if( bRet )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Create a new author if necessary
+std::size_t DocumentRedlineManager::GetRedlineAuthor()
+{
+ return SW_MOD()->GetRedlineAuthor();
+}
+
+/// Insert new author into the Table for the Readers etc.
+std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
+{
+ return SW_MOD()->InsertRedlineAuthor(rNew);
+}
+
+void DocumentRedlineManager::UpdateRedlineAttr()
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->IsVisible() )
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
+{
+ return maRedlinePasswd;
+}
+
+void DocumentRedlineManager::SetRedlinePassword(
+ /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
+{
+ maRedlinePasswd = rNewPassword;
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+/// Set comment text for the Redline, which is inserted later on via
+/// AppendRedline. Is used by Autoformat.
+/// A null pointer resets the mode. The pointer is not copied, so it
+/// needs to stay valid!
+void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
+{
+ m_rDoc.SetAutoFormatRedline( nullptr != pText );
+ if( pText )
+ {
+ mpAutoFormatRedlnComment.reset( new OUString( *pText ) );
+ }
+ else
+ {
+ mpAutoFormatRedlnComment.reset();
+ }
+
+ mnAutoFormatRedlnCommentNo = nSeqNo;
+}
+
+DocumentRedlineManager::~DocumentRedlineManager()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */