offapi/com/sun/star/text/CellProperties.idl | 6 + sw/qa/extras/uiwriter/uiwriter5.cxx | 103 ++++++++++++++++++++++++++ sw/source/core/bastyp/init.cxx | 4 - sw/source/core/doc/DocumentRedlineManager.cxx | 28 ++++++- sw/source/core/frmedt/fetab.cxx | 49 +++++++++++- sw/source/core/unocore/unomap.cxx | 1 6 files changed, 185 insertions(+), 6 deletions(-)
New commits: commit d1004cdd6a445ae73673b0ca360ae034b0ec09f2 Author: László Németh <[email protected]> AuthorDate: Tue May 9 16:51:06 2023 +0200 Commit: László Németh <[email protected]> CommitDate: Thu May 11 13:51:47 2023 +0200 tdf#150673 sw offapi: add change tracking of table column deletion This is a minimal extension of the text range based change tracking to record and apply table column deletions with full Undo/Redo support. Add property HasTextChangesOnly to com::sun::star::text::CellProperties. During recording of track changes, deletion of table columns wasn't recorded: columns were removed completely with their text content. Now the deletion deletes the cell content with change tracking, setting also HasTextChangesOnly property of table cells to FALSE. If a tracked deletion is accepted in a deleted column, and the result is an empty cell, the column will be removed, if HasTextChangesOnly property of the deleted cell is FALSE. Note: Deletion of empty columns isn't recorded. Hiding deleted columns hasn't been supported yet in the Hide Changes mode. Follow-up to commit 05366b8e6683363688de8708a3d88cf144c7a2bf "tdf#60382 sw offapi: add change tracking of table/row deletion". Change-Id: I36915d7a58f66b4a3bdaf90495cb998d29c36561 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151593 Tested-by: Jenkins Reviewed-by: László Németh <[email protected]> diff --git a/offapi/com/sun/star/text/CellProperties.idl b/offapi/com/sun/star/text/CellProperties.idl index 2448c25c8439..58c9718c0dc2 100644 --- a/offapi/com/sun/star/text/CellProperties.idl +++ b/offapi/com/sun/star/text/CellProperties.idl @@ -130,6 +130,12 @@ published service CellProperties @since LibreOffice 6.3 */ [optional, readonly, property] com::sun::star::text::XText ParentText; + + /** If TRUE, the table cell wasn't deleted or inserted with its tracked cell content + + @since LibreOffice 7.6 + */ + [optional, property, maybevoid] boolean HasTextChangesOnly; }; diff --git a/sw/qa/extras/uiwriter/uiwriter5.cxx b/sw/qa/extras/uiwriter/uiwriter5.cxx index ee7c041b78c5..38fe2143a992 100644 --- a/sw/qa/extras/uiwriter/uiwriter5.cxx +++ b/sw/qa/extras/uiwriter/uiwriter5.cxx @@ -2453,6 +2453,109 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf147180_empty_rows) CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testRedlineTableColumnDeletion) +{ + // load a table, and delete the first column with enabled change tracking: + // now the column is not deleted silently, but keeps the deleted cell content, + // and only accepting it will result the deletion of the table column. + 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 table column with enabled change tracking + // (HasTextChangesOnly property of the cell will be false) + dispatchCommand(mxComponent, ".uno:DeleteColumns", {}); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + // This was 1 (deleted cell without change tracking) + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2); + + // accept the deletion + SwEditShell* const pEditShell(pDoc->GetEditShell()); + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pEditShell->GetRedlineCount()); + pEditShell->AcceptRedline(0); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + // deleted column + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 1); + + // Undo, and repeat the previous test, but only with deletion of the text content of the cells + // (HasTextChangesOnly property will be removed by Undo) + + dispatchCommand(mxComponent, ".uno:Undo", {}); + dispatchCommand(mxComponent, ".uno:Undo", {}); + + // first column exists again + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2); + + // delete table column with enabled change tracking + dispatchCommand(mxComponent, ".uno:SelectColumn", {}); + dispatchCommand(mxComponent, ".uno:Delete", {}); + + // Table column still exists + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2); + + // accept the deletion of the content of the first cell + CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), pEditShell->GetRedlineCount()); + pEditShell->AcceptRedline(0); + + // table column was still not deleted + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2); + + // Undo, and delete the column without change tracking + + dispatchCommand(mxComponent, ".uno:Undo", {}); + dispatchCommand(mxComponent, ".uno:Undo", {}); + + // table exists again + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//page[1]//body/tab"); + assertXPath(pXmlDoc, "//page[1]//body/tab/row/cell", 2); + + // disable change tracking + pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowDelete + | RedlineFlags::ShowInsert); + + CPPUNIT_ASSERT_MESSAGE("redlining should be off", + !pDoc->getIDocumentRedlineAccess().IsRedlineOn()); + + // delete table column without change tracking + dispatchCommand(mxComponent, ".uno:DeleteColumns", {}); + + // the table column was deleted + discardDumpedLayout(); + 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/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index a5bcd736966e..f2a4d974cfa1 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -204,8 +204,8 @@ WhichRangesContainer const aTableSetRange(svl::Items< WhichRangesContainer const aTableLineSetRange(svl::Items< RES_FILL_ORDER, RES_FRM_SIZE, - // HasTextChangesOnly RES_LR_SPACE, RES_UL_SPACE, + // HasTextChangesOnly RES_PRINT, RES_PRINT, RES_PROTECT, RES_PROTECT, RES_VERT_ORIENT, RES_VERT_ORIENT, @@ -218,6 +218,8 @@ WhichRangesContainer const aTableLineSetRange(svl::Items< WhichRangesContainer const aTableBoxSetRange(svl::Items< RES_FILL_ORDER, RES_FRM_SIZE, RES_LR_SPACE, RES_UL_SPACE, + // HasTextChangesOnly + RES_PRINT, RES_PRINT, RES_PROTECT, RES_PROTECT, RES_VERT_ORIENT, RES_VERT_ORIENT, RES_BACKGROUND, RES_SHADOW, diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index 4e9176cc340a..8ce8a4873aab 100644 --- a/sw/source/core/doc/DocumentRedlineManager.cxx +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -457,6 +457,30 @@ namespace if ( !pBox ) return; + // tracked column deletion + + const SvxPrintItem *pHasBoxTextChangesOnlyProp = + pBox->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); + // empty table cell with property "HasTextChangesOnly" = false + if ( pHasBoxTextChangesOnlyProp && !pHasBoxTextChangesOnlyProp->GetValue() ) + { + SwCursor aCursor( *pPos, nullptr ); + if ( pBox->IsEmpty() ) + { + // TODO check the other cells of the column + // before removing the column + pPos->GetDoc().DeleteCol( aCursor ); + return; + } + else + { + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + pPos->GetDoc().SetBoxAttr( aCursor, aHasTextChangesOnly ); + } + } + + // tracked row deletion + const SwTableLine* pLine = pBox->GetUpper(); const SvxPrintItem *pHasTextChangesOnlyProp = pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); @@ -2330,9 +2354,9 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCall { if ( aSttIdx.GetNode().IsTableNode() ) { - SvxPrintItem aNotTracked(RES_PRINT, false); + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); SwCursor aCursor( SwPosition(aSttIdx), nullptr ); - m_rDoc.SetRowNotTracked( aCursor, aNotTracked, /*bAll=*/true ); + m_rDoc.SetRowNotTracked( aCursor, aHasTextChangesOnly, /*bAll=*/true ); } ++aSttIdx; } diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx index c51413e3ba9b..daca7b86bc9c 100644 --- a/sw/source/core/frmedt/fetab.cxx +++ b/sw/source/core/frmedt/fetab.cxx @@ -276,6 +276,49 @@ bool SwFEShell::DeleteCol() } CurrShell aCurr( this ); + + // tracked deletion: remove only textbox content, + // and set IsNoTracked table box property to false + if ( GetDoc()->GetDocShell()->IsChangeRecording() ) + { + StartUndo(SwUndoId::COL_DELETE); + StartAllAction(); + + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + pWrtShell->SelectTableCol(); + + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Col ); + + TableWait aWait( 20, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + for (size_t i = 0; i < aBoxes.size(); ++i) + { + SwTableBox *pBox = aBoxes[i]; + if ( pBox->GetSttNd() ) + { + SwNodeIndex aIdx( *pBox->GetSttNd(), 1 ); + SwCursor aCursor( SwPosition(aIdx), nullptr ); + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + GetDoc()->SetBoxAttr( aCursor, aHasTextChangesOnly ); + } + } + + SwEditShell* pEditShell = GetDoc()->GetEditShell(); + SwRedlineTable::size_type nPrev = pEditShell->GetRedlineCount(); + pEditShell->Delete(); + + EndAllActionAndCall(); + EndUndo(SwUndoId::COL_DELETE); + + // track column deletion only if there were tracked text changes + // FIXME redline count can be the same in special cases, e.g. adding a + // new tracked deletion with removing an own tracked insertion... + if ( nPrev != pEditShell->GetRedlineCount() ) + return true; + } + StartAllAction(); // search boxes via the layout @@ -349,11 +392,11 @@ bool SwFEShell::DeleteRow(bool bCompleteTable) StartAllAction(); // tracked deletion: remove only textbox content, - // and set IsNoTracked table line property to false + // and set HasTextChangesOnly table line property to false if ( bRecordChanges ) { - SvxPrintItem aNotTracked(RES_PRINT, false); - GetDoc()->SetRowNotTracked( *getShellCursor( false ), aNotTracked ); + SvxPrintItem aHasTextChangesOnly(RES_PRINT, false); + GetDoc()->SetRowNotTracked( *getShellCursor( false ), aHasTextChangesOnly ); if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) pWrtShell->SelectTableRow(); diff --git a/sw/source/core/unocore/unomap.cxx b/sw/source/core/unocore/unomap.cxx index 40223b34e474..68565eadaf6f 100644 --- a/sw/source/core/unocore/unomap.cxx +++ b/sw/source/core/unocore/unomap.cxx @@ -186,6 +186,7 @@ o3tl::span<const SfxItemPropertyMapEntry> SwUnoPropertyMapProvider::GetPropertyM { UNO_NAME_ROW_SPAN, FN_UNO_CELL_ROW_SPAN, cppu::UnoType<sal_Int32>::get(), 0, 0 }, { UNO_NAME_CELL_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, { UNO_NAME_PARENT_TEXT, FN_UNO_PARENT_TEXT, cppu::UnoType<text::XText>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, + { UNO_NAME_HAS_TEXT_CHANGES_ONLY, RES_PRINT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, REDLINE_NODE_PROPERTIES }; m_aMapEntriesArr[nPropertyId] = aCellMap_Impl;
