sw/inc/swtable.hxx                       |    2 
 sw/qa/extras/uiwriter/uiwriter5.cxx      |   49 ++++++++++++++++
 sw/source/core/table/swtable.cxx         |   31 ++++++++++
 sw/source/core/unocore/unocrsrhelper.cxx |   28 +++++++++
 sw/source/filter/ww8/docxtableexport.cxx |   92 +++++++++++++++++--------------
 5 files changed, 161 insertions(+), 41 deletions(-)

New commits:
commit 85a47bbb8340e65a19dc1ceaac768902a771ee77
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Fri May 12 17:03:57 2023 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Wed May 17 13:04:43 2023 +0200

    tdf#155328 sw tracked table column: add DOCX export/import
    
    Follow-up to commit ffd8d20d368a885d6d786749278fa438573227a7
    "tdf#150673 sw xmloff: import/export tracked table column" and
    commit 896c2199d9f0a28bd405dd2d1068f5e2973cdf06
    "tdf#79069 DOCX: support tracked table (row) deletion".
    
    Change-Id: Ifbe7b8b83e7071367104a09b4b559513227db786
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151709
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/sw/inc/swtable.hxx b/sw/inc/swtable.hxx
index 0e01f1caecb5..77ec4163eb31 100644
--- a/sw/inc/swtable.hxx
+++ b/sw/inc/swtable.hxx
@@ -552,6 +552,8 @@ public:
         sal_uInt16 nMaxStep ) const
         { return const_cast<SwTableBox*>(this)->FindEndOfRowSpan( rTable, 
nMaxStep ); }
     void RegisterToFormat( SwFormat& rFormat ) ;
+    // get redline for the table cell, if it exists
+    SwRedlineTable::size_type GetRedline() const;
     // get redline type
     RedlineType GetRedlineType() const;
 };
diff --git a/sw/qa/extras/uiwriter/uiwriter5.cxx 
b/sw/qa/extras/uiwriter/uiwriter5.cxx
index 4dd0a42d83c0..0bb6db185e37 100644
--- a/sw/qa/extras/uiwriter/uiwriter5.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter5.cxx
@@ -2641,6 +2641,55 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, 
testTdf150673_RedlineTableColumnDeletionWi
     assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2);
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, 
testRedlineTableColumnDeletionWithDOCXExport)
+{
+    // load a 1-row table, and delete the first column with enabled change 
tracking:
+    createSwDoc("tdf118311.fodt");
+    SwDoc* pDoc = getSwDoc();
+
+    // turn on red-lining and show changes
+    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | 
RedlineFlags::ShowDelete
+                                                      | 
RedlineFlags::ShowInsert);
+    CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+                           pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+    CPPUNIT_ASSERT_MESSAGE(
+        "redlines should be visible",
+        
IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+    // check table
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "//page[1]//body/tab");
+    assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2);
+
+    // delete first table column with enabled change tracking
+    // (HasTextChangesOnly property of the cell will be false)
+    dispatchCommand(mxComponent, ".uno:DeleteColumns", {});
+
+    // Deleted text content with change tracking,
+    // but not table deletion
+    discardDumpedLayout();
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "//page[1]//body/tab");
+    assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2);
+
+    // Save it to a DOCX and load it back.
+    // Exporting change tracking of the cell wasn't supported.
+    // Also Manage Changes for the import.
+    reload("Office Open XML Text", "tdf79069_tracked_table_deletion.docx");
+    pDoc = getSwDoc();
+
+    // accept the deletion of the content of the first cell
+    SwEditShell* const pEditShell(pDoc->GetEditShell());
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), 
pEditShell->GetRedlineCount());
+    pEditShell->AcceptRedline(0);
+
+    // table column was deleted
+    // (working export/import of HasTextChangesOnly of table cells)
+    pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "//page[1]//body/tab");
+    assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 1);
+}
+
 CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf128335)
 {
     // Load the bugdoc, which has 3 textboxes.
diff --git a/sw/source/core/table/swtable.cxx b/sw/source/core/table/swtable.cxx
index 83cb70fc0a65..8b2d395c05eb 100644
--- a/sw/source/core/table/swtable.cxx
+++ b/sw/source/core/table/swtable.cxx
@@ -2960,6 +2960,37 @@ void SwTableBox::ActualiseValueBox()
     }
 }
 
+SwRedlineTable::size_type SwTableBox::GetRedline() const
+{
+    const SwStartNode *pSttNd = GetSttNd();
+
+    if ( !pSttNd || GetRedlineType() == RedlineType::None )
+        return SwRedlineTable::npos;
+
+    SwPosition aCellStart( *GetSttNd(), SwNodeOffset(0) );
+    SwPosition aCellEnd( *GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
+    SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
+    const SwRedlineTable& aRedlineTable = 
GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+    SwRedlineTable::size_type nRedlinePos = 0;
+    for( ; nRedlinePos < aRedlineTable.size(); ++nRedlinePos )
+    {
+        const SwRangeRedline* pRedline = aRedlineTable[ nRedlinePos ];
+
+        if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
+        {
+            // no more redlines in the actual cell,
+            // check the next ones
+            break;
+        }
+
+        // redline in the cell
+        if ( aCellStart <= *pRedline->Start() )
+            return nRedlinePos;
+    }
+
+    return SwRedlineTable::npos;
+}
+
 RedlineType SwTableBox::GetRedlineType() const
 {
     const SwRedlineTable& aRedlineTable = 
GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
diff --git a/sw/source/core/unocore/unocrsrhelper.cxx 
b/sw/source/core/unocore/unocrsrhelper.cxx
index 1e37c4015b1d..831cffd5ac1d 100644
--- a/sw/source/core/unocore/unocrsrhelper.cxx
+++ b/sw/source/core/unocore/unocrsrhelper.cxx
@@ -1497,7 +1497,8 @@ void makeTableCellRedline( SwTableBox& rTableBox,
     std::u16string_view rRedlineType,
     const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
 {
-    IDocumentRedlineAccess* pRedlineAccess = 
&rTableBox.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess();
+    SwDoc* pDoc = rTableBox.GetFrameFormat()->GetDoc();
+    IDocumentRedlineAccess* pRedlineAccess = 
&pDoc->getIDocumentRedlineAccess();
 
     RedlineType eType;
     if ( rRedlineType == u"TableCellInsert" )
@@ -1513,6 +1514,31 @@ void makeTableCellRedline( SwTableBox& rTableBox,
         throw lang::IllegalArgumentException();
     }
 
+    // set table row property "HasTextChangesOnly" to false
+    // to handle tracked deletion or insertion of the table row on the UI
+    const SvxPrintItem *pHasTextChangesOnlyProp =
+         
rTableBox.GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+    if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+    {
+        SvxPrintItem aSetTracking(RES_PRINT, false);
+        SwNodeIndex aInsPos( *rTableBox.GetSttNd(), 1 );
+        // as a workaround for the cells without text content,
+        // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+        if ( rTableBox.IsEmpty() )
+        {
+            SwPaM aPaM(aInsPos);
+            pDoc->getIDocumentContentOperations().InsertString( aPaM,
+                    OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+            aPaM.SetMark();
+            aPaM.GetMark()->SetContent(0);
+            makeRedline(aPaM, RedlineType::TableCellInsert == eType
+                    ? u"Insert"
+                    : u"Delete", rRedlineProperties);
+        }
+        SwCursor aCursor( SwPosition(aInsPos), nullptr );
+        pDoc->SetBoxAttr( aCursor, aSetTracking );
+    }
+
     comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
     std::size_t nAuthor = 0;
     OUString sAuthor;
diff --git a/sw/source/filter/ww8/docxtableexport.cxx 
b/sw/source/filter/ww8/docxtableexport.cxx
index aa8697c2f150..51fb3540c9df 100644
--- a/sw/source/filter/ww8/docxtableexport.cxx
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -697,52 +697,64 @@ void DocxAttributeOutput::TableCellRedline(
     bool bRemovePersonalInfo
         = 
SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
 
-    // search next Redline
-    const SwExtraRedlineTable& aExtraRedlineTable
-        = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
-    for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < 
aExtraRedlineTable.GetSize();
-         ++nCurRedlinePos)
+    // check table row property "HasTextChangesOnly"
+    SwRedlineTable::size_type nChange = pTabBox->GetRedline();
+    if (nChange != SwRedlineTable::npos)
     {
-        SwExtraRedline* pExtraRedline = 
aExtraRedlineTable.GetRedline(nCurRedlinePos);
-        const SwTableCellRedline* pTableCellRedline
-            = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
-        if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
+        const SwRedlineTable& aRedlineTable
+            = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+        const SwRangeRedline* pRedline = aRedlineTable[nChange];
+        SwTableCellRedline* pTableCellRedline = nullptr;
+        bool bIsInExtra = false;
+
+        // use the original DOCX redline data stored in ExtraRedlineTable,
+        // if it exists and its type wasn't changed
+        const SwExtraRedlineTable& aExtraRedlineTable
+            = 
m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+        for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < 
aExtraRedlineTable.GetSize();
+             ++nCurRedlinePos)
         {
-            // Redline for this table cell
-            const SwRedlineData& aRedlineData = 
pTableCellRedline->GetRedlineData();
-            RedlineType nRedlineType = aRedlineData.GetType();
-            switch (nRedlineType)
+            SwExtraRedline* pExtraRedline = 
aExtraRedlineTable.GetRedline(nCurRedlinePos);
+            pTableCellRedline = 
dynamic_cast<SwTableCellRedline*>(pExtraRedline);
+            if (pTableCellRedline && &pTableCellRedline->GetTableBox() == 
pTabBox)
             {
-                case RedlineType::TableCellInsert:
-                case RedlineType::TableCellDelete:
-                {
-                    OString aId(OString::number(m_nRedlineId++));
-                    const OUString& 
rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
-                    OString aAuthor(OUStringToOString(
-                        bRemovePersonalInfo
-                            ? "Author" + 
OUString::number(GetExport().GetInfoID(rAuthor))
-                            : rAuthor,
-                        RTL_TEXTENCODING_UTF8));
-
-                    sal_Int32 nElement
-                        = nRedlineType == RedlineType::TableCellInsert ? 
XML_cellIns : XML_cellDel;
-                    const DateTime aDateTime = aRedlineData.GetTimeStamp();
-                    bool bNoDate = bRemovePersonalInfo
-                                   || (aDateTime.GetYear() == 1970 && 
aDateTime.GetMonth() == 1
-                                       && aDateTime.GetDay() == 1);
-                    if (bNoDate)
-                        m_pSerializer->singleElementNS(XML_w, nElement, 
FSNS(XML_w, XML_id), aId,
-                                                       FSNS(XML_w, 
XML_author), aAuthor);
-                    else
-                        m_pSerializer->singleElementNS(
-                            XML_w, nElement, FSNS(XML_w, XML_id), aId, 
FSNS(XML_w, XML_author),
-                            aAuthor, FSNS(XML_w, XML_date), 
DateTimeToOString(aDateTime));
-                }
+                bIsInExtra = true;
                 break;
-                default:
-                    break;
             }
         }
+
+        const SwRedlineData& aRedlineData
+            = bIsInExtra &&
+                      // still the same type (an inserted cell could become a 
tracked deleted one)
+                      pRedline->GetRedlineData().GetType() == 
pRedline->GetRedlineData().GetType()
+                  ? pTableCellRedline->GetRedlineData()
+                  : pRedline->GetRedlineData();
+
+        // Note: all redline ranges and table row redline (with the same 
author and timestamp)
+        // use the same redline id in OOXML exported by MSO, but it seems, the 
recent solution
+        // (different IDs for different ranges, also row changes) is also 
portable.
+        OString aId(OString::number(m_nRedlineId++));
+        const OUString& 
rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+        OString aAuthor(OUStringToOString(
+            bRemovePersonalInfo ? "Author" + 
OUString::number(GetExport().GetInfoID(rAuthor))
+                                : rAuthor,
+            RTL_TEXTENCODING_UTF8));
+
+        const DateTime aDateTime = aRedlineData.GetTimeStamp();
+        bool bNoDate = bRemovePersonalInfo
+                       || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() 
== 1
+                           && aDateTime.GetDay() == 1);
+
+        if (bNoDate)
+            m_pSerializer->singleElementNS(
+                XML_w, RedlineType::Delete == pRedline->GetType() ? 
XML_cellDel : XML_cellIns,
+                FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor);
+        else
+            m_pSerializer->singleElementNS(
+                XML_w, RedlineType::Delete == pRedline->GetType() ? 
XML_cellDel : XML_cellIns,
+                FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor, 
FSNS(XML_w, XML_date),
+                DateTimeToOString(aDateTime));
+        return;
     }
 }
 

Reply via email to