schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng | 2 sw/qa/core/doc/doc.cxx | 22 ++++++++ sw/source/core/doc/DocumentRedlineManager.cxx | 31 ++++++++++-- sw/source/core/inc/rolbck.hxx | 1 sw/source/core/undo/rolbck.cxx | 19 +++++++ sw/source/core/undo/undel.cxx | 11 ++++ 6 files changed, 82 insertions(+), 4 deletions(-)
New commits: commit ad2782a447ee6162d98aa3a92484f7971f22863a Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue May 27 08:37:14 2025 +0200 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Tue May 27 10:46:03 2025 +0200 tdf#166319 sw interdependent redlines: handle reject of ins-then-fmt's fmt The bugdoc has <ins>AA<format>BB</format>CC</ins> in it, rejecting the BB part resulted in <ins>AA</ins>BB<ins>CC</ins>, while the expected result would be to reject the underlying insert of BB. The problem is that the reject code is not aware that the format has an underlying insert, so the format gets rejected, and as a side effect the insert redline is gone, too. Fix this by extending sw::DocumentRedlineManager::RejectRedlineRange() to recognize this insert-then-format case: act like rejecting the insert, not like rejecting the format. The surrounding plain inserts are not yet rejected, though. Also update extend sw doc model xml dump code that was useful to see while working on this. Change-Id: I6718b9c306239837392ae7cd3f0d28aef6b4f71e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185877 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index 10023519509e..c4030642619e 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -4035,7 +4035,7 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. </rng:optional> </rng:define> - <!-- TODO(vmiklos) no proposal for insert/delete redline under another redline --> + <!-- https://lists.freedesktop.org/archives/libreoffice/2025-May/093327.html Hierarchical tracked changes --> <rng:define name="text-changed-region" combine="choice"> <rng:element name="text:changed-region"> <rng:ref name="text-changed-region-attr"/> diff --git a/sw/qa/core/doc/doc.cxx b/sw/qa/core/doc/doc.cxx index 39fdf63fb03c..55607e565f9b 100644 --- a/sw/qa/core/doc/doc.cxx +++ b/sw/qa/core/doc/doc.cxx @@ -821,6 +821,28 @@ CPPUNIT_TEST_FIXTURE(SwCoreDocTest, testInsThenFormat) // - Actual : 3 // i.e. the insert redlines before/after BBB were not accepted. CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rRedlines.size()); + + // And when rejecting the format-on-insert: + pWrtShell->Undo(); + CPPUNIT_ASSERT_EQUAL(u"AAABBBCCC"_ustr, pTextNode->GetText()); + // Undo() creates a new cursor. + pCursor = pWrtShell->GetCursor(); + pCursor->DeleteMark(); + pWrtShell->SttEndDoc(/*bStt=*/true); + // Move inside "BBB". + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, /*bBasicCall=*/false); + nRedline = 0; + rRedlines.FindAtPosition(*pCursor->Start(), nRedline); + // A redline is found. + CPPUNIT_ASSERT_LESS(rRedlines.size(), nRedline); + pWrtShell->RejectRedline(nRedline); + + // Then make sure the format-on-insert is rejected, i.e. BBB is not in the text anymore: + // Without the accompanying fix in place, this test would have failed with: + // - Expected: AAACCC + // - Actual : AAABBBCCC + // i.e. only the format part of BBB was rejected, it wasn't removed from the document. + CPPUNIT_ASSERT_EQUAL(u"AAACCC"_ustr, pTextNode->GetText()); } CPPUNIT_TEST_FIXTURE(SwCoreDocTest, testDelThenFormat) diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index 82048aa836e4..b9a9f7677115 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -3595,10 +3595,22 @@ bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPosOr } else if (pTmp->GetRedlineData(0).CanCombineForAcceptReject(aOrigData)) { + bool bFormatOnInsert = pTmp->GetType() == RedlineType::Format + && pTmp->GetStackCount() > 1 + && pTmp->GetType(1) == RedlineType::Insert; if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { - std::unique_ptr<SwUndoRejectRedline> pUndoRdl - = std::make_unique<SwUndoRejectRedline>(*pTmp); + std::unique_ptr<SwUndoRedline> pUndoRdl; + if (bFormatOnInsert) + { + // Format on insert: this is rejected by accepting the format + deleting the + // range. + pUndoRdl = std::make_unique<SwUndoAcceptRedline>(*pTmp); + } + else + { + pUndoRdl = std::make_unique<SwUndoRejectRedline>(*pTmp); + } #if OSL_DEBUG_LEVEL > 0 pUndoRdl->SetRedlineCountDontCheck(true); #endif @@ -3606,7 +3618,20 @@ bool DocumentRedlineManager::RejectRedlineRange(SwRedlineTable::size_type nPosOr } nPamEndtNI = pTmp->Start()->GetNodeIndex(); nPamEndCI = pTmp->Start()->GetContentIndex(); - bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete); + + if (bFormatOnInsert) + { + // Accept the format itself and then reject the insert by deleting the range. + SwPaM aPam(*pTmp->Start(), *pTmp->End()); + bRet |= lcl_AcceptRedline(maRedlineTable, nRdlIdx, bCallDelete); + // Handles undo/redo itself. + m_rDoc.getIDocumentContentOperations().DeleteRange(aPam); + } + else + { + bRet |= lcl_RejectRedline(maRedlineTable, nRdlIdx, bCallDelete); + } + nRdlIdx++; //we will decrease it in the loop anyway. } else if (CanCombineTypesForAcceptReject(aOrigData, *pTmp) diff --git a/sw/source/core/inc/rolbck.hxx b/sw/source/core/inc/rolbck.hxx index a8877bdc400c..5b8926c1af5c 100644 --- a/sw/source/core/inc/rolbck.hxx +++ b/sw/source/core/inc/rolbck.hxx @@ -130,6 +130,7 @@ public: virtual ~SwHistorySetText() override; virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + void dumpAsXml(xmlTextWriterPtr pWriter) const override; }; class SwHistorySetTextField final : public SwHistoryHint diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx index fdf3dc8f10d6..ba5ba3f9db6d 100644 --- a/sw/source/core/undo/rolbck.cxx +++ b/sw/source/core/undo/rolbck.cxx @@ -280,6 +280,25 @@ void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool ) } } +void SwHistorySetText::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistorySetText")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("node-index"), + BAD_CAST(OString::number(sal_Int32(m_nNodeIndex)).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("start"), + BAD_CAST(OString::number(sal_Int32(m_nStart)).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("end"), + BAD_CAST(OString::number(sal_Int32(m_nEnd)).getStr())); + SwHistoryHint::dumpAsXml(pWriter); + + if (m_pAttr) + { + m_pAttr->dumpAsXml(pWriter); + } + + (void)xmlTextWriterEndElement(pWriter); +} + SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, SwNodeOffset nNodePos ) : SwHistoryHint( HSTRY_SETTXTFLDHNT ) , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) ) diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx index f72f24c8f40a..93501c9f7b2f 100644 --- a/sw/source/core/undo/undel.cxx +++ b/sw/source/core/undo/undel.cxx @@ -1376,6 +1376,17 @@ void SwUndoDelete::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDelete")); (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + if (m_aSttStr) + { + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("stt-str"), + BAD_CAST(m_aSttStr->toUtf8().getStr())); + } + if (m_aEndStr) + { + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("end-str"), + BAD_CAST(m_aEndStr->toUtf8().getStr())); + } + SwUndo::dumpAsXml(pWriter); SwUndoSaveContent::dumpAsXml(pWriter); (void)xmlTextWriterEndElement(pWriter);