tdf#143510 DOCX import: fix tracked table drag & drop

Accept or reject of tracked drag & drop table (row)
move weren't handled, resulting an extra empty table
(rows) instead of removing the deleted or inserted
table (rows).

Note: tables moved by drag & drop with track changes use
w:moveFromRangeStart/End and w:moveToRangeStart/End
bookmark-like elements to sign the boundary of the
moved text. Now moveFrom/moveTo import is handled by
tracked table (row) deletion and insertion.

Change-Id: Ie382645fef28f57b30d3504fb28ac08489f705c0
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119406
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/ooxmlexport/data/TC-table-DnD-move.docx b/sw/qa/extras/ooxmlexport/data/TC-table-DnD-move.docx
new file mode 100644
index 0000000..f231d6f
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/TC-table-DnD-move.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/TC-table-Separate-Move.docx b/sw/qa/extras/ooxmlexport/data/TC-table-Separate-Move.docx
new file mode 100644
index 0000000..227f286
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/TC-table-Separate-Move.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/TC-table-rowDND-front.docx b/sw/qa/extras/ooxmlexport/data/TC-table-rowDND-front.docx
new file mode 100644
index 0000000..17d3fee
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/TC-table-rowDND-front.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/TC-table-rowDND.docx b/sw/qa/extras/ooxmlexport/data/TC-table-rowDND.docx
new file mode 100644
index 0000000..9608736
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/TC-table-rowDND.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 1864c4b..431d5d7 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -881,6 +881,56 @@ DECLARE_OOXMLEXPORT_TEST(testTdf104797, "tdf104797.docx")
    CPPUNIT_ASSERT_EQUAL( OUString( " Will this sentence be duplicated ADDED STUFF?" ), getRun( getParagraph( 2 ), 4 )->getString());
}

DECLARE_OOXMLEXPORT_TEST(testTdf143510, "TC-table-DnD-move.docx")
{
    // check moveFromRangeStart/End and moveToRangeStart/End for tracked table move by drag & drop
    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
    if (mbExported)
    {
        // This was 0 (missing tracked table row deletion/insertion)
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[1]/w:tr/w:trPr/w:del", 2);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[2]/w:tr/w:trPr/w:ins", 2);
    }
}

DECLARE_OOXMLEXPORT_TEST(testTdf143510_table_from_row, "TC-table-Separate-Move.docx")
{
    // check moveFromRangeStart/End and moveToRangeStart/End for tracked table move by drag & drop
    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
    if (mbExported)
    {
        // This was 0 (missing tracked table row deletion/insertion)
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[1]/w:tr/w:trPr/w:del", 1);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[1]/w:tr[3]/w:trPr/w:del", 1);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[2]/w:tr", 1);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl[2]/w:tr/w:trPr/w:ins", 1);
    }
}

DECLARE_OOXMLEXPORT_TEST(testTdf143510_within_table, "TC-table-rowDND.docx")
{
    // check moveFromRangeStart/End and moveToRangeStart/End for tracked table row move by DnD
    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
    if (mbExported)
    {
        // This was 0 (missing tracked table row deletion/insertion)
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:trPr/w:del", 1);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:trPr/w:ins", 1);
    }
}

DECLARE_OOXMLEXPORT_TEST(testTdf143510_within_table2, "TC-table-rowDND-front.docx")
{
    // check moveFromRangeStart/End and moveToRangeStart/End for tracked table row move by DnD
    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
    if (mbExported)
    {
        // This was 0 (missing tracked table row deletion/insertion)
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[1]/w:trPr/w:ins", 1);
        assertXPath(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:trPr/w:del", 1);
    }
}

DECLARE_OOXMLEXPORT_TEST(testTdf113608_runAwayNumbering, "tdf113608_runAwayNumbering.docx")
{
    // check that an incorrect numbering style is not applied
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index a3e8b00..7a9ac75 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -2454,6 +2454,19 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
        }
    }
    break;
    case NS_ooxml::LN_EG_RangeMarkupElements_moveFromRangeStart:
        if (m_pImpl->hasTableManager())
            m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_DELETE) );
    break;
    case NS_ooxml::LN_EG_RangeMarkupElements_moveToRangeStart:
        if (m_pImpl->hasTableManager())
            m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_INSERT) );
    break;
    case NS_ooxml::LN_EG_RangeMarkupElements_moveFromRangeEnd:
    case NS_ooxml::LN_EG_RangeMarkupElements_moveToRangeEnd:
         if (m_pImpl->hasTableManager())
            m_pImpl->getTableManager().setMoved( OUString() );
    break;
    case NS_ooxml::LN_paratrackchange:
        m_pImpl->StartParaMarkerChange( );
        [[fallthrough]];
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.cxx b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
index a3a76dcc..35749d6 100644
--- a/writerfilter/source/dmapper/DomainMapperTableManager.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
@@ -30,6 +30,8 @@
#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <numeric>
#include "TrackChangesHandler.hxx"
#include <oox/token/tokens.hxx>

namespace writerfilter::dmapper {

@@ -449,6 +451,7 @@ void DomainMapperTableManager::startLevel( )
    m_aTablePositions.push_back( pNewPositionHandler );
    // empty name will be replaced by the table style name, if it exists
    m_aTableStyleNames.push_back( OUString() );
    m_aMoved.push_back( OUString() );

    TablePositionHandlerPtr pTmpPosition;
    TablePropertyMapPtr pTmpProperties( new TablePropertyMap( ) );
@@ -507,6 +510,7 @@ void DomainMapperTableManager::endLevel( )
    // in the endTable method called in endLevel.
    m_aTablePositions.pop_back();
    m_aTableStyleNames.pop_back();
    m_aMoved.pop_back( );
    m_aParagraphsToEndTable.pop();
}

@@ -727,6 +731,19 @@ void DomainMapperTableManager::endOfRowAction()
        pPropMap->dumpXml();
        TagLogger::getInstance().endElement();
#endif

        // set row insertion/deletion at tracked drag & drop of tables
        OUString aMoved = getMoved();
        if ( !aMoved.isEmpty() )
        {
            auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>(
                    aMoved == getPropertyName( PROP_TABLE_ROW_DELETE )
                        ? oox::XML_tableRowDelete
                        : oox::XML_tableRowInsert );
            uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
            pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::makeAny( aTableRedlineProperties ));
        }

        insertRowProps(pPropMap);
    }
    else if (shouldInsertRow(pCellWidths, pTableGrid, nGrids, bIsIncompleteGrid))
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.hxx b/writerfilter/source/dmapper/DomainMapperTableManager.hxx
index 8c337e6..ef90297 100644
--- a/writerfilter/source/dmapper/DomainMapperTableManager.hxx
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.hxx
@@ -43,6 +43,8 @@ class DomainMapperTableManager : public TableManager
    /// Are we in a shape (text append stack is not empty) or in the body document?
    bool m_bIsInShape;
    std::vector< OUString > m_aTableStyleNames;
    /// Moved table (in moveRangeFromStart...moveRangeFromEnd or moveRangeToStart...moveRangeToEnd)
    std::vector< OUString > m_aMoved;
    /// Grab-bag of table look attributes for preserving.
    comphelper::SequenceAsHashMap m_aTableLook;
    std::vector< TablePositionHandlerPtr > m_aTablePositions;
@@ -128,6 +130,41 @@ public:

    void setIsInShape(bool bIsInShape);

    // moveFromRangeStart and moveToRangeStart are there
    // in the first paragraph in the first cell of the
    // table moved by drag & drop with track changes, but
    // moveFromRangeEnd and moveToRangeEnd follow the
    // table element w:tbl in the same level (not in paragraph).
    // (Special indexing is related to the load of the tables:
    // first-level tables handled by two levels during the
    // import, to support table join etc. In the first cell,
    // setMoved() writes the first level from these two levels
    // i.e. second startLevel() hasn't been called, yet.)
    // TODO: check drag & drop of only a part of the tables.
    void setMoved(OUString sMoved)
    {
        if ( m_aMoved.empty() )
            return;

        if ( !sMoved.isEmpty() )
            m_aMoved.end()[-1] = sMoved;
        else if ( m_aMoved.size() >= 2 )
            // next table rows weren't moved
            m_aMoved.end()[-2] = "";
        else
            m_aMoved.end()[-1] = "";
    }

    OUString getMoved() const
    {
        if ( m_aMoved.size() >= 2 && !m_aMoved.end()[-2].isEmpty() )
           return m_aMoved.end()[-2];
        else if ( !m_aMoved.empty() )
           return m_aMoved.end()[-1];

        return OUString();
    }

};

}
diff --git a/writerfilter/source/ooxml/model.xml b/writerfilter/source/ooxml/model.xml
index 77dce04..da6d564 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -17472,6 +17472,8 @@
    </resource>
    <resource name="CT_MarkupRangeBookmark" resource="Properties">
      <attribute name="id" tokenid="ooxml:CT_MarkupRangeBookmark_id"/>
      <action name="end" tokenid="ooxml:EG_RangeMarkupElements_moveFromRangeEnd" action="sendPropertiesWithId" sendtokenid="ooxml:EG_RangeMarkupElements_moveFromRangeEnd"/>
      <action name="end" tokenid="ooxml:EG_RangeMarkupElements_moveToRangeEnd" action="sendPropertiesWithId" sendtokenid="ooxml:EG_RangeMarkupElements_moveToRangeEnd"/>
    </resource>
    <resource name="CT_PermStart" resource="Properties">
      <attribute name="id" tokenid="ooxml:CT_PermStart_id"/>
@@ -17501,6 +17503,8 @@
    <resource name="CT_MoveBookmark" resource="Properties">
      <attribute name="author" tokenid="ooxml:CT_MoveBookmark_author"/>
      <attribute name="date" tokenid="ooxml:CT_MoveBookmark_date"/>
      <action name="end" tokenid="ooxml:EG_RangeMarkupElements_moveFromRangeStart" action="sendPropertiesWithId" sendtokenid="ooxml:EG_RangeMarkupElements_moveFromRangeStart"/>
      <action name="end" tokenid="ooxml:EG_RangeMarkupElements_moveToRangeStart" action="sendPropertiesWithId" sendtokenid="ooxml:EG_RangeMarkupElements_moveToRangeStart"/>
    </resource>
    <resource name="CT_Comment" resource="XNote">
      <attribute name="id" action="checkId"/>