diff options
author | László Németh <nemeth@numbertext.org> | 2021-11-09 16:11:03 +0100 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-11-09 20:14:57 +0100 |
commit | 2fe0881906462cbcbe8baf81068010310fb1c97a (patch) | |
tree | 3348d13a124df717e3a61ade349f8f7d09d5d4c6 | |
parent | 83490dc97ca76c64a7d46839f4dbb8b1610f169f (diff) |
tdf#145089 DOCX export: fix track changes of table row insertion
Newly inserted table rows lost their change tracking during
DOCX import, i.e. rejection didn't remove the rows.
Note: start to clean-up DOCX export and SwTableLine::IsDeleted(),
preparing the fix for tdf#145091.
Follow-up to commit dbc82c02eb24ec1c97c6ee32069771d8deb394f9
"tdf#143358 sw: track insertion of empty table rows".
Change-Id: Ib90b745632ec4aeb30651fbff209ecef69657f4d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124922
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r-- | sw/inc/swtable.hxx | 7 | ||||
-rw-r--r-- | sw/qa/extras/uiwriter/uiwriter2.cxx | 53 | ||||
-rw-r--r-- | sw/source/core/table/swtable.cxx | 71 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 69 |
4 files changed, 135 insertions, 65 deletions
diff --git a/sw/inc/swtable.hxx b/sw/inc/swtable.hxx index f358d348ab5f..e4ba92aaa6df 100644 --- a/sw/inc/swtable.hxx +++ b/sw/inc/swtable.hxx @@ -397,8 +397,13 @@ public: // it doesn't contain box content bool IsEmpty() const; + // Update TextChangesOnly property based on the redlines of the table row. + // rRedlinePos: search from this redline to speed up SwTable::IsDeleted(). + // Return with the redline, which associated to the row change (latest deletion + // in the case of deleted row, the first insertion in the case of row insertion + // or npos, if TextChangesOnly is true, i.e. the table row is not deleted or inserted). + SwRedlineTable::size_type UpdateTextChangesOnly(SwRedlineTable::size_type& rRedlinePos) const; // is it a tracked deleted row - // (search its first redline from rRedlinePos to speed up SwTable::IsDeleted()) bool IsDeleted(SwRedlineTable::size_type& rRedlinePos) const; }; diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx index 8ab21d4b458a..7fd1ffb9a546 100644 --- a/sw/qa/extras/uiwriter/uiwriter2.cxx +++ b/sw/qa/extras/uiwriter/uiwriter2.cxx @@ -4889,6 +4889,59 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testRedlineTableRowInsertionWithReject) assertXPath(pXmlDoc, "//page[1]//body/tab/row", 1); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf145089_RedlineTableRowInsertionDOCX) +{ + // load a 1-row table, and insert a row with enabled change tracking + SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf118311.fodt"); + + // 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 and its single row + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row", 1); + + // insert rows before and after with enabled change tracking + // (HasTextChangesOnly property of the row will be false, and + // add dummy characters CH_TXT_TRACKED_DUMMY_CHAR) + dispatchCommand(mxComponent, ".uno:InsertRowsBefore", {}); + dispatchCommand(mxComponent, ".uno:InsertRowsAfter", {}); + + // save it to DOCX + reload("Office Open XML Text", "tdf145089.docx"); + SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + SwViewShell* pViewShell + = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + pViewShell->Reformat(); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row", 3); + + // reject redlines + SwDoc* pDOCXDoc(pTextDoc->GetDocShell()->GetDoc()); + SwEditShell* const pEditShell(pDOCXDoc->GetEditShell()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(2), pEditShell->GetRedlineCount()); + pEditShell->RejectRedline(0); + pEditShell->RejectRedline(0); + + discardDumpedLayout(); + + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + + // This was 3 (not rejected row insertion) + assertXPath(pXmlDoc, "//page[1]//body/tab/row", 1); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf128603) { // Load the bugdoc, which has 3 textboxes. diff --git a/sw/source/core/table/swtable.cxx b/sw/source/core/table/swtable.cxx index 9f68ca5203ff..da6255a73770 100644 --- a/sw/source/core/table/swtable.cxx +++ b/sw/source/core/table/swtable.cxx @@ -1592,12 +1592,17 @@ bool SwTable::IsDeleted() const return true; } -bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const -{ - bool bRet = false; +// TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells. +// At tracked row deletion, return with the newest deletion of the row or +// at tracked row insertion, return with the oldest insertion in the row, which +// contain the change data of the row change. +// If the return value is SwRedlineTable::npos, there is no tracked row change. +SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(SwRedlineTable::size_type& rRedlinePos) const +{ + SwRedlineTable::size_type nRet = SwRedlineTable::npos; const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); if ( aRedlineTable.empty() ) - return false; + 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 @@ -1607,6 +1612,8 @@ bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const { const SwTableBoxes & rBoxes = GetTabBoxes(); size_t nBoxes = rBoxes.size(); + bool bInsertion = false; + for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex) { auto pBox = rBoxes[nBoxIndex]; @@ -1616,10 +1623,11 @@ bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const continue; } + bool bHasRedline = false; SwPosition aCellStart( SwNodeIndex( *pBox->GetSttNd(), 0 ) ); SwPosition aCellEnd( SwNodeIndex( *pBox->GetSttNd()->EndOfSectionNode(), -1 ) ); SwNodeIndex pEndNodeIndex(aCellEnd.nNode.GetNode()); - for( bRet = false ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos ) + for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos ) { const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ]; @@ -1630,26 +1638,61 @@ bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const break; } - // redline in the cell, it must be a delete redline + // redline in the cell if ( aCellStart <= *pRedline->Start() ) { - bRet = RedlineType::Delete == pRedline->GetType(); - if ( !bRet ) - // other type of redline, e.g. tracked row insertion - // contains an insert redline at the beginning of the first cell - return false; + bHasRedline = true; + RedlineType nType = pRedline->GetType(); + + // first insert redline + if ( !bInsertion && RedlineType::Insert == nType ) + { + bInsertion = true; + nRet = rRedlinePos; + continue; + // TODO check older delete redlines to remove row change, if needed + } + + // 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() ) ) ) + { + nRet = rRedlinePos; + } } } - if ( !bRet ) + if ( !bHasRedline && !bInsertion ) { // not deleted cell content: the row is not empty - return false; + // 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 } } - return bRet; + return nRet; +} + +bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const +{ + SwRedlineTable::size_type nPos = UpdateTextChangesOnly(rRedlinePos); + if ( nPos != SwRedlineTable::npos ) + { + const SwRedlineTable& aRedlineTable = + GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); + if ( RedlineType::Delete == aRedlineTable[nPos]->GetType() ) + return true; + } + return false; } SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp ) diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 035e1e8e4d62..b2b7a047b0f2 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -4379,68 +4379,37 @@ void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); const SwTableLine * pTabLine = pTabBox->GetUpper(); - // check table row property "HasTextChangesOnly" (used only for tracked deletion, yet) - const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable(); - const SvxPrintItem *pHasTextChangesOnlyProp = - pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); - bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); - if ( !aRedlineTable.empty() && pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() ) - { - // Tracked row deletion is associated to the newest redline range in the row. - // Search it to get the date and the mandatory author. - const SwTableBoxes & rBoxes = pTabLine->GetTabBoxes(); - SwPosition aRowStart( SwNodeIndex( *rBoxes[0]->GetSttNd(), 0 ) ); - SwPosition aRowEnd( SwNodeIndex( *rBoxes[rBoxes.size() - 1]->GetSttNd()->EndOfSectionNode(), -1 ) ); - SwNodeIndex pEndNodeIndex(aRowEnd.nNode.GetNode()); - - SwRedlineTable::size_type nLastDeletion = SwRedlineTable::npos; - for( SwRedlineTable::size_type n = 0; n < aRedlineTable.size(); ++n ) - { - const SwRangeRedline* pRedline = aRedlineTable[ n ]; - - if ( pRedline->Start()->nNode > pEndNodeIndex ) - break; - - if( RedlineType::Delete != pRedline->GetType() ) - continue; - - // redline is in the table row, and newer, than the previous - if ( aRowStart <= *pRedline->Start() ) - { - if ( nLastDeletion == SwRedlineTable::npos || - aRedlineTable [ nLastDeletion ]->GetRedlineData().GetTimeStamp() < - pRedline->GetRedlineData().GetTimeStamp() ) - { - nLastDeletion = n; - } - } - } - - if ( nLastDeletion != SwRedlineTable::npos ) - { - const SwRedlineData& aRedlineData = aRedlineTable[ nLastDeletion ]->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 + // check table row property "HasTextChangesOnly" + SwRedlineTable::size_type nPos(0); + SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos); + if ( nChange != SwRedlineTable::npos ) + { + const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable(); + const SwRangeRedline* pRedline = aRedlineTable[ nChange ]; + const SwRedlineData& aRedlineData = 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 ) ); - OString aDate( DateTimeToOString( bRemovePersonalInfo + OString aDate( DateTimeToOString( bRemovePersonalInfo ? DateTime(Date( 1, 1, 1970 )) // Epoch time : aRedlineData.GetTimeStamp() ) ); - m_pSerializer->singleElementNS( XML_w, XML_del, + m_pSerializer->singleElementNS( XML_w, + RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, FSNS( XML_w, XML_date ), aDate ); - return; - } + return; } // search next Redline (only deletion of empty rows and all insertions imported from a DOCX) |