sw/inc/doc.hxx | 8 sw/qa/extras/unowriter/data/tdf134252_onlytable_protected.fodt | 87 ++++++++++ sw/qa/extras/unowriter/unowriter.cxx | 59 ++++++ sw/source/core/docnode/ndtbl.cxx | 18 +- sw/source/core/frmedt/fecopy.cxx | 20 -- sw/source/core/frmedt/fetab.cxx | 3 sw/source/core/unocore/unoobj2.cxx | 2 sw/source/core/unocore/unotbl.cxx | 2 8 files changed, 175 insertions(+), 24 deletions(-)
New commits: commit c598e0accf24e149520a22a8c062249f147844c7 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Thu Feb 4 12:36:41 2021 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Feb 4 18:34:49 2021 +0100 sw: fix copying and deleting of table in section via API This is a follow-up to 7ab349296dac79dad3fec09f60348efcbb9ea17e. The first problem was that tables that contain protected cells refused to be deleted, which caused an infinite loop in SwXTextRange::DeleteAndInsert(). This also affected SwXTextTable::dispose(), and DDE tables. Fix this by forcibly deleting even protected cells from UNO APIs; protection is an UI feature. The second problem was that pasting the table SwFEShell::Paste() would set up aCpyPam so that it selects from the start node of the table to the last text node in the table, excluding the table cell and table end nodes, which caused: DocumentContentOperationsManager.cxx:3548: An insufficient number of nodes were copied! Fix this by setting up aCpyPam over the entire body section of the clipboard document. Change-Id: I8c6caee5e75260dff79f106efc11f9669feddc0c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110411 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 987f660960c2..124403befa9b 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -21,6 +21,7 @@ // SwDoc interfaces #include <o3tl/deleter.hxx> +#include <o3tl/typed_flags_set.hxx> #include <o3tl/sorted_vector.hxx> #include <vcl/idle.hxx> #include "swdllapi.h" @@ -1196,7 +1197,8 @@ public: sal_uInt16 nCnt = 1, bool bBehind = true ); // Delete Columns/Rows in table. - bool DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn = false ); + enum class RowColMode { DeleteRow = 0, DeleteColumn = 1, DeleteProtected = 2 }; + bool DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode eMode = RowColMode::DeleteRow); void DeleteRow( const SwCursor& rCursor ); void DeleteCol( const SwCursor& rCursor ); @@ -1668,6 +1670,10 @@ private: }; +namespace o3tl { + template<> struct typed_flags<SwDoc::RowColMode> : is_typed_flags<SwDoc::RowColMode, 3> {}; +} + // This method is called in Dtor of SwDoc and deletes cache of ContourObjects. void ClrContourCache(); diff --git a/sw/qa/extras/unowriter/data/tdf134252_onlytable_protected.fodt b/sw/qa/extras/unowriter/data/tdf134252_onlytable_protected.fodt new file mode 100644 index 000000000000..f588fdba0515 --- /dev/null +++ b/sw/qa/extras/unowriter/data/tdf134252_onlytable_protected.fodt @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns :config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="ur n:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:font-face-decls> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/> + <style:font-face style:name="Lohit Devanagari" svg:font-family="'Lohit Devanagari'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Source Han Serif CN" svg:font-family="'Source Han Serif CN'" style:font-family-generic="system" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="graphic"> + <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/> + <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Source Han Serif CN" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Lohit Devanagari" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Source Han Serif CN" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Lohit Devanagari" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false"/> + </style:default-style> + <style:default-style style:family="table"> + <style:table-properties table:border-model="collapsing"/> + </style:default-style> + <style:default-style style:family="table-row"> + <style:table-row-properties fo:keep-together="auto"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/> + <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/> + <text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="Table1" style:family="table"> + <style:table-properties style:width="17cm" table:align="margins"/> + </style:style> + <style:style style:name="Table1.A" style:family="table-column"> + <style:table-column-properties style:column-width="17cm" style:rel-column-width="65535*"/> + </style:style> + <style:style style:name="Table1.A1" style:family="table-cell"> + <style:table-cell-properties fo:padding="0.097cm" fo:border="0.05pt solid #000000"/> + </style:style> + <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties officeooo:rsid="00078615" officeooo:paragraph-rsid="00078615"/> + </style:style> + <style:style style:name="P3" style:family="paragraph" style:parent-style-name="Table_20_Contents"> + <style:text-properties officeooo:rsid="00095b34" officeooo:paragraph-rsid="00095b34"/> + </style:style> + <style:style style:name="Sect1" style:family="section"> + <style:section-properties fo:background-color="#81d41a" style:editable="false"> + <style:columns fo:column-count="1" fo:column-gap="0cm"/> + <style:background-image/> + </style:section-properties> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="21.001cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm"> + <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1"/> + </office:master-styles> + <office:body> + <office:text> + <text:sequence-decls> + <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/> + <text:sequence-decl text:display-outline-level="0" text:name="Table"/> + <text:sequence-decl text:display-outline-level="0" text:name="Text"/> + <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/> + <text:sequence-decl text:display-outline-level="0" text:name="Figure"/> + </text:sequence-decls> + <text:section text:style-name="Sect1" text:name="Section1"> + <table:table table:name="Table1" table:style-name="Table1"> + <table:table-column table:style-name="Table1.A"/> + <table:table-row> + <table:table-cell table:style-name="Table1.A1" office:value-type="string" table:protected="true"> + <text:p text:style-name="P3">baz</text:p> + </table:table-cell> + </table:table-row> + </table:table> + </text:section> + <text:p text:style-name="P1">foo</text:p> + </office:text> + </office:body> +</office:document> diff --git a/sw/qa/extras/unowriter/unowriter.cxx b/sw/qa/extras/unowriter/unowriter.cxx index 0266723d9e4d..cfe3dbb13702 100644 --- a/sw/qa/extras/unowriter/unowriter.cxx +++ b/sw/qa/extras/unowriter/unowriter.cxx @@ -497,6 +497,65 @@ CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTableAtEnd) xCursor->getString()); } +CPPUNIT_TEST_FIXTURE(SwUnoWriter, testSectionAnchorCopyTable) +{ + // this contains a section that ends with a table (plus another section) + load(DATA_DIRECTORY, "tdf134252_onlytable_protected.fodt"); + + uno::Reference<text::XTextTablesSupplier> const xTextTablesSupplier(mxComponent, + uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> const xTables(xTextTablesSupplier->getTextTables(), + uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); + + uno::Reference<text::XTextSectionsSupplier> const xTextSectionsSupplier(mxComponent, + uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> const xSections( + xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + + uno::Reference<text::XTextContent> const xSection(xSections->getByIndex(0), uno::UNO_QUERY); + uno::Reference<text::XTextRange> const xAnchor(xSection->getAnchor()); + CPPUNIT_ASSERT_EQUAL(OUString("baz" SAL_NEWLINE_STRING), xAnchor->getString()); + + // copy the content of the section to a clipboard document + uno::Reference<datatransfer::XTransferableSupplier> const xTS( + uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getCurrentController(), + uno::UNO_QUERY); + uno::Reference<datatransfer::XTransferableTextSupplier> const xTTS(xTS, uno::UNO_QUERY); + uno::Reference<datatransfer::XTransferable> const xTransferable( + xTTS->getTransferableForTextRange(xAnchor)); + + // check this doesn't throw + CPPUNIT_ASSERT(xAnchor->getStart().is()); + CPPUNIT_ASSERT(xAnchor->getEnd().is()); + + // replace section content + xAnchor->setString("quux"); + + // table in section was deleted, but not section itself + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xTables->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(OUString("\"" + "quux" /*SAL_NEWLINE_STRING*/ "\""), + OUString("\"" + xAnchor->getString() + "\"")); + + // now paste it + uno::Reference<text::XTextViewCursorSupplier> const xTVCS(xTS, uno::UNO_QUERY); + uno::Reference<text::XTextViewCursor> const xCursor(xTVCS->getViewCursor()); + xCursor->gotoEnd(false); + xTS->insertTransferable(xTransferable); + + // table in section was pasted, but not section itself + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + xCursor->gotoStart(true); + CPPUNIT_ASSERT_EQUAL( + OUString("quux" SAL_NEWLINE_STRING "foo" SAL_NEWLINE_STRING "baz" SAL_NEWLINE_STRING), + xCursor->getString()); +} + CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTextRangeInTable) { load(DATA_DIRECTORY, "bookmarkintable.fodt"); diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index 5321d026b2ba..c1e4d5040184 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -1946,22 +1946,28 @@ void SwDoc::DeleteCol( const SwCursor& rCursor ) // Thus delete the Columns GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr); - DeleteRowCol( aBoxes, true ); + DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr); } -bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) +bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode) { - if( ::HasProtectedCells( rBoxes )) + if (!(eMode & SwDoc::RowColMode::DeleteProtected) + && ::HasProtectedCells(rBoxes)) + { return false; + } OSL_ENSURE( !rBoxes.empty(), "No valid Box list" ); SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); if( !pTableNd ) return false; - if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr) + if (!(eMode & SwDoc::RowColMode::DeleteProtected) + && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr) + { return false; + } ::ClearFEShellTabCols(*this, nullptr); SwSelBoxes aSelBoxes( rBoxes ); @@ -1970,7 +1976,7 @@ bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) tools::Long nMax = 0; if( rTable.IsNewModel() ) { - if( bColumn ) + if (eMode & SwDoc::RowColMode::DeleteColumn) rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax ); else rTable.FindSuperfluousRows( aSelBoxes ); @@ -2129,7 +2135,7 @@ bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) if (rTable.IsNewModel()) { - if (bColumn) + if (eMode & SwDoc::RowColMode::DeleteColumn) rTable.PrepareDeleteCol( nMin, nMax ); rTable.FindSuperfluousRows( aSelBoxes ); if (pUndo) diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx index 7780f9dbb885..b30d0275c878 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -712,25 +712,19 @@ bool SwFEShell::Paste(SwDoc& rClpDoc, bool bNestedTable) CurrShell aCurr( this ); // then till end of the nodes array SwNodeIndex aIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 ); - SwPaM aCpyPam( aIdx ); //DocStart + // select content section, whatever it may contain + SwPaM aCpyPam(aIdx, SwNodeIndex(rClpDoc.GetNodes().GetEndOfContent(), -1)); + if (SwContentNode *const pAtEnd = aCpyPam.GetNode(true).GetContentNode()) + { + pAtEnd->MakeEndIndex(&aCpyPam.GetPoint()->nContent); + } // If there are table formulas in the area, then display the table first // so that the table formula can calculate a new value first // (individual boxes in the area are retrieved via the layout) SwFieldType* pTableFieldTyp = GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table ); - SwTableNode *const pSrcNd = aCpyPam.GetNode().GetTableNode(); - if( !pSrcNd ) // table node ? - { // don't skip !! - SwContentNode* pCNd = aCpyPam.GetNode().GetContentNode(); - if( pCNd ) - aCpyPam.GetPoint()->nContent.Assign( pCNd, 0 ); - else if( !aCpyPam.Move( fnMoveForward, GoInNode )) - aCpyPam.Move( fnMoveBackward, GoInNode ); - } - - aCpyPam.SetMark(); - aCpyPam.Move( fnMoveForward, GoInDoc ); + SwTableNode *const pSrcNd = aCpyPam.GetNode(false).GetTableNode(); bool bRet = true; StartAllAction(); diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx index 97878a01e3ea..85d4daba2666 100644 --- a/sw/source/core/frmedt/fetab.cxx +++ b/sw/source/core/frmedt/fetab.cxx @@ -294,9 +294,8 @@ bool SwFEShell::DeleteCol() // then delete the column StartUndo(SwUndoId::COL_DELETE); - bRet = GetDoc()->DeleteRowCol( aBoxes, true ); + bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); EndUndo(SwUndoId::COL_DELETE); - } else bRet = false; diff --git a/sw/source/core/unocore/unoobj2.cxx b/sw/source/core/unocore/unoobj2.cxx index eb73fa0f1e80..6afc81076269 100644 --- a/sw/source/core/unocore/unoobj2.cxx +++ b/sw/source/core/unocore/unoobj2.cxx @@ -791,7 +791,7 @@ static void DeleteTable(SwDoc & rDoc, SwTable& rTable) } // note: if the table is the content in the section, this will create // a new text node - that's desirable here - rDoc.DeleteRowCol(aSelBoxes); + rDoc.DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected); } void SwXTextRange::DeleteAndInsert( diff --git a/sw/source/core/unocore/unotbl.cxx b/sw/source/core/unocore/unotbl.cxx index 132a43a55a62..96cfe437158f 100644 --- a/sw/source/core/unocore/unotbl.cxx +++ b/sw/source/core/unocore/unotbl.cxx @@ -2190,7 +2190,7 @@ void SwXTextTable::dispose() SwSelBoxes aSelBoxes; for(auto& rBox : pTable->GetTabSortBoxes() ) aSelBoxes.insert(rBox); - pFormat->GetDoc()->DeleteRowCol(aSelBoxes); + pFormat->GetDoc()->DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected); } void SAL_CALL SwXTextTable::addEventListener( _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits