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 dcccb89dd37cc8fee40d70eec434688a24b7d629
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Jan 6 08:32:50 2026 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Sat Jan 10 10:34:04 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/+/196918
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins

diff --git a/sw/qa/core/doc/DocumentRedlineManager.cxx 
b/sw/qa/core/doc/DocumentRedlineManager.cxx
index d2a51a18d897..11194129f49e 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 9f80926582d9..e3f0d864fbf1 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -2226,6 +2226,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