sw/qa/core/doc/DocumentRedlineManager.cxx     |   45 ++++++++++++++++++++++++++
 sw/qa/core/doc/data/fmt.docx                  |binary
 sw/source/core/doc/DocumentRedlineManager.cxx |    6 +++
 3 files changed, 51 insertions(+)

New commits:
commit d0573fff47767656494d26d523cca65429319c26
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 6 08:32:50 2026 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Fri Jan 9 09:53:02 2026 +0100

    Related: tdf#168751 sw interdependent redlines, format on del: fix 
del-on-fmt
    
    Open the bugdoc which has a <format>BBBCCCDDD</format> redline. Select
    CCC, press delete: now CCC is only covered by a delete redline instead
    of a format-on-delete redline.
    
    The trouble is in sw::DocumentRedlineManager::PreAppendDeleteRedline(),
    where rCtx.pRedl (format redline) initially covers BBBCCCDDD, but then
    it gets reduced to cover only BBB. We also create a pNew redline to
    cover only DDD. This gives Writer a way to set rCtx.pNewRedl's range to
    CCC without an overlap. The downside is that now reject-all doesn't
    modify the format of CCC anymore, so some unexpected boldness remains in
    the document.
    
    Fix this similar to how
    sw::DocumentRedlineManager::PreAppendForeignRedline()'s rCtx.eCmpPos ==
    SwComparePosition::Inside case works: except there PushData() is used to
    push the redline data "over" (before creating pNew), and here we use
    PushData() to push the redline data "under".
    
    This way UI creates a model where format is always on top of delete (and
    not the other way around), which is useful, since DOCX only has markup
    for that order and ODT explicitly says this order should be used in
    files.
    
    Change-Id: Ida640ad7bb99ed89faef306dac2b840ae3be53f3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196592
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/sw/qa/core/doc/DocumentRedlineManager.cxx 
b/sw/qa/core/doc/DocumentRedlineManager.cxx
index 6bc0e2fef3bd..fdbc9ca90808 100644
--- a/sw/qa/core/doc/DocumentRedlineManager.cxx
+++ b/sw/qa/core/doc/DocumentRedlineManager.cxx
@@ -392,6 +392,51 @@ CPPUNIT_TEST_FIXTURE(Test, testDelThenFormatOwn)
         CPPUNIT_ASSERT(!rRedlineData.Next());
     }
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testFormatThenDel)
+{
+    // Given an "AAA <format>BBB CCC DDD</format> EEE" document:
+    createSwDoc("fmt.docx");
+    SwDocShell* pDocShell = getSwDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    // Skip "AAA BBB ".
+    pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 8, 
/*bBasicCall=*/false);
+    // Select "CCC".
+    pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 3, 
/*bBasicCall=*/false);
+
+    // When deleting CCC:
+    pWrtShell->DelLeft();
+
+    // Then make sure the resulting new "delete" redline still tracks 
formatting:
+    SwDoc* pDoc = pDocShell->GetDoc();
+    IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+    SwRedlineTable& rRedlines = rIDRA.GetRedlineTable();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), rRedlines.size());
+    {
+        const SwRedlineData& rRedlineData = rRedlines[0]->GetRedlineData(0);
+        CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+        CPPUNIT_ASSERT(!rRedlineData.Next());
+    }
+    {
+        const SwRedlineData& rRedlineData = rRedlines[1]->GetRedlineData(0);
+        // Without the accompanying fix in place, this test would have failed 
with:
+        // - Expected: 2 (Format)
+        // - Actual  : 1 (Delete)
+        // i.e. the middle redline was just "delete", not "format-on-delete", 
so formatting remained
+        // in the document after reject-all.
+        CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+        CPPUNIT_ASSERT(rRedlineData.Next());
+        const SwRedlineData& rRedlineData2 = rRedlines[1]->GetRedlineData(1);
+        CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlineData2.GetType());
+        CPPUNIT_ASSERT(!rRedlineData2.Next());
+    }
+    {
+        const SwRedlineData& rRedlineData = rRedlines[2]->GetRedlineData(0);
+        CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+        CPPUNIT_ASSERT(!rRedlineData.Next());
+    }
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/doc/data/fmt.docx b/sw/qa/core/doc/data/fmt.docx
new file mode 100644
index 000000000000..29fa091007e2
Binary files /dev/null and b/sw/qa/core/doc/data/fmt.docx differ
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx 
b/sw/source/core/doc/DocumentRedlineManager.cxx
index 5b64c1ed8fc6..e357407143cc 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -2227,6 +2227,12 @@ void 
DocumentRedlineManager::PreAppendDeleteRedline(AppendRedlineContext& rCtx)
             {
                 if( *rCtx.pEnd != *rCtx.pRStt )
                 {
+                    // At this point, rCtx.pRedl is the old format redline and 
rCtx.pNewRedl is the
+                    // new delete redline. Make sure that when this pNewRedl 
gets appended, it still
+                    // has the formatting redline data from rCtx.pRedl / pNew, 
and format is on top
+                    // of delete.
+                    rCtx.pNewRedl->PushData(*rCtx.pRedl);
+
                     SwRangeRedline* pNew = new SwRangeRedline( *rCtx.pRedl );
                     pNew->SetStart( *rCtx.pEnd );
                     rCtx.pRedl->SetEnd( *rCtx.pStart, rCtx.pREnd );

Reply via email to