tdf#143359 sw: track deletion of empty table rows

Empty table rows were deleted immediately during
change tracking, or in the case of a deleted table
also with non-empty rows, accepting table deletion
kept empty rows.

Note: as a workaround for tracking of the empty rows,
i.e. rows without text content, add a redline with
invisible text ZWJ in the first cell of the empty row.

See also commit a483a44ca00f43a64ae51d62b8fbb4129a413f6d
"tdf#143215 DOCX import: fix tracked empty row insertion/deletion",
commit b50d386dfa70f7c1d4eb1a49091ec9dd782b767b
"tdf#142701 track changes: fix layout regression of image deletion"
and commit 05366b8e6683363688de8708a3d88cf144c7a2bf
"tdf#60382 sw offapi: add change tracking of table/row deletion".

Note: switch off unnecessary redlining of tdf#132744 unit test
to keep the sake of that test (i.e. cut of the selected table
with empty rows).

Change-Id: Ief59008e8714fb88afdfce867598e3dda21bfba5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121784
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/inc/hintids.hxx b/sw/inc/hintids.hxx
index 739a6de..1642c09 100644
--- a/sw/inc/hintids.hxx
+++ b/sw/inc/hintids.hxx
@@ -182,6 +182,10 @@
#define CH_TXT_ATR_SUBST_FIELDSTART ("[")
#define CH_TXT_ATR_SUBST_FIELDEND ("]")

// a non-visible dummy character to track deleted tables,
// table rows, and images anchored to characters
#define CH_TXT_TRACKED_DUMMY_CHAR u'\x200D'

/*
 * Enums for the hints
 */
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 590aa62..19ebe1e 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -1615,6 +1615,13 @@
    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf132744.odt");
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();

    // disable change tracking to cut the table
    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowDelete
                                                      | RedlineFlags::ShowInsert);

    CPPUNIT_ASSERT_MESSAGE("redlining should be off",
                           !pDoc->getIDocumentRedlineAccess().IsRedlineOn());

    CPPUNIT_ASSERT_EQUAL(1, getShapes());

    dispatchCommand(mxComponent, ".uno:SelectAll", {});
diff --git a/sw/source/core/docnode/ndtbl1.cxx b/sw/source/core/docnode/ndtbl1.cxx
index 3df6b32..c36e7e3 100644
--- a/sw/source/core/docnode/ndtbl1.cxx
+++ b/sw/source/core/docnode/ndtbl1.cxx
@@ -35,6 +35,8 @@
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentState.hxx>
#include <IDocumentContentOperations.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pam.hxx>
#include <swcrsr.hxx>
@@ -557,7 +559,24 @@
    aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );

    for( auto pLn : aRowArr )
    {
        ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
        // as a workaround for the rows without text content,
        // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
        if (pLn->IsEmpty())
        {
            SwNodeIndex aInsPos( *(pLn->GetTabBoxes()[0]->GetSttNd()), 1 );
            RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
            getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
            SwPaM aPaM(aInsPos);
            getIDocumentContentOperations().InsertString( aPaM,
                    OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
            aPaM.SetMark();
            aPaM.GetMark()->nContent.Assign(aPaM.GetContentNode(), 0);
            getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
            getIDocumentContentOperations().DeleteAndJoin( aPaM );
        }
    }

    getIDocumentState().SetModified();
}
diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx
index 04c0468..11103b9 100644
--- a/sw/source/core/frmedt/fetab.cxx
+++ b/sw/source/core/frmedt/fetab.cxx
@@ -332,6 +332,8 @@
    // and set IsNoTracked table line property to false
    if ( GetDoc()->GetDocShell()->IsChangeRecording() )
    {
        SwEditShell* pEditShell = GetDoc()->GetEditShell();
        SwRedlineTable::size_type nOldRedlineCount = pEditShell->GetRedlineCount();
        StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
        StartAllAction();

@@ -341,8 +343,6 @@
        if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
            pWrtShell->SelectTableRow();

        SwEditShell* pEditShell = GetDoc()->GetEditShell();
        SwRedlineTable::size_type nPrev = pEditShell->GetRedlineCount();
        pEditShell->Delete();

        EndAllActionAndCall();
@@ -351,7 +351,7 @@
        // track row deletion only if there were tracked text changes
        // FIXME redline count can be the same in special cases, e.g. adding a
        // new tracked deletion with removing an own tracked insertion...
        if ( nPrev != pEditShell->GetRedlineCount() )
        if ( nOldRedlineCount != pEditShell->GetRedlineCount() )
            return true;
    }

diff --git a/sw/source/core/unocore/unocrsrhelper.cxx b/sw/source/core/unocore/unocrsrhelper.cxx
index be694be..e8e6c40 100644
--- a/sw/source/core/unocore/unocrsrhelper.cxx
+++ b/sw/source/core/unocore/unocrsrhelper.cxx
@@ -35,6 +35,7 @@
#include <svx/unoshape.hxx>

#include <cmdid.h>
#include <hintids.hxx>
#include <unotextrange.hxx>
#include <unodraw.hxx>
#include <unofootnote.hxx>
@@ -1411,11 +1412,12 @@
        SvxPrintItem aSetTracking(RES_PRINT, false);
        SwNodeIndex aInsPos( *(rTableLine.GetTabBoxes()[0]->GetSttNd()), 1 );
        // as a workaround for the rows without text content,
        // add a redline with invisible text ZWJ
        // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
        if ( rTableLine.IsEmpty() )
        {
            SwPaM aPaM(aInsPos);
            pDoc->getIDocumentContentOperations().InsertString( aPaM, u"‍" );
            pDoc->getIDocumentContentOperations().InsertString( aPaM,
                    OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
            aPaM.SetMark();
            aPaM.GetMark()->nContent.Assign(aPaM.GetContentNode(), 0);
            makeRedline(aPaM, RedlineType::TableRowInsert == eType
diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx
index 1b35fbb..dae15a9 100644
--- a/sw/source/uibase/wrtsh/delete.cxx
+++ b/sw/source/uibase/wrtsh/delete.cxx
@@ -18,6 +18,7 @@
 */

#include <cmdid.h>
#include <hintids.hxx>
#include <wrtsh.hxx>
#include <swcrsr.hxx>
#include <editeng/lrspitem.hxx>
@@ -463,8 +464,9 @@
                    ( eAnchorId == RndStdIds::FLY_AT_CHAR || eAnchorId == RndStdIds::FLY_AS_CHAR ) )
            {
                sal_Int32 nRedlineLength = 1;
                // create a double ZWJ anchor point of the image to record the deletion, if needed
                // (otherwise use the existing anchor point of the image anchored *as* character)
                // create a double CH_TXT_TRACKED_DUMMY_CHAR anchor point of the image to record the
                // deletion, if needed (otherwise use the existing anchor point of the image anchored
                // *as* character)
                if ( eAnchorId == RndStdIds::FLY_AT_CHAR )
                {
                    nRedlineLength = 2;
@@ -472,7 +474,8 @@
                    UnSelectFrame();
                    RedlineFlags eOld = GetRedlineFlags();
                    SetRedlineFlags( eOld | RedlineFlags::Ignore );
                    Insert( OUString( u"‍‍" ) );
                    Insert( OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) +
                            OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
                    SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
                    SwCursorShell::Left(1, CRSR_SKIP_CHARS);
                    anchor.SetAnchor(GetCursor()->GetPoint());