diff options
-rw-r--r-- | sw/inc/swtable.hxx | 2 | ||||
-rw-r--r-- | sw/qa/extras/uiwriter/uiwriter5.cxx | 49 | ||||
-rw-r--r-- | sw/source/core/table/swtable.cxx | 31 | ||||
-rw-r--r-- | sw/source/core/unocore/unocrsrhelper.cxx | 28 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxtableexport.cxx | 92 |
5 files changed, 161 insertions, 41 deletions
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; } } |