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              |   95 +++++++++++++++++++-------
 4 files changed, 161 insertions(+), 29 deletions(-)

New commits:
commit 8c028b7e41e3d350d0e67005b16faf0159cc5c12
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: Fri Dec 17 14:02:29 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.
    
    Change-Id: I426c445da760f36f718d737f34ccdb904e87aac3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126919
    Tested-by: Jenkins
    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 bddc76755920..bafc66489929 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -201,6 +201,7 @@ public:
     void testTdf104425();
     void testTdf104814();
     void testTableRedlineRedoCrash();
+    void testTableRemoveHasTextChangesOnly();
     void testTdf66405();
     void testTdf35021_tabOverMarginDemo();
     void testTdf106701_tabOverMarginAutotab();
@@ -324,6 +325,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);
@@ -1678,6 +1680,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..ae5dc4c437e5 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;
+                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..9033fdccbac5 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,87 @@ 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 *pNew = 
const_cast<SwTableLine*>(this)->ClaimFrameFormat();
+            pNew->SetFormatAttr( aUnsetTracking );
         }
     }
 

Reply via email to