sw/inc/editsh.hxx                 |    3 +
 sw/qa/core/edit/edit.cxx          |   50 +++++++++++++++++++++++++++
 sw/source/core/edit/edredln.cxx   |   68 +++++++++++++++++++++++++++++++++++---
 sw/source/uibase/uiview/view2.cxx |    5 ++
 4 files changed, 122 insertions(+), 4 deletions(-)

New commits:
commit 1df71464588d4cfcba708cf00ef7b6ac94574c8f
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Mar 20 09:08:43 2025 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Mar 20 15:17:09 2025 +0100

    cool#11357 sw redline reinstate: handle inserts in selection
    
    In case the cursor is inside an insert redline, then
    .uno:ReinstateTrackedChange works for that change, but if multiple
    inserts are selected, then this simply results in a reject with no
    history.
    
    This happens because SwView::Execute() has a bool for the accept, so if
    we have accept/reject/reinstate slots, then by default reinstate is
    handled as a reject (this is not terrible, reinstate is meant to be
    "reject with history").
    
    Fix the problem by adding a new
    SwEditShell::ReinstateRedlinesInSelection() which can decide what
    redlines are in the selection and then by invoking reinstate on these
    redlines.
    
    Similar to accept/reject, it works to only partially select a redline,
    which results in a split: the non-selected part is left unchanged and we
    invoke the action on the selected part.
    
    Change-Id: I21a5896f9bd983d8ca8e2480826421c6bffbe556
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183170
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/inc/editsh.hxx b/sw/inc/editsh.hxx
index c9444a3e0f78..2e2c6b1f3c65 100644
--- a/sw/inc/editsh.hxx
+++ b/sw/inc/editsh.hxx
@@ -156,6 +156,8 @@ class SAL_DLLPUBLIC_RTTI SwEditShell : public SwCursorShell
 
     void SetSectionAttr_( SwSectionFormat& rSectFormat, const SfxItemSet& rSet 
);
 
+    void ReinstatePaM(const SwRangeRedline& rRedline, SwPaM& rPaM);
+
     using SwViewShell::UpdateFields;
     using sw::BroadcastingModify::GetInfo;
 
@@ -960,6 +962,7 @@ public:
     bool AcceptRedlinesInSelection();
     bool RejectRedlinesInSelection();
     void ReinstateRedline(SwRedlineTable::size_type nPos);
+    void ReinstateRedlinesInSelection();
 
     /** Search Redline for this Data and @return position in array.
      If not found, return SwRedlineTable::npos. */
diff --git a/sw/qa/core/edit/edit.cxx b/sw/qa/core/edit/edit.cxx
index 10705063a001..89d596d88989 100644
--- a/sw/qa/core/edit/edit.cxx
+++ b/sw/qa/core/edit/edit.cxx
@@ -117,6 +117,56 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineReinstateSingleInsert)
     CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rInnerRedlineData.GetType());
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testRedlineReinstateInsertsInSelection)
+{
+    // Given a document with two insertions:
+    createSwDoc();
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->Insert("aaa");
+    SwModule* pModule = SwModule::get();
+    pModule->SetRedlineAuthor("Alice");
+    RedlineFlags nMode = pWrtShell->GetRedlineFlags();
+    pWrtShell->SetRedlineFlags(nMode | RedlineFlags::On);
+    pWrtShell->Insert("bbb");
+    pWrtShell->SetRedlineFlags(nMode);
+    pWrtShell->Insert("ccc");
+    pWrtShell->SetRedlineFlags(nMode | RedlineFlags::On);
+    pWrtShell->Insert("ddd");
+    pWrtShell->SetRedlineFlags(nMode);
+    pWrtShell->Insert("eee");
+
+    // When a 2nd user reinstates those changes with a selection:
+    pModule->SetRedlineAuthor("Bob");
+    // Create a selection that excludes the initial "a" and the last "e":
+    pWrtShell->SttPara(/*bSelect=*/false);
+    pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    pWrtShell->EndPara(/*bSelect=*/true);
+    pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 1, 
/*bBasicCall=*/false);
+    dispatchCommand(mxComponent, ".uno:ReinstateTrackedChange", {});
+
+    // Then make sure this results in deletes on top of inserts:
+    SwDoc* pDoc = pWrtShell->GetDoc();
+    IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+    SwRedlineTable& rRedlines = rIDRA.GetRedlineTable();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2
+    // - Actual  : 0
+    // i.e. a reject was performed instead of a reinstate.
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rRedlines.size());
+    const SwRangeRedline* pRedline1 = rRedlines[0];
+    const SwRedlineData& rRedlineData1 = pRedline1->GetRedlineData(0);
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlineData1.GetType());
+    CPPUNIT_ASSERT(rRedlineData1.Next());
+    const SwRedlineData& rInnerRedlineData1 = *rRedlineData1.Next();
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rInnerRedlineData1.GetType());
+    const SwRangeRedline* pRedline2 = rRedlines[1];
+    const SwRedlineData& rRedlineData2 = pRedline2->GetRedlineData(0);
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlineData2.GetType());
+    CPPUNIT_ASSERT(rRedlineData2.Next());
+    const SwRedlineData& rInnerRedlineData2 = *rRedlineData2.Next();
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rInnerRedlineData2.GetType());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/core/edit/edredln.cxx b/sw/source/core/edit/edredln.cxx
index 8d992e56ddef..71bf47e60fc3 100644
--- a/sw/source/core/edit/edredln.cxx
+++ b/sw/source/core/edit/edredln.cxx
@@ -80,6 +80,14 @@ bool SwEditShell::AcceptRedline( SwRedlineTable::size_type 
nPos )
     return bRet;
 }
 
+void SwEditShell::ReinstatePaM(const SwRangeRedline& rRedline, SwPaM& rPaM)
+{
+    if (rRedline.GetType() == RedlineType::Insert)
+    {
+        DeleteSel(rPaM, /*isArtificialSelection=*/true);
+    }
+}
+
 void SwEditShell::ReinstateRedline(SwRedlineTable::size_type nPos)
 {
     CurrShell aCurr(this);
@@ -95,10 +103,7 @@ void 
SwEditShell::ReinstateRedline(SwRedlineTable::size_type nPos)
     SwPaM aPaM(*rRedline.GetPoint());
     aPaM.SetMark();
     *aPaM.GetMark() = *rRedline.GetMark();
-    if (rRedline.GetType() == RedlineType::Insert)
-    {
-        DeleteSel(aPaM, /*isArtificialSelection=*/true);
-    }
+    ReinstatePaM(rRedline, aPaM);
 
     EndAllAction();
 }
@@ -174,6 +179,61 @@ bool SwEditShell::RejectRedlinesInSelection()
     return bRet;
 }
 
+void SwEditShell::ReinstateRedlinesInSelection()
+{
+    CurrShell aCurr( this );
+    StartAllAction();
+    if (!IsRedlineOn())
+    {
+        RedlineFlags nMode = GetRedlineFlags();
+        SetRedlineFlags(nMode | RedlineFlags::On, /*bRecordAllViews=*/false);
+    }
+
+    SwPosition aCursorStart(*GetCursor()->Start());
+    SwPosition aCursorEnd(*GetCursor()->End());
+    SwRedlineTable& rTable = 
GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+    for (size_t nIndex = 0; nIndex < rTable.size(); ++nIndex)
+    {
+        const SwRangeRedline& rRedline = *rTable[nIndex];
+        if (!rRedline.HasMark() || !rRedline.IsVisible())
+        {
+            continue;
+        }
+
+        if (*rRedline.End() < aCursorStart)
+        {
+            // Ends before the selection, skip to the next redline.
+            continue;
+        }
+
+        if (*rRedline.Start() > aCursorEnd)
+        {
+            // Starts after the selection, can stop.
+            break;
+        }
+
+        // Check if the redline is only partially selected.
+        const SwPosition* pStart = rRedline.Start();
+        if (*pStart < aCursorStart)
+        {
+            pStart = &aCursorStart;
+        }
+        const SwPosition* pEnd = rRedline.End();
+        if (*pEnd > aCursorEnd)
+        {
+            pEnd = &aCursorEnd;
+        }
+
+        // Process the (partially) selected redline.
+        SwPaM aPaM(*pEnd);
+        aPaM.SetMark();
+        *aPaM.GetMark() = *pStart;
+        ReinstatePaM(rRedline, aPaM);
+    }
+
+    EndAllAction();
+}
+
 // Set the comment at the Redline
 bool SwEditShell::SetRedlineComment( const OUString& rS )
 {
diff --git a/sw/source/uibase/uiview/view2.cxx 
b/sw/source/uibase/uiview/view2.cxx
index 7dae30f73886..fe46ebea00be 100644
--- a/sw/source/uibase/uiview/view2.cxx
+++ b/sw/source/uibase/uiview/view2.cxx
@@ -910,6 +910,7 @@ void SwView::Execute(SfxRequest &rReq)
             if( pCursor->HasMark() && nRedline == SwRedlineTable::npos)
             {
                 bool bAccept = FN_REDLINE_ACCEPT_DIRECT == nSlot || 
FN_REDLINE_ACCEPT_TONEXT == nSlot;
+                bool bReinstate = nSlot == FN_REDLINE_REINSTATE_DIRECT;
                 SwUndoId eUndoId = bAccept ? SwUndoId::ACCEPT_REDLINE : 
SwUndoId::REJECT_REDLINE;
                 SwWrtShell& rSh = GetWrtShell();
                 SwRewriter aRewriter;
@@ -921,6 +922,10 @@ void SwView::Execute(SfxRequest &rReq)
                 }
                 if ( bAccept )
                     m_pWrtShell->AcceptRedlinesInSelection();
+                else if (bReinstate)
+                {
+                    m_pWrtShell->ReinstateRedlinesInSelection();
+                }
                 else
                     m_pWrtShell->RejectRedlinesInSelection();
                 if ( bTableSelection )

Reply via email to