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 b6cba0119f8028b426f5eee55be9761971dbfdee Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue May 27 08:37:14 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue May 27 14:51:55 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/+/185891 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index e03c3dd2d256..6144cfebf93b 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -4136,7 +4136,7 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. </rng:element> </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 6488b9d12500..2e0497aa76da 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 8501838444c2..557380025ba4 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -3594,10 +3594,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 @@ -3605,7 +3617,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 111b752e3781..a96712fe305e 100644 --- a/sw/source/core/inc/rolbck.hxx +++ b/sw/source/core/inc/rolbck.hxx @@ -129,6 +129,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 ad90afca7dae..7c2634932da3 100644 --- a/sw/source/core/undo/rolbck.cxx +++ b/sw/source/core/undo/rolbck.cxx @@ -282,6 +282,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 8e6caf96d426..6d71d440ef1d 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);