sw/inc/crsrsh.hxx | 18 sw/inc/doc.hxx | 9 sw/inc/ndarr.hxx | 7 sw/inc/node.hxx | 2 sw/inc/undobj.hxx | 12 sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt | 15 sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt | 90 ++ sw/qa/extras/layout/layout.cxx | 66 + sw/qa/extras/odfimport/odfimport.cxx | 4 sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt | 20 sw/qa/extras/uiwriter/data/hiddenSectionsAroundPageBreak.fodt | 21 sw/qa/extras/uiwriter/data/pagebreak-source.fodt | 123 +++ sw/qa/extras/uiwriter/data/pagebreak-target.fodt | 129 +++ sw/qa/extras/uiwriter/data/table-at-end-of-cell.fodt | 219 ++++++ sw/qa/extras/uiwriter/uiwriter.cxx | 177 ++++ sw/qa/extras/uiwriter/uiwriter2.cxx | 23 sw/qa/extras/unowriter/data/tdf134252_onlytable_protected.fodt | 87 ++ sw/qa/extras/unowriter/unowriter.cxx | 62 + sw/source/core/crsr/crsrsh.cxx | 358 +++++++++- sw/source/core/crsr/pam.cxx | 5 sw/source/core/crsr/swcrsr.cxx | 17 sw/source/core/doc/DocumentContentOperationsManager.cxx | 97 +- sw/source/core/doc/doccorr.cxx | 7 sw/source/core/doc/docedt.cxx | 4 sw/source/core/docnode/ndsect.cxx | 4 sw/source/core/docnode/ndtbl.cxx | 299 ++++---- sw/source/core/docnode/nodes.cxx | 49 + sw/source/core/docnode/section.cxx | 7 sw/source/core/edit/eddel.cxx | 36 - sw/source/core/edit/edglss.cxx | 19 sw/source/core/edit/editsh.cxx | 2 sw/source/core/frmedt/fecopy.cxx | 35 sw/source/core/frmedt/fetab.cxx | 3 sw/source/core/inc/UndoTable.hxx | 2 sw/source/core/inc/frame.hxx | 2 sw/source/core/inc/layfrm.hxx | 1 sw/source/core/inc/sectfrm.hxx | 2 sw/source/core/inc/txtfrm.hxx | 2 sw/source/core/layout/calcmove.cxx | 13 sw/source/core/layout/findfrm.cxx | 8 sw/source/core/layout/flowfrm.cxx | 120 +-- sw/source/core/layout/frmtool.cxx | 23 sw/source/core/layout/ftnfrm.cxx | 10 sw/source/core/layout/pagechg.cxx | 5 sw/source/core/layout/sectfrm.cxx | 38 + sw/source/core/layout/tabfrm.cxx | 6 sw/source/core/layout/trvlfrm.cxx | 25 sw/source/core/layout/wsfrm.cxx | 12 sw/source/core/text/txtfrm.cxx | 15 sw/source/core/text/xmldump.cxx | 9 sw/source/core/undo/undobj.cxx | 22 sw/source/core/undo/unins.cxx | 4 sw/source/core/undo/untbl.cxx | 8 sw/source/core/undo/untblk.cxx | 12 sw/source/core/unocore/unoobj2.cxx | 14 sw/source/core/unocore/unotbl.cxx | 2 sw/source/core/unocore/unotext.cxx | 6 sw/source/filter/xml/XMLRedlineImportHelper.cxx | 5 sw/source/uibase/wrtsh/move.cxx | 46 + sw/source/uibase/wrtsh/select.cxx | 36 - 60 files changed, 1993 insertions(+), 481 deletions(-)
New commits: commit 0a0196bc0fe18449debae5f6353c1ddb312df916 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Mon Mar 4 15:45:07 2024 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Mar 7 15:14:42 2024 +0100 sw: SelectAll of section with RES_PAGEDESC corner-case The main problem here is that if a document has a RES_PAGEDESC on its first body text node, and you paste a section whose first text node also has a RES_PAGEDESC, the result inevitably has a page break that wasn't there before, and which is unwanted. SwEditShell::CopySelToDoc() needs a change to include the end node of a section at the start, so it is copied. Change CopyImplImpl() to insert a non-textnode *before* a text node at the insert position, instead of after it. This simplifies the implementation: only SwFEShell::Paste() needs to care about removing an empty trailing paragraph, but SwEditShell::CopySelToDoc() needs no changes; both functions would need to delete the empty paragraph when inserting after. Several tests such as CppunitTest_sw_ooxmlexport3 testCrashWhileSave fail because of this, which can be solved by removing the DelFullPara() call in SwXText::copyText() that is now unnecessary. Generalise and simplify the "bAfterTable" code in CopyImplImpl(): it doesn't really matter what is before the insert position, what matters is if the pasted text starts with a table or section. Also, the fly-anchor-correction code (both here and in SwUndoInserts::RedoImpl()) needs to move to the first text node also in case a section was inserted (but the equal-looking code *before* inserting remains as is!), in the situation where the last node will be deleted. Now there are some test failures: unowriter.cxx:430:Assertion Test name: (anonymous namespace)::testSectionAnchorCopyTableAtStart::TestBody equality assertion failed - Expected: quux foo bar - Actual : quux foo This is because the end position was created from SwNodeIndex aInsPos only, it needs to take also the aDestIdx from the "if (pEndTextNd)" branch. testTdf134250::TestBody finished in: 867ms uiwriter2.cxx:462:Assertion Test name: testTdf134250::TestBody equality assertion failed - Expected: 1 - Actual : 2 The section is pasted now, so there are 2. uiwriter3.cxx:1519:Assertion Test name: testTdf135733::TestBody equality assertion failed - Expected: 1 - Actual : 2 Table is now inserted before the first paragraph, which has a RES_PAGEDESC. (presumably regression from commit 9667e5ffd18d6167344e102b89a393bc981644ec) For the backport, this also fails: uiwriter2.cxx:1220:testTdf141175::TestBody equality assertion failed - Expected: 2 - Actual : 1 Because the clipboard doc starts with a table node, it takes different path in Paste and merges the source table cells into target table cells. Probably users don't care about this corner case, and it doesn't do anything obviously wrong. On master it does yet different things because Cut creates table delete redlines. Change-Id: I820e381113fee90a81249afbc2280bfc3ddb7647 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164401 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit fcd4222d36e1864452163e5c94976eea353bbaf0) diff --git a/sw/qa/extras/uiwriter/data/pagebreak-source.fodt b/sw/qa/extras/uiwriter/data/pagebreak-source.fodt new file mode 100644 index 000000000000..e09c1b447cfd --- /dev/null +++ b/sw/qa/extras/uiwriter/data/pagebreak-source.fodt @@ -0,0 +1,123 @@ +<?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: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:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:dc="http://purl.org/dc/eleme nts/1.1/" xmlns:ooo="http://openoffice.org/2004/office" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office: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:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0 " xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2024-03-05T16:51:41.526594818</meta:creation-date><dc:date>2024-03-05T17:24:50.151189313</dc:date><meta:editing-duration>PT24M14S</meta:editing-duration><meta:editing-cycles>5</meta:editing-cycles><meta:generator>CIB_OfficeDev/6.4.0.24$Linux_X86_64 LibreOffice_project/2b8ce275a2d1aae9319dfc266e677412b611101d</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="0" meta:word-count="0" meta:character-count="0" meta:non-whitespace-character-count="0"/></office:meta> + <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="Noto Sans Devanagari" svg:font-family="'Noto Sans Devanagari'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif CJK SC'" 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:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans 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" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans 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:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <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="P2" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties/> + </style:style> + <style:style style:name="P3" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="Standard"> + <style:paragraph-properties style:page-number="auto"/> + </style:style> + <style:style style:name="Sect1" style:family="section"> + <style:section-properties fo:background-color="transparent" 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="SourceSection"> + <text:p text:style-name="P3"><text:hidden-paragraph text:condition="ooow:TRUE" text:is-hidden="true"/></text:p> + <text:p text:style-name="P2"/> + </text:section> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/data/pagebreak-target.fodt b/sw/qa/extras/uiwriter/data/pagebreak-target.fodt new file mode 100644 index 000000000000..f059e33f1514 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/pagebreak-target.fodt @@ -0,0 +1,129 @@ +<?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: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:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:dc="http://purl.org/dc/eleme nts/1.1/" xmlns:ooo="http://openoffice.org/2004/office" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office: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:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0 " xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2024-03-05T16:57:01.179149401</meta:creation-date><dc:date>2024-03-05T16:58:35.972978544</dc:date><meta:editing-duration>PT1M35S</meta:editing-duration><meta:editing-cycles>2</meta:editing-cycles><meta:generator>CIB_OfficeDev/6.4.0.24$Linux_X86_64 LibreOffice_project/2b8ce275a2d1aae9319dfc266e677412b611101d</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="0" meta:word-count="0" meta:character-count="0" meta:non-whitespace-character-count="0"/></office:meta> + <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="Noto Sans Devanagari" svg:font-family="'Noto Sans Devanagari'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif CJK SC'" 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" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans 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" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Noto Sans 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"/> + <style:style style:name="Frame_20_contents" style:display-name="Frame contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra"/> + <style:style style:name="Frame" style:family="graphic"> + <style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" fo:margin-left="0.201cm" fo:margin-right="0.201cm" fo:margin-top="0.201cm" fo:margin-bottom="0.201cm" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph-content" style:horizontal-pos="center" style:horizontal-rel="paragraph-content" fo:padding="0.15cm" fo:border="0.06pt solid #000000"/> + </style:style> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <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="P1" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name="Standard"> + <style:paragraph-properties style:page-number="auto"/> + </style:style> + <style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame"> + <style:graphic-properties style:vertical-pos="top" style:vertical-rel="page-content" style:horizontal-pos="center" style:horizontal-rel="page"/> + </style:style> + <style:style style:name="Sect1" style:family="section"> + <style:section-properties style:editable="false"> + <style:columns fo:column-count="1" fo:column-gap="0cm"/> + </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: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><draw:frame draw:style-name="fr1" draw:name="Frame1" text:anchor-type="page" text:anchor-page-number="1" svg:width="2cm" draw:z-index="0"> + <draw:text-box fo:min-height="0.499cm"> + <text:p text:style-name="Frame_20_contents"/> + </draw:text-box> + </draw:frame> + <text:section text:style-name="Sect1" text:name="TargetSection"> + <text:p text:style-name="P1"/> + </text:section> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx index beb7fc718d80..9e1a8c4c3b50 100644 --- a/sw/qa/extras/uiwriter/uiwriter.cxx +++ b/sw/qa/extras/uiwriter/uiwriter.cxx @@ -749,6 +749,119 @@ void SwUiWriterTest::testThreadedException() CPPUNIT_ASSERT(!bRes); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testPasteTableAtFlyAnchor) +{ + SwDoc * pDoc(createDoc()); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR); + anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint()); + SfxItemSet flySet(pDoc->GetAttrPool(), svl::Items<RES_ANCHOR, RES_ANCHOR>{}); + flySet.Put(anchor); + SwFlyFrameFormat const* pFly = dynamic_cast<SwFlyFrameFormat const*>( + pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true)); + CPPUNIT_ASSERT(pFly != nullptr); + CPPUNIT_ASSERT(pFly->GetFrame() != nullptr); + pWrtShell->SelFlyGrabCursor(); + pWrtShell->GetDrawView()->UnmarkAll(); + CPPUNIT_ASSERT(pWrtShell->GetCurrFlyFrame() != nullptr); + + // insert table in fly + SwInsertTableOptions tableOpt(SwInsertTableFlags::DefaultBorder, 0); + pWrtShell->InsertTable(tableOpt, 2, 2); + + // select table + pWrtShell->SelAll(); + + lcl_dispatchCommand(mxComponent, ".uno:Copy", {}); + + // move cursor back to body + pWrtShell->ClearMark(); + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->GetCurrFlyFrame()); + + lcl_dispatchCommand(mxComponent, ".uno:Paste", {}); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode()); + + pWrtShell->Undo(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode()); + + // the problem was that Redo moved the fly anchor into the first table cell + pWrtShell->Redo(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode()); + + pWrtShell->Undo(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testCopyPastePageBreak) +{ + rtl::Reference<SwTransferable> pTransfer; + { + SwDoc * pDoc(createDoc("pagebreak-source.fodt")); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + pWrtShell->SelAll(); + + pTransfer = new SwTransferable(*pWrtShell); + pTransfer->Copy(); + + //lcl_dispatchCommand(mxComponent, ".uno:Copy", {}); + + mxComponent->dispose(); + mxComponent.clear(); + } + + SwDoc * pDoc(createDoc("pagebreak-target.fodt")); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty<OUString>(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + + //paste not working in this branch? lcl_dispatchCommand(mxComponent, ".uno:Paste", {}); + TransferableDataHelper aHelper(pTransfer.get()); + SwTransferable::Paste(*pWrtShell, aHelper); + + CPPUNIT_ASSERT_EQUAL(2, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty<OUString>(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(size_t(2), pDoc->GetSections().size()); + CPPUNIT_ASSERT_EQUAL(OUString("SourceSection"), pWrtShell->GetCurrSection()->GetSectionName()); + // the problem was that there was a page break now + CPPUNIT_ASSERT_EQUAL(1, getPages()); + + pWrtShell->Undo(); + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty<OUString>(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + CPPUNIT_ASSERT_EQUAL(1, getPages()); + + pWrtShell->Redo(); + CPPUNIT_ASSERT_EQUAL(2, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty<OUString>(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(size_t(2), pDoc->GetSections().size()); + CPPUNIT_ASSERT_EQUAL(OUString("SourceSection"), pWrtShell->GetCurrSection()->GetSectionName()); + CPPUNIT_ASSERT_EQUAL(1, getPages()); + + pWrtShell->Undo(); + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty<OUString>(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + CPPUNIT_ASSERT_EQUAL(1, getPages()); +} + void SwUiWriterTest::testBookmarkCopy() { SwDoc * pDoc(createDoc()); @@ -4622,7 +4735,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf134250) Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); // this would crash in 2 different ways lcl_dispatchCommand(mxComponent, ".uno:Undo", {}); @@ -4638,7 +4751,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf134250) Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); lcl_dispatchCommand(mxComponent, ".uno:Undo", {}); Scheduler::ProcessEventsToIdle(); @@ -4651,7 +4764,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf134250) Scheduler::ProcessEventsToIdle(); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); } void SwUiWriterTest::testDde() diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx index f37f96a8a1fb..f6748fc1200b 100644 --- a/sw/qa/extras/uiwriter/uiwriter2.cxx +++ b/sw/qa/extras/uiwriter/uiwriter2.cxx @@ -1217,7 +1217,9 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf141175) lcl_dispatchCommand(mxComponent, ".uno:Paste", {}); Scheduler::ProcessEventsToIdle(); - CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xIndexAccess->getCount()); + // the paste now takes a different path, where it doesn't insert a neseted + // table but instead merges the source table cells into the target table cells + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount()); CPPUNIT_ASSERT_EQUAL(1, getPages()); } diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index c6a1dae2a0e2..8318cc5d5ba8 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -26,6 +26,7 @@ #include <IDocumentSettingAccess.hxx> #include <UndoManager.hxx> #include <docary.hxx> +#include <pamtyp.hxx> #include <textboxhelper.hxx> #include <dcontact.hxx> #include <grfatr.hxx> @@ -4784,26 +4785,25 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // Move the PaM one node back from the insert position, so that // the position doesn't get moved pCopyPam->SetMark(); - bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); - // If the position was shifted from more than one node, an end node has been skipped - bool bAfterTable = false; - if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > 1) + bool bCanMoveBack = false; + // First check if it will be able to move *to* first copied node. + // Note this doesn't just check IsStartNode() because SwDoc::AppendDoc() + // intentionally sets it to the body start node, perhaps it should just + // call SplitNode instead? + if (!pStt->nNode.GetNode().IsSectionNode() && !pStt->nNode.GetNode().IsTableNode()) { - // First go back to the original place - pCopyPam->GetPoint()->nNode = rPos.nNode; - pCopyPam->GetPoint()->nContent = rPos.nContent; - - bCanMoveBack = false; - bAfterTable = true; + bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); } if( !bCanMoveBack ) { pCopyPam->GetPoint()->nNode--; + pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0); assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0); } SwNodeRange aRg( pStt->nNode, pEnd->nNode ); SwNodeIndex aInsPos( rPos.nNode ); + ::std::optional<SwIndex> oInsContentIndex; const bool bOneNode = pStt->nNode == pEnd->nNode; SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode(); SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode(); @@ -4953,8 +4953,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // We have to set the correct PaM for Undo, if this PaM starts in a textnode, // the undo operation will try to merge this node after removing the table. // If we didn't split a textnode, the PaM should start at the inserted table node - if( rPos.nContent.GetIndex() == pDestTextNd->Len() ) - { // Insertion at the last position of a textnode (empty or not) + if (pDestTextNd->Len() && rPos.nContent.GetIndex() == pDestTextNd->Len()) + { // Insertion at the last position of a textnode ++aInsPos; // The table will be inserted behind the text node } else if( rPos.nContent.GetIndex() ) @@ -4988,27 +4988,18 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo aRg.aEnd--; } } - else if( bCanMoveBack ) - { // Insertion at the first position of a text node. It will not be split, the table - // will be inserted before the text node. - // See below, before the SetInsertRange function of the undo object will be called, - // the CpyPam would be moved to the next content position. This has to be avoided - // We want to be moved to the table node itself thus we have to set bCanMoveBack - // and to manipulate pCopyPam. - bCanMoveBack = false; - pCopyPam->GetPoint()->nNode--; - } + assert(!bCanMoveBack); } pDestTextNd = aInsPos.GetNode().GetTextNode(); if (pEndTextNd) { - SwIndex aDestIdx( rPos.nContent ); + oInsContentIndex.emplace(rPos.nContent); if( !pDestTextNd ) { pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); - aDestIdx.Assign( pDestTextNd, 0 ); + oInsContentIndex->Assign(pDestTextNd, 0); aInsPos--; // if we have to insert an extra text node @@ -5026,7 +5017,7 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo PUSH_NUMRULE_STATE } - pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ), + pEndTextNd->CopyText(pDestTextNd, *oInsContentIndex, SwIndex(pEndTextNd), pEnd->nContent.GetIndex() ); // Also copy all format templates @@ -5080,21 +5071,30 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo bCopyBookmarks = false; } + // init *again* - because CopyWithFlyInFly moved startPos + SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1), + SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode())); // at-char anchors post SplitNode are on index 0 of 2nd node and will // remain there - move them back to the start (end would also work?) // ... also for at-para anchors; here start is preferable because // it's consistent with SplitNode from SwUndoInserts::RedoImpl() - if (pFlysAtInsPos) + if (pFlysAtInsPos + && (bCanMoveBack + || startPos.nNode.GetNode().IsTextNode() + || (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode() + && startPos.nNode.GetNode().IsSectionNode()))) // not into table { - // init *again* - because CopyWithFlyInFly moved startPos - SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1), - SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode())); if (bCanMoveBack) { // pCopyPam is actually 1 before the copy range so move it fwd SwPaM temp(*pCopyPam->GetPoint()); temp.Move(fnMoveForward, GoInContent); startPos = *temp.GetPoint(); } + else if (startPos.nNode.GetNode().IsSectionNode()) + { // probably on top-level start node, so no CheckNodesRange here; + GoNextNds(&startPos.nNode, false); // SwFEShell::Paste() deletes node + startPos.nContent.Assign(startPos.nNode.GetNode().GetContentNode(), 0); + } assert(startPos.nNode.GetNode().IsContentNode()); SwPosition startPosAtPara(startPos); startPosAtPara.nContent.Assign(nullptr, 0); @@ -5155,27 +5155,32 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo } else // incremented in (!pSttTextNd && pDestTextNd) above { - pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0); + // assign also content index in this case, see testSectionAnchorCopyTableAtStart + assert(oInsContentIndex); + assert(oInsContentIndex->GetIdxReg() == aInsPos.GetNode().GetContentNode()); + pCopyPam->GetMark()->nContent = (*oInsContentIndex); } rPos = *pCopyPam->GetMark(); } else *pCopyPam->GetMark() = rPos; - if ( !bAfterTable ) - pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode ); + if (bCanMoveBack) + { + pCopyPam->Move(fnMoveForward, GoInContent); + } else { pCopyPam->GetPoint()->nNode++; // Reset the offset to 0 as it was before the insertion pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0); - // If the next node is a start node, then step back: the start node - // has been copied and needs to be in the selection for the undo + // If the next node is a start node, then step back: SetInsertRange() + // will add 1 in this case, but that is too much... if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode()) pCopyPam->GetPoint()->nNode--; - } + oInsContentIndex.reset(); pCopyPam->Exchange(); // Also copy all bookmarks diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 3082f4551862..8e8587994c88 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1837,7 +1837,7 @@ void SwNodes::CopyNodes( const SwNodeRange& rRange, // If the end of the section is outside the copy range, // the section node will skipped, not copied! // If someone want to change this behaviour, he has to adjust the function - // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it. + // lcl_NonCopyCount() which relies on it. if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) { // copy of the whole section, so create a new SectionNode diff --git a/sw/source/core/edit/edglss.cxx b/sw/source/core/edit/edglss.cxx index bad2bb719a86..49683c682371 100644 --- a/sw/source/core/edit/edglss.cxx +++ b/sw/source/core/edit/edglss.cxx @@ -235,6 +235,15 @@ bool SwEditShell::CopySelToDoc( SwDoc* pInsDoc ) // but we want to copy the table and the start node before // the first cell as well. *aPaM.Start() = SwPosition(*oSelectAll->first); + if (SwSectionNode const* pSection = oSelectAll->first->GetSectionNode()) + { + if (aPaM.End()->nNode.GetIndex() < pSection->EndOfSectionIndex()) + { + // include section end so that section is copied + aPaM.End()->nNode = *oSelectAll->first->GetNodes()[pSection->EndOfSectionIndex() + 1]; + aPaM.End()->nContent.Assign(aPaM.End()->nNode.GetNode().GetContentNode(), 0); + } + } } bRet = GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false ) || bRet; } diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx index ccbe00c35ca6..a39b7479e669 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -1039,9 +1039,24 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) --aIndexBefore; + // copying to the clipboard, the section is inserted + // at the start of the nodes, followed by empty text node + bool const isSourceSection(aCpyPam.Start()->nNode.GetNode().IsSectionNode() + && aCpyPam.End()->nNode.GetIndex() == aCpyPam.Start()->nNode.GetNode().EndOfSectionIndex() + 1 + && aCpyPam.End()->nNode.GetNode().IsTextNode() + && aCpyPam.End()->nNode.GetNode().GetTextNode()->Len() == 0); + pClpDoc->getIDocumentContentOperations().CopyRange( aCpyPam, rInsPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false ); // Note: aCpyPam is invalid now + if (isSourceSection + && aIndexBefore.GetNode().IsStartNode() + && rInsPos.nNode.GetNode().GetTextNode()->Len() == 0) + { // if there is an empty text node at the start, it + // should be *replaced* by the section, so delete it + GetDoc()->getIDocumentContentOperations().DelFullPara(rPaM); + } + ++aIndexBefore; SwPaM aPaM(SwPosition(aIndexBefore), SwPosition(rInsPos.nNode)); diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx index 01561018ec56..8258498b9181 100644 --- a/sw/source/core/undo/untblk.cxx +++ b/sw/source/core/undo/untblk.cxx @@ -358,6 +358,10 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext) rPam.GetPoint()->nNode.GetIndex())); ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(rPam); + bool const isMoveFlyAnchors(!oMvBkwrd // equivalent to bCanMoveBack + || m_pUndoNodeIndex->GetNode().IsTextNode() + || (oMvBkwrd->GetNode().IsStartNode() + && m_pUndoNodeIndex->GetNode().IsSectionNode())); // re-insert content again (first detach m_pUndoNodeIndex!) sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex(); @@ -371,7 +375,7 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext) // at-char anchors post SplitNode are on index 0 of 2nd node and will // remain there - move them back to the start (end would also work?) - if (pFlysAtInsPos) + if (pFlysAtInsPos && isMoveFlyAnchors) { for (SwFrameFormat * pFly : *pFlysAtInsPos) { diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx index a8f91620622c..4b5e16c028e2 100644 --- a/sw/source/core/unocore/unotext.cxx +++ b/sw/source/core/unocore/unotext.cxx @@ -2286,12 +2286,6 @@ SwXText::copyText( // us, even if we have only a single paragraph. m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, /*bCopyAll=*/false, /*bCheckPos=*/true, /*bCopyText=*/false); } - if (!pFirstNode) - { // the node at rPos was split; get rid of the first empty one so - // that the pasted table is first - auto pDelCursor(m_pImpl->m_pDoc->CreateUnoCursor(SwPosition(SwNodeIndex(*GetStartNode(), 1)))); - m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pDelCursor); - } } else { commit fa7cdf1777aa063d3b1cf89a75a66e2ed319c4a9 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Mar 18 20:59:36 2021 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Mar 7 12:57:42 2024 +0100 Related: tdf#133933 sw: fix assertion failure in the SwIndexReg dtor Make sure that by the time we delete a text node, no SwIndexes point to us. Change-Id: I9248a2ebe529e5a0aab0e37bf676e669b9bddb1d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112698 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> (cherry picked from commit e782937228479054336b564a4c34697a12530469) diff --git a/sw/source/core/inc/UndoTable.hxx b/sw/source/core/inc/UndoTable.hxx index 2b51dbdbb111..8815cb0881c7 100644 --- a/sw/source/core/inc/UndoTable.hxx +++ b/sw/source/core/inc/UndoTable.hxx @@ -280,7 +280,7 @@ class SwUndoTableCpyTable : public SwUndo //b6341295: When redlining is active, PrepareRedline has to create the //redlining attributes for the new and the old table cell content static std::unique_ptr<SwUndo> PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox, - const SwPosition& rPos, bool& rJoin, bool bRedo ); + SwPosition& rPos, bool& rJoin, bool bRedo ); public: SwUndoTableCpyTable(const SwDoc* pDoc); diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index 14cc8de03a84..a07d951dc288 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -2517,7 +2517,7 @@ void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) // Otherwise aInsIdx has been moved during the Undo operation if( pEntry->bJoin ) { - SwPaM const& rLastPam = + SwPaM& rLastPam = rContext.GetCursorSupplier().GetCurrentShellCursor(); pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(), pEntry->bJoin, true ); @@ -2632,7 +2632,7 @@ void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex // rJoin is true if Redo() is calling and the content has already been merged std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox, - const SwPosition& rPos, bool& rJoin, bool bRedo ) + SwPosition& rPos, bool& rJoin, bool bRedo ) { std::unique_ptr<SwUndo> pUndo; // b62341295: Redline for copying tables @@ -2656,6 +2656,10 @@ std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const if( !bRedo && rPos.nNode.GetNode().GetTextNode() ) { // Try to merge, if not called by Redo() rJoin = true; + + // Park this somewhere else so nothing points to the to-be-deleted node. + rPos.nContent.Assign(pText, 0); + pText->JoinNext(); } } commit 126dd55667826f5d1268a570f7e2930b1b78fc43 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Thu Jan 5 12:32:09 2023 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Wed Mar 6 20:39:47 2024 +0100 tdf#152710 fixes squashed required by testTdf134250 tdf#152710 sw: invalidate SwUnoCursors properly in DeleteRangeImpl() This crashes with: list.cxx:44: corrupt document structure, bailing out of infinite loop ndtxt.cxx:5437: void SwTextNode::TriggerNodeUpdate(const sw::LegacyModifyHint&): Assertion `dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat)' failed. Because the redline from 7 to 9 is deleted, but then some cursor ends up on node 10 which is invalid as it is an end node. [ 6] 0x60666a0 StartNode , [ 7] 0x61195e0 StartNode , [ 8] 0x61197a8 TextNode "tainment", [ 9] 0x6119670 EndNode , [ 10] 0x6066730 EndNode , The first problem is that DeleteRangeImpl() uses the point node as the target position for PaMCorrAbs(), but in this case the point node will be deleted. PaMCorrAbs() has a check to invalidate SwUnoCursors that would be moved out of their parent sections, but due to the first problem it can't check it, and the second problem is that lcl_FindUnoCursorSection() doesn't work on redline sections, as those have node type SwNormalStartNode. After fixing the invalidation, subsequent access to the SwXTextCursor throws exceptions and importing the file fails. (regression from commit 477e489e71b4a96ff10d9f2d2b802d91dec3e319) Thanks to Dave Gilbert for identifying the problematic DeleteRange() call. Change-Id: I48a373cc122073b82bc47513fdae684f45b0efb8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145077 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit 8e05bdd26f21fc304978ff3b454cf355841ec75f) tdf#152710 sw: call and fix DeleteSection() instead Turns out there's a function to delete a complete nodes array section - and it has the same problem? Why does it move indexes only from startnode + 1? Let's try to fix it to be more consistent. Change-Id: Iedacc10e29c1646c4ccc85e53a479b0351f5cfcc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145078 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit 475e59d29b7a6cc7f058af8ff863b3bb1a2a84a5) diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index 1722a52dc312..c6a1dae2a0e2 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -1972,6 +1972,18 @@ DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos, cons return bRet; } +static auto GetCorrPosition(SwPaM const& rPam) -> SwPosition +{ + // tdf#152710 target position must be on node that survives deletion + // so that PaMCorrAbs can invalidate SwUnoCursors properly + return rPam.GetPoint()->nNode.GetNode().IsContentNode() + ? *rPam.GetPoint() + : rPam.GetMark()->nNode.GetNode().IsContentNode() + ? *rPam.GetMark() + // this would be the result in SwNodes::RemoveNode() + : SwPosition(SwNodeIndex(rPam.End()->nNode.GetNode(), +1)); +} + /// Delete a full Section of the NodeArray. /// The passed Node is located somewhere in the designated Section. void DocumentContentOperationsManager::DeleteSection( SwNode *pNode ) @@ -1989,8 +2001,9 @@ void DocumentContentOperationsManager::DeleteSection( SwNode *pNode ) { // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area - SwNodeIndex aMvStt( aSttIdx, 1 ); - SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true ); + SwPaM const range(aSttIdx, aEndIdx); + SwPosition const pos(GetCorrPosition(range)); + ::PaMCorrAbs(range, pos); } m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 ); @@ -4098,7 +4111,10 @@ bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, SwDeleteFla // Move all cursors out of the deleted range, but first copy the // passed PaM, because it could be a cursor that would be moved! SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); - ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() ); + { + SwPosition const pos(GetCorrPosition(aDelPam)); + ::PaMCorrAbs(aDelPam, pos); + } bool const bSuccess( DeleteRangeImplImpl(aDelPam, flags) ); if (bSuccess) diff --git a/sw/source/core/doc/doccorr.cxx b/sw/source/core/doc/doccorr.cxx index 8e2769b353e5..a2564119ce06 100644 --- a/sw/source/core/doc/doccorr.cxx +++ b/sw/source/core/doc/doccorr.cxx @@ -33,11 +33,14 @@ namespace /// returns NULL if no restrictions apply const SwStartNode* lcl_FindUnoCursorSection( const SwNode& rNode ) { - const SwStartNode* pStartNode = rNode.StartOfSectionNode(); + const SwStartNode* pStartNode = rNode.IsStartNode() ? rNode.GetStartNode() : rNode.StartOfSectionNode(); while( ( pStartNode != nullptr ) && ( pStartNode->StartOfSectionNode() != pStartNode ) && - ( pStartNode->GetStartNodeType() == SwNormalStartNode ) ) + // section node is only start node allowing overlapped delete + pStartNode->IsSectionNode() ) + { pStartNode = pStartNode->StartOfSectionNode(); + } return pStartNode; } diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx index 0a0c75bc9816..b42a28eeacb7 100644 --- a/sw/source/filter/xml/XMLRedlineImportHelper.cxx +++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx @@ -726,9 +726,8 @@ void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo) SAL_WARN("sw.xml", "Recursive change tracking, removing"); // reuse aPaM to remove it from nodes that will be deleted *aPaM.GetPoint() = SwPosition(pRedlineInfo->pContentIndex->GetNode()); - aPaM.SetMark(); - *aPaM.GetMark() = SwPosition(*pRedlineInfo->pContentIndex->GetNode().EndOfSectionNode()); - pDoc->getIDocumentContentOperations().DeleteRange(aPaM); + aPaM.DeleteMark(); + pDoc->getIDocumentContentOperations().DeleteSection(&aPaM.GetPoint()->nNode.GetNode()); } else { commit 67679b3c8a1c550dbcc1f9d80b6b3e77a256ff10 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Mon Jan 18 12:35:12 2021 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Wed Mar 6 20:39:47 2024 +0100 sw: fix ASAN use-after-free in testTdf39721 The problem is that the cursor is positioned somehow on an SwEndNode. Then lcl_CursorOk(pNew) is false and it gets deleted immediately. Change-Id: I1789280a5116da4e5b7739e311b18c5c54074b1f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109541 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sberg...@redhat.com> (cherry picked from commit 43fbe2fbd6a65fa63fa819c6778c3378c2ff6e1f) diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index 3814b3175738..85e9eb837017 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -120,6 +120,10 @@ SwPaM * SwCursorShell::CreateCursor() // don't create new Cursor with active table Selection assert(!IsTableMode()); + // ensure that m_pCurrentCursor is valid; if it's invalid it would be + // copied to pNew and then pNew would be deleted in UpdateCursor() below + ClearUpCursors(); + // New cursor as copy of current one. Add to the ring. // Links point to previously created one, ie forward. SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor ); commit beba56614ddc53d193647c7403be05ff88c50e8c Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Thu Feb 4 12:36:41 2021 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Wed Mar 6 16:06:41 2024 +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> (cherry picked from commit c598e0accf24e149520a22a8c062249f147844c7) diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 66ec563e7a14..241ae3b656be 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 <vcl/idle.hxx> #include "swdllapi.h" #include "swtypes.hxx" @@ -1190,7 +1191,8 @@ public: // Delete Columns/Rows in table. void DelTable(SwTableNode * pTable); - 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 ); @@ -1659,6 +1661,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 985d1171ac4d..a935128bac23 100644 --- a/sw/qa/extras/unowriter/unowriter.cxx +++ b/sw/qa/extras/unowriter/unowriter.cxx @@ -9,6 +9,7 @@ #include <swmodeltestbase.hxx> #include <com/sun/star/awt/FontSlant.hpp> +//#include <com/sun/star/datatransfer/XTransferableTextSupplier.hpp> #include <com/sun/star/table/XCellRange.hpp> #include <com/sun/star/text/TextContentAnchorType.hpp> #include <com/sun/star/text/AutoTextContainer.hpp> @@ -358,6 +359,67 @@ CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXAutoTextGroup) xAutoTextContainer->removeByName(sGroupName); } +#if 0 +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()); +} +#endif + CPPUNIT_TEST_FIXTURE(SwUnoWriter, testXURI) { uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index 379c0a7666be..71874cd24a0d 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -1929,7 +1929,7 @@ 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); } @@ -2093,18 +2093,24 @@ void SwDoc::DelTable(SwTableNode *const pTableNd) getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 ); } -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 ); @@ -2113,7 +2119,7 @@ bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) long nMax = 0; if( rTable.IsNewModel() ) { - if( bColumn ) + if (eMode & SwDoc::RowColMode::DeleteColumn) rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax ); else rTable.FindSuperfluousRows( aSelBoxes ); @@ -2147,7 +2153,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 8589b8eed579..ccbe00c35ca6 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -686,25 +686,19 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) OSL_ENSURE( pClpDoc, "no clipboard document" ); // then till end of the nodes array SwNodeIndex aIdx( pClpDoc->GetNodes().GetEndOfExtras(), 2 ); - SwPaM aCpyPam( aIdx ); //DocStart + // select content section, whatever it may contain + SwPaM aCpyPam(aIdx, SwNodeIndex(pClpDoc->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 1dec2e3102a6..936ababfaea4 100644 --- a/sw/source/core/frmedt/fetab.cxx +++ b/sw/source/core/frmedt/fetab.cxx @@ -296,9 +296,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 62180c0bc230..53c881a8400a 100644 --- a/sw/source/core/unocore/unoobj2.cxx +++ b/sw/source/core/unocore/unoobj2.cxx @@ -790,6 +790,20 @@ void SwXTextRange::SetPositions(const SwPaM& rPam) m_pImpl->SetMark(*pMark); } +#if 0 +static void DeleteTable(SwDoc & rDoc, SwTable& rTable) +{ + SwSelBoxes aSelBoxes; + for (auto& rBox : rTable.GetTabSortBoxes()) + { + aSelBoxes.insert(rBox); + } + // note: if the table is the content in the section, this will create + // a new text node - that's desirable here + rDoc.DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected); +} +#endif + void SwXTextRange::DeleteAndInsert( const OUString& rText, ::sw::DeleteAndInsertMode const eMode) { diff --git a/sw/source/core/unocore/unotbl.cxx b/sw/source/core/unocore/unotbl.cxx index fa378c7e8ae1..f149c3a4ce16 100644 --- a/sw/source/core/unocore/unotbl.cxx +++ b/sw/source/core/unocore/unotbl.cxx @@ -2227,7 +2227,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( commit daeb75e6e3f5a3534665ac2cc15f6bcc12bc9fdb Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Feb 14 19:22:42 2024 +0600 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Mon Mar 4 11:04:22 2024 +0100 tdf#159565: make sure to handle leading hidden section correctly Change-Id: I41c7d2b6e765f03c72a968fd05e8de7047f1ce41 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163371 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163478 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit a0485be018a68c96329a2fd30d59cdf07cb36363) diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 52f82a6b9caf..cfeac8305f82 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -327,7 +327,7 @@ public: void ExtendedSelectAll(bool bFootnotes = true); /// If ExtendedSelectAll() was called and selection didn't change since then. ::std::optional<::std::pair<SwNode const*, ::std::vector<SwTableNode*>>> ExtendedSelectedAll() const; - enum class StartsWith { None, Table, HiddenPara }; + enum class StartsWith { None, Table, HiddenPara, HiddenSection }; /// If document body starts with a table or starts/ends with hidden paragraph. StartsWith StartsWith_(); diff --git a/sw/inc/ndarr.hxx b/sw/inc/ndarr.hxx index 7504a283fdf3..8b635f9f4778 100644 --- a/sw/inc/ndarr.hxx +++ b/sw/inc/ndarr.hxx @@ -133,6 +133,11 @@ class SW_DLLPUBLIC SwNodes final SwNodes( SwDoc* pDoc ); + // Returns start of the document section (PostIts/Inserts/Autotext/Redlines/Content), + // or of a specific fly / header / footer / footnote, where this node is, which must not + // be crossed when moving backwards + sal_uLong StartOfGlobalSection(const SwNode& node) const; + public: ~SwNodes(); @@ -188,7 +193,7 @@ public: static void GoEndOfSection(SwNodeIndex *); SwContentNode* GoNext(SwNodeIndex *) const; - static SwContentNode* GoPrevious(SwNodeIndex *); + static SwContentNode* GoPrevious(SwNodeIndex *, bool canCrossBoundary = false); /** Go to next content-node that is not protected or hidden (Both set FALSE ==> GoNext/GoPrevious!!!). */ diff --git a/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt b/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt new file mode 100644 index 000000000000..2095c7173046 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/FrameInHiddenSection.fodt @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:body> + <office:text> + <text:section text:name="Section1"> + <text:section text:name="Section2Hidden" text:display="none"> + <text:p><draw:frame text:anchor-type="paragraph" svg:x="1cm" svg:y="1cm" svg:width="1cm"> + <draw:text-box/> + </draw:frame>lorem</text:p> + </text:section> + <text:section text:name="Section3"/> + <text:section text:name="Section4"/> + <text:section text:name="Section5"> + <text:p>ipsum</text:p> + </text:section> + </text:section> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx index 23444c5093b3..beb7fc718d80 100644 --- a/sw/qa/extras/uiwriter/uiwriter.cxx +++ b/sw/qa/extras/uiwriter/uiwriter.cxx @@ -94,11 +94,13 @@ #include <com/sun/star/util/XPropertyReplace.hpp> #include <com/sun/star/beans/PropertyAttribute.hpp> #include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> #include <com/sun/star/text/TextMarkupType.hpp> #include <com/sun/star/chart2/data/XDataSource.hpp> #include <com/sun/star/document/XEmbeddedObjectSupplier2.hpp> #include <com/sun/star/drawing/XShape.hpp> #include <com/sun/star/linguistic2/XLinguProperties.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> #include <o3tl/cppunittraitshelper.hxx> #include <o3tl/deleter.hxx> #include <osl/file.hxx> @@ -8205,6 +8207,25 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testHiddenSectionsAroundPageBreak) CPPUNIT_ASSERT_EQUAL(OUString("Landscape"), getProperty<OUString>(xCursor, "PageStyleName")); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf159565) +{ + // Given a document with a hidden section in the beginning, additionally containing a frame + createDoc("FrameInHiddenSection.fodt"); + + lcl_dispatchCommand(mxComponent, u".uno:SelectAll", {}); + + // Check that the selection covers the whole visible text + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<view::XSelectionSupplier> xSelSupplier(xModel->getCurrentController(), uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xSelections(xSelSupplier->getSelection(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelections->getCount()); + uno::Reference<text::XTextRange> xSelection(xSelections->getByIndex(0), uno::UNO_QUERY); + + // Without the fix, this would fail - there was no selection + CPPUNIT_ASSERT_EQUAL(OUString(u"" SAL_NEWLINE_STRING SAL_NEWLINE_STRING "ipsum"), + xSelection->getString()); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SwUiWriterTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index 4eb56af09887..3814b3175738 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -758,6 +758,8 @@ static typename SwCursorShell::StartsWith StartsWith(SwStartNode const& rStart) switch (rNode.GetNodeType()) { case SwNodeType::Section: + if (rNode.GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; continue; case SwNodeType::Table: return SwCursorShell::StartsWith::Table; @@ -782,11 +784,16 @@ static typename SwCursorShell::StartsWith EndsWith(SwStartNode const& rStart) switch (rNode.GetNodeType()) { case SwNodeType::End: - if (rNode.StartOfSectionNode()->IsTableNode()) + if (auto pStartNode = rNode.StartOfSectionNode(); pStartNode->IsTableNode()) { return SwCursorShell::StartsWith::Table; } -//TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode()); + else if (pStartNode->IsSectionNode()) + { + if (pStartNode->GetSectionNode()->GetSection().IsHidden()) + return SwCursorShell::StartsWith::HiddenSection; + } + //TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode()); break; case SwNodeType::Text: if (rNode.GetTextNode()->IsHidden()) @@ -3401,7 +3408,7 @@ bool SwCursorShell::FindValidContentNode( bool bOnlyText ) GetDoc()->GetDocShell()->IsReadOnlyUI() ) return true; - if( m_pCurrentCursor->HasMark() ) + if( m_pCurrentCursor->HasMark() && !mbSelectAll ) ClearMark(); // first check for frames diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx index 985b5ffe75a8..f8b0213a67cc 100644 --- a/sw/source/core/crsr/swcrsr.cxx +++ b/sw/source/core/crsr/swcrsr.cxx @@ -888,7 +888,7 @@ static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd, rPam.SetMark(); rPam.GetPoint()->nNode = rEndNd; - pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode ); + pCNd = SwNodes::GoPrevious(&rPam.GetPoint()->nNode, true); if( !pCNd ) return false; pCNd->MakeEndIndex( &rPam.GetPoint()->nContent ); @@ -908,7 +908,7 @@ static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd, if( !bFirst ) { rPam.GetPoint()->nNode = rSttNd; - pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode ); + pCNd = SwNodes::GoPrevious(&rPam.GetPoint()->nNode, true); if( !pCNd ) return false; pCNd->MakeEndIndex( &rPam.GetPoint()->nContent ); diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index afa730df9a27..3082f4551862 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1295,17 +1295,49 @@ SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx) const return static_cast<SwContentNode*>(pNd); } -SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx) +sal_uLong SwNodes::StartOfGlobalSection(const SwNode& node) const +{ + const sal_uLong pos = node.GetIndex(); + if (GetEndOfExtras().GetIndex() < pos) + // Regular ContentSection + return GetEndOfExtras().GetIndex() + sal_uLong(1); + if (GetEndOfAutotext().GetIndex() < pos) + // Redlines + return GetEndOfAutotext().GetIndex() + sal_uLong(1); + if (GetEndOfInserts().GetIndex() < pos) + { + // Flys/Headers/Footers + if (auto* p = node.FindFlyStartNode()) + return p->GetIndex(); + if (auto* p = node.FindHeaderStartNode()) + return p->GetIndex(); + if (auto* p = node.FindFooterStartNode()) + return p->GetIndex(); + return GetEndOfInserts().GetIndex() + sal_uLong(1); + } + if (GetEndOfPostIts().GetIndex() < pos) + { + // Footnotes + if (auto* p = node.FindFootnoteStartNode()) + return p->GetIndex(); + return GetEndOfPostIts().GetIndex() + sal_uLong(1); + } + return sal_uLong(0); +} + +SwContentNode* SwNodes::GoPrevious(SwNodeIndex* pIdx, bool canCrossBoundary) { if( !pIdx->GetIndex() ) return nullptr; SwNodeIndex aTmp( *pIdx, -1 ); + sal_uLong aGlobalStart( + canCrossBoundary ? sal_uLong(0) : aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); SwNode* pNd = nullptr; - while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() ) + while (aTmp > aGlobalStart && !(pNd = &aTmp.GetNode())->IsContentNode()) --aTmp; - if( !aTmp.GetIndex() ) + if (aTmp <= aGlobalStart) pNd = nullptr; else (*pIdx) = aTmp; @@ -1985,8 +2017,9 @@ SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx, { bool bFirst = true; SwNodeIndex aTmp( *pIdx ); + sal_uLong aGlobalStart(aTmp.GetNodes().StartOfGlobalSection(pIdx->GetNode())); const SwNode* pNd; - while( aTmp > 0 ) + while (aTmp > aGlobalStart) { pNd = & aTmp.GetNode(); if (SwNodeType::End == pNd->GetNodeType()) commit f19d133bf4f07e46eb9ae40865cfc9be46af35af Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Fri Feb 9 11:56:19 2024 +0600 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Mon Mar 4 11:04:22 2024 +0100 tdf#159565 prerequisite: make hidden sections have zero-height frames As mentioned in commit bb733957dd39e6f0b9d80bb59eb0177188794797 (tdf#114973 sw: enable SelectAll with hidden para at start/end, 2023-01-27), the hidden sections didn't have frames. That prevented correct handling of the case when such a frame was in the beginning of the document. This change re-implements the hidden section to use 0-height frames, like hidden paragraphs, as a pre-requisite for a follow-up change. Some layout breakages noticed while working on this are unit-tested now. This change needed to handle the case when the first section is hidden, and then goes a page break with page style. In this case, the page style must apply to the very first page of the document. Implementing this now, when the frame that defines the page style is not the first in the document, I accidentally fixed also the previously broken case when the first paragraph was hidden. Now the page style defined in the second paragraph's page break will apply correctly. This change makes hidden sections break outer section's frames. This means that when text borders are shown, there will be an artifact in the place of the hidden sections (a horizontal line breaking outer frame). I suppose it's not a problem, actually helping to see the layout better, so in line with the "show text borders" helper functionality. If this proves to be problematic, this can be handled specially in a follow-up. [backport: move invalidations from SwSectionFrame::SwClientNotify() to SwSectionFrame::Modify()] Change-Id: I14ebf0559b463186aba28902cd10c5cc978ba456 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163151 Tested-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163477 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> (cherry picked from commit ff7f1b59e22092d8548459e75fe912db852f056f) diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 26e6895e88cd..52f82a6b9caf 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -260,7 +260,7 @@ private: SAL_DLLPRIVATE bool LRMargin( bool, bool bAPI = false ); SAL_DLLPRIVATE bool IsAtLRMargin( bool, bool bAPI = false ) const; - SAL_DLLPRIVATE bool isInHiddenTextFrame(SwShellCursor* pShellCursor); + SAL_DLLPRIVATE bool isInHiddenFrame(SwShellCursor* pShellCursor); SAL_DLLPRIVATE bool GoStartWordImpl(); SAL_DLLPRIVATE bool GoEndWordImpl(); diff --git a/sw/inc/node.hxx b/sw/inc/node.hxx index d12e06d3e950..f4067cd57c4b 100644 --- a/sw/inc/node.hxx +++ b/sw/inc/node.hxx @@ -541,7 +541,7 @@ public: const SwSection& GetSection() const { return *m_pSection; } SwSection& GetSection() { return *m_pSection; } - SwFrame *MakeFrame( SwFrame* ); + SwFrame* MakeFrame(SwFrame* pSib, bool bHidden); /** Creates the frms for the SectionNode (i.e. the SectionFrames). On default the frames are created until the end of the range. diff --git a/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt b/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt new file mode 100644 index 000000000000..fbefc5c48046 --- /dev/null +++ b/sw/qa/extras/layout/data/largeTopMarginAndHiddenFirstSection.fodt @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:styles> + <style:style style:name="Standard" style:family="paragraph" style:class="text"> + <style:paragraph-properties fo:margin-top="1in" fo:margin-bottom="0" style:contextual-spacing="false" fo:line-height="12pt"/> + </style:style> + </office:styles> + <office:body> + <office:text> + <text:section text:name="Hidden" text:display="none"/> + <text:section text:name="Shown"/> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt b/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt new file mode 100644 index 000000000000..5fae6a491704 --- /dev/null +++ b/sw/qa/extras/layout/data/pageBreakInHiddenSection.fodt @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:master-page-name="Landscape"> + <style:paragraph-properties style:page-number="1"/> + </style:style> + <style:style style:name="P2" style:family="paragraph" style:master-page-name="Landscape"> + <style:paragraph-properties style:page-number="1"/> + <style:text-properties text:display="none"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p>First line</text:p> + <text:section text:name="Hidden" text:display="none"> + <text:p text:style-name="P1"/> + </text:section> + <text:section text:name="Shown"> + <text:p>Before break (still first page)</text:p> + </text:section> + <text:section text:name="HiddenText"> + <text:p text:style-name="P2"/> + </text:section> + <text:p>After break</text:p> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> + <text:p/> -e ... etc. - the rest is truncated