diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 05:03:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 05:03:24 +0000 |
commit | e3cf16e6fbf8d39cad8762f002b6db1d4f61ed36 (patch) | |
tree | 3c1753125149dcf36ba42a57f1574369e8524225 /sw | |
parent | Adding debian version 4:24.2.2-3. (diff) | |
download | libreoffice-e3cf16e6fbf8d39cad8762f002b6db1d4f61ed36.tar.xz libreoffice-e3cf16e6fbf8d39cad8762f002b6db1d4f61ed36.zip |
Merging upstream version 4:24.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw')
41 files changed, 538 insertions, 115 deletions
diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index ea95cef856..99cb33c758 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -838,6 +838,7 @@ public: std::vector<SwFrameFormat const*> GetFlyFrameFormats( FlyCntType eType, bool bIgnoreTextBoxes); + SwFrameFormat* GetFlyFrameFormatByName( const OUString& sFrameFormatName ); // Copy formats in own arrays and return them. SwFrameFormat *CopyFrameFormat ( const SwFrameFormat& ); diff --git a/sw/inc/editsh.hxx b/sw/inc/editsh.hxx index 1fb9ff3a5b..2b75353015 100644 --- a/sw/inc/editsh.hxx +++ b/sw/inc/editsh.hxx @@ -152,7 +152,7 @@ class SW_DLLPUBLIC SwEditShell : public SwCursorShell that will be used by GetGraphic() and GetGraphicSize(). */ SAL_DLLPRIVATE SwGrfNode *GetGrfNode_() const ; - SAL_DLLPRIVATE void DeleteSel(SwPaM& rPam, bool isArtificialSelection, bool* pUndo = nullptr); + SAL_DLLPRIVATE void DeleteSel(SwPaM& rPam, bool isArtificialSelection, bool goLeft = false, bool* pUndo = nullptr); SAL_DLLPRIVATE void SetSectionAttr_( SwSectionFormat& rSectFormat, const SfxItemSet& rSet ); @@ -178,7 +178,7 @@ public: /** Delete content of all ranges. If whole nodes are selected, these nodes get deleted. */ - bool Delete(bool isArtificialSelection = false); + bool Delete(bool isArtificialSelection = false, bool goLeft = false); /// Remove a complete paragraph. bool DelFullPara(); diff --git a/sw/inc/usrfld.hxx b/sw/inc/usrfld.hxx index 28d582c4c5..f7371ef52c 100644 --- a/sw/inc/usrfld.hxx +++ b/sw/inc/usrfld.hxx @@ -102,7 +102,7 @@ inline void SwUserFieldType::SetType(sal_uInt16 nSub) * Tracks the number format and the language, conversion between the float and * string representation is independent from the system locale. */ -class SwUserField final : public SwValueField +class SW_DLLPUBLIC SwUserField final : public SwValueField { sal_uInt16 m_nSubType; diff --git a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx index 117b48d016..bfe2e08d85 100644 --- a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx +++ b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx @@ -251,6 +251,17 @@ scanAccessibilityIssuesOnNodes(SwDoc* pDocument) return aIssues; } +CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testCheckTabsinTOC) +{ + createSwDoc("Tabs-in-TOC.odt"); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + sw::AccessibilityCheck aCheck(pDoc); + aCheck.check(); + auto& aIssues = aCheck.getIssueCollection().getIssues(); + CPPUNIT_ASSERT_EQUAL(size_t(0), aIssues.size()); +} + void checkIssuePosition(std::shared_ptr<sfx::AccessibilityIssue> const& pIssue, int nLine, sal_Int32 nStart, sal_Int32 nEnd, SwNodeOffset nIndex) { diff --git a/sw/qa/core/accessibilitycheck/data/Tabs-in-TOC.odt b/sw/qa/core/accessibilitycheck/data/Tabs-in-TOC.odt Binary files differnew file mode 100644 index 0000000000..2b3ce54cc5 --- /dev/null +++ b/sw/qa/core/accessibilitycheck/data/Tabs-in-TOC.odt diff --git a/sw/qa/core/layout/data/floattable-header.docx b/sw/qa/core/layout/data/floattable-header.docx Binary files differnew file mode 100644 index 0000000000..baddd365ce --- /dev/null +++ b/sw/qa/core/layout/data/floattable-header.docx diff --git a/sw/qa/core/layout/data/floattable-in-section.docx b/sw/qa/core/layout/data/floattable-in-section.docx Binary files differindex a0e9090bcc..9aab264867 100644 --- a/sw/qa/core/layout/data/floattable-in-section.docx +++ b/sw/qa/core/layout/data/floattable-in-section.docx diff --git a/sw/qa/core/layout/layact.cxx b/sw/qa/core/layout/layact.cxx index d432ae52b7..8923d6b0e8 100644 --- a/sw/qa/core/layout/layact.cxx +++ b/sw/qa/core/layout/layact.cxx @@ -86,6 +86,28 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf157096) CPPUNIT_ASSERT_EQUAL(1, getPages()); } + +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInSection) +{ + // Given a document with multiple sections, the 2nd section on page 1 has a one-page floating + // table: + createSwDoc("floattable-in-section.docx"); + + // When laying out that document: + SwDoc* pDoc = getSwDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + + // Then make sure the table is on page 1, not on page 2: + auto pPage1 = pLayout->Lower()->DynCastPageFrame(); + CPPUNIT_ASSERT(pPage1); + // Without the fix in place, it would have failed, the table was on page 2, not on page 1. + CPPUNIT_ASSERT(pPage1->GetSortedObjs()); + SwSortedObjs& rPage1Objs = *pPage1->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage1Objs.size()); + auto pPage2 = pPage1->GetNext()->DynCastPageFrame(); + CPPUNIT_ASSERT(pPage2); + CPPUNIT_ASSERT(!pPage2->GetSortedObjs()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/layout/tabfrm.cxx b/sw/qa/core/layout/tabfrm.cxx index e0d099c771..9bfbd1b82e 100644 --- a/sw/qa/core/layout/tabfrm.cxx +++ b/sw/qa/core/layout/tabfrm.cxx @@ -17,6 +17,8 @@ #include <anchoredobject.hxx> #include <flyfrm.hxx> #include <flyfrms.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> namespace { @@ -235,6 +237,25 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWrappedByTableNested) CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pDoc->GetTableFrameFormats()->GetFormatCount()); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDoc->GetSpzFrameFormats()->GetFormatCount()); } + +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyHeader) +{ + // Given a document with 8 pages: a first page ending in a manual page break, then a multi-page + // floating table on pages 2..8: + createSwDoc("floattable-header.docx"); + CPPUNIT_ASSERT_EQUAL(8, getPages()); + + // When creating a new paragraph at doc start: + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->SplitNode(); + // Without the accompanying fix in place, this test would have crashed here. + pWrtShell->CalcLayout(); + + // Then make sure we get one more page, since the first page is now 2 pages: + CPPUNIT_ASSERT_EQUAL(9, getPages()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/htmlexport/data/tdf160390.fodt b/sw/qa/extras/htmlexport/data/tdf160390.fodt new file mode 100644 index 0000000000..53d6144ff1 --- /dev/null +++ b/sw/qa/extras/htmlexport/data/tdf160390.fodt @@ -0,0 +1,17 @@ +<?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:automatic-styles> + <style:style style:name="P1" style:family="paragraph"> + <style:text-properties style:text-underline-style="solid" fo:font-weight="bold"/> + </style:style> + <style:style style:name="T1" style:family="text"> + <style:text-properties style:text-underline-style="none" fo:font-weight="normal"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p text:style-name="P1">foo<text:span text:style-name="T1"> </text:span></text:p> + </office:text> + </office:body> +</office:document>
\ No newline at end of file diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx index de2e9da4c6..42099f3bc4 100644 --- a/sw/qa/extras/htmlexport/htmlexport.cxx +++ b/sw/qa/extras/htmlexport/htmlexport.cxx @@ -3053,6 +3053,13 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder) CPPUNIT_ASSERT(parseXml(maTempFile)); } +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160390) +{ + // This document must not hang infinitely on HTML export + createSwDoc("tdf160390.fodt"); + ExportToHTML(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/layout/data/tdf160526.fodt b/sw/qa/extras/layout/data/tdf160526.fodt new file mode 100644 index 0000000000..37cf73fb8e --- /dev/null +++ b/sw/qa/extras/layout/data/tdf160526.fodt @@ -0,0 +1,47 @@ +<?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" 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:font-face-decls> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" 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:start-line-spacing-horizontal="8pt" draw:start-line-spacing-vertical="8pt" draw:end-line-spacing-horizontal="8pt" draw:end-line-spacing-vertical="8pt" style:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" style:letter-kerning="true"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties style:punctuation-wrap="hanging" style:line-break="strict" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" style:letter-kerning="true" fo:hyphenate="false"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph"> + <style:paragraph-properties fo:text-align="center"/> + </style:style> + <style:style style:name="gr1" style:family="graphic"> + <style:graphic-properties draw:fill-color="#81d41a" draw:textarea-horizontal-align="justify" draw:textarea-vertical-align="middle" draw:auto-grow-height="false" fo:min-height="3cm" fo:min-width="12cm" style:run-through="foreground" style:wrap="parallel" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:vertical-rel="paragraph" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" draw:wrap-influence-on-position="once-concurrent" style:flow-with-text="false"/> + <style:paragraph-properties style:writing-mode="lr-tb"/> + </style:style> + <style:style style:name="gr2" style:family="graphic"> + <style:graphic-properties draw:textarea-horizontal-align="justify" draw:textarea-vertical-align="middle" draw:auto-grow-height="false" fo:min-height="25cm" fo:min-width="7cm" style:run-through="foreground" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="top" style:vertical-rel="baseline" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" draw:wrap-influence-on-position="once-concurrent" style:flow-with-text="false"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="210mm" fo:page-height="297mm" 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="20pt" style:layout-grid-ruby-height="10pt" 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="0"/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"/> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </office:master-styles> + <office:body> + <office:text> + <text:p><draw:custom-shape text:anchor-type="paragraph" draw:z-index="1" draw:name="Shape 1" draw:style-name="gr1" svg:width="12cm" svg:height="3cm" svg:x="4cm" svg:y="0cm"> + <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:type="rectangle" draw:enhanced-path="M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N"/> + </draw:custom-shape>Foo</text:p> + <text:p><draw:custom-shape text:anchor-type="as-char" draw:z-index="0" draw:name="Shape 2" draw:style-name="gr2" svg:width="7cm" svg:height="25cm"> + <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:type="rectangle" draw:enhanced-path="M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N"/> + </draw:custom-shape></text:p> + </office:text> + </office:body> +</office:document>
\ No newline at end of file diff --git a/sw/qa/extras/layout/data/tdf160549.fodt b/sw/qa/extras/layout/data/tdf160549.fodt new file mode 100644 index 0000000000..fd8425eedd --- /dev/null +++ b/sw/qa/extras/layout/data/tdf160549.fodt @@ -0,0 +1,60 @@ +<?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" 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:font-face-decls> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" 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:start-line-spacing-horizontal="8pt" draw:start-line-spacing-vertical="8pt" draw:end-line-spacing-horizontal="8pt" draw:end-line-spacing-vertical="8pt" 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:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt" style:letter-kerning="true"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="36pt" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" style:font-name="Liberation Serif" fo:font-size="12pt"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <style:style style:name="Graphics" style:family="graphic"> + <style:graphic-properties text:anchor-type="paragraph" svg:x="0" svg:y="0" style:wrap="dynamic" style:number-wrapped-paragraphs="no-limit" style:wrap-contour="false" style:vertical-pos="top" style:vertical-rel="paragraph" style:horizontal-pos="center" style:horizontal-rel="paragraph" draw:fill="none"/> + </style:style> + <style:style style:name="Frame" style:family="graphic"> + <style:graphic-properties text:anchor-type="paragraph" svg:x="0" svg:y="0" fo:margin-left="2mm" fo:margin-right="2mm" fo:margin-top="2mm" fo:margin-bottom="2mm" 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" draw:fill="none" fo:padding="1.5mm" fo:border="0.06pt solid #000000"/> + </style:style> + <style:default-page-layout> + <style:page-layout-properties style:layout-grid-standard-mode="true"/> + </style:default-page-layout> + </office:styles> + <office:automatic-styles> + <style:style style:name="fr1" style:family="graphic" style:parent-style-name="Frame"> + <style:graphic-properties fo:margin-left="0" fo:margin-right="0" fo:margin-top="0" fo:margin-bottom="0" style:wrap="none" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:vertical-rel="paragraph" style:horizontal-pos="right" style:horizontal-rel="page-content" fo:background-color="#ffffff" style:background-transparency="100%" draw:fill="solid" draw:fill-color="#ffffff" draw:opacity="0%" fo:padding="0pt" fo:border="none" style:writing-mode="lr-tb" draw:wrap-influence-on-position="once-successive"/> + </style:style> + <style:style style:name="fr2" style:family="graphic" style:parent-style-name="Graphics"> + <style:graphic-properties fo:margin-left="0" fo:margin-right="0" fo:margin-top="0" fo:margin-bottom="0" style:vertical-pos="top" style:vertical-rel="baseline" fo:border="none" style:mirror="none" fo:clip="rect(0pt, 0pt, 0pt, 0pt)" draw:luminance="0%" draw:contrast="0%" draw:red="0%" draw:green="0%" draw:blue="0%" draw:gamma="100%" draw:color-inversion="false" draw:image-opacity="100%" draw:color-mode="standard"/> + </style:style> + <style:page-layout style:name="pm1" style:page-usage="mirrored"> + <style:page-layout-properties fo:page-width="148mm" fo:page-height="210mm" style:num-format="1" style:print-orientation="landscape" fo:margin-top="15mm" fo:margin-bottom="15mm" fo:margin-left="15mm" fo:margin-right="15mm" style:writing-mode="lr-tb"/> + <style:header-style> + <style:header-footer-properties fo:min-height="5mm" fo:margin-bottom="1mm" style:dynamic-spacing="true"/> + </style:header-style> + </style:page-layout> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1"> + <style:header> + <text:p><draw:frame draw:style-name="fr1" draw:name="Frame1" text:anchor-type="paragraph" svg:y="1mm" draw:z-index="0"> + <draw:text-box fo:min-height="5mm" fo:min-width="4cm"> + <text:p>foobar</text:p> + </draw:text-box> + </draw:frame></text:p> + </style:header> + </style:master-page> + </office:master-styles> + <office:body> + <office:text> + <text:p><draw:custom-shape draw:style-name="fr2" draw:name="Image2" text:anchor-type="as-char" svg:width="8cm" svg:height="19cm" draw:z-index="1"> + <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:type="rectangle" draw:enhanced-path="M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N"/> + </draw:custom-shape><text:s/></text:p> + </office:text> + </office:body> +</office:document>
\ No newline at end of file diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index a074a35dc6..98c77b18ec 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -2353,6 +2353,27 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testPageBreakInHiddenSection) assertXPath(pXmlDoc, "//page[4]/body/section/infos/bounds"_ostr, "height"_ostr, u"0"_ustr); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf160549) +{ + // Given a document with a large as-char object, alone in its paragraph, shifted down by a + // header object: it must not hang in a layout loop on import (similar to i84870, but not + // fixed by its fix) + createSwDoc("tdf160549.fodt"); + // The object is the first in the document; it must not move to the next page + CPPUNIT_ASSERT_EQUAL(1, getPages()); +} + +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf160526) +{ + // Given a document with a large as-char object, alone in its paragraph, shifted down by + // another body object + createSwDoc("tdf160526.fodt"); + // It must move to the next page + CPPUNIT_ASSERT_EQUAL(2, getPages()); + auto pExportDump = parseLayoutDump(); + assertXPath(pExportDump, "//page[2]/body/txt/anchored/SwAnchoredDrawObject"_ostr); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/odfexport/data/tdf160700.odt b/sw/qa/extras/odfexport/data/tdf160700.odt Binary files differnew file mode 100644 index 0000000000..bc1515da3f --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf160700.odt diff --git a/sw/qa/extras/odfexport/odfexport2.cxx b/sw/qa/extras/odfexport/odfexport2.cxx index 4983608290..f654821acf 100644 --- a/sw/qa/extras/odfexport/odfexport2.cxx +++ b/sw/qa/extras/odfexport/odfexport2.cxx @@ -21,6 +21,7 @@ #include <com/sun/star/text/XDocumentIndex.hpp> #include <com/sun/star/text/XDocumentIndexesSupplier.hpp> #include <com/sun/star/text/XTextColumns.hpp> +#include <com/sun/star/text/XTextField.hpp> #include <com/sun/star/text/XTextFieldsSupplier.hpp> #include <com/sun/star/text/XTextTable.hpp> #include <com/sun/star/text/XTextTablesSupplier.hpp> @@ -1343,6 +1344,35 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf159438) u"bookmark3"_ustr); } +CPPUNIT_TEST_FIXTURE(Test, testTdf160700) +{ + // Given a document with an empty numbered paragraph, and a cross-reference to it + loadAndReload("tdf160700.odt"); + + // Refresh fields and ensure cross-reference to numbered para is okay + auto xTextFieldsSupplier(mxComponent.queryThrow<text::XTextFieldsSupplier>()); + auto xFieldsAccess(xTextFieldsSupplier->getTextFields()); + + xFieldsAccess.queryThrow<util::XRefreshable>()->refresh(); + + auto xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); + auto xTextField(xFields->nextElement().queryThrow<text::XTextField>()); + // Save must not create markup with text:bookmark-end element before text:bookmark-start + // Withoud the fix, this would fail with + // - Expected: 1 + // - Actual : Error: Reference source not found + // i.e., the bookmark wasn't imported, and the field had no proper source + CPPUNIT_ASSERT_EQUAL(u"1"_ustr, xTextField->getPresentation(false)); + + xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + // Check that we export the bookmark in the empty paragraph as a single text:bookmark + // element. Another walid markup is text:bookmark-start followed by text:bookmark-end + // (in that order). The problem was, that text:bookmark-end was before text:bookmark-start. + assertXPathChildren(pXmlDoc, "//office:text/text:list/text:list-item/text:p"_ostr, 1); + assertXPath(pXmlDoc, "//office:text/text:list/text:list-item/text:p/text:bookmark"_ostr); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/uiwriter/uiwriter5.cxx b/sw/qa/extras/uiwriter/uiwriter5.cxx index c6353f980d..702f6d7dd3 100644 --- a/sw/qa/extras/uiwriter/uiwriter5.cxx +++ b/sw/qa/extras/uiwriter/uiwriter5.cxx @@ -3007,7 +3007,6 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf156487) assertXPath(pXmlDoc, "/metafile/push/push/push/textarray/text"_ostr, 1); } -#ifndef DBG_UTIL CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf149498) { // load a table, and delete the first column with enabled change tracking: @@ -3023,7 +3022,6 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf149498) // this would crash due to bookmark over cell boundary dispatchCommand(mxComponent, ".uno:Undo", {}); } -#endif CPPUNIT_TEST_FIXTURE(SwUiWriterTest5, testTdf150673_RedlineTableColumnDeletionWithExport) { diff --git a/sw/qa/extras/unowriter/unowriter.cxx b/sw/qa/extras/unowriter/unowriter.cxx index 8bcadbaf42..80b9e556f7 100644 --- a/sw/qa/extras/unowriter/unowriter.cxx +++ b/sw/qa/extras/unowriter/unowriter.cxx @@ -1201,6 +1201,27 @@ CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf129841) CPPUNIT_ASSERT_EQUAL(aRefColor, aColor); } +CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf160278) +{ + createSwDoc(); + auto xTextDocument(mxComponent.queryThrow<css::text::XTextDocument>()); + auto xText(xTextDocument->getText()); + xText->setString(u"123"_ustr); + CPPUNIT_ASSERT_EQUAL(u"123"_ustr, xText->getString()); + auto xCursor = xText->createTextCursorByRange(xText->getEnd()); + xCursor->goLeft(1, true); + CPPUNIT_ASSERT_EQUAL(u"3"_ustr, xCursor->getString()); + // Insert an SMP character U+1f702 (so it's two UTF-16 code units, 0xd83d 0xdf02): + xCursor->setString(u"🜂"_ustr); + // Without the fix, the replacement would expand the cursor one too many characters to the left, + // and the cursor text would become "2🜂", failing the next test: + CPPUNIT_ASSERT_EQUAL(u"🜂"_ustr, xCursor->getString()); + xCursor->setString(u"test"_ustr); + CPPUNIT_ASSERT_EQUAL(u"test"_ustr, xCursor->getString()); + // This test would fail, too; the text would be "1test": + CPPUNIT_ASSERT_EQUAL(u"12test"_ustr, xText->getString()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/uitest/writer_tests2/formatParagraph.py b/sw/qa/uitest/writer_tests2/formatParagraph.py index e90d0fd7f9..bfa6eff48d 100644 --- a/sw/qa/uitest/writer_tests2/formatParagraph.py +++ b/sw/qa/uitest/writer_tests2/formatParagraph.py @@ -177,7 +177,7 @@ class formatParagraph(UITestCase): self.assertEqual(get_state_as_dict(xspinWidow)["Text"], "2") self.assertEqual(get_state_as_dict(xcheckWidow)["Selected"], "false") self.assertEqual(get_state_as_dict(xcheckOrphan)["Selected"], "false") - self.assertEqual(get_state_as_dict(xcheckSplitPara)["Selected"], "true") + self.assertEqual(get_state_as_dict(xcheckSplitPara)["Selected"], "false") self.assertEqual(get_state_as_dict(xcheckKeepPara)["Selected"], "true") diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx index e515b5cae7..dfa94c3ed9 100644 --- a/sw/source/core/access/AccessibilityCheck.cxx +++ b/sw/source/core/access/AccessibilityCheck.cxx @@ -901,6 +901,11 @@ public: } case '\t': { + // Don't warn about tabs in ToC + auto pSection = SwDoc::GetCurrSection(SwPosition(*pTextNode, 0)); + if (pSection && pSection->GetTOXBase()) + continue; + if (bPreviousWasChar) { ++nTabCount; diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx index c492212487..2038941230 100644 --- a/sw/source/core/doc/docfly.cxx +++ b/sw/source/core/doc/docfly.cxx @@ -149,6 +149,25 @@ SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreText return pRetFormat; } +SwFrameFormat* SwDoc::GetFlyFrameFormatByName( const OUString& rFrameFormatName ) +{ + auto pFrameFormats = GetSpzFrameFormats(); + auto it = pFrameFormats->findByTypeAndName( RES_FLYFRMFMT, rFrameFormatName ); + auto endIt = pFrameFormats->typeAndNameEnd(); + for ( ; it != endIt; ++it) + { + sw::SpzFrameFormat* pFlyFormat = *it; + const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx(); + if( !pIdx || !pIdx->GetNodes().IsDocNodes() ) + continue; + + const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ]; + if( !pNd->IsNoTextNode()) + return pFlyFormat; + } + return nullptr; +} + std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats( FlyCntType const eType, bool const bIgnoreTextBoxes) { diff --git a/sw/source/core/edit/eddel.cxx b/sw/source/core/edit/eddel.cxx index 9eb51da617..b8d6b0e395 100644 --- a/sw/source/core/edit/eddel.cxx +++ b/sw/source/core/edit/eddel.cxx @@ -33,7 +33,8 @@ #include <strings.hrc> #include <vector> -void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, bool *const pUndo) +void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, bool goLeft, + bool* const pUndo) { auto const oSelectAll(StartsWith_() != SwCursorShell::StartsWith::None ? ExtendedSelectedAll() @@ -127,11 +128,12 @@ void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, bool } } + rPam.Normalize(goLeft); // change tracking case: will make sure to end up in the correct point // Selection is not needed anymore rPam.DeleteMark(); } -bool SwEditShell::Delete(bool const isArtificialSelection) +bool SwEditShell::Delete(bool const isArtificialSelection, bool goLeft) { CurrShell aCurr( this ); bool bRet = false; @@ -159,7 +161,7 @@ bool SwEditShell::Delete(bool const isArtificialSelection) for(SwPaM& rPaM : GetCursor()->GetRingContainer()) { - DeleteSel(rPaM, isArtificialSelection, &bUndo); + DeleteSel(rPaM, isArtificialSelection, goLeft, &bUndo); } // If undo container then close here diff --git a/sw/source/core/fields/expfld.cxx b/sw/source/core/fields/expfld.cxx index 6ed4cdb7c1..47c675cc42 100644 --- a/sw/source/core/fields/expfld.cxx +++ b/sw/source/core/fields/expfld.cxx @@ -233,7 +233,7 @@ const SwTextNode* GetBodyTextNode( const SwDoc& rDoc, SwPosition& rPos, else pContentFrame = pPgFrame->FindLastBodyContent(); - if( pContentFrame ) + if( pContentFrame && !pContentFrame->IsInFootnote() ) { assert(pContentFrame->IsTextFrame()); SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pContentFrame)); diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index a705ef2511..1a0a1260a1 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -809,6 +809,12 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext) unlockPositionOfObjects( pPg ); pPg = static_cast<SwPageFrame*>(pPg->GetNext()); } + if (m_pRoot->IsSuperfluous()) // could be newly set now! + { + bool bOld = IsAgain(); + m_pRoot->RemoveSuperfluous(); + SetAgain(bOld); + } // reset flag for special interrupt content formatting. mbFormatContentOnInterrupt = false; } @@ -1433,7 +1439,11 @@ bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLa PopFormatLayout(); } } - // else: don't calc content frames any more + else if (pLay->IsSctFrame() && pLow->IsTextFrame() && pLow == pLay->GetLastLower()) + { + // else: only calc the last text lower of sections + pLow->OptCalc(); + } if ( IsAgain() ) return false; diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx index 0f048c4e80..5472695e97 100644 --- a/sw/source/core/layout/paintfrm.cxx +++ b/sw/source/core/layout/paintfrm.cxx @@ -7012,7 +7012,8 @@ void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage, const SwRect &rRect ) const { - const bool bSubsOpt = isSubsidiaryLinesEnabled() || isSubsidiaryLinesForSectionsEnabled(); + const bool bSubsOpt + = isSubsidiaryLinesEnabled() || (IsSctFrame() && isSubsidiaryLinesForSectionsEnabled()); if ( bSubsOpt ) PaintSubsidiaryLines( pPage, rRect ); diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index c4a742c037..adffbba177 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1165,7 +1165,13 @@ bool SwTabFrame::Split(const SwTwips nCutPos, bool bTryToSplit, OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" ); if ( !IsInSct() ) { - m_pTable->SetRowsToRepeat(0); + // This would mean the layout modifies the doc model, so RowsToRepeat drops to 0 while + // there are existing row frames with RepeatedHeadline == true. Avoid this at least + // inside split flys, it would lead to a crash in SwTabFrame::MakeAll(). + if (!pFly || !pFly->IsFlySplitAllowed()) + { + m_pTable->SetRowsToRepeat(0); + } return false; } else @@ -1517,7 +1523,8 @@ namespace } } } - if (rTab.IsCollapsingBorders() && !rCell.Lower()->IsRowFrame()) + assert(rCell.Lower()); + if (rTab.IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame()) { if (rRow.GetTopMarginForLowers() != 0 || rRow.GetBottomMarginForLowers() != 0) @@ -6371,6 +6378,10 @@ void SwTabFrame::dumpAsXml(xmlTextWriterPtr writer) const { (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("tab")); SwFrame::dumpAsXmlAttributes( writer ); + + (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("has-follow-flow-line"), + BAD_CAST(OString::boolean(m_bHasFollowFlowLine).getStr())); + if ( HasFollow() ) (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() ); diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx index e19b834a56..97eff3e607 100644 --- a/sw/source/core/text/frmform.cxx +++ b/sw/source/core/text/frmform.cxx @@ -1081,6 +1081,54 @@ void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew ) MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) ); } +static bool isFirstVisibleFrameInBody(const SwTextFrame* pFrame) +{ + const SwFrame* pBodyFrame = pFrame->FindBodyFrame(); + if (!pBodyFrame) + return false; + for (const SwFrame* pCur = pFrame;;) + { + for (const SwFrame* pPrev = pCur->GetPrev(); pPrev; pPrev = pPrev->GetPrev()) + if (!pPrev->IsHiddenNow()) + return false; + pCur = pCur->GetUpper(); + assert(pCur); // We found pBodyFrame, right? + if (pCur->IsBodyFrame()) + return true; + } +} + +static bool hasFly(const SwTextFrame* pFrame) +{ + if (auto pDrawObjs = pFrame->GetDrawObjs(); pDrawObjs && pDrawObjs->size()) + { + auto anchorId = (*pDrawObjs)[0]->GetFrameFormat()->GetAnchor().GetAnchorId(); + if (anchorId == RndStdIds::FLY_AT_PARA || anchorId == RndStdIds::FLY_AT_CHAR) + return true; + } + return false; +} + +static bool hasAtPageFly(const SwFrame* pFrame) +{ + auto pPageFrame = pFrame->FindPageFrame(); + if (!pPageFrame) + return false; + auto pPageDrawObjs = pPageFrame->GetDrawObjs(); + if (pPageDrawObjs) + { + for (const auto pObject : *pPageDrawObjs) + if (pObject->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) + return true; + } + return false; +} + +static bool isReallyEmptyMaster(const SwTextFrame* pFrame) +{ + return pFrame->IsEmptyMaster() && (!pFrame->GetDrawObjs() || !pFrame->GetDrawObjs()->size()); +} + void SwTextFrame::FormatAdjust( SwTextFormatter &rLine, WidowsAndOrphans &rFrameBreak, TextFrameIndex const nStrLen, @@ -1108,27 +1156,55 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine, ? 1 : 0; SwTextFormatInfo& rInf = rLine.GetInfo(); + bool bEmptyWithSplitFly = false; if (nNew == 0 && !nStrLen && !rInf.GetTextFly().IsOn() && IsEmptyWithSplitFly()) { // Empty paragraph, so IsBreakNow() is not called, but we should split the fly portion and // the paragraph marker. nNew = 1; + bEmptyWithSplitFly = true; } + const SwFrame *pBodyFrame = FindBodyFrame(); + // i#84870 // no split of text frame, which only contains an as-character anchored object - bool bOnlyContainsAsCharAnchoredObj = + bool bLoneAsCharAnchoredObj = + pBodyFrame && !IsFollow() && nStrLen == TextFrameIndex(1) && GetDrawObjs() && GetDrawObjs()->size() == 1 && (*GetDrawObjs())[0]->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR; - // Still try split text frame if we have columns. - if (FindColFrame()) - bOnlyContainsAsCharAnchoredObj = false; - - if ( nNew && bOnlyContainsAsCharAnchoredObj ) + if (bLoneAsCharAnchoredObj) { - nNew = 0; + // Still try split text frame if we have columns. + if (FindColFrame()) + bLoneAsCharAnchoredObj = false; + // tdf#160526: only no split if there is no preceding frames on same page + else if (!isFirstVisibleFrameInBody(this)) + bLoneAsCharAnchoredObj = false; + else + nNew = 0; + } + else if (nNew) + { + if (IsFollow()) + { + // tdf#160549: do not split the frame at the very beginning again, if its master was empty + auto precede = static_cast<SwTextFrame*>(GetPrecede()); + assert(precede); + auto precedeText = precede->DynCastTextFrame(); + assert(precedeText); + if (isReallyEmptyMaster(precedeText)) + nNew = 0; + } + else if (!bEmptyWithSplitFly) + { + // Do not split immediately in the beginning of page (unless there is an at-para or + // at-char or at-page fly, which pushes the rest down) + if (isFirstVisibleFrameInBody(this) && !hasFly(this) && !hasAtPageFly(pBodyFrame)) + nNew = 0; + } } if ( nNew ) @@ -1136,8 +1212,6 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine, SplitFrame( nEnd ); } - const SwFrame *pBodyFrame = FindBodyFrame(); - const tools::Long nBodyHeight = pBodyFrame ? ( IsVertical() ? pBodyFrame->getFrameArea().Width() : pBodyFrame->getFrameArea().Height() ) : 0; @@ -1239,9 +1313,10 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine, // content or contains no content, but has a numbering. // i#84870 - No split, if text frame only contains one // as-character anchored object. - if ( !bOnlyContainsAsCharAnchoredObj && - (nStrLen > TextFrameIndex(0) || - bHasVisibleNumRule ) + if (!bLoneAsCharAnchoredObj + && (bHasVisibleNumRule + || (nStrLen > TextFrameIndex(0) + && (nEnd != rLine.GetStart() || rInf.GetRest()))) ) { SplitFrame( nEnd ); @@ -1265,7 +1340,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine, SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight; //#i84870# - no shrink of text frame, if it only contains one as-character anchored object. - if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj ) + if (nChg < 0 && !bDelta && bLoneAsCharAnchoredObj) { nChg = 0; } diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index 72f1c809e2..52157df0ca 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -2599,11 +2599,17 @@ void SwUndoTableCpyTable::AddBoxBefore( const SwTableBox& rBox, bool bDelContent if( bDelContent ) { SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 ); - pDoc->GetNodes().MakeTextNode( aInsIdx.GetNode(), pDoc->GetDfltTextFormatColl() ); + SwTextNode *const pNewNode(pDoc->GetNodes().MakeTextNode(aInsIdx.GetNode(), pDoc->GetDfltTextFormatColl())); SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() ); if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + { // move cursors to new node which precedes aPam + SwPosition const pos(*pNewNode, 0); + ::PaMCorrAbs(aPam, pos); + } pEntry->pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true); + } } pEntry->pBoxNumAttr = std::make_unique<SfxItemSetFixed< @@ -2627,6 +2633,13 @@ void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); DEBUG_REDLINE( pDoc ) + { // move cursors to first node which was inserted + SwPaM pam(SwNodeIndex(*rBox.GetSttNd(), 1)); + assert(pam.GetPoint()->GetNode().IsTextNode()); + pam.SetMark(); + pam.Move(fnMoveForward, GoInContent); + ::PaMCorrAbs(pam, *pam.GetPoint()); + } if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) { SwPosition aTmpPos( rIdx ); diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx index 7880a749b9..62c4c76ac3 100644 --- a/sw/source/core/unocore/unoframe.cxx +++ b/sw/source/core/unocore/unoframe.cxx @@ -1713,18 +1713,7 @@ void SwXFrame::setPropertyValue(const OUString& rPropertyName, const ::uno::Any& } else { - const size_t nCount = pDoc->GetFlyCount(FLYCNTTYPE_FRM); - - SwFrameFormat* pChain = nullptr; - for( size_t i = 0; i < nCount; ++i ) - { - SwFrameFormat* pFormat2 = pDoc->GetFlyNum(i, FLYCNTTYPE_FRM); - if(sChainName == pFormat2->GetName() ) - { - pChain = pFormat2; - break; - } - } + SwFrameFormat* pChain = pDoc->GetFlyFrameFormatByName(sChainName); if(pChain) { SwFrameFormat* pSource = bNextFrame ? pFormat : pChain; diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx index 49562c1d02..df02c4773a 100644 --- a/sw/source/core/unocore/unoobj.cxx +++ b/sw/source/core/unocore/unoobj.cxx @@ -752,13 +752,19 @@ void SwXTextCursor::DeleteAndInsert(std::u16string_view aText, } if(nTextLen) { + // Store node and content indexes prior to insertion: to select the inserted text, + // we need to account for possible surrogate pairs, combining characters, etc.; it + // is easier to just restore the correct position from the indexes. + const auto start = pCurrent->Start(); + const auto nodeIndex = start->GetNodeIndex(); + const auto contentIndex = start->GetContentIndex(); const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR( - rDoc, *pCurrent, aText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints))); + rDoc, SwPaM(*start, pCurrent), aText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints))); OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." ); - SwUnoCursorHelper::SelectPam(*pUnoCursor, true); - pCurrent->Left(aText.size()); + pCurrent->SetMark(); + pCurrent->GetPoint()->Assign(nodeIndex, contentIndex); } pCurrent = pCurrent->GetNext(); } while (pCurrent != pUnoCursor); diff --git a/sw/source/core/unocore/unoportenum.cxx b/sw/source/core/unocore/unoportenum.cxx index 494cec7468..709d79ef4d 100644 --- a/sw/source/core/unocore/unoportenum.cxx +++ b/sw/source/core/unocore/unoportenum.cxx @@ -150,8 +150,11 @@ namespace bool const hasOther = isExpanded && rStartPos != rEndPos; bool const bStartPosInNode = rStartPos.GetNode() == rOwnNode; bool const bEndPosInNode = rEndPos.GetNode() == rOwnNode; + // tdf#160700: Crossrefbookmarks only need separate start and end, when the start + // isn't in the end position (so in empty nodes, no need to handle them specially) sw::mark::CrossRefBookmark* const pCrossRefMark = !isExpanded && bStartPosInNode + && rStartPos.GetContentIndex() < rStartPos.GetContentNode()->Len() ? dynamic_cast<sw::mark::CrossRefBookmark*>(pBkmk) : nullptr; diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index c880082018..f298f93dc5 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -1524,8 +1524,9 @@ void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, for (auto it = items.begin(); it != items.end();) { - HTMLStartEndPos* pTest = *it; - sal_Int32 nTestEnd = pTest->GetEnd(); + auto itTest = it++; // forward early, allow 'continue', and keep a copy for 'erase' + HTMLStartEndPos* pTest = *itTest; + const sal_Int32 nTestEnd = pTest->GetEnd(); if (nTestEnd <= nStart) continue; @@ -1533,28 +1534,25 @@ void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, const SfxPoolItem& rTestItem = pTest->GetItem(); // only the corresponding OnTag attributes have to be considered - if (rTestItem.Which() == nWhich && HTML_ON_VALUE == GetHTMLItemState(rTestItem)) - { - // if necessary, insert the second part of the split - // attribute - if (nTestEnd > nEnd) - InsertItem(pTest->GetItem(), nEnd, nTestEnd); + if (rTestItem.Which() != nWhich || HTML_ON_VALUE != GetHTMLItemState(rTestItem)) + continue; - if (nTestStart >= nStart) - { - // the Test item only starts after the new end of the - // attribute. Therefore, it can be completely erased. - it = items.erase(it); - std::erase(m_aEndLst[pTest->GetEnd()], pTest); - delete pTest; - continue; - } + // if necessary, insert the second part of the split attribute + if (nTestEnd > nEnd) + InsertItem(rTestItem, nEnd, nTestEnd); - // the start of the new attribute corresponds to the new - // end of the attribute - FixSplittedItem(pTest, nStart); + if (nTestStart >= nStart) + { + // the Test item only starts after the new end of the + // attribute. Therefore, it can be completely erased. + it = items.erase(itTest); + std::erase(m_aEndLst[nTestEnd], pTest); + delete pTest; + continue; } - ++it; + + // the start of the new attribute corresponds to the new end of the attribute + FixSplittedItem(pTest, nStart); } } } diff --git a/sw/source/filter/html/htmlctxt.cxx b/sw/source/filter/html/htmlctxt.cxx index 80245ba2ea..0e87b83644 100644 --- a/sw/source/filter/html/htmlctxt.cxx +++ b/sw/source/filter/html/htmlctxt.cxx @@ -656,7 +656,8 @@ void SwHTMLParser::InsertAttrs( SfxItemSet &rItemSet, } #endif - for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + SfxItemIter aIter2(rItemSet); + for (const SfxPoolItem* pItem = aIter2.GetCurItem(); pItem; pItem = aIter2.NextItem()) { HTMLAttr **ppAttr = nullptr; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 68437c9529..e33e8708f4 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -2658,33 +2658,36 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) assert(pFieldmark); - if (pFieldmark->GetFieldname() == ODF_FORMDATE) + if (pFieldmark) { - if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only + if (pFieldmark->GetFieldname() == ODF_FORMDATE) { - OutputField( nullptr, ww::eFORMDATE, OUString(), FieldFlags::Close ); + if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only + { + OutputField( nullptr, ww::eFORMDATE, OUString(), FieldFlags::Close ); + } } - } - else - { - ww::eField eFieldId = lcl_getFieldId( pFieldmark ); - if (pFieldmark->GetFieldname() == ODF_UNHANDLED) + else { - IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); - if ( it != pFieldmark->GetParameters()->end() ) + ww::eField eFieldId = lcl_getFieldId( pFieldmark ); + if (pFieldmark->GetFieldname() == ODF_UNHANDLED) { - OUString sFieldId; - it->second >>= sFieldId; - eFieldId = static_cast<ww::eField>(sFieldId.toInt32()); + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sFieldId; + it->second >>= sFieldId; + eFieldId = static_cast<ww::eField>(sFieldId.toInt32()); + } } - } - OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close ); + OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close ); - if (pFieldmark->GetFieldname() == ODF_FORMTEXT - && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX ) - { - AppendBookmark( pFieldmark->GetName() ); + if (pFieldmark->GetFieldname() == ODF_FORMTEXT + && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX ) + { + AppendBookmark( pFieldmark->GetName() ); + } } } } diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx index 10ccaa4878..976a68b88a 100644 --- a/sw/source/filter/ww8/ww8par.cxx +++ b/sw/source/filter/ww8/ww8par.cxx @@ -27,6 +27,7 @@ #include <com/sun/star/frame/XModel.hpp> #include <com/sun/star/packages/XPackageEncryption.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> #include <i18nlangtag/languagetag.hxx> @@ -4808,27 +4809,35 @@ void SwWW8ImplReader::ReadDocVars() aDocVarStrings, &aDocVarStringIds, &aDocValueStrings); if (m_bVer67) return; - uno::Reference<document::XDocumentPropertiesSupplier> xDPS( - m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); - uno::Reference<document::XDocumentProperties> xDocProps( - xDPS->getDocumentProperties()); - OSL_ENSURE(xDocProps.is(), "DocumentProperties is null"); - uno::Reference<beans::XPropertyContainer> xUserDefinedProps = - xDocProps->getUserDefinedProperties(); - OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null"); - - for(size_t i=0; i<aDocVarStrings.size(); i++) + uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<css::lang::XMultiServiceFactory> xTextFactory(m_pDocShell->GetModel(), uno::UNO_QUERY); + uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters(); + for(size_t i = 0; i < aDocVarStrings.size(); i++) { const OUString &rName = aDocVarStrings[i]; uno::Any aValue; - aValue <<= rName; - try { - xUserDefinedProps->addProperty( rName, - beans::PropertyAttribute::REMOVABLE, - aValue ); - } catch (const uno::Exception &) { - // ignore + if (aDocValueStrings.size() > i) + { + OUString value = aDocValueStrings[i]; + value = value.replaceAll("\r\n", "\n"); + value = value.replaceAll("\r", "\n"); + aValue <<= value; + } + + uno::Reference< beans::XPropertySet > xMaster; + OUString sFieldMasterService("com.sun.star.text.FieldMaster.User." + rName); + + // Find or create Field Master + if (xFieldMasterAccess->hasByName(sFieldMasterService)) + { + xMaster.set(xFieldMasterAccess->getByName(sFieldMasterService), uno::UNO_QUERY_THROW); + } + else + { + xMaster.set(xTextFactory->createInstance("com.sun.star.text.FieldMaster.User"), uno::UNO_QUERY_THROW); + xMaster->setPropertyValue("Name", uno::Any(rName)); } + xMaster->setPropertyValue("Content", aValue); } } diff --git a/sw/source/filter/ww8/ww8par5.cxx b/sw/source/filter/ww8/ww8par5.cxx index c87c33dde0..6750fa21ae 100644 --- a/sw/source/filter/ww8/ww8par5.cxx +++ b/sw/source/filter/ww8/ww8par5.cxx @@ -58,6 +58,7 @@ #include <IDocumentState.hxx> #include <flddat.hxx> #include <docufld.hxx> +#include <usrfld.hxx> #include <reffld.hxx> #include <IMark.hxx> #include <expfld.hxx> @@ -1831,12 +1832,29 @@ eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr ) aData = aData.replaceAll("\"", ""); } - const auto pType(static_cast<SwDocInfoFieldType*>( - m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo))); - SwDocInfoField aField(pType, nSub|nReg, aData, GetFieldResult(pF), nFormat); - if (bDateTime) - ForceFieldLanguage(aField, nLang); - m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + bool bDone = false; + if (DI_CUSTOM == nSub) + { + const auto pType(static_cast<SwUserFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::User, aData, false))); + if (pType) + { + SwUserField aField(pType, 0, nFormat); + if (bDateTime) + ForceFieldLanguage(aField, nLang); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + bDone = true; + } + } + if (!bDone) + { + const auto pType(static_cast<SwDocInfoFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo))); + SwDocInfoField aField(pType, nSub|nReg, aData, GetFieldResult(pF), nFormat); + if (bDateTime) + ForceFieldLanguage(aField, nLang); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + } return eF_ResT::OK; } diff --git a/sw/source/ui/fldui/fldref.cxx b/sw/source/ui/fldui/fldref.cxx index a08715c8ca..434e10a6a2 100644 --- a/sw/source/ui/fldui/fldref.cxx +++ b/sw/source/ui/fldui/fldref.cxx @@ -268,7 +268,7 @@ void SwFieldRefPage::Reset(const SfxItemSet* ) nFieldDlgFormatSel = 0; sal_uInt16 nFormatBoxPosition = USHRT_MAX; - if( !IsRefresh() ) + if( !IsFieldEdit() ) { sal_Int32 nIdx{ 0 }; const OUString sUserData = GetUserData(); diff --git a/sw/source/ui/index/cnttab.cxx b/sw/source/ui/index/cnttab.cxx index a5d3bf92dd..535dc298dd 100644 --- a/sw/source/ui/index/cnttab.cxx +++ b/sw/source/ui/index/cnttab.cxx @@ -2933,7 +2933,7 @@ SwTOXWidget* SwTokenWindow::InsertItem(const OUString& rText, const SwFormToken& //use the first two chars as symbol OUString sTmp(SwAuthorityFieldType::GetAuthFieldName( static_cast<ToxAuthorityField>(rToken.nAuthorityField))); - pButton->SetText(sTmp.copy(0, 2)); + pButton->SetText(sTmp.copy(0, std::min(sTmp.getLength(), sal_Int32(2)))); } sal_uInt32 nIndex = GetControlIndex( rToken.eTokenType ); @@ -3140,7 +3140,7 @@ void SwTokenWindow::InsertAtSelection(const SwFormToken& rToken) //use the first two chars as symbol OUString sTmp(SwAuthorityFieldType::GetAuthFieldName( static_cast<ToxAuthorityField>(aToInsertToken.nAuthorityField))); - pButton->SetText(sTmp.copy(0, 2)); + pButton->SetText(sTmp.copy(0, std::min(sTmp.getLength(), sal_Int32(2)))); } pButton->Check(); diff --git a/sw/source/ui/index/swuiidxmrk.cxx b/sw/source/ui/index/swuiidxmrk.cxx index 8a6f74b86e..03d5733a87 100644 --- a/sw/source/ui/index/swuiidxmrk.cxx +++ b/sw/source/ui/index/swuiidxmrk.cxx @@ -441,6 +441,7 @@ IMPL_LINK_NOARG(SwIndexMarkPane, SyncSelectionHdl, weld::Button&, void) m_xApplyToAllCB->show(); m_xSearchCaseSensitiveCB->show(); m_xSearchCaseWordOnlyCB->show(); + m_xDialog->resize_to_request(); m_xApplyToAllCB->set_sensitive(!m_aOrgStr.isEmpty() && !(nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FLY_ANY ))); SearchTypeHdl(*m_xApplyToAllCB); diff --git a/sw/source/uibase/shells/textsh.cxx b/sw/source/uibase/shells/textsh.cxx index 97de985401..29242d9946 100644 --- a/sw/source/uibase/shells/textsh.cxx +++ b/sw/source/uibase/shells/textsh.cxx @@ -885,10 +885,13 @@ void SwTextShell::ExecRotateTransliteration( SfxRequest const & rReq ) } else { - rSh.Push(); // save cur cursor - if ((rSh.IsEndWrd() || rSh.IsStartWord() || rSh.IsInWord()) && rSh.SelWrd()) + if (rSh.IsEndSentence()) + { + rSh.BwdSentence(true); + rSh.TransliterateText(m_aRotateCase.getNextMode()); + } + else if ((rSh.IsEndWrd() || rSh.IsStartWord() || rSh.IsInWord()) && rSh.SelWrd()) rSh.TransliterateText(m_aRotateCase.getNextMode()); - rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); } } } diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx index e7a09d0165..46b0a40cca 100644 --- a/sw/source/uibase/wrtsh/delete.cxx +++ b/sw/source/uibase/wrtsh/delete.cxx @@ -171,7 +171,7 @@ bool SwWrtShell::DelLeft() { SwActContext aActContext(this); ResetCursorStack(); - Delete(false); + Delete(false, true); UpdateAttr(); } if( IsBlockMode() ) @@ -274,7 +274,7 @@ bool SwWrtShell::DelLeft() SwCursorShell::Pop( SwCursorShell::PopMode::DeleteStack ); } } - bool bRet = Delete(true); + bool bRet = Delete(true, true); if( !bRet && bSwap ) SwCursorShell::SwapPam(); CloseMark( bRet ); |