summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2021-11-09 16:11:03 +0100
committerLászló Németh <nemeth@numbertext.org>2021-11-09 20:14:57 +0100
commit2fe0881906462cbcbe8baf81068010310fb1c97a (patch)
tree3348d13a124df717e3a61ade349f8f7d09d5d4c6
parent83490dc97ca76c64a7d46839f4dbb8b1610f169f (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.hxx7
-rw-r--r--sw/qa/extras/uiwriter/uiwriter2.cxx53
-rw-r--r--sw/source/core/table/swtable.cxx71
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx69
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)