diff options
Diffstat (limited to 'sw/source/core/doc/DocumentRedlineManager.cxx')
-rw-r--r-- | sw/source/core/doc/DocumentRedlineManager.cxx | 2761 |
1 files changed, 1679 insertions, 1082 deletions
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index 1c63619a8e1b..710baaf41b94 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -20,8 +20,10 @@ #include <frmfmt.hxx> #include <rootfrm.hxx> #include <txtfrm.hxx> +#include <txtfld.hxx> #include <doc.hxx> #include <docsh.hxx> +#include <wrtsh.hxx> #include <fmtfld.hxx> #include <frmtool.hxx> #include <IDocumentUndoRedo.hxx> @@ -38,6 +40,8 @@ #include <strings.hrc> #include <swmodule.hxx> #include <osl/diagnose.h> +#include <editeng/prntitem.hxx> +#include <comphelper/lok.hxx> using namespace com::sun::star; @@ -52,17 +56,17 @@ using namespace com::sun::star; // 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()); + assert(dynamic_cast<SwContentIndexReg*>(&pPos->GetNode()) + == pPos->GetContentNode()); - SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode(); + SwTextNode* pTextNode = pPos->GetNode().GetTextNode(); if( pTextNode == nullptr ) { - assert(pPos->nContent == 0); + assert(pPos->GetContentIndex() == 0); } else { - assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len()); + assert(pPos->GetContentIndex() >= 0 && pPos->GetContentIndex() <= pTextNode->Len()); } } @@ -75,7 +79,7 @@ using namespace com::sun::star; // check validity of the redline table. Checks redline bounds, and make // sure the redlines are sorted and non-overlapping. - void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess ) + void lcl_CheckRedline( const IDocumentRedlineAccess& redlineAccess ) { const SwRedlineTable& rTable = redlineAccess.GetRedlineTable(); @@ -91,7 +95,7 @@ using namespace com::sun::star; 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 ) @@ -144,22 +148,22 @@ 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); + rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode()); SwPosition currentStart(*rPam.Start()); - SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode()); + SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode()); while (!pStartNode) { // note: branch only taken for redlines, not fieldmarks SwStartNode *const pTableOrSectionNode( - currentStart.nNode.GetNode().IsTableNode() - ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode()) - : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode())); + currentStart.GetNode().IsTableNode() + ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode()) + : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode())); if ( !pTableOrSectionNode ) { SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)"); return; } - for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) + for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) { pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden); } @@ -177,15 +181,20 @@ void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) } } } - currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1; - currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0); - pStartNode = currentStart.nNode.GetNode().GetTextNode(); + currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 ); + pStartNode = currentStart.GetNode().GetTextNode(); } if (currentStart < *rPam.End()) { SwTextNode * pNode(pStartNode); do { + // deleted text node: remove it from "hidden" list + // to update numbering in Show Changes mode + SwPosition aPos( *pNode, pNode->Len() ); + if ( pNode->GetNumRule() && aPos < *rPam.End() ) + pNode->RemoveFromListRLHidden(); + std::vector<SwTextFrame*> frames; SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode); for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) @@ -194,6 +203,8 @@ void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) { frames.push_back(pFrame); } + // set anchored objects as deleted + pFrame->SetDrawObjsAsDeleted(true); } if (frames.empty()) { @@ -229,9 +240,9 @@ void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) } SwNodeIndex tmp(*pLast); // skip over hidden sections! - pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false)); + pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false)); } - while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex()); + while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex()); } // fields last - SwGetRefField::UpdateField requires up-to-date frames UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes @@ -243,23 +254,24 @@ void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) { - if (rDoc.IsClipBoard()) + // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs() + if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark()) { return; } bool isAppendObjsCalled(false); - rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode); + rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->GetNode()); SwPosition currentStart(*rPam.Start()); - SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode()); + SwTextNode * pStartNode(rPam.Start()->GetNode().GetTextNode()); while (!pStartNode) { // note: branch only taken for redlines, not fieldmarks - SwStartNode const*const pTableOrSectionNode( - currentStart.nNode.GetNode().IsTableNode() - ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode()) - : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode())); + SwStartNode *const pTableOrSectionNode( + currentStart.GetNode().IsTableNode() + ? static_cast<SwStartNode*>(currentStart.GetNode().GetTableNode()) + : static_cast<SwStartNode*>(currentStart.GetNode().GetSectionNode())); assert(pTableOrSectionNode); // known pathology - for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) + for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) { pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None); } @@ -267,19 +279,23 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) { // 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); + ::MakeFrames(&rDoc, currentStart.GetNode(), *pTableOrSectionNode->EndOfSectionNode()); isAppendObjsCalled = true; } - currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1; - currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0); - pStartNode = currentStart.nNode.GetNode().GetTextNode(); + currentStart.Assign( pTableOrSectionNode->EndOfSectionIndex() + 1 ); + pStartNode = currentStart.GetNode().GetTextNode(); } if (currentStart < *rPam.End()) { SwTextNode * pNode(pStartNode); do { + // undeleted text node: add it to the "hidden" list + // to update numbering in Show Changes mode + SwPosition aPos( *pNode, pNode->Len() ); + if ( pNode->GetNumRule() && aPos < *rPam.End() ) + pNode->AddToListRLHidden(); + std::vector<SwTextFrame*> frames; SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode); for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) @@ -288,6 +304,8 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) { frames.push_back(pFrame); } + // set anchored objects as not deleted + pFrame->SetDrawObjsAsDeleted(false); } if (frames.empty()) { @@ -303,6 +321,12 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) break; } + // no nodes can be unmerged by this - skip MakeFrames() etc. + if (rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()) + { + break; // continue with AppendAllObjs() + } + // 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); @@ -333,7 +357,7 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) 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); + ::MakeFrames(&rDoc, start.GetNode(), end.GetNode()); 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 @@ -342,9 +366,9 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) } SwNodeIndex tmp(*pLast); // skip over hidden sections! - pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false)); + pNode = static_cast<SwTextNode*>(SwNodes::GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false)); } - while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex()); + while (pNode && pNode->GetIndex() <= rPam.End()->GetNodeIndex()); } if (!isAppendObjsCalled) @@ -361,8 +385,14 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) // fields last - SwGetRefField::UpdateField requires up-to-date frames UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes - // update SwPostItMgr / notes in the margin - rDoc.GetDocShell()->Broadcast( + const SwTextNode *pTextNode = rPam.GetPointNode().GetTextNode(); + SwTextAttr* pTextAttr = pTextNode ? pTextNode->GetFieldTextAttrAt(rPam.GetPoint()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default) : nullptr; + SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pTextAttr)); + if (pTextField && comphelper::LibreOfficeKit::isActive() ) + rDoc.GetDocShell()->Broadcast( + SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED)); + else + rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) ); } @@ -373,19 +403,19 @@ namespace bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 ) { const SwContentNode* pCNd; - if( 0 != rPos2.nContent.GetIndex() ) + if( 0 != rPos2.GetContentIndex() ) return false; - if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() ) + if( rPos2.GetNodeIndex() - 1 != rPos1.GetNodeIndex() ) return false; - pCNd = rPos1.nNode.GetNode().GetContentNode(); - return pCNd && rPos1.nContent.GetIndex() == pCNd->Len(); + pCNd = rPos1.GetNode().GetContentNode(); + return pCNd && rPos1.GetContentIndex() == 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(); + SwTextNode* pToNode = rTo.GetNode().GetTextNode(); + SwTextNode* pFromNode = rFrom.GetNode().GetTextNode(); if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode) { const SwPaM aPam(*pToNode); @@ -398,12 +428,11 @@ namespace // using Undo, remove direct paragraph formatting of the "To" paragraph, // and apply here direct paragraph formatting of the "From" paragraph - SfxItemSet aTmp( - rDoc.GetAttrPool(), - svl::Items< + SfxItemSetFixed< 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>{}); + RES_CNTNT, RES_FRMATR_END - 1> + aTmp(rDoc.GetAttrPool()); SfxItemSet aTmp2(aTmp); pToNode->GetParaAttr(aTmp, 0, 0); @@ -415,10 +444,10 @@ namespace { for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem) { - sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem); + sal_uInt16 nWhich = aTmp.GetWhichByOffset(nItem); if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) && SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) ) - aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich ); + aTmp2.Put( aTmp.GetPool()->GetUserOrPoolDefaultItem(nWhich) ); } } @@ -430,6 +459,105 @@ namespace return nullptr; } + // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted) + void lcl_DeleteTrackedTableRow ( const SwPosition* pPos ) + { + const SwTableBox* pBox = pPos->GetNode().GetTableBox(); + if ( !pBox ) + return; + + // tracked column deletion + + const SvxPrintItem *pHasBoxTextChangesOnlyProp = + pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); + // empty table cell with property "HasTextChangesOnly" = false + if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() ) + { + SwCursor aCursor( *pPos, nullptr ); + if ( pBox->IsEmpty() ) + { + // tdf#155747 remove table cursor + pPos->GetDoc().GetDocShell()->GetWrtShell()->EnterStdMode(); + // TODO check the other cells of the column + // before removing the column + pPos->GetDoc().DeleteCol( aCursor ); + return; + } + else + { + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly ); + } + } + + // tracked row deletion + + const SwTableLine* pLine = pBox->GetUpper(); + const SvxPrintItem *pHasTextChangesOnlyProp = + pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); + // empty table row with property "HasTextChangesOnly" = false + if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() ) + { + if ( pLine->IsEmpty() ) + { + SwCursor aCursor( *pPos, nullptr ); + pPos->GetDoc().DeleteRow( aCursor ); + } + else + { + // update property "HasTextChangesOnly" + SwRedlineTable::size_type nPos = 0; + (void)pLine->UpdateTextChangesOnly(nPos); + } + } + } + + // at rejection of a deletion in a table, remove the tracking of the table row + // (also at accepting the last redline insertion of a tracked table row insertion) + void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion ) + { + const SwTableBox* pBox = pPos->GetNode().GetTableBox(); + if ( !pBox ) + return; + + // tracked column deletion + + const SvxPrintItem *pHasBoxTextChangesOnlyProp = + pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); + // table cell property "HasTextChangesOnly" is set and its value is false + if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() ) + { + SvxPrintItem aUnsetTracking(RES_PRINT, true); + SwCursor aCursor( *pPos, nullptr ); + pPos->GetDoc().SetBoxAttr( aCursor, aUnsetTracking ); + } + + // tracked row deletion + + const SwTableLine* pLine = pBox->GetUpper(); + const SvxPrintItem *pHasTextChangesOnlyProp = + pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); + // table row property "HasTextChangesOnly" is set and its value is false + if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() ) + { + bool bNoMoreInsertion = false; + if ( !bRejectDeletion ) + { + SwRedlineTable::size_type nPos = 0; + SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false); + + if ( SwRedlineTable::npos == nInsert ) + bNoMoreInsertion = true; + } + if ( bRejectDeletion || bNoMoreInsertion ) + { + SvxPrintItem aUnsetTracking(RES_PRINT, true); + SwCursor aCursor( *pPos, nullptr ); + pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking ); + } + } + } + bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos, bool bCallDelete, const SwPosition* pSttRng = nullptr, @@ -485,7 +613,15 @@ namespace case SwComparePosition::Outside: case SwComparePosition::Equal: - rArr.DeleteAndDestroy( rPos-- ); + { + bool bInsert = RedlineType::Insert == pRedl->GetType(); + SwPosition aPos(pRedl->Start()->GetNode()); + rArr.DeleteAndDestroy( rPos-- ); + + // remove tracking of the table row, if needed + if ( bInsert ) + lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false ); + } break; default: @@ -549,13 +685,13 @@ namespace if( pDelStt && pDelEnd ) { SwPaM aPam( *pDelStt, *pDelEnd ); - SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode(); - SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode(); + SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode(); + SwContentNode* pCEndNd = pDelEnd->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 ) + if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->GetContentIndex() == 0 ) lcl_CopyStyle(*pREnd, *pRStt); if( bDelRedl ) @@ -565,7 +701,10 @@ namespace rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore)); if( pCSttNd && pCEndNd ) + { rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); + lcl_DeleteTrackedTableRow( aPam.End() ); + } else if (pCSttNd && !pCEndNd) { aPam.GetBound().nContent.Assign( nullptr, 0 ); @@ -667,8 +806,8 @@ namespace { SwPaM aPam( *pDelStt, *pDelEnd ); - SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode(); - SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode(); + SwContentNode* pCSttNd = pDelStt->GetNode().GetContentNode(); + SwContentNode* pCEndNd = pDelEnd->GetNode().GetContentNode(); if( bDelRedl ) delete pRedl; @@ -677,16 +816,19 @@ namespace rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore)); if( pCSttNd && pCEndNd ) + { rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); + lcl_DeleteTrackedTableRow( aPam.End() ); + } else if (pCSttNd && !pCEndNd) { aPam.GetBound().nContent.Assign( nullptr, 0 ); aPam.GetBound( false ).nContent.Assign( nullptr, 0 ); - if (aPam.End()->nNode.GetNode().IsStartNode()) + if (aPam.End()->GetNode().IsStartNode()) { // end node will be deleted too! see nNodeDiff+1 - --aPam.End()->nNode; + aPam.End()->Adjust(SwNodeOffset(-1)); } - assert(!aPam.End()->nNode.GetNode().IsStartNode()); + assert(!aPam.End()->GetNode().IsStartNode()); rDoc.getIDocumentContentOperations().DelFullPara( aPam ); } else @@ -709,6 +851,9 @@ namespace if( pRedl->GetExtraData() ) pRedl->GetExtraData()->Reject( *pRedl ); + // remove tracking of the table row, if needed + lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true ); + switch( eCmp ) { case SwComparePosition::Inside: @@ -815,7 +960,7 @@ namespace // 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(); + SwTextNode* pTNd = pStt->GetNode().GetTextNode(); if( pTNd ) { // expand range to the whole paragraph @@ -855,6 +1000,18 @@ namespace return bRet; } + bool lcl_AcceptInnerInsertRedline(SwRedlineTable& rArr, SwRedlineTable::size_type& rPos, + int nDepth) + { + SwRangeRedline* pRedl = rArr[rPos]; + SwDoc& rDoc = pRedl->GetDoc(); + SwPaM const updatePaM(*pRedl->Start(), *pRedl->End()); + + pRedl->PopAllDataAfter(nDepth); + sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM); + return true; + } + typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos, bool bCallDelete, const SwPosition* pSttRng, @@ -869,8 +1026,7 @@ namespace int nCount = 0; const SwPosition* pStt = rPam.Start(), - * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark() - : rPam.GetPoint(); + * pEnd = rPam.End(); const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n ); if( pFnd && // Is new a part of it? ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd )) @@ -925,32 +1081,30 @@ namespace // 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(); + auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* SwDoc& rDoc = rPam.GetDoc(); - if( !pStt->nContent.GetIndex() && - !rDoc.GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() ) + if( !pStt->GetContentIndex() && + !rDoc.GetNodes()[ pStt->GetNodeIndex() - 1 ]->IsContentNode() ) { const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr ); if( pRedl ) { const SwPosition* pRStt = pRedl->Start(); - if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() == - pStt->nNode.GetIndex() - 1 ) + if( !pRStt->GetContentIndex() && pRStt->GetNodeIndex() == + pStt->GetNodeIndex() - 1 ) *pStt = *pRStt; } } - if( pEnd->nNode.GetNode().IsContentNode() && - !rDoc.GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() && - pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() ) + if( pEnd->GetNode().IsContentNode() && + !rDoc.GetNodes()[ pEnd->GetNodeIndex() + 1 ]->IsContentNode() && + pEnd->GetContentIndex() == pEnd->GetNode().GetContentNode()->Len() ) { const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr ); if( pRedl ) { const SwPosition* pREnd = pRedl->End(); - if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() == - pEnd->nNode.GetIndex() + 1 ) + if( !pREnd->GetContentIndex() && pREnd->GetNodeIndex() == + pEnd->GetNodeIndex() + 1 ) *pEnd = *pREnd; } } @@ -972,9 +1126,9 @@ namespace { m_pCursor->SetMark(); *m_pCursor->GetMark() = *m_rRedline.GetMark(); - *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent()); + m_rRedline.GetMark()->Assign(rDoc.GetNodes().GetEndOfContent()); } - *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent()); + m_rRedline.GetPoint()->Assign(rDoc.GetNodes().GetEndOfContent()); } ~TemporaryRedlineUpdater() { @@ -989,8 +1143,6 @@ 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) { @@ -1051,12 +1203,12 @@ void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode ) } for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop) - for (size_t i = 0; i < mpRedlineTable->size(); ++i) + for (size_t i = 0; i < maRedlineTable.size(); ++i) { - SwRangeRedline *const pRedline((*mpRedlineTable)[i]); + SwRangeRedline *const pRedline = maRedlineTable[i]; (pRedline->*pFnc)(nLoop, i, false); - while (mpRedlineTable->size() <= i - || (*mpRedlineTable)[i] != pRedline) + while (maRedlineTable.size() <= i + || maRedlineTable[i] != pRedline) { // ensure current position --i; // a previous redline may have been deleted } @@ -1064,7 +1216,7 @@ void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode ) //SwRangeRedline::MoveFromSection routinely changes //the keys that mpRedlineTable is sorted by - mpRedlineTable->Resort(); + maRedlineTable.Resort(); CheckAnchoredFlyConsistency(m_rDoc); CHECK_REDLINE( *this ) @@ -1099,31 +1251,29 @@ void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode) const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const { - return *mpRedlineTable; + return maRedlineTable; } SwRedlineTable& DocumentRedlineManager::GetRedlineTable() { - return *mpRedlineTable; + return maRedlineTable; } const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const { - return *mpExtraRedlineTable; + return maExtraRedlineTable; } SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() { - return *mpExtraRedlineTable; -} - -bool DocumentRedlineManager::HasExtraRedlineTable() const -{ - return mpExtraRedlineTable != nullptr; + return maExtraRedlineTable; } bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const { + if (&rNode.GetNodes() != &m_rDoc.GetNodes()) + return false; + SwPosition aPos(rNode); SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines(); SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()), @@ -1167,342 +1317,490 @@ Behaviour of Delete-Redline: the Delete */ IDocumentRedlineAccess::AppendResult -DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete) +DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete, + sal_uInt32 nMoveIDToDelete) { - bool bMerged = false; CHECK_REDLINE( *this ) - if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags)) + if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags)) { - pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); + 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 AppendResult::IGNORED; + } - if( m_rDoc.IsAutoFormatRedline() ) + // Collect MoveID's of the redlines we delete. + // If there is only 1, then we should use its ID. (continuing the move) + std::set<sal_uInt32> deletedMoveIDs; + + bool bMerged = false; + + pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); + + if( m_rDoc.IsAutoFormatRedline() ) + { + pNewRedl->SetAutoFormat(); + if( moAutoFormatRedlnComment && !moAutoFormatRedlnComment->isEmpty() ) { - pNewRedl->SetAutoFormat(); - if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() ) - { - pNewRedl->SetComment( *mpAutoFormatRedlnComment ); - pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo ); - } + pNewRedl->SetComment( *moAutoFormatRedlnComment ); + pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo ); } + } - SwPosition* pStt = pNewRedl->Start(), - * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark() - : pNewRedl->GetPoint(); + auto [pStt, pEnd] = pNewRedl->StartEnd(); // SwPosition* + { + SwTextNode* pTextNode = pStt->GetNode().GetTextNode(); + if( pTextNode == nullptr ) { - SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode(); - if( pTextNode == nullptr ) + if( pStt->GetContentIndex() > 0 ) { - if( pStt->nContent > 0 ) - { - OSL_ENSURE( false, "Redline start: non-text-node with content" ); - pStt->nContent = 0; - } + OSL_ENSURE( false, "Redline start: non-text-node with content" ); + pStt->SetContent( 0 ); } - else + } + else + { + if( pStt->GetContentIndex() > pTextNode->Len() ) { - if( pStt->nContent > pTextNode->Len() ) - { - OSL_ENSURE( false, "Redline start: index after text" ); - pStt->nContent = pTextNode->Len(); - } + OSL_ENSURE( false, "Redline start: index after text" ); + pStt->SetContent( pTextNode->Len() ); } - pTextNode = pEnd->nNode.GetNode().GetTextNode(); - if( pTextNode == nullptr ) + } + pTextNode = pEnd->GetNode().GetTextNode(); + if( pTextNode == nullptr ) + { + if( pEnd->GetContentIndex() > 0 ) { - if( pEnd->nContent > 0 ) - { - OSL_ENSURE( false, "Redline end: non-text-node with content" ); - pEnd->nContent = 0; - } + OSL_ENSURE( false, "Redline end: non-text-node with content" ); + pEnd->SetContent(0); } - else + } + else + { + if( pEnd->GetContentIndex() > pTextNode->Len() ) { - if( pEnd->nContent > pTextNode->Len() ) - { - OSL_ENSURE( false, "Redline end: index after text" ); - pEnd->nContent = pTextNode->Len(); - } + OSL_ENSURE( false, "Redline end: index after text" ); + pEnd->SetContent( 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; + } + 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; + const SwRedlineTable::size_type nStartPos = n; + bool bDec = false; - for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n ) - { - bDec = false; + for( ; pNewRedl && n < maRedlineTable.size(); bDec ? n : ++n ) + { + bDec = false; - SwRangeRedline* pRedl = (*mpRedlineTable)[ n ]; - SwPosition* pRStt = pRedl->Start(), - * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() - : pRedl->GetPoint(); + SwRangeRedline* pRedl = maRedlineTable[ n ]; + auto [pRStt, pREnd] = pRedl->StartEnd(); - // #i8518# remove empty redlines while we're at it - if( ( *pRStt == *pREnd ) && - ( pRedl->GetContentIdx() == nullptr ) ) - { - mpRedlineTable->DeleteAndDestroy(n); - continue; - } + // #i8518# remove empty redlines while we're at it + if( ( *pRStt == *pREnd ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + maRedlineTable.DeleteAndDestroy(n); + continue; + } - SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ); + SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ); - switch( pNewRedl->GetType() ) + if ( SwComparePosition::Before == eCmpPos && !IsPrevPos( *pEnd, *pRStt )) + break; + + switch( pNewRedl->GetType() ) + { + case RedlineType::Insert: + switch( pRedl->GetType() ) { case RedlineType::Insert: - switch( pRedl->GetType() ) + if( pRedl->IsOwnRedline( *pNewRedl ) && + // don't join inserted characters with moved text + !pRedl->IsMoved() ) { - 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 )) + bool bDelete = false; + bool bMaybeNotify = false; + + // Merge if applicable? + if( (( SwComparePosition::Behind == eCmpPos && + IsPrevPos( *pREnd, *pStt ) ) || + ( SwComparePosition::CollideStart == eCmpPos ) || + ( SwComparePosition::OverlapBehind == eCmpPos ) ) && + pRedl->CanCombine( *pNewRedl ) && + ( n+1 >= maRedlineTable.size() || + ( *maRedlineTable[ n+1 ]->Start() >= *pEnd && + *maRedlineTable[ n+1 ]->Start() != *pREnd ) ) ) + { + pRedl->SetEnd( *pEnd, pREnd ); + if( !pRedl->HasValidRange() ) { - pRedl->SetStart( *pStt, pRStt ); // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl ); - - bMerged = true; - bDelete = true; + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); } - 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; - } + bMerged = true; + bDelete = true; } - else if( SwComparePosition::Inside == eCmpPos ) + else if( (( SwComparePosition::Before == eCmpPos && + IsPrevPos( *pEnd, *pRStt ) ) || + ( SwComparePosition::CollideEnd == eCmpPos ) || + ( SwComparePosition::OverlapBefore == eCmpPos ) ) && + pRedl->CanCombine( *pNewRedl ) && + ( !n || + *maRedlineTable[ n-1 ]->End() != *pRStt )) { - // 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 ); - } + pRedl->SetStart( *pStt, pRStt ); + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); + + bMerged = true; + bDelete = true; } 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; - } + // own insert-over-insert redlines: + // just scrap the inside ones + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; } - else if ( SwComparePosition::OverlapBehind == eCmpPos ) + else if( SwComparePosition::OverlapBehind == eCmpPos ) { - // handle overlapping redlines in broken documents - pNewRedl->SetStart( *pREnd ); + *pStt = *pREnd; + if( ( *pStt == *pEnd ) && + ( pNewRedl->GetContentIdx() == nullptr ) ) + bDelete = bMaybeNotify = true; } - else if ( SwComparePosition::OverlapBefore == eCmpPos ) + 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; - } + bDelete = bMaybeNotify = true; + } + else if( SwComparePosition::Inside == eCmpPos ) + { + bDelete = bMaybeNotify = true; + bMerged = true; + } + else if( SwComparePosition::Equal == eCmpPos ) + bDelete = bMaybeNotify = true; + + if( bDelete ) + { + delete pNewRedl; + pNewRedl = nullptr; + bCompress = true; + + if (bMaybeNotify) + MaybeNotifyRedlineModification(*pRedl, m_rDoc); + + // set IsMoved checking nearby redlines + if (n < maRedlineTable.size()) // in case above 're-insert' failed + maRedlineTable.isMoved(n); + } + } + else if( SwComparePosition::Inside == eCmpPos ) + { + // split up + if( *pEnd != *pREnd ) + { + SwRangeRedline* pCpy = new SwRangeRedline( *pRedl ); + pCpy->SetStart( *pEnd ); + maRedlineTable.Insert( pCpy ); + } + pRedl->SetEnd( *pStt, pREnd ); + if( ( *pStt == *pRStt ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + } + else if( !pRedl->HasValidRange() ) + { + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.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 ); + maRedlineTable.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; + + MaybeNotifyRedlineModification(*pRedl, m_rDoc); + } + } + break; + case RedlineType::Delete: + if( SwComparePosition::Inside == eCmpPos ) + { + // split up + if( *pEnd != *pREnd ) + { + SwRangeRedline* pCpy = new SwRangeRedline( *pRedl ); + pCpy->SetStart( *pEnd ); + maRedlineTable.Insert( pCpy ); + } + pRedl->SetEnd( *pStt, pREnd ); + if( ( *pStt == *pRStt ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + } + else if( !pRedl->HasValidRange() ) + { + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.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 ); + maRedlineTable.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 + maRedlineTable.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 + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl, n ); + bDec = true; + break; + + case SwComparePosition::OverlapBehind: + pRedl->SetEnd( *pStt, pREnd ); + if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr ) + { + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; } break; - case RedlineType::Delete: - if( SwComparePosition::Inside == eCmpPos ) + + case SwComparePosition::Equal: + case SwComparePosition::Outside: + // Overlaps the current one completely or has the + // same dimension, delete the old one + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + break; + + case SwComparePosition::Inside: + // Overlaps the current one completely, + // split or shorten the new one + if( *pEnd != *pREnd ) { - // split up - if( *pEnd != *pREnd ) + if( *pEnd != *pRStt ) { - SwRangeRedline* pCpy = new SwRangeRedline( *pRedl ); - pCpy->SetStart( *pEnd ); - mpRedlineTable->Insert( pCpy ); + SwRangeRedline* pNew = new SwRangeRedline( *pRedl ); + pNew->SetStart( *pEnd ); + pRedl->SetEnd( *pStt, pREnd ); + if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr ) + maRedlineTable.DeleteAndDestroy( n ); + AppendRedline( pNew, bCallDelete ); + n = 0; // re-initialize + bDec = true; } + } + else pRedl->SetEnd( *pStt, pREnd ); - if( ( *pStt == *pRStt ) && - ( pRedl->GetContentIdx() == nullptr ) ) + 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) { - mpRedlineTable->DeleteAndDestroy( n ); - bDec = true; + pNewRedl->SetEnd(*pRStt, pEnd); } - else if( !pRedl->HasValidRange() ) + else if (*pStt == *pRStt) { - // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl, n ); + pNewRedl->SetStart(*pREnd, pStt); } - } - 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 ) + else { - delete pNewRedl; - pNewRedl = nullptr; - bCompress = true; + SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl ); + pNew->SetStart( *pREnd ); + pNewRedl->SetEnd( *pRStt, pEnd ); + AppendRedline( pNew, bCallDelete ); + n = 0; // re-initialize + bDec = true; } } - else if ( SwComparePosition::Equal == eCmpPos ) + break; + + case SwComparePosition::Inside: + case SwComparePosition::Equal: + delete pNewRedl; + pNewRedl = nullptr; + bCompress = true; + + MaybeNotifyRedlineModification(*pRedl, m_rDoc); + break; + + case SwComparePosition::OverlapBefore: + case SwComparePosition::OverlapBehind: + if( pRedl->IsOwnRedline( *pNewRedl ) && + pRedl->CanCombine( *pNewRedl )) { - // handle identical redlines in broken documents - // delete old (delete) redline - mpRedlineTable->DeleteAndDestroy( n ); + // 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 ); + maRedlineTable.DeleteAndDestroy( n ); bDec = true; } - else if ( SwComparePosition::OverlapBehind == eCmpPos ) - { // Another workaround for broken redlines - pNewRedl->SetStart( *pREnd ); - } + else if( SwComparePosition::OverlapBehind == eCmpPos ) + pNewRedl->SetStart( *pREnd, pStt ); + else + pNewRedl->SetEnd( *pRStt, pEnd ); 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 ) + case SwComparePosition::CollideEnd: + if (pRStt->GetContentIndex() != 0 + && pRStt->GetNode() != pREnd->GetNode()) + { // tdf#147466 HACK: don't combine in this case to avoid the tdf#119571 code from *undeleting* section nodes + break; + } + [[fallthrough]]; + case SwComparePosition::CollideStart: + if( pRedl->IsOwnRedline( *pNewRedl ) && + pRedl->CanCombine( *pNewRedl ) ) + { + if( IsHideChanges( meRedlineFlags )) { - mpRedlineTable->DeleteAndDestroy( n ); - bDec = true; + // Before we can merge, we make it visible! + // We insert temporarily so that pNew is + // also dealt with when moving the indices. + maRedlineTable.Insert(pNewRedl); + pRedl->Show(0, maRedlineTable.GetPos(pRedl)); + maRedlineTable.Remove( pNewRedl ); + pRStt = pRedl->Start(); + pREnd = pRedl->End(); } - break; - case SwComparePosition::Equal: - case SwComparePosition::Outside: - // Overlaps the current one completely or has the - // same dimension, delete the old one - mpRedlineTable->DeleteAndDestroy( n ); + // 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; - break; - case SwComparePosition::Inside: - // Overlaps the current one completely, - // split or shorten the new one - if( *pEnd != *pREnd ) + if( *(pNewRedl->Start()) <= *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; - } + // 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; } - else - pRedl->SetEnd( *pStt, pREnd ); - break; - default: - break; + + maRedlineTable.DeleteAndDestroy( nToBeDeleted ); } break; default: @@ -1510,699 +1808,661 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall } break; - case RedlineType::Delete: - switch( pRedl->GetType() ) + 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 ) && + // tdf#116084 tdf#121176 don't combine anonymized deletion + // and anonymized insertion, i.e. with the same dummy timestamp + !pRedl->GetRedlineData(0).IsAnonymized() ) { - case RedlineType::Delete: + // Collect MoveID's of the redlines we delete. + if (nMoveIDToDelete > 1 && maRedlineTable[n]->GetMoved() > 0 + && (eCmpPos == SwComparePosition::Equal + || eCmpPos == SwComparePosition::Inside + || eCmpPos == SwComparePosition::Outside + || eCmpPos == SwComparePosition::OverlapBefore + || eCmpPos == SwComparePosition::OverlapBehind)) + { + deletedMoveIDs.insert(maRedlineTable[n]->GetMoved()); + } + + // 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::Outside: + case SwComparePosition::Equal: + bCompress = true; + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + [[fallthrough]]; + + case SwComparePosition::Inside: + if( bCallDelete ) { - // Overlaps the current one completely, - // split the new one - if (*pEnd == *pREnd) - { - pNewRedl->SetEnd(*pRStt, pEnd); - } - else if (*pStt == *pRStt) + // 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->GetContentIndex() == 0) && + pEnd->GetNode().IsEndNode() ) { - pNewRedl->SetStart(*pREnd, pStt); + pEnd->Adjust(SwNodeOffset(-1)); + m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl ); } else + m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl ); + + bCompress = true; + } + delete pNewRedl; + pNewRedl = nullptr; + + // No need to call MaybeNotifyRedlineModification, because a notification + // was already sent in DocumentRedlineManager::DeleteRedline + break; + + case SwComparePosition::Outside: + { + maRedlineTable.Remove( n ); + bDec = true; + if( bCallDelete ) { - SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl ); - pNew->SetStart( *pREnd ); - pNewRedl->SetEnd( *pRStt, pEnd ); - AppendRedline( pNew, bCallDelete ); + TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl); + m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl ); n = 0; // re-initialize - bDec = true; } + delete pRedl; } 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 ); + SwPaM aPam( *pRStt, *pEnd ); + + if( *pEnd == *pREnd ) + maRedlineTable.DeleteAndDestroy( n ); else - pNewRedl->SetEnd( *pREnd, pEnd ); - mpRedlineTable->DeleteAndDestroy( n ); + { + pRedl->SetStart( *pEnd, pRStt ); + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl, n ); + } + + if( bCallDelete ) + { + TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl); + m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); + n = 0; // re-initialize + } 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 ) ) + case SwComparePosition::OverlapBehind: { - if( IsHideChanges( meRedlineFlags )) + SwPaM aPam( *pStt, *pREnd ); + + if( *pStt == *pRStt ) { - // 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(); + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; } - - // 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; + pRedl->SetEnd( *pStt, pREnd ); - if( *(pNewRedl->Start()) <= *pREnd ) + if( bCallDelete ) { - // 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; + TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl); + m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); + n = 0; // re-initialize bDec = true; } - - mpRedlineTable->DeleteAndDestroy( nToBeDeleted ); } break; default: break; } - break; - case RedlineType::Insert: + meRedlineFlags = eOld; + } + else { - // 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 ) ) - { + // 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; - // 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 ) + switch( eCmpPos ) + { + case SwComparePosition::Equal: { - 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 ) + pRedl->PushData( *pNewRedl ); + delete pNewRedl; + pNewRedl = nullptr; + if( IsHideChanges( meRedlineFlags )) { - // 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; + pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); } - break; + bCompress = true; - 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; + // set IsMoved checking nearby redlines + SwRedlineTable::size_type nRIdx = maRedlineTable.GetPos(pRedl); + if (nRIdx < maRedlineTable.size()) // in case above 're-insert' failed + maRedlineTable.isMoved(nRIdx); - case SwComparePosition::OverlapBefore: - { - SwPaM aPam( *pRStt, *pEnd ); + } + break; - if( *pEnd == *pREnd ) - mpRedlineTable->DeleteAndDestroy( n ); - else + 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 ); - } - - if( bCallDelete ) - { - TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl); - m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); - n = 0; // re-initialize + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl, n ); + bDec = true; } - bDec = true; } - break; - - case SwComparePosition::OverlapBehind: + else { - SwPaM aPam( *pStt, *pREnd ); - - if( *pStt == *pRStt ) + pNewRedl->PushData( *pRedl, false ); + if( *pREnd != *pEnd ) { - mpRedlineTable->DeleteAndDestroy( n ); - bDec = true; + pNew = new SwRangeRedline( *pRedl ); + pNew->SetStart( *pEnd ); } - else - pRedl->SetEnd( *pStt, pREnd ); - - if( bCallDelete ) + pRedl->SetEnd( *pStt, pREnd ); + if( !pRedl->HasValidRange() ) { - TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl); - m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam ); - n = 0; // re-initialize - bDec = true; + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl, n ); } } - break; - default: - break; } + 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; + 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; - switch( eCmpPos ) + case SwComparePosition::OverlapBefore: { - case SwComparePosition::Equal: + if( *pEnd == *pREnd ) { pRedl->PushData( *pNewRedl ); - delete pNewRedl; - pNewRedl = nullptr; + pNewRedl->SetEnd( *pRStt, pEnd ); if( IsHideChanges( meRedlineFlags )) { - pRedl->Hide(0, mpRedlineTable->GetPos(pRedl)); + maRedlineTable.Insert(pNewRedl); + pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); + maRedlineTable.Remove( pNewRedl ); } - bCompress = true; } - break; - - case SwComparePosition::Inside: + else { - 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 ); - } - } + pNew = new SwRangeRedline( *pRedl ); + pNew->PushData( *pNewRedl ); + pNew->SetEnd( *pEnd ); + pNewRedl->SetEnd( *pRStt, pEnd ); + pRedl->SetStart( *pNew->End(), pRStt ) ; + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); + bDec = true; } - break; + } + break; - case SwComparePosition::Outside: + case SwComparePosition::OverlapBehind: + { + if( *pStt == *pRStt ) { pRedl->PushData( *pNewRedl ); - if( *pEnd == *pREnd ) - { - pNewRedl->SetEnd( *pRStt, pEnd ); - } - else if (*pStt == *pRStt) - { - pNewRedl->SetStart(*pREnd, pStt); - } - else + pNewRedl->SetStart( *pREnd, pStt ); + if( IsHideChanges( meRedlineFlags )) { - pNew = new SwRangeRedline( *pNewRedl ); - pNew->SetEnd( *pRStt ); - pNewRedl->SetStart( *pREnd, pStt ); + maRedlineTable.Insert( pNewRedl ); + pRedl->Hide(0, maRedlineTable.GetPos(pRedl)); + maRedlineTable.Remove( pNewRedl ); } - bCompress = true; } - break; - - case SwComparePosition::OverlapBefore: + else { - 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->SetStart( *pStt ); + pNewRedl->SetStart( *pREnd, pStt ); + pRedl->SetEnd( *pNew->Start(), pREnd ); + if( !pRedl->HasValidRange() ) { - 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 ); - } + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); } } - break; - default: - break; } + break; + default: + break; + } - // insert the pNew part (if it exists) - if( pNew ) - { - mpRedlineTable->Insert( pNew ); + // insert the pNew part (if it exists) + if( pNew ) + { + maRedlineTable.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?" ); + // 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; - } + // restart (now with pRedl being split up) + n = 0; + bDec = true; } } - break; + } + 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 RedlineType::Format: + switch( eCmpPos ) + { + case SwComparePosition::OverlapBefore: + pRedl->SetStart( *pEnd, pRStt ); + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl, n ); + bDec = true; + break; - case SwComparePosition::OverlapBehind: - pRedl->SetEnd( *pStt, pREnd ); - 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::Equal: + case SwComparePosition::Outside: + // Overlaps the current one completely or has the + // same dimension, delete the old one + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + break; - case SwComparePosition::Inside: - // Overlaps the current one completely, - // split or shorten the new one - if( *pEnd != *pREnd ) + case SwComparePosition::Inside: + // Overlaps the current one completely, + // split or shorten the new one + if( *pEnd != *pREnd ) + { + if( *pEnd != *pRStt ) { - 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 + SwRangeRedline* pNew = new SwRangeRedline( *pRedl ); + pNew->SetStart( *pEnd ); pRedl->SetEnd( *pStt, pREnd ); - break; - default: - break; + if( ( *pStt == *pRStt ) && + ( pRedl->GetContentIdx() == nullptr ) ) + maRedlineTable.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::Format: + switch( pRedl->GetType() ) + { + case RedlineType::Insert: + case RedlineType::Delete: + switch( eCmpPos ) { - 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; + + MaybeNotifyRedlineModification(*pRedl, m_rDoc); + break; + + case SwComparePosition::Outside: + // Overlaps the current one completely, + // split or shorten the new one + if (*pEnd == *pREnd) { - case SwComparePosition::OverlapBefore: + 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 ); - break; - - case SwComparePosition::OverlapBehind: - pNewRedl->SetStart( *pREnd, pStt ); - break; + 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 + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + } + break; - case SwComparePosition::Equal: - case SwComparePosition::Inside: + case SwComparePosition::Inside: + if( pRedl->IsOwnRedline( *pNewRedl ) && + pRedl->CanCombine( *pNewRedl )) + { + // own one can be ignored completely 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; + MaybeNotifyRedlineModification(*pRedl, m_rDoc); } - break; - case RedlineType::Format: - switch( eCmpPos ) + else if( *pREnd == *pEnd ) + // or else only shorten the current one + pRedl->SetEnd( *pStt, pREnd ); + else if( *pRStt == *pStt ) { - 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; + // or else only shorten the current one + pRedl->SetStart( *pEnd, pRStt ); + // re-insert + maRedlineTable.Remove( n ); + maRedlineTable.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 ); + 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( *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; + maRedlineTable.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 == 0 || *maRedlineTable[ n-1 ]->End() < *pStt)) + { + // If that's the case we can merge it, meaning + // the new one covers this well + pNewRedl->SetEnd( *pREnd, pEnd ); + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; + } + break; + case SwComparePosition::CollideStart: + if( pRedl->IsOwnRedline( *pNewRedl ) && + pRedl->CanCombine( *pNewRedl ) && + (n+1 >= maRedlineTable.size() || + (*maRedlineTable[ n+1 ]->Start() >= *pEnd && + *maRedlineTable[ n+1 ]->Start() != *pREnd))) + { + // If that's the case we can merge it, meaning + // the new one covers this well + pNewRedl->SetStart( *pRStt, pStt ); + maRedlineTable.DeleteAndDestroy( n ); + bDec = true; } break; default: break; } break; - - case RedlineType::FmtColl: - // How should we behave here? - // insert as is - break; default: break; } + break; + + case RedlineType::FmtColl: + // How should we behave here? + // insert as is + break; + default: + break; } + } - if( pNewRedl ) + if( pNewRedl ) + { + if( ( *pStt == *pEnd ) && + ( pNewRedl->GetContentIdx() == nullptr ) ) + { // Do not insert empty redlines + delete pNewRedl; + pNewRedl = nullptr; + } + else { - if( ( *pStt == *pEnd ) && - ( pNewRedl->GetContentIdx() == nullptr ) ) - { // Do not insert empty redlines - delete pNewRedl; - pNewRedl = nullptr; - } - else + if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) { - if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) + if ( pStt->GetContentIndex() != 0 ) { - 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()) + // 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 (except paragraphs of the removed tables). + + SwContentNode* pDelNd = pStt->GetNode().GetContentNode(); + // start copying the style of the first paragraph from the end of the range + SwContentNode* pTextNd = pEnd->GetNode().GetContentNode(); + SwNodeIndex aIdx( pEnd->GetNode() ); + bool bFirst = true; + + while (pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex()) + { + if( pTextNd->IsTextNode() ) { - pTextNode = pTextNd->GetTextNode(); - if (pTextNode && pDelNode != pTextNode ) + SwPosition aPos(aIdx); + + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { - SwPosition aPos(aIdx); + bCompress = true; - if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + // 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) { - bCompress = true; + std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData; + if (!bFirst) + pExtraData->SetFormatAll(false); + xRedlineExtraData.reset(pExtraData); + pPar->SetExtraData( xRedlineExtraData.get() ); + } - // split redline to store ExtraData per paragraphs - SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl ); - pPar->SetStart( aPos ); - pNewRedl->SetEnd( aPos ); + // skip empty redlines without ExtraData + // FIXME: maybe checking pExtraData is redundant here + if ( pExtraData || *pPar->Start() != *pPar->End() ) + maRedlineTable.Insert( pPar ); + else + delete pPar; + } + + // modify paragraph formatting + lcl_CopyStyle(*pStt, aPos); + } - // get extradata for reset formatting of the modified paragraph - SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false); - if (pExtraData) + if (bFirst) + bFirst = false; + + // Jump to the previous paragraph and if needed, skip paragraphs of + // the removed table(s) in the range to avoid leaving empty tables + // because of the non-continuous redline range over the table. + // FIXME: this is not enough for tables with inner redlines, where + // tracked deletion of the text containing such a table leaves an + // empty table at the place of the table (a problem inherited from OOo). + pTextNd = nullptr; + while( --aIdx > *pDelNd && !aIdx.GetNode().IsContentNode() ) + { + // possible table end + if( aIdx.GetNode().IsEndNode() && aIdx.GetNode().FindTableNode() ) + { + SwNodeIndex aIdx2 = aIdx; + // search table start and skip table paragraphs + while ( pDelNd->GetIndex() < aIdx2.GetIndex() ) + { + SwTableNode* pTable = aIdx2.GetNode().GetTableNode(); + if( pTable && + pTable->EndOfSectionNode()->GetIndex() == aIdx.GetIndex() ) { - std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData; - if (!bFirst) - pExtraData->SetFormatAll(false); - xRedlineExtraData.reset(pExtraData); - pPar->SetExtraData( xRedlineExtraData.get() ); + aIdx = aIdx2; + break; } - - // skip empty redlines without ExtraData - // FIXME: maybe checking pExtraData is redundant here - if ( pExtraData || *pPar->Start() != *pPar->End() ) - mpRedlineTable->Insert( pPar ); - else - delete pPar; + --aIdx2; } - - // modify paragraph formatting - lcl_CopyStyle(*pStt, aPos); } - pTextNd = SwNodes::GoPrevious( &aIdx ); - - if (bFirst) - bFirst = false; } + + if (aIdx.GetNode().IsContentNode()) + pTextNd = aIdx.GetNode().GetContentNode(); } } - bool const ret = mpRedlineTable->Insert( pNewRedl ); - assert(ret || !pNewRedl); - if (ret && !pNewRedl) + + // delete tables of the deletion explicitly, to avoid + // remaining empty tables after accepting the rejection + // and visible empty tables in Hide Changes mode + // (this was the case, if tables have already contained + // other tracked changes) + // FIXME: because of recursive nature of AppendRedline, + // this doesn't work for selections with multiple tables + if ( m_rDoc.GetIDocumentUndoRedo().DoesUndo() ) { - bMerged = true; // treat InsertWithValidRanges as "merge" + SwNodeIndex aSttIdx( pStt->GetNode() ); + SwNodeIndex aEndIdx( pEnd->GetNode() ); + while ( aSttIdx < aEndIdx ) + { + if ( aSttIdx.GetNode().IsTableNode() ) + { + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + SwCursor aCursor( SwPosition(aSttIdx), nullptr ); + m_rDoc.SetRowNotTracked( aCursor, aHasTextChangesOnly, /*bAll=*/true ); + } + ++aSttIdx; + } } } + bool const ret = maRedlineTable.Insert( pNewRedl ); + assert(ret || !pNewRedl); + if (ret && !pNewRedl) + { + bMerged = true; // treat InsertWithValidRanges as "merge" + } } - - if( bCompress ) - CompressRedlines(); } - else + + // If we deleted moved redlines, and there was only 1 MoveID, then we should use that + // We overwrite those that was given right now, so it cannot be deeper under other redline + if (nMoveIDToDelete > 1 && deletedMoveIDs.size() == 1) { - if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() ) + sal_uInt32 nNewMoveID = *(deletedMoveIDs.begin()); + if (nNewMoveID > 1) // MoveID==1 is for old, unrecognised moves, leave them alone { - 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; + for (n = 0; n < maRedlineTable.size(); ++n) + { + if (maRedlineTable[n]->GetMoved() == nMoveIDToDelete) + { + maRedlineTable[n]->SetMoved(nNewMoveID); + } + } } - delete pNewRedl; - pNewRedl = nullptr; } + + if( bCompress ) + CompressRedlines(nStartPos); + CHECK_REDLINE( *this ) return (nullptr != pNewRedl) @@ -2226,7 +2486,7 @@ bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl // Make equivalent of 'AppendRedline' checks inside here too - mpExtraRedlineTable->Insert( pNewRedl ); + maExtraRedlineTable.Insert( pNewRedl ); } else { @@ -2268,7 +2528,7 @@ bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRed // Make equivalent of 'AppendRedline' checks inside here too - mpExtraRedlineTable->Insert( pNewRedl ); + maExtraRedlineTable.Insert( pNewRedl ); } else { @@ -2294,7 +2554,7 @@ bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRed return nullptr != pNewRedl; } -void DocumentRedlineManager::CompressRedlines() +void DocumentRedlineManager::CompressRedlines(size_t nStartIndex) { CHECK_REDLINE( *this ) @@ -2306,20 +2566,19 @@ void DocumentRedlineManager::CompressRedlines() 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 (nStartIndex == 0) + nStartIndex = 1; + for( SwRedlineTable::size_type n = nStartIndex; n < maRedlineTable.size(); ++n ) + { + SwRangeRedline* pPrev = maRedlineTable[ n-1 ], + * pCur = maRedlineTable[ n ]; + auto [pPrevStt,pPrevEnd] = pPrev->StartEnd(); + auto [pCurStt, pCurEnd] = pCur->StartEnd(); + if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) && - pPrevStt->nNode.GetNode().StartOfSectionNode() == - pCurEnd->nNode.GetNode().StartOfSectionNode() && - !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() ) + pPrevStt->GetNode().StartOfSectionNode() == + pCurEnd->GetNode().StartOfSectionNode() && + !pCurEnd->GetNode().StartOfSectionNode()->IsTableNode() ) { // we then can merge them SwRedlineTable::size_type nPrevIndex = n-1; @@ -2327,7 +2586,7 @@ void DocumentRedlineManager::CompressRedlines() pCur->Show(0, n); pPrev->SetEnd( *pCur->End() ); - mpRedlineTable->DeleteAndDestroy( n ); + maRedlineTable.DeleteAndDestroy( n ); --n; if( pFnc ) (pPrev->*pFnc)(0, nPrevIndex, false); @@ -2341,17 +2600,14 @@ void DocumentRedlineManager::CompressRedlines() bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange ) { bool bChg = false; + auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition* SwRedlineTable::size_type n = 0; - const SwPosition* pStt = rRange.Start(); - const SwPosition* pEnd = rRange.End(); - GetRedline( *pStt, &n ); - for ( ; n < mpRedlineTable->size(); ++n) + //FIXME overlapping problem GetRedline( *pStt, &n ); + for ( ; n < maRedlineTable.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) + SwRangeRedline * pRedline = maRedlineTable[ n ]; + auto [pRedlineStart, pRedlineEnd] = pRedline->StartEnd(); + if (*pRedlineStart <= *pStt && *pEnd <= *pRedlineEnd) { bChg = true; int nn = 0; @@ -2379,18 +2635,18 @@ bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange ) case 3: pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove); - mpRedlineTable->DeleteAndDestroy( n-- ); + maRedlineTable.DeleteAndDestroy( n-- ); pRedline = nullptr; break; } if (pRedline && !pRedline->HasValidRange()) { // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedline, n ); + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedline, n ); } if( pNew ) - mpRedlineTable->Insert( pNew, n ); + maRedlineTable.Insert( pNew, n ); } else if (*pEnd < *pRedlineStart) break; @@ -2417,26 +2673,22 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd } } - const SwPosition* pStt = rRange.Start(), - * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark() - : rRange.GetPoint(); + auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition* SwRedlineTable::size_type n = 0; GetRedline( *pStt, &n ); - for( ; n < mpRedlineTable->size() ; ++n ) + for( ; n < maRedlineTable.size() ; ++n ) { - SwRangeRedline* pRedl = (*mpRedlineTable)[ n ]; + SwRangeRedline* pRedl = maRedlineTable[ n ]; if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() ) continue; - SwPosition* pRStt = pRedl->Start(), - * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() - : pRedl->GetPoint(); + auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition* switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) ) { case SwComparePosition::Equal: case SwComparePosition::Outside: pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove); - mpRedlineTable->DeleteAndDestroy( n-- ); + maRedlineTable.DeleteAndDestroy( n-- ); bChg = true; break; @@ -2445,8 +2697,8 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd pRedl->SetStart( *pEnd, pRStt ); pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl ); + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); --n; break; @@ -2457,8 +2709,8 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd if( !pRedl->HasValidRange() ) { // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl ); + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); --n; } break; @@ -2472,8 +2724,8 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd pRedl->SetStart( *pEnd, pRStt ); pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl ); + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); --n; } else @@ -2492,12 +2744,12 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd if( !pRedl->HasValidRange() ) { // re-insert - mpRedlineTable->Remove( n ); - mpRedlineTable->Insert( pRedl ); + maRedlineTable.Remove( n ); + maRedlineTable.Insert( pRedl ); --n; } if( pCpy ) - mpRedlineTable->Insert( pCpy ); + maRedlineTable.Insert( pCpy ); } } break; @@ -2508,14 +2760,14 @@ bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUnd if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() ) { pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove); - mpRedlineTable->DeleteAndDestroy( n-- ); + maRedlineTable.DeleteAndDestroy( n-- ); bChg = true; break; } [[fallthrough]]; case SwComparePosition::Before: - n = mpRedlineTable->size(); + n = maRedlineTable.size(); break; default: break; @@ -2539,38 +2791,114 @@ bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSave 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 SwNodeOffset nNdIdx = rNd.GetIndex(); + // if the table only contains good (i.e. non-overlapping) data, we can do a binary search + if (!maRedlineTable.HasOverlappingElements()) { - const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; - sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(), - nMk = pTmp->GetMark()->nNode.GetIndex(); - if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; } + // binary search to the first redline with end >= the needle + auto it = std::lower_bound(maRedlineTable.begin(), maRedlineTable.end(), rNd, + [&nNdIdx](const SwRangeRedline* lhs, const SwNode& /*rhs*/) + { + return lhs->End()->GetNodeIndex() < nNdIdx; + }); + for( ; it != maRedlineTable.end(); ++it) + { + const SwRangeRedline* pTmp = *it; + auto [pStart, pEnd] = pTmp->StartEnd(); // SwPosition* + SwNodeOffset nStart = pStart->GetNodeIndex(), + nEnd = pEnd->GetNodeIndex(); - if( ( RedlineType::Any == nType || nType == pTmp->GetType()) && - nMk <= nNdIdx && nNdIdx <= nPt ) - return n; + if( ( RedlineType::Any == nType || nType == pTmp->GetType()) && + nStart <= nNdIdx && nNdIdx <= nEnd ) + return std::distance(maRedlineTable.begin(), it); - if( nMk > nNdIdx ) - break; + if( nStart > nNdIdx ) + break; + } + } + else + { + for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n ) + { + const SwRangeRedline* pTmp = maRedlineTable[ n ]; + SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(), + nMk = pTmp->GetMark()->GetNodeIndex(); + if( nPt < nMk ) + std::swap( nMk, nPt ); + + if( ( RedlineType::Any == nType || nType == pTmp->GetType()) && + nMk <= nNdIdx && nNdIdx <= nPt ) + return n; + + if( nMk > nNdIdx ) + break; + } } return SwRedlineTable::npos; // #TODO - add 'SwExtraRedlineTable' also ? } +SwRedlineTable::size_type +DocumentRedlineManager::GetRedlineEndPos(SwRedlineTable::size_type nStartPos, const SwNode& rNd, + RedlineType nType) const +{ + //if the start is already invalid + if (nStartPos >= maRedlineTable.size()) + return nStartPos; + + const SwNodeOffset nNdIdx = rNd.GetIndex(); + SwRedlineTable::size_type nEndPos = nStartPos; + SwRedlineTable::size_type nEndPosTry = nEndPos + 1; + + while (nEndPosTry < maRedlineTable.size() + && maRedlineTable[nEndPosTry]->Start()->GetNodeIndex() <= nNdIdx) + { + if (RedlineType::Any == nType || nType == maRedlineTable[nEndPosTry]->GetType()) + { + nEndPos = nEndPosTry; + } + nEndPosTry++; + } + return nEndPos; +} + +void DocumentRedlineManager::UpdateRedlineContentNode(SwRedlineTable::size_type nStartPos, + SwRedlineTable::size_type nEndPos) const +{ + for (SwRedlineTable::size_type n = nStartPos; n <= nEndPos; ++n) + { + //just in case we got wrong input + if (n >= maRedlineTable.size()) + return; + + SwPosition* pStart = maRedlineTable[n]->Start(); + SwPosition* pEnd = maRedlineTable[n]->End(); + SwContentNode* pCont = pStart->GetNode().GetContentNode(); + if (pCont) + { + pStart->nContent.Assign(pCont, pStart->nContent.GetIndex()); + } + pCont = pEnd->GetNode().GetContentNode(); + if (pCont) + { + pEnd->nContent.Assign(pCont, pEnd->nContent.GetIndex()); + } + } +} + bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const { SwPosition currentStart(*rPam.Start()); SwPosition currentEnd(*rPam.End()); - SwNodeIndex pEndNodeIndex(currentEnd.nNode.GetNode()); + const SwNode& rEndNode(currentEnd.GetNode()); - for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->nNode.GetNode(), nType ); - n < mpRedlineTable->size(); ++n ) + for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->GetNode(), nType ); + n < maRedlineTable.size(); ++n ) { - const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; + const SwRangeRedline* pTmp = maRedlineTable[ n ]; - if ( pTmp->Start()->nNode > pEndNodeIndex ) + if ( pTmp->Start()->GetNode() > rEndNode ) break; if( RedlineType::Any != nType && nType != pTmp->GetType() ) @@ -2591,27 +2919,24 @@ bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, b const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos, SwRedlineTable::size_type* pFndPos ) const { - SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0; + SwRedlineTable::size_type nO = maRedlineTable.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(); + const SwRangeRedline* pRedl = maRedlineTable[ nM ]; + auto [pStt, pEnd] = pRedl->StartEnd(); if( pEnd == pStt ? *pStt == rPos : ( *pStt <= rPos && rPos < *pEnd ) ) { - while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() && - rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() ) + while( nM && rPos == *maRedlineTable[ nM - 1 ]->End() && + rPos == *maRedlineTable[ nM - 1 ]->Start() ) { --nM; - pRedl = (*mpRedlineTable)[ nM ]; + pRedl = maRedlineTable[ nM ]; } // if there are format and insert changes in the same position // show insert change first. @@ -2619,19 +2944,19 @@ const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos // 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() ) ) + if( nM && rPos >= *maRedlineTable[ nM - 1 ]->Start() && + rPos <= *maRedlineTable[ nM - 1 ]->End() && + ( RedlineType::Insert == maRedlineTable[ nM - 1 ]->GetType() ) ) { --nM; - pRedl = (*mpRedlineTable)[ nM ]; + pRedl = maRedlineTable[ nM ]; } - else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() && - rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() && - ( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) ) + else if( ( nM + 1 ) <= nO && rPos >= *maRedlineTable[ nM + 1 ]->Start() && + rPos <= *maRedlineTable[ nM + 1 ]->End() && + ( RedlineType::Insert == maRedlineTable[ nM + 1 ]->GetType() ) ) { ++nM; - pRedl = (*mpRedlineTable)[ nM ]; + pRedl = maRedlineTable[ nM ]; } } @@ -2658,7 +2983,100 @@ const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos // #TODO - add 'SwExtraRedlineTable' also ? } -bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete ) +bool DocumentRedlineManager::AcceptRedlineRange(SwRedlineTable::size_type nPosOrigin, + SwRedlineTable::size_type& nPosStart, + SwRedlineTable::size_type& nPosEnd, + bool bCallDelete) +{ + bool bRet = false; + + SwRangeRedline* pTmp = maRedlineTable[nPosOrigin]; + SwRedlineTable::size_type nRdlIdx = nPosEnd + 1; + SwRedlineData aOrigData = pTmp->GetRedlineData(0); + + SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex(); + sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex(); + SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex(); + sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex(); + do + { + nRdlIdx--; + pTmp = maRedlineTable[nRdlIdx]; + if (pTmp->Start()->GetNodeIndex() < nPamStartNI + || (pTmp->Start()->GetNodeIndex() == nPamStartNI + && pTmp->Start()->GetContentIndex() < nPamStartCI)) + break; + + if (pTmp->End()->GetNodeIndex() > nPamEndtNI + || (pTmp->End()->GetNodeIndex() == nPamEndtNI + && pTmp->End()->GetContentIndex() > nPamEndCI)) + { + } + else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData)) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoAcceptRedline>(*pTmp)); + } + nPamEndtNI = pTmp->Start()->GetNodeIndex(); + nPamEndCI = pTmp->Start()->GetContentIndex(); + bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete); + nRdlIdx++; //we will decrease it in the loop anyway. + } + else if (aOrigData.GetType() == RedlineType::Insert + && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1 + && pTmp->GetType(1) == RedlineType::Insert + && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData)) + { + // The Insert redline we want to accept has a deletion redline too + // we should leave the deletion redline, and only accept the inner insert. + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoAcceptRedline>(*pTmp, 1)); + } + nPamEndtNI = pTmp->Start()->GetNodeIndex(); + nPamEndCI = pTmp->Start()->GetContentIndex(); + bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1); + nRdlIdx++; //we will decrease it in the loop anyway. + } + } while (nRdlIdx > 0); + return bRet; +} + +bool DocumentRedlineManager::AcceptMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete) +{ + assert(nMovedID > 1); // 0, and 1 is reserved + bool bRet = false; + SwRedlineTable::size_type nRdlIdx = maRedlineTable.size(); + + while (nRdlIdx > 0) + { + nRdlIdx--; + SwRangeRedline* pTmp = maRedlineTable[nRdlIdx]; + if (pTmp->GetMoved(0) == nMovedID + || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID)) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoAcceptRedline>(*pTmp)); + } + + if (pTmp->GetMoved(0) == nMovedID) + bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete); + else + bRet |= lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1); + + nRdlIdx++; //we will decrease it in the loop anyway. + } + } + return bRet; +} + +bool DocumentRedlineManager::AcceptRedline(SwRedlineTable::size_type nPos, bool bCallDelete, + bool bRange) { bool bRet = false; @@ -2667,9 +3085,11 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool (RedlineFlags::ShowMask & meRedlineFlags) ) SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); - SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ]; - pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true); - pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true); + SwRangeRedline* pTmp = maRedlineTable[ nPos ]; + bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized(); + + pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true); + pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true); if( pTmp->HasMark() && pTmp->IsVisible() ) { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) @@ -2683,7 +3103,28 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool int nLoopCnt = 2; sal_uInt16 nSeqNo = pTmp->GetSeqNo(); - do { + if (bRange && !nSeqNo && !bAnonym + && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode()) + { + sal_uInt32 nMovedID = pTmp->GetMoved(0); + if (nMovedID > 1) + { + // Accept all redlineData with this unique move id + bRet |= AcceptMovedRedlines(nMovedID, bCallDelete); + } + else + { + SwRedlineTable::size_type nPosStart = nPos; + SwRedlineTable::size_type nPosEnd = nPos; + + maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true); + + // Accept redlines between pPamStart-pPamEnd. + // but only those that can be combined with the selected. + bRet |= AcceptRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete); + } + } + else do { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { @@ -2691,21 +3132,21 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool std::make_unique<SwUndoAcceptRedline>(*pTmp) ); } - bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete ); + bRet |= lcl_AcceptRedline( maRedlineTable, nPos, bCallDelete ); if( nSeqNo ) { if( SwRedlineTable::npos == nPos ) nPos = 0; SwRedlineTable::size_type nFndPos = 2 == nLoopCnt - ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos ) - : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ); + ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos ) + : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ); if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) && SwRedlineTable::npos != ( nFndPos = - mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) ) + maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) ) { nPos = nFndPos; - pTmp = (*mpRedlineTable)[ nPos ]; + pTmp = maRedlineTable[ nPos ]; } else nLoopCnt = 0; @@ -2713,7 +3154,7 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool else nLoopCnt = 0; - } while( nLoopCnt ); + } while (nLoopCnt); if( bRet ) { @@ -2731,7 +3172,7 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool // #TODO - add 'SwExtraRedlineTable' also ? } -bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete ) +bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth ) { // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != @@ -2741,17 +3182,34 @@ bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete // 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 ); + std::shared_ptr<SwUnoCursor> const pPam(m_rDoc.CreateUnoCursor(*rPam.GetPoint(), false)); + if (rPam.HasMark()) + { + pPam->SetMark(); + *pPam->GetMark() = *rPam.GetMark(); + } + lcl_AdjustRedlineRange(*pPam); if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr ); - m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam )); + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoAcceptRedline>(*pPam, nDepth)); } - int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable, - bCallDelete, aPam ); + int nRet = 0; + if (nDepth == 0) + { + nRet = lcl_AcceptRejectRedl(lcl_AcceptRedline, maRedlineTable, bCallDelete, *pPam); + } + else + { + // For now it is called only if it is an Insert redline in a delete redline. + SwRedlineTable::size_type nRdlIdx = 0; + maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx); + if (lcl_AcceptInnerInsertRedline(maRedlineTable, nRdlIdx, 1)) + nRet = 1; + } if( nRet > 0 ) { CompressRedlines(); @@ -2779,19 +3237,18 @@ bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam ) { - const SwPosition* pStt = rPam.Start(), - * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark() - : rPam.GetPoint(); + auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* - const sal_uLong nSttIdx = pStt->nNode.GetIndex(); - const sal_uLong nEndIdx = pEnd->nNode.GetIndex(); + const SwNodeOffset nSttIdx = pStt->GetNodeIndex(); + const SwNodeOffset nEndIdx = pEnd->GetNodeIndex(); - for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n ) + for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n ) { - const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; - sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(), - nMk = pTmp->GetMark()->nNode.GetIndex(); - if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; } + const SwRangeRedline* pTmp = maRedlineTable[ n ]; + SwNodeOffset nPt = pTmp->GetPoint()->GetNodeIndex(), + nMk = pTmp->GetMark()->GetNodeIndex(); + if( nPt < nMk ) + std::swap( nMk, nPt ); if( RedlineType::ParagraphFormat == pTmp->GetType() && ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) ) @@ -2802,7 +3259,114 @@ void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam } } -bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete ) +bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPosOrigin, + SwRedlineTable::size_type& nPosStart, + SwRedlineTable::size_type& nPosEnd, + bool bCallDelete) +{ + bool bRet = false; + + SwRangeRedline* pTmp = maRedlineTable[nPosOrigin]; + SwRedlineTable::size_type nRdlIdx = nPosEnd + 1; + SwRedlineData aOrigData = pTmp->GetRedlineData(0); + + SwNodeOffset nPamStartNI = maRedlineTable[nPosStart]->Start()->GetNodeIndex(); + sal_Int32 nPamStartCI = maRedlineTable[nPosStart]->Start()->GetContentIndex(); + SwNodeOffset nPamEndtNI = maRedlineTable[nPosEnd]->End()->GetNodeIndex(); + sal_Int32 nPamEndCI = maRedlineTable[nPosEnd]->End()->GetContentIndex(); + do + { + nRdlIdx--; + pTmp = maRedlineTable[nRdlIdx]; + if (pTmp->Start()->GetNodeIndex() < nPamStartNI + || (pTmp->Start()->GetNodeIndex() == nPamStartNI + && pTmp->Start()->GetContentIndex() < nPamStartCI)) + break; + + if (pTmp->End()->GetNodeIndex() > nPamEndtNI + || (pTmp->End()->GetNodeIndex() == nPamEndtNI + && pTmp->End()->GetContentIndex() > nPamEndCI)) + { + } + else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData)) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + std::unique_ptr<SwUndoRejectRedline> pUndoRdl + = std::make_unique<SwUndoRejectRedline>(*pTmp); +#if OSL_DEBUG_LEVEL > 0 + pUndoRdl->SetRedlineCountDontCheck(true); +#endif + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl)); + } + nPamEndtNI = pTmp->Start()->GetNodeIndex(); + nPamEndCI = pTmp->Start()->GetContentIndex(); + bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete); + nRdlIdx++; //we will decrease it in the loop anyway. + } + else if (aOrigData.GetType() == RedlineType::Insert + && pTmp->GetType() == RedlineType::Delete && pTmp->GetStackCount() > 1 + && pTmp->GetType(1) == RedlineType::Insert + && pTmp->GetRedlineData(1).CanCombineForAcceptReject(aOrigData)) + { + // The Insert redline we want to reject has a deletion redline too + // without the insert, the delete is meaningless + // so we rather just accept the deletion redline + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + std::unique_ptr<SwUndoRejectRedline> pUndoRdl + = std::make_unique<SwUndoRejectRedline>(*pTmp, 1); +#if OSL_DEBUG_LEVEL > 0 + pUndoRdl->SetRedlineCountDontCheck(true); +#endif + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl)); + } + nPamEndtNI = pTmp->Start()->GetNodeIndex(); + nPamEndCI = pTmp->Start()->GetContentIndex(); + bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete); + nRdlIdx++; //we will decrease it in the loop anyway. + } + + } while (nRdlIdx > 0); + return bRet; +} + +bool DocumentRedlineManager::RejectMovedRedlines(sal_uInt32 nMovedID, bool bCallDelete) +{ + assert(nMovedID > 1); // 0, and 1 is reserved + bool bRet = false; + SwRedlineTable::size_type nRdlIdx = maRedlineTable.size(); + + while (nRdlIdx > 0) + { + nRdlIdx--; + SwRangeRedline* pTmp = maRedlineTable[nRdlIdx]; + if (pTmp->GetMoved(0) == nMovedID + || (pTmp->GetStackCount() > 1 && pTmp->GetMoved(1) == nMovedID)) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + std::unique_ptr<SwUndoRejectRedline> pUndoRdl + = std::make_unique<SwUndoRejectRedline>(*pTmp); +#if OSL_DEBUG_LEVEL > 0 + pUndoRdl->SetRedlineCountDontCheck(true); +#endif + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndoRdl)); + } + + if (pTmp->GetMoved(0) == nMovedID) + bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete); + else + bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete); + + nRdlIdx++; //we will decrease it in the loop anyway. + } + } + return bRet; +} + +bool DocumentRedlineManager::RejectRedline(SwRedlineTable::size_type nPos, + bool bCallDelete, bool bRange) { bool bRet = false; @@ -2811,9 +3375,11 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool (RedlineFlags::ShowMask & meRedlineFlags) ) SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); - SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ]; - pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true); - pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true); + SwRangeRedline* pTmp = maRedlineTable[ nPos ]; + bool bAnonym = pTmp->GetRedlineData(0).IsAnonymized(); + + pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true); + pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true); if( pTmp->HasMark() && pTmp->IsVisible() ) { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) @@ -2827,7 +3393,28 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool int nLoopCnt = 2; sal_uInt16 nSeqNo = pTmp->GetSeqNo(); - do { + if (bRange && !nSeqNo && !bAnonym + && !pTmp->Start()->GetNode().StartOfSectionNode()->IsTableNode()) + { + sal_uInt32 nMovedID = pTmp->GetMoved(0); + if (nMovedID > 1) + { + // Reject all redlineData with this unique move id + bRet |= RejectMovedRedlines(nMovedID, bCallDelete); + } + else + { + SwRedlineTable::size_type nPosStart = nPos; + SwRedlineTable::size_type nPosEnd = nPos; + maRedlineTable.getConnectedArea(nPos, nPosStart, nPosEnd, true); + + // Reject items between pPamStart-pPamEnd + // but only those that can be combined with the selected. + + bRet |= RejectRedlineRange(nPos, nPosStart, nPosEnd, bCallDelete); + } + } + else do { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { @@ -2835,21 +3422,21 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool std::make_unique<SwUndoRejectRedline>( *pTmp ) ); } - bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete ); + bRet |= lcl_RejectRedline( maRedlineTable, nPos, bCallDelete ); if( nSeqNo ) { if( SwRedlineTable::npos == nPos ) nPos = 0; SwRedlineTable::size_type nFndPos = 2 == nLoopCnt - ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos ) - : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ); + ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos ) + : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ); if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) && SwRedlineTable::npos != ( nFndPos = - mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) ) + maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) ) { nPos = nFndPos; - pTmp = (*mpRedlineTable)[ nPos ]; + pTmp = maRedlineTable[ nPos ]; } else nLoopCnt = 0; @@ -2857,7 +3444,7 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool else nLoopCnt = 0; - } while( nLoopCnt ); + } while (nLoopCnt); if( bRet ) { @@ -2875,7 +3462,7 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool // #TODO - add 'SwExtraRedlineTable' also ? } -bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete ) +bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete, sal_Int8 nDepth ) { // Switch to visible in any case if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) != @@ -2891,11 +3478,23 @@ bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr ); - m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) ); + m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam, nDepth) ); + } + + int nRet = 0; + if (nDepth == 0) + { + nRet = lcl_AcceptRejectRedl(lcl_RejectRedline, maRedlineTable, bCallDelete, aPam); + } + else + { + // For now it is called only if it is an Insert redline in a delete redline. + SwRedlineTable::size_type nRdlIdx = 0; + maRedlineTable.FindAtPosition(*rPam.Start(), nRdlIdx); + if (lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete)) + nRet = 1; } - int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable, - bCallDelete, aPam ); if( nRet > 0 ) { CompressRedlines(); @@ -2928,11 +3527,11 @@ void DocumentRedlineManager::AcceptAllRedline(bool bAccept) OUString sUndoStr; IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo(); - if (mpRedlineTable->size() > 1) + if (maRedlineTable.size() > 1) { { SwRewriter aRewriter; - aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size())); + aRewriter.AddRule(UndoArg1, OUString::number(maRedlineTable.size())); sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES)); } @@ -2941,12 +3540,12 @@ void DocumentRedlineManager::AcceptAllRedline(bool bAccept) rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter); } - while (!mpRedlineTable->empty() && bSuccess) + while (!maRedlineTable.empty() && bSuccess) { if (bAccept) - bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true); + bSuccess = AcceptRedline(maRedlineTable.size() - 1, true); else - bSuccess = RejectRedline(mpRedlineTable->size() - 1, true); + bSuccess = RejectRedline(maRedlineTable.size() - 1, true); } if (!sUndoStr.isEmpty()) @@ -2971,12 +3570,12 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons if( pFnd ) { const SwPosition* pEnd = pFnd->End(); - if( !pEnd->nNode.GetNode().IsContentNode() ) + if( !pEnd->GetNode().IsContentNode() ) { - SwNodeIndex aTmp( pEnd->nNode ); + SwNodeIndex aTmp( pEnd->GetNode() ); SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp ); - if( !pCNd || ( aTmp == rSttPos.nNode && - pCNd->Len() == rSttPos.nContent.GetIndex() )) + if( !pCNd || ( aTmp == rSttPos.GetNode() && + pCNd->Len() == rSttPos.GetContentIndex() )) pFnd = nullptr; } if( pFnd ) @@ -2986,9 +3585,9 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons do { bRestart = false; - for( ; !pFnd && n < mpRedlineTable->size(); ++n ) + for( ; !pFnd && n < maRedlineTable.size(); ++n ) { - pFnd = (*mpRedlineTable)[ n ]; + pFnd = maRedlineTable[ n ]; if( pFnd->HasMark() && pFnd->IsVisible() ) { *rPam.GetMark() = *pFnd->Start(); @@ -3004,9 +3603,9 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons // Merge all of the same type and author that are // consecutive into one Selection. const SwPosition* pPrevEnd = pFnd->End(); - while( ++n < mpRedlineTable->size() ) + while( ++n < maRedlineTable.size() ) { - const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; + const SwRangeRedline* pTmp = maRedlineTable[ n ]; if( pTmp->HasMark() && pTmp->IsVisible() ) { const SwPosition *pRStt; @@ -3030,14 +3629,14 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons const SwRangeRedline* pSaveFnd = pFnd; SwContentNode* pCNd; - SwNodeIndex* pIdx = &rPam.GetMark()->nNode; - if( !pIdx->GetNode().IsContentNode() ) + SwPosition* pPos = rPam.GetMark(); + if( !pPos->GetNode().IsContentNode() ) { - pCNd = m_rDoc.GetNodes().GoNextSection( pIdx ); + pCNd = SwNodes::GoNextSection(pPos); if( pCNd ) { - if( *pIdx <= rPam.GetPoint()->nNode ) - rPam.GetMark()->nContent.Assign( pCNd, 0 ); + if( pPos->GetNode() <= rPam.GetPoint()->GetNode() ) + pPos->Assign( *pCNd, 0 ); else pFnd = nullptr; } @@ -3045,14 +3644,14 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons if( pFnd ) { - pIdx = &rPam.GetPoint()->nNode; - if( !pIdx->GetNode().IsContentNode() ) + pPos = rPam.GetPoint(); + if( !pPos->GetNode().IsContentNode() ) { - pCNd = SwNodes::GoPrevSection( pIdx ); + pCNd = SwNodes::GoPrevSection( pPos ); if( pCNd ) { - if( *pIdx >= rPam.GetMark()->nNode ) - rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + if( pPos->GetNode() >= rPam.GetMark()->GetNode() ) + pPos->Assign( *pCNd, pCNd->Len() ); else pFnd = nullptr; } @@ -3061,7 +3660,7 @@ const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) cons if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() ) { - if( n < mpRedlineTable->size() ) + if( n < maRedlineTable.size() ) { bRestart = true; *rPam.GetPoint() = *pSaveFnd->End(); @@ -3097,12 +3696,12 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons if( pFnd ) { const SwPosition* pStt = pFnd->Start(); - if( !pStt->nNode.GetNode().IsContentNode() ) + if( !pStt->GetNode().IsContentNode() ) { - SwNodeIndex aTmp( pStt->nNode ); - SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp ); - if( !pCNd || ( aTmp == rSttPos.nNode && - !rSttPos.nContent.GetIndex() )) + SwNodeIndex aTmp( pStt->GetNode() ); + SwContentNode* pCNd = SwNodes::GoNextSection(&aTmp); + if( !pCNd || ( aTmp == rSttPos.GetNode() && + !rSttPos.GetContentIndex() )) pFnd = nullptr; } if( pFnd ) @@ -3114,7 +3713,7 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons while( !pFnd && 0 < n ) { - pFnd = (*mpRedlineTable)[ --n ]; + pFnd = maRedlineTable[ --n ]; if( pFnd->HasMark() && pFnd->IsVisible() ) { *rPam.GetMark() = *pFnd->End(); @@ -3131,7 +3730,7 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons const SwPosition* pNextStt = pFnd->Start(); while( 0 < n ) { - const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ]; + const SwRangeRedline* pTmp = maRedlineTable[ --n ]; if( pTmp->HasMark() && pTmp->IsVisible() ) { const SwPosition *pREnd; @@ -3157,14 +3756,14 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons const SwRangeRedline* pSaveFnd = pFnd; SwContentNode* pCNd; - SwNodeIndex* pIdx = &rPam.GetMark()->nNode; - if( !pIdx->GetNode().IsContentNode() ) + SwPosition* pPos = rPam.GetMark(); + if( !pPos->GetNode().IsContentNode() ) { - pCNd = SwNodes::GoPrevSection( pIdx ); + pCNd = SwNodes::GoPrevSection( pPos ); if( pCNd ) { - if( *pIdx >= rPam.GetPoint()->nNode ) - rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() ); + if( pPos->GetNode() >= rPam.GetPoint()->GetNode() ) + pPos->Assign( *pCNd, pCNd->Len() ); else pFnd = nullptr; } @@ -3172,14 +3771,14 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons if( pFnd ) { - pIdx = &rPam.GetPoint()->nNode; - if( !pIdx->GetNode().IsContentNode() ) + pPos = rPam.GetPoint(); + if( !pPos->GetNode().IsContentNode() ) { - pCNd = m_rDoc.GetNodes().GoNextSection( pIdx ); + pCNd = SwNodes::GoNextSection(pPos); if( pCNd ) { - if( *pIdx <= rPam.GetMark()->nNode ) - rPam.GetPoint()->nContent.Assign( pCNd, 0 ); + if( pPos->GetNode() <= rPam.GetMark()->GetNode() ) + pPos->Assign( *pCNd, 0 ); else pFnd = nullptr; } @@ -3212,16 +3811,14 @@ const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) cons 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(); + auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition* SwRedlineTable::size_type n = 0; if( GetRedlineTable().FindAtPosition( *pStt, n ) ) { - for( ; n < mpRedlineTable->size(); ++n ) + for( ; n < maRedlineTable.size(); ++n ) { bRet = true; - SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; + SwRangeRedline* pTmp = maRedlineTable[ n ]; if( pStt != pEnd && *pTmp->Start() > *pEnd ) break; @@ -3283,11 +3880,11 @@ void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, m_rDoc.SetAutoFormatRedline( nullptr != pText ); if( pText ) { - mpAutoFormatRedlnComment.reset( new OUString( *pText ) ); + moAutoFormatRedlnComment = *pText; } else { - mpAutoFormatRedlnComment.reset(); + moAutoFormatRedlnComment.reset(); } mnAutoFormatRedlnCommentNo = nSeqNo; |