sw/inc/IDocumentRedlineAccess.hxx | 16 sw/inc/redline.hxx | 2 sw/qa/extras/uiwriter/data/tdf157662_redlineNestedInsertDelete.odt |binary sw/qa/extras/uiwriter/uiwriter5.cxx | 62 ++ sw/source/core/doc/DocumentRedlineManager.cxx | 274 +++++++++- sw/source/core/doc/docredln.cxx | 35 + sw/source/core/edit/edredln.cxx | 4 sw/source/core/inc/DocumentRedlineManager.hxx | 19 sw/source/core/inc/UndoCore.hxx | 4 sw/source/core/inc/UndoRedline.hxx | 10 sw/source/core/undo/undobj.cxx | 4 sw/source/core/undo/unredln.cxx | 25 12 files changed, 419 insertions(+), 36 deletions(-)
New commits: commit 52fa7aed48632166e064e6a227e034f0981c4205 Author: Attila Szűcs <attila.sz...@collabora.com> AuthorDate: Mon Aug 28 07:40:20 2023 +0200 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Sat Oct 14 11:55:27 2023 +0200 tdf#157662 SW: redline: accept/reject done for all parts Tracked changes divided into smaller parts when they are overlapping. But if the Author, and Change time, and some more is equal, then they can be combined into 1 change later.. Modified AcceptRedline / RejectRedline, to seek for all these parts that are neightbour to each other, and can be combined, and reject/accept them all at once. Even those that are deepen in the tree. i.e.: insert that have a delete redline too. when rejecting an insert redline, that have a delete redline too, the delete redline is accepted instead. (have the same result.) when accepting an insert redline, that have a delete redline too, The delete redline remains, while the insert is deleted. (=accepted) made some limitations to lessen the probability of regression: No anonym, No table, No seqNo, and dont use it in RejectAll/AcceptAll Added unittest to check that accept/reject handle more redlines at once, but not too many.. Change-Id: Ibd0a39f7847b22b279a797babb30ba162e70a513 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157950 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/sw/inc/IDocumentRedlineAccess.hxx b/sw/inc/IDocumentRedlineAccess.hxx index 73f87fa8c799..c2b71aa1005a 100644 --- a/sw/inc/IDocumentRedlineAccess.hxx +++ b/sw/inc/IDocumentRedlineAccess.hxx @@ -183,15 +183,23 @@ public: virtual void SetRedlineMove(/*[in]*/bool bFlag) = 0; - virtual bool AcceptRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) = 0; + virtual bool AcceptRedline(/*[in]*/ SwRedlineTable::size_type nPos, /*[in]*/ bool bCallDelete, + /*[in]*/ bool bRange = false) + = 0; - virtual bool AcceptRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) = 0; + virtual bool AcceptRedline(/*[in]*/ const SwPaM& rPam, /*[in]*/ bool bCallDelete, + /*[in]*/ sal_Int8 nDepth = 0) + = 0; virtual void AcceptRedlineParagraphFormatting(/*[in]*/const SwPaM& rPam ) = 0; - virtual bool RejectRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) = 0; + virtual bool RejectRedline(/*[in]*/ SwRedlineTable::size_type nPos, + /*[in]*/ bool bCallDelete, /*[in]*/ bool bRange = false) + = 0; - virtual bool RejectRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) = 0; + virtual bool RejectRedline(/*[in]*/ const SwPaM& rPam, /*[in]*/ bool bCallDelete, + /*[in]*/ sal_Int8 nDepth = 0) + = 0; virtual const SwRangeRedline* SelNextRedline(/*[in]*/SwPaM& rPam) const = 0; diff --git a/sw/inc/redline.hxx b/sw/inc/redline.hxx index 7ef6b9cad20f..d8eba6480618 100644 --- a/sw/inc/redline.hxx +++ b/sw/inc/redline.hxx @@ -144,6 +144,7 @@ public: void SetMoved() { m_bMoved = true; } bool IsMoved() const { return m_bMoved; } bool CanCombine( const SwRedlineData& rCmp ) const; + bool CanCombineForAcceptReject( const SwRedlineData& rCmp ) const; // ExtraData gets copied, the pointer is therefore not taken over by // the RedlineObject @@ -261,6 +262,7 @@ public: void PushData( const SwRangeRedline& rRedl, bool bOwnAsNext = true ); bool PopData(); + bool PopAllDataAfter(int depth); /** Returns textual description of a redline data element of diff --git a/sw/qa/extras/uiwriter/data/tdf157662_redlineNestedInsertDelete.odt b/sw/qa/extras/uiwriter/data/tdf157662_redlineNestedInsertDelete.odt new file mode 100644 index 000000000000..d97521559a84 Binary files /dev/null and b/sw/qa/extras/uiwriter/data/tdf157662_redlineNestedInsertDelete.odt differ diff --git a/sw/qa/extras/uiwriter/uiwriter5.cxx b/sw/qa/extras/uiwriter/uiwriter5.cxx index 285a3adb7c61..d97e0a8911f0 100644 --- a/sw/qa/extras/uiwriter/uiwriter5.cxx +++ b/sw/qa/extras/uiwriter/uiwriter5.cxx @@ -2231,6 +2231,68 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testRedlineDOCXTableMoveToFrame) CPPUNIT_ASSERT(!xTableNames->hasByName("Table2")); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf157662_AcceptInsertRedlineCutWithDeletion) +{ + createSwDoc("tdf157662_redlineNestedInsertDelete.odt"); + SwDoc* pDoc = getSwDoc(); + + // turn on red-lining and show changes + pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete + | RedlineFlags::ShowInsert); + CPPUNIT_ASSERT_MESSAGE("redlining should be on", + pDoc->getIDocumentRedlineAccess().IsRedlineOn()); + CPPUNIT_ASSERT_MESSAGE( + "redlines should be visible", + IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); + + SwEditShell* const pEditShell(pDoc->GetEditShell()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(9), pEditShell->GetRedlineCount()); + + // Accept the insert that splitted into 3 parts .. accept all 3 of them + pEditShell->AcceptRedline(6); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(7), pEditShell->GetRedlineCount()); + // The middle had a delete too, rejecting the delete will remove that redline too. + pEditShell->RejectRedline(6); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(6), pEditShell->GetRedlineCount()); + + // Accept insert that splitted into 4 parts, but separeted to 2-2 parts, with another insert. + // It will accept only 2 parts, that is not separated. It leave the deletion. + pEditShell->AcceptRedline(0); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(5), pEditShell->GetRedlineCount()); + // Accepting the delete will remove that redline. + // (only that one, as its other half is separated from it with an insert) + pEditShell->AcceptRedline(0); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(4), pEditShell->GetRedlineCount()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf157662_RejectInsertRedlineCutWithDeletion) +{ + createSwDoc("tdf157662_redlineNestedInsertDelete.odt"); + SwDoc* pDoc = getSwDoc(); + + // turn on red-lining and show changes + pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete + | RedlineFlags::ShowInsert); + CPPUNIT_ASSERT_MESSAGE("redlining should be on", + pDoc->getIDocumentRedlineAccess().IsRedlineOn()); + CPPUNIT_ASSERT_MESSAGE( + "redlines should be visible", + IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); + + SwEditShell* const pEditShell(pDoc->GetEditShell()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(9), pEditShell->GetRedlineCount()); + + // Reject the insert that splitted into 3 parts .. reject all 3 of them + // it even remove the deletion, that was on the 2. insert... + pEditShell->RejectRedline(6); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(6), pEditShell->GetRedlineCount()); + + // Reject insert that splitted into 4 parts, but separeted to 2-2 parts, with another insert. + // It will reject only 2 parts, that is not separated. It remove the deletion. + pEditShell->RejectRedline(0); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(4), pEditShell->GetRedlineCount()); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf143215) { // load a table with tracked insertion of an empty row diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index 75c5ba1ccc10..f5ab5a9fdba3 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -992,6 +992,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, @@ -2863,7 +2875,118 @@ const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos // #TODO - add 'SwExtraRedlineTable' also ? } -bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete ) +namespace +{ +bool lcl_CanCombineWithRange(SwRangeRedline* pTmp, SwRangeRedline* pOther, SwPosition* pPamAct, + SwPosition* pPamOther, SwPosition* pPamAct2, SwPosition* pPamOther2) +{ + if (!pOther->IsVisible()) + return false; + + if (*pPamAct != *pPamOther) + return false; + + if (!pTmp->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(0))) + { + if (pOther->GetStackCount() <= 1 + || !pTmp->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(1))) + return false; + } + if (pPamAct2->GetNode().StartOfSectionNode() != pPamOther2->GetNode().StartOfSectionNode()) + return false; + + return true; +} +} + +void DocumentRedlineManager::FindRangeToAcceptReject(SwRedlineTable::size_type nPos, + SwPosition** pPamStart, SwPosition** pPamEnd, + SwRedlineTable::size_type& nPosEnd) const +{ + SwRangeRedline* pTmp = maRedlineTable[nPos]; + nPosEnd = nPos; + SwRedlineTable::size_type nPosStart = nPos; + SwRangeRedline* pOther; + + while (nPosStart > 0 && (pOther = maRedlineTable[nPosStart - 1]) + && lcl_CanCombineWithRange(pTmp, pOther, *pPamStart, pOther->End(), pOther->Start(), + *pPamEnd)) + { + nPosStart--; + *pPamStart = pOther->Start(); + } + while (nPosEnd + 1 < maRedlineTable.size() && (pOther = maRedlineTable[nPosEnd + 1]) + && lcl_CanCombineWithRange(pTmp, pOther, *pPamEnd, pOther->Start(), pOther->End(), + *pPamStart)) + { + nPosEnd++; + *pPamEnd = pOther->End(); + } +} + +bool DocumentRedlineManager::AcceptRedlineRange(SwRedlineTable::size_type nPos, bool bCallDelete, + SwPosition* pPamStart, SwPosition* pPamEnd, + SwRedlineTable::size_type& nPosEnd) +{ + bool bRet = false; + + SwRangeRedline* pTmp = maRedlineTable[nPos]; + SwRedlineTable::size_type nRdlIdx = nPosEnd + 1; + SwRedlineData aOrigData = pTmp->GetRedlineData(0); + + SwNodeOffset nPamStartNI = pPamStart->GetNodeIndex(); + sal_Int32 nPamStartCI = pPamStart->GetContentIndex(); + SwNodeOffset nPamEndtNI = pPamEnd->GetNodeIndex(); + sal_Int32 nPamEndCI = pPamEnd->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::AcceptRedline(SwRedlineTable::size_type nPos, bool bCallDelete, + bool bRange) { bool bRet = false; @@ -2873,6 +2996,8 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); 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() ) @@ -2888,7 +3013,18 @@ 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()) + { + auto [pPamStart, pPamEnd] = pTmp->StartEnd(); + SwRedlineTable::size_type nPosEnd; + FindRangeToAcceptReject(nPos, &pPamStart, &pPamEnd, nPosEnd); + + // Accept redlines between pPamStart-pPamEnd. + // but only those that can be combined with the selected. + bRet |= AcceptRedlineRange(nPos, bCallDelete, pPamStart, pPamEnd, nPosEnd); + } + else do { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { @@ -2918,7 +3054,7 @@ bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool else nLoopCnt = 0; - } while( nLoopCnt ); + } while (nLoopCnt); if( bRet ) { @@ -2936,7 +3072,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) != @@ -2957,11 +3093,23 @@ bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr ); - m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAcceptRedline>(*pPam)); + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoAcceptRedline>(*pPam, nDepth)); } - int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, maRedlineTable, - bCallDelete, *pPam ); + 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(); @@ -3011,7 +3159,79 @@ void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam } } -bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete ) +bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPos, bool bCallDelete, + SwPosition* pPamStart, SwPosition* pPamEnd, + SwRedlineTable::size_type& nPosEnd) +{ + bool bRet = false; + + SwRangeRedline* pTmp = maRedlineTable[nPos]; + SwRedlineTable::size_type nRdlIdx = nPosEnd + 1; + SwRedlineData aOrigData = pTmp->GetRedlineData(0); + + SwNodeOffset nPamStartNI = pPamStart->GetNodeIndex(); + sal_Int32 nPamStartCI = pPamStart->GetContentIndex(); + SwNodeOffset nPamEndtNI = pPamEnd->GetNodeIndex(); + sal_Int32 nPamEndCI = pPamEnd->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::RejectRedline(SwRedlineTable::size_type nPos, + bool bCallDelete, bool bRange) { bool bRet = false; @@ -3021,6 +3241,8 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags ); 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() ) @@ -3036,7 +3258,19 @@ 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()) + { + auto [pPamStart, pPamEnd] = pTmp->StartEnd(); + SwRedlineTable::size_type nPosEnd; + FindRangeToAcceptReject(nPos, &pPamStart, &pPamEnd, nPosEnd); + + // Reject items between pPamStart-pPamEnd + // but only those that can be combined with the selected. + + bRet |= RejectRedlineRange(nPos, bCallDelete, pPamStart, pPamEnd, nPosEnd); + } + else do { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { @@ -3066,7 +3300,7 @@ bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool else nLoopCnt = 0; - } while( nLoopCnt ); + } while (nLoopCnt); if( bRet ) { @@ -3084,7 +3318,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) != @@ -3100,11 +3334,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, maRedlineTable, - bCallDelete, aPam ); if( nRet > 0 ) { CompressRedlines(); diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx index 205e532712eb..50c259faee6b 100644 --- a/sw/source/core/doc/docredln.cxx +++ b/sw/source/core/doc/docredln.cxx @@ -1109,6 +1109,20 @@ bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const *m_pExtraData == *rCmp.m_pExtraData )); } +// Check if we could/should accept/reject the 2 redlineData at the same time. +// No need to check its childs equality +bool SwRedlineData::CanCombineForAcceptReject(const SwRedlineData& rCmp) const +{ + return m_nAuthor == rCmp.m_nAuthor && + m_eType == rCmp.m_eType && + m_sComment == rCmp.m_sComment && + deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) && + m_bMoved == rCmp.m_bMoved && + (( !m_pExtraData && !rCmp.m_pExtraData ) || + ( m_pExtraData && rCmp.m_pExtraData && + *m_pExtraData == *rCmp.m_pExtraData )); +} + /// ExtraData is copied. The Pointer's ownership is thus NOT transferred /// to the Redline Object! void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData ) @@ -1981,6 +1995,27 @@ bool SwRangeRedline::PopData() return true; } +bool SwRangeRedline::PopAllDataAfter(int depth) +{ + assert(depth > 0); + SwRedlineData* pCur = m_pRedlineData; + while (depth > 1) + { + pCur = pCur->m_pNext; + if (!pCur) + return false; + depth--; + } + + while (pCur->m_pNext) + { + SwRedlineData* pToDelete = pCur->m_pNext; + pCur->m_pNext = pToDelete->m_pNext; + delete pToDelete; + } + return true; +} + sal_uInt16 SwRangeRedline::GetStackCount() const { sal_uInt16 nRet = 1; diff --git a/sw/source/core/edit/edredln.cxx b/sw/source/core/edit/edredln.cxx index 835a1c37389c..1778745f57e4 100644 --- a/sw/source/core/edit/edredln.cxx +++ b/sw/source/core/edit/edredln.cxx @@ -68,7 +68,7 @@ bool SwEditShell::AcceptRedline( SwRedlineTable::size_type nPos ) { CurrShell aCurr( this ); StartAllAction(); - bool bRet = GetDoc()->getIDocumentRedlineAccess().AcceptRedline( nPos, true ); + bool bRet = GetDoc()->getIDocumentRedlineAccess().AcceptRedline( nPos, true, true ); if( !nPos && !::IsExtraData( GetDoc() ) ) lcl_InvalidateAll( this ); EndAllAction(); @@ -79,7 +79,7 @@ bool SwEditShell::RejectRedline( SwRedlineTable::size_type nPos ) { CurrShell aCurr( this ); StartAllAction(); - bool bRet = GetDoc()->getIDocumentRedlineAccess().RejectRedline( nPos, true ); + bool bRet = GetDoc()->getIDocumentRedlineAccess().RejectRedline( nPos, true, true ); if( !nPos && !::IsExtraData( GetDoc() ) ) lcl_InvalidateAll( this ); EndAllAction(); diff --git a/sw/source/core/inc/DocumentRedlineManager.hxx b/sw/source/core/inc/DocumentRedlineManager.hxx index a3644f4aea9e..2f9c133605fa 100644 --- a/sw/source/core/inc/DocumentRedlineManager.hxx +++ b/sw/source/core/inc/DocumentRedlineManager.hxx @@ -89,15 +89,19 @@ public: virtual void SetRedlineMove(/*[in]*/bool bFlag) override; - virtual bool AcceptRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) override; + virtual bool AcceptRedline(/*[in]*/ SwRedlineTable::size_type nPos, /*[in]*/ bool bCallDelete, + /*[in]*/ bool bRange = false) override; - virtual bool AcceptRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) override; + virtual bool AcceptRedline(/*[in]*/ const SwPaM& rPam, /*[in]*/ bool bCallDelete, + /*[in]*/ sal_Int8 nDepth = 0) override; virtual void AcceptRedlineParagraphFormatting(/*[in]*/const SwPaM& rPam) override; - virtual bool RejectRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) override; + virtual bool RejectRedline(/*[in]*/ SwRedlineTable::size_type nPos, /*[in]*/ bool bCallDelete, + /*[in]*/ bool bRange = false) override; - virtual bool RejectRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) override; + virtual bool RejectRedline(/*[in]*/ const SwPaM& rPam, /*[in]*/ bool bCallDelete, + /*[in]*/ sal_Int8 nDepth = 0) override; virtual void AcceptAllRedline(/*[in]*/bool bAcceptReject) override; @@ -137,6 +141,13 @@ public: private: + void FindRangeToAcceptReject(SwRedlineTable::size_type nPos, SwPosition** pPamStart, + SwPosition** pPamEnd, SwRedlineTable::size_type& nPosEnd) const; + bool RejectRedlineRange(SwRedlineTable::size_type nPos, bool bCallDelete, SwPosition* pPamStart, + SwPosition* pPamEnd, SwRedlineTable::size_type& nPosEnd); + bool AcceptRedlineRange(SwRedlineTable::size_type nPos, bool bCallDelete, SwPosition* pPamStart, + SwPosition* pPamEnd, SwRedlineTable::size_type& nPosEnd); + DocumentRedlineManager(DocumentRedlineManager const&) = delete; DocumentRedlineManager& operator=(DocumentRedlineManager const&) = delete; diff --git a/sw/source/core/inc/UndoCore.hxx b/sw/source/core/inc/UndoCore.hxx index 5c4b709ffb70..94891df09e49 100644 --- a/sw/source/core/inc/UndoCore.hxx +++ b/sw/source/core/inc/UndoCore.hxx @@ -61,6 +61,7 @@ public: #if OSL_DEBUG_LEVEL > 0 sal_uInt16 m_nRedlineCount; + bool m_bRedlineCountDontCheck; bool m_bRedlineMoved; #endif }; @@ -78,6 +79,9 @@ public: void push_back(std::unique_ptr<SwRedlineSaveData> pNew) { m_Data.push_back(std::move(pNew)); } const SwRedlineSaveData& operator[](size_t const nIdx) const { return *m_Data[ nIdx ]; } SwRedlineSaveData& operator[](size_t const nIdx) { return *m_Data[ nIdx ]; } +#if OSL_DEBUG_LEVEL > 0 + void SetRedlineCountDontCheck(bool bCheck) { m_Data[0]->m_bRedlineCountDontCheck=bCheck; } +#endif }; namespace sw { diff --git a/sw/source/core/inc/UndoRedline.hxx b/sw/source/core/inc/UndoRedline.hxx index f1cf45800fdb..127aeae0aa29 100644 --- a/sw/source/core/inc/UndoRedline.hxx +++ b/sw/source/core/inc/UndoRedline.hxx @@ -36,12 +36,13 @@ protected: std::unique_ptr<SwRedlineSaveDatas> mpRedlSaveData; SwUndoId mnUserId; bool mbHiddenRedlines; + sal_Int8 mnDepth; // index of the SwRedlineData in SwRangeRedline virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam); virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam); public: - SwUndoRedline( SwUndoId nUserId, const SwPaM& rRange ); + SwUndoRedline( SwUndoId nUserId, const SwPaM& rRange, sal_Int8 nDepth = 0 ); virtual ~SwUndoRedline() override; @@ -49,6 +50,9 @@ public: virtual void RedoImpl( ::sw::UndoRedoContext & ) override; sal_uInt16 GetRedlSaveCount() const; +#if OSL_DEBUG_LEVEL > 0 + void SetRedlineCountDontCheck(bool bCheck); +#endif }; class SwUndoRedlineDelete final : public SwUndoRedline @@ -106,7 +110,7 @@ private: virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; public: - SwUndoAcceptRedline( const SwPaM& rRange ); + SwUndoAcceptRedline( const SwPaM& rRange, sal_Int8 nDepth = 0 ); virtual void RepeatImpl( ::sw::RepeatContext & ) override; }; @@ -117,7 +121,7 @@ private: virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; public: - SwUndoRejectRedline( const SwPaM& rRange ); + SwUndoRejectRedline( const SwPaM& rRange, sal_Int8 nDepth = 0 ); virtual void RepeatImpl( ::sw::RepeatContext & ) override; }; diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx index dce7f3eb2724..bd258dcd5767 100644 --- a/sw/source/core/undo/undobj.cxx +++ b/sw/source/core/undo/undobj.cxx @@ -1410,6 +1410,7 @@ SwRedlineSaveData::SwRedlineSaveData( #if OSL_DEBUG_LEVEL > 0 m_nRedlineCount = rSttPos.GetNode().GetDoc().getIDocumentRedlineAccess().GetRedlineTable().size(); + m_bRedlineCountDontCheck = false; m_bRedlineMoved = rRedl.IsMoved(); #endif } @@ -1525,7 +1526,8 @@ void SwUndo::SetSaveData( SwDoc& rDoc, SwRedlineSaveDatas& rSData ) #if OSL_DEBUG_LEVEL > 0 // check redline count against count saved in RedlineSaveData object // except in the case of moved redlines - assert(rSData.empty() || rSData[0].m_bRedlineMoved || + assert( + rSData.empty() || rSData[0].m_bRedlineMoved || rSData[0].m_bRedlineCountDontCheck || (rSData[0].m_nRedlineCount == rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())); // "redline count not restored properly" #endif diff --git a/sw/source/core/undo/unredln.cxx b/sw/source/core/undo/unredln.cxx index a8a572a40d4d..f77ceb3d26d8 100644 --- a/sw/source/core/undo/unredln.cxx +++ b/sw/source/core/undo/unredln.cxx @@ -37,10 +37,11 @@ #include <sortopt.hxx> #include <docedt.hxx> -SwUndoRedline::SwUndoRedline( SwUndoId nUsrId, const SwPaM& rRange ) +SwUndoRedline::SwUndoRedline( SwUndoId nUsrId, const SwPaM& rRange, sal_Int8 nDepth ) : SwUndo( SwUndoId::REDLINE, &rRange.GetDoc() ), SwUndRng( rRange ), mnUserId( nUsrId ), - mbHiddenRedlines( false ) + mbHiddenRedlines( false ), + mnDepth( nDepth ) { // consider Redline SwDoc& rDoc = rRange.GetDoc(); @@ -88,6 +89,14 @@ sal_uInt16 SwUndoRedline::GetRedlSaveCount() const return mpRedlSaveData ? mpRedlSaveData->size() : 0; } +#if OSL_DEBUG_LEVEL > 0 +void SwUndoRedline::SetRedlineCountDontCheck(bool bCheck) +{ + if (mpRedlSaveData) + mpRedlSaveData->SetRedlineCountDontCheck(bCheck); +} +#endif + void SwUndoRedline::UndoImpl(::sw::UndoRedoContext & rContext) { SwDoc& rDoc = rContext.GetDoc(); @@ -417,14 +426,14 @@ void SwUndoRedlineSort::SetSaveRange( const SwPaM& rRange ) m_nSaveEndContent = rPos.GetContentIndex(); } -SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange ) - : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange ) +SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange, sal_Int8 nDepth /* = 0 */ ) + : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange, nDepth ) { } void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) { - rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false); + rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false, mnDepth); } void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext) @@ -432,14 +441,14 @@ void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext) rContext.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext.GetRepeatPaM(), true); } -SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange ) - : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange ) +SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange, sal_Int8 nDepth /* = 0 */ ) + : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange, nDepth ) { } void SwUndoRejectRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) { - rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false); + rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false, mnDepth); } void SwUndoRejectRedline::RepeatImpl(::sw::RepeatContext & rContext)