sw/qa/extras/uiwriter/uiwriter4.cxx           |   75 ++++++++++++++++++++
 sw/source/core/doc/DocumentRedlineManager.cxx |   16 +++-
 sw/source/core/docnode/ndtbl1.cxx             |    4 -
 sw/source/core/table/swtable.cxx              |   97 +++++++++++++++++++-------
 4 files changed, 163 insertions(+), 29 deletions(-)

New commits:
commit 02b8bcf06c431ea4694d9fe13d6aa59100c79c05
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Wed Dec 15 12:31:27 2021 +0100
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Tue Dec 21 08:51:53 2021 +0100

    tdf#146244 sw: update HasTextChangesOnly in modified rows
    
    It's not forbidden to write in rows deleted or
    inserted with change tracking, also Accept/Reject
    only part of the text changes here. Improve to
    handle these in SwTableLine::UpdateTextChangesOnly()
    by keeping property HasTextChangesOnly = false
    (tracked row change) only if
    
    1) there is an insert redline, which is the oldest redline in
       the row (tracked row insertion) or
    
    2) there is a delete redline, which is the newest redline in
       the row, and no text outside of redlines, and no insert
       redline in the row, i.e. whole text content is deleted
       (tracked row deletion).
    
    Also update HasTextChangesOnly table row property at accepting
    SwRangeRedlines in changed table rows.
    
    And follow-up bug fix commits of this:
    commit bc206f7fd3b4a9e843c81f39dcd2faeeff9c785a
    "cid#1495785 silence Unchecked return value" and
    commit 213173edc5e47bb6da11f9a301f4dca71854a48c
    "sw: fix crash with commit 8c028b7e41e3d350d0e67005b16faf0159cc5c12".
    
    (cherry-picked from commit 8c028b7e41e3d350d0e67005b16faf0159cc5c12,
    commit bc206f7fd3b4a9e843c81f39dcd2faeeff9c785a and
    commit 213173edc5e47bb6da11f9a301f4dca71854a48c)
    
    Change-Id: I2bc556c732e1d0fdca24678c79351aafbfc156b2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127195
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx 
b/sw/qa/extras/uiwriter/uiwriter4.cxx
index ddeb659f361d..44d48697799f 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -199,6 +199,7 @@ public:
     void testTdf104425();
     void testTdf104814();
     void testTableRedlineRedoCrash();
+    void testTableRemoveHasTextChangesOnly();
     void testTdf66405();
     void testTdf35021_tabOverMarginDemo();
     void testTdf106701_tabOverMarginAutotab();
@@ -321,6 +322,7 @@ public:
     CPPUNIT_TEST(testTdf104425);
     CPPUNIT_TEST(testTdf104814);
     CPPUNIT_TEST(testTableRedlineRedoCrash);
+    CPPUNIT_TEST(testTableRemoveHasTextChangesOnly);
     CPPUNIT_TEST(testTdf66405);
     CPPUNIT_TEST(testTdf35021_tabOverMarginDemo);
     CPPUNIT_TEST(testTdf106701_tabOverMarginAutotab);
@@ -1515,6 +1517,79 @@ void SwUiWriterTest4::testTableRedlineRedoCrash()
     rIDRA.AcceptAllRedline(true);
 }
 
+void SwUiWriterTest4::testTableRemoveHasTextChangesOnly()
+{
+    //createSwDoc(DATA_DIRECTORY, "tdf91292_paraBackground.docx");
+    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "TC-table-del-add.docx");
+    CPPUNIT_ASSERT(pDoc);
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    CPPUNIT_ASSERT(pWrtShell);
+
+    // disable Record Changes
+    dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+    CPPUNIT_ASSERT_MESSAGE("redlining should be off",
+                           !pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+    // 4 rows in Show Changes mode
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 4);
+
+    // Accepting tracked deletions results 3 rows
+    IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess());
+    rIDRA.AcceptAllRedline(/*bAccept=*/true);
+    Scheduler::ProcessEventsToIdle();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 3);
+
+    // Undo: 4 rows again
+    pDoc->GetIDocumentUndoRedo().Undo();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 4);
+
+    // Accepting again: 3 rows (Undo of HasTextChangesOnly is correct)
+    rIDRA.AcceptAllRedline(/*bAccept=*/true);
+    Scheduler::ProcessEventsToIdle();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 3);
+
+    // Undo: 4 rows again
+    pDoc->GetIDocumentUndoRedo().Undo();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 4);
+
+    // Move the cursor after the redline, and insert some text without change 
tracking
+    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    pWrtShell->Insert("X");
+
+    // Accepting again: 4 rows (extra text keeps the deleted row)
+    rIDRA.AcceptAllRedline(/*bAccept=*/true);
+    Scheduler::ProcessEventsToIdle();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 4);
+
+    // delete the extra text with change tracking:
+    // this resulted tracked row deletion again, because of missing
+    // removing of HasTextChangeOnly SwTabLine property at accepting deletions 
previously
+
+    // disable Record Changes
+    dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+    CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+                           pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+
+    dispatchCommand(mxComponent, ".uno:SwBackSpace", {});
+    rIDRA.AcceptAllRedline(/*bAccept=*/true);
+    Scheduler::ProcessEventsToIdle();
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    // This was 3
+    assertXPath(pXmlDoc, "/root/page[1]/body/tab[1]/row", 4);
+}
+
 void SwUiWriterTest4::testTdf66405()
 {
     // Imported formula should have zero margins
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx 
b/sw/source/core/doc/DocumentRedlineManager.cxx
index 5211dff34147..32ab0a476f6a 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -445,11 +445,19 @@ namespace
         const SvxPrintItem *pHasTextChangesOnlyProp =
                 
pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
         // empty table row with property "HasTextChangesOnly" = false
-        if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() &&
-             pLine->IsEmpty() )
+        if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
         {
-            SwCursor aCursor( *pPos, nullptr );
-            pPos->GetDoc().DeleteRow( aCursor );
+            if ( pLine->IsEmpty() )
+            {
+                SwCursor aCursor( *pPos, nullptr );
+                pPos->GetDoc().DeleteRow( aCursor );
+            }
+            else
+            {
+                // update property "HasTextChangesOnly"
+                SwRedlineTable::size_type nPos = 0;
+                (void)pLine->UpdateTextChangesOnly(nPos);
+            }
         }
     }
 
diff --git a/sw/source/core/docnode/ndtbl1.cxx 
b/sw/source/core/docnode/ndtbl1.cxx
index d7f3c5419590..f300cec48c5d 100644
--- a/sw/source/core/docnode/ndtbl1.cxx
+++ b/sw/source/core/docnode/ndtbl1.cxx
@@ -573,7 +573,9 @@ void SwDoc::SetRowNotTracked( const SwCursor& rCursor, 
const SvxPrintItem &rNew,
         // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
         // (unless the table is part of a bigger deletion, where the
         // new redline can cause a problem)
-        if (!bAll && pLn->IsEmpty())
+        if ( !bAll &&
+            // HasTextChangesOnly == false, i.e. a tracked row insertion or 
deletion
+            !rNew.GetValue() && pLn->IsEmpty() )
         {
             SwNodeIndex aInsPos( *(pLn->GetTabBoxes()[0]->GetSttNd()), 1 );
             RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
diff --git a/sw/source/core/table/swtable.cxx b/sw/source/core/table/swtable.cxx
index 63d60625fea9..183775c42ecc 100644
--- a/sw/source/core/table/swtable.cxx
+++ b/sw/source/core/table/swtable.cxx
@@ -38,6 +38,7 @@
 #include <docary.hxx>
 #include <frame.hxx>
 #include <swtable.hxx>
+#include <swcrsr.hxx>
 #include <ndtxt.hxx>
 #include <tabcol.hxx>
 #include <tabfrm.hxx>
@@ -1602,8 +1603,6 @@ SwRedlineTable::size_type 
SwTableLine::UpdateTextChangesOnly(SwRedlineTable::siz
 {
     SwRedlineTable::size_type nRet = SwRedlineTable::npos;
     const SwRedlineTable& aRedlineTable = 
GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
-    if ( aRedlineTable.empty() )
-        return nRet;
 
     // check table row property "HasTextChangesOnly", if it's defined and its
     // value is false, and all text content is in delete redlines, the row is 
deleted
@@ -1614,7 +1613,9 @@ SwRedlineTable::size_type 
SwTableLine::UpdateTextChangesOnly(SwRedlineTable::siz
         const SwTableBoxes & rBoxes = GetTabBoxes();
         size_t nBoxes = rBoxes.size();
         bool bInsertion = false;
-
+        bool bPlainTextInLine = false;
+        SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
+        SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos;
         for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < 
aRedlineTable.size(); ++nBoxIndex)
         {
             auto pBox = rBoxes[nBoxIndex];
@@ -1624,10 +1625,11 @@ SwRedlineTable::size_type 
SwTableLine::UpdateTextChangesOnly(SwRedlineTable::siz
                continue;
             }
 
-            bool bHasRedline = false;
+            bool bHasRedlineInBox = false;
             SwPosition aCellStart( SwNodeIndex( *pBox->GetSttNd(), 0 ) );
             SwPosition aCellEnd( SwNodeIndex( 
*pBox->GetSttNd()->EndOfSectionNode(), -1 ) );
             SwNodeIndex pEndNodeIndex(aCellEnd.nNode.GetNode());
+            SwRangeRedline* pPreviousDeleteRedline = nullptr;
             for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos )
             {
                 const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ];
@@ -1642,42 +1644,89 @@ SwRedlineTable::size_type 
SwTableLine::UpdateTextChangesOnly(SwRedlineTable::siz
                 // redline in the cell
                 if ( aCellStart <= *pRedline->Start() )
                 {
-                    bHasRedline = true;
+                    if ( !bHasRedlineInBox )
+                    {
+                        bHasRedlineInBox = true;
+                        // plain text before the first redline in the text
+                        if ( pRedline->Start()->nContent.GetIndex() > 0 )
+                            bPlainTextInLine = true;
+                    }
+
                     RedlineType nType = pRedline->GetType();
 
                     // first insert redline
-                    if ( !bInsertion && RedlineType::Insert == nType )
+                    if ( !bInsertion )
                     {
-                        bInsertion = true;
-                        nRet = rRedlinePos;
-                        continue;
-                        // TODO check older delete redlines to remove row 
change, if needed
+                        if ( RedlineType::Insert == nType )
+                        {
+                            bInsertion = true;
+                        }
+                        else
+                        {
+                            // plain text between the delete redlines
+                            if ( pPreviousDeleteRedline &&
+                                *pPreviousDeleteRedline->End() < 
*pRedline->Start() )
+                            {
+                                bPlainTextInLine = true;
+                            }
+                            pPreviousDeleteRedline = 
const_cast<SwRangeRedline*>(pRedline);
+                        }
                     }
 
-                    // search newest deletion or oldest insertion
-                    if ( ( !bInsertion && RedlineType::Delete == nType &&
-                           ( nRet == SwRedlineTable::npos ||
-                             
aRedlineTable[nRet]->GetRedlineData().GetTimeStamp() <
-                                 pRedline->GetRedlineData().GetTimeStamp() ) ) 
||
-                         ( bInsertion && RedlineType::Insert == nType &&
-                           ( nRet == SwRedlineTable::npos ||
-                             
aRedlineTable[nRet]->GetRedlineData().GetTimeStamp() >
-                                 pRedline->GetRedlineData().GetTimeStamp() ) ) 
)
+                    // search newest and oldest redlines
+                    if ( nNewestRedline == SwRedlineTable::npos ||
+                             
aRedlineTable[nNewestRedline]->GetRedlineData().GetTimeStamp() <
+                                 pRedline->GetRedlineData().GetTimeStamp() )
                     {
-                        nRet = rRedlinePos;
+                        nNewestRedline = rRedlinePos;
+                    }
+                    if ( nOldestRedline == SwRedlineTable::npos ||
+                             
aRedlineTable[nOldestRedline]->GetRedlineData().GetTimeStamp() >
+                                 pRedline->GetRedlineData().GetTimeStamp() )
+                    {
+                        nOldestRedline = rRedlinePos;
                     }
                 }
             }
 
-            if ( !bHasRedline && !bInsertion )
+            // there is text content outside of redlines: not a deletion
+            if ( !bInsertion && ( !bHasRedlineInBox || ( 
pPreviousDeleteRedline &&
+                 ( pPreviousDeleteRedline->End()->nNode < aCellEnd.nNode ||
+                   pPreviousDeleteRedline->End()->nContent.GetIndex() <
+                           aCellEnd.nNode.GetNode().GetContentNode()->Len() ) 
) ) )
             {
+                bPlainTextInLine = true;
                 // not deleted cell content: the row is not empty
                 // maybe insertion of a row, try to search it
                 bInsertion = true;
-                // drop collected deletion
-                nRet = SwRedlineTable::npos;
             }
-            // TODO: check also text outside of the redlines
+        }
+
+        // choose return redline, if it exists or remove changed row attribute
+        if ( bInsertion && SwRedlineTable::npos != nOldestRedline &&
+                RedlineType::Insert == aRedlineTable[ nOldestRedline 
]->GetType() )
+        {
+            // there is an insert redline, which is the oldest redline in the 
row
+            nRet = nOldestRedline;
+        }
+        else if ( !bInsertion && !bPlainTextInLine && SwRedlineTable::npos != 
nNewestRedline &&
+                RedlineType::Delete == aRedlineTable[ nNewestRedline 
]->GetType() )
+        {
+            // there is a delete redline, which is the newest redline in the 
row,
+            // and no text outside of redlines, and no insert redline in the 
row,
+            // i.e. whole text content is deleted
+            nRet = nNewestRedline;
+        }
+        else
+        {
+            // no longer tracked row insertion or deletion
+            nRet = SwRedlineTable::npos;
+            // set TextChangesOnly = true to remove the tracked deletion
+            SvxPrintItem aUnsetTracking(RES_PRINT, true);
+            SwFrameFormat *pFormat = 
const_cast<SwTableLine*>(this)->ClaimFrameFormat();
+            pFormat->LockModify();
+            pFormat->SetFormatAttr( aUnsetTracking );
+            pFormat->UnlockModify();
         }
     }
 

Reply via email to