From 8ceff95c69cf9bd9ff5ab3a4b5689925b8bd6a59 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 7 Jun 2024 13:47:06 +0200 Subject: Merging upstream version 4:24.2.4. Signed-off-by: Daniel Baumann --- sw/CppunitTest_sw_core_layout.mk | 1 + sw/CppunitTest_sw_writerfilter_filter.mk | 59 +++++ sw/Module_sw.mk | 1 + sw/inc/IDocumentSettingAccess.hxx | 2 + sw/inc/doc.hxx | 30 ++- sw/qa/core/frmedt/frmedt.cxx | 35 +++ sw/qa/core/layout/calcmove.cxx | 87 +++++++ sw/qa/core/layout/data/bad-split-section.odt | Bin 0 -> 29305 bytes sw/qa/core/layout/data/ignore-top-margin-fly.odt | Bin 0 -> 11395 bytes .../core/layout/data/ignore-top-margin-table.docx | Bin 0 -> 12465 bytes sw/qa/core/layout/data/ignore-top-margin.docx | Bin 0 -> 14904 bytes sw/qa/core/layout/layact.cxx | 22 ++ sw/qa/core/objectpositioning/objectpositioning.cxx | 48 ++++ sw/qa/core/text/data/A011-charheight.rtf | 27 ++ sw/qa/core/text/data/Broken indent demo.odt | Bin 0 -> 9970 bytes sw/qa/core/text/data/tdf156146.fodt | 281 +++++++++++++++++++++ sw/qa/core/text/itrform2.cxx | 18 ++ sw/qa/core/text/text.cxx | 62 +++++ .../txtnode/data/plain-content-control-copy.docx | Bin 0 -> 12178 bytes sw/qa/core/txtnode/txtnode.cxx | 23 ++ sw/qa/extras/odfexport/odfexport2.cxx | 6 +- sw/qa/extras/ooxmlexport/data/StyleRef-DE.docx | Bin 0 -> 19230 bytes sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 12 +- sw/qa/extras/ooxmlexport/ooxmlexport9.cxx | 4 +- sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx | 2 + sw/qa/extras/uiwriter/data/pagebreak-source.fodt | 131 ++++++++++ sw/qa/extras/uiwriter/data/pagebreak-target.fodt | 137 ++++++++++ .../uiwriter/data/stylewithlistandindents.fodt | 182 +++++++++++++ sw/qa/extras/uiwriter/data/table-in-table.fodt | 29 +++ sw/qa/extras/uiwriter/data/tdf160842.fodt | 75 ++++++ sw/qa/extras/uiwriter/data/tdf161172.fodt | 35 +++ sw/qa/extras/uiwriter/uiwriter.cxx | 121 +++++++++ sw/qa/extras/uiwriter/uiwriter2.cxx | 6 +- sw/qa/extras/uiwriter/uiwriter3.cxx | 7 +- sw/qa/extras/uiwriter/uiwriter6.cxx | 157 ++++++++++++ sw/qa/extras/uiwriter/uiwriter8.cxx | 121 +++++++++ sw/qa/extras/uiwriter/uiwriter9.cxx | 56 ++++ sw/qa/extras/unowriter/data/tdf161035.fodt | 9 + sw/qa/extras/unowriter/unowriter.cxx | 27 ++ .../ww8export/data/draw-obj-rtl-no-mirror-vml.docx | Bin 0 -> 14534 bytes sw/qa/extras/ww8export/ww8export4.cxx | 24 ++ sw/qa/uibase/uno/uno.cxx | 24 ++ .../cppunittests/filter/WriterFilter.cxx | 57 +++++ .../filter/data/draw-obj-rtl-no-mirror.docx | Bin 0 -> 12859 bytes sw/source/core/access/accfrmobj.cxx | 15 +- sw/source/core/crsr/crsrsh.cxx | 12 +- .../core/doc/DocumentContentOperationsManager.cxx | 89 ++++--- sw/source/core/doc/DocumentSettingManager.cxx | 21 ++ sw/source/core/doc/docdraw.cxx | 12 + sw/source/core/doc/docfmt.cxx | 10 +- sw/source/core/doc/docnum.cxx | 98 +++++-- sw/source/core/doc/doctxm.cxx | 3 +- sw/source/core/docnode/nodes.cxx | 2 +- sw/source/core/draw/dcontact.cxx | 21 +- sw/source/core/draw/dview.cxx | 129 ++++++---- sw/source/core/edit/autofmt.cxx | 2 +- sw/source/core/edit/edglss.cxx | 8 + sw/source/core/edit/ednumber.cxx | 12 +- sw/source/core/fields/fldbas.cxx | 10 +- sw/source/core/fields/reffld.cxx | 66 +++-- sw/source/core/frmedt/fecopy.cxx | 40 ++- sw/source/core/frmedt/fefly1.cxx | 60 ++++- sw/source/core/frmedt/feshview.cxx | 59 ++++- sw/source/core/inc/DocumentSettingManager.hxx | 2 + sw/source/core/inc/frame.hxx | 3 + sw/source/core/inc/sectfrm.hxx | 2 +- sw/source/core/layout/anchoreddrawobject.cxx | 4 + sw/source/core/layout/anchoredobject.cxx | 11 +- sw/source/core/layout/calcmove.cxx | 38 +++ sw/source/core/layout/flowfrm.cxx | 5 + sw/source/core/layout/fly.cxx | 4 +- sw/source/core/layout/flylay.cxx | 41 +-- sw/source/core/layout/frmtool.cxx | 40 +-- sw/source/core/layout/layact.cxx | 4 +- sw/source/core/layout/pagechg.cxx | 25 +- sw/source/core/layout/trvlfrm.cxx | 39 +-- .../objectpositioning/anchoredobjectposition.cxx | 5 +- sw/source/core/text/inftxt.hxx | 6 +- sw/source/core/text/itratr.hxx | 2 +- sw/source/core/text/itrform2.cxx | 13 +- sw/source/core/text/itrtxt.hxx | 2 +- sw/source/core/txtnode/atrflyin.cxx | 3 +- sw/source/core/txtnode/thints.cxx | 4 +- sw/source/core/undo/undraw.cxx | 31 ++- sw/source/core/undo/unnum.cxx | 4 +- sw/source/core/undo/untblk.cxx | 6 +- sw/source/core/unocore/unocrsrhelper.cxx | 8 +- sw/source/core/unocore/unoportenum.cxx | 4 +- sw/source/core/unocore/unotext.cxx | 6 - sw/source/filter/basflt/fltshell.cxx | 2 +- sw/source/filter/ww8/wrtw8esh.cxx | 6 + sw/source/filter/ww8/ww8par.cxx | 1 + sw/source/filter/xml/xmlexp.cxx | 1 + sw/source/filter/xml/xmlimp.cxx | 20 ++ sw/source/ui/frmdlg/frmpage.cxx | 3 +- sw/source/uibase/docvw/edtwin.cxx | 10 +- sw/source/uibase/shells/drwbassh.cxx | 10 + sw/source/uibase/uiview/viewtab.cxx | 2 +- sw/source/uibase/uno/SwXDocumentSettings.cxx | 36 +++ sw/source/uibase/utlui/content.cxx | 2 +- 100 files changed, 2658 insertions(+), 334 deletions(-) create mode 100644 sw/CppunitTest_sw_writerfilter_filter.mk create mode 100644 sw/qa/core/layout/calcmove.cxx create mode 100644 sw/qa/core/layout/data/bad-split-section.odt create mode 100644 sw/qa/core/layout/data/ignore-top-margin-fly.odt create mode 100644 sw/qa/core/layout/data/ignore-top-margin-table.docx create mode 100644 sw/qa/core/layout/data/ignore-top-margin.docx create mode 100644 sw/qa/core/text/data/A011-charheight.rtf create mode 100644 sw/qa/core/text/data/Broken indent demo.odt create mode 100644 sw/qa/core/text/data/tdf156146.fodt create mode 100644 sw/qa/core/txtnode/data/plain-content-control-copy.docx create mode 100644 sw/qa/extras/ooxmlexport/data/StyleRef-DE.docx create mode 100644 sw/qa/extras/uiwriter/data/pagebreak-source.fodt create mode 100644 sw/qa/extras/uiwriter/data/pagebreak-target.fodt create mode 100644 sw/qa/extras/uiwriter/data/stylewithlistandindents.fodt create mode 100644 sw/qa/extras/uiwriter/data/table-in-table.fodt create mode 100644 sw/qa/extras/uiwriter/data/tdf160842.fodt create mode 100644 sw/qa/extras/uiwriter/data/tdf161172.fodt create mode 100644 sw/qa/extras/unowriter/data/tdf161035.fodt create mode 100644 sw/qa/extras/ww8export/data/draw-obj-rtl-no-mirror-vml.docx create mode 100644 sw/qa/writerfilter/cppunittests/filter/WriterFilter.cxx create mode 100644 sw/qa/writerfilter/cppunittests/filter/data/draw-obj-rtl-no-mirror.docx (limited to 'sw') diff --git a/sw/CppunitTest_sw_core_layout.mk b/sw/CppunitTest_sw_core_layout.mk index 5eb874400d..d64a43f7d3 100644 --- a/sw/CppunitTest_sw_core_layout.mk +++ b/sw/CppunitTest_sw_core_layout.mk @@ -14,6 +14,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,sw_core_layout)) $(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_core_layout)) $(eval $(call gb_CppunitTest_add_exception_objects,sw_core_layout, \ + sw/qa/core/layout/calcmove \ sw/qa/core/layout/fly \ sw/qa/core/layout/flycnt \ sw/qa/core/layout/frmtool \ diff --git a/sw/CppunitTest_sw_writerfilter_filter.mk b/sw/CppunitTest_sw_writerfilter_filter.mk new file mode 100644 index 0000000000..ad61c66f1e --- /dev/null +++ b/sw/CppunitTest_sw_writerfilter_filter.mk @@ -0,0 +1,59 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,sw_writerfilter_filter)) + +$(eval $(call gb_CppunitTest_use_externals,sw_writerfilter_filter,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sw_writerfilter_filter, \ + sw/qa/writerfilter/cppunittests/filter/WriterFilter \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sw_writerfilter_filter, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + oox \ + sal \ + subsequenttest \ + test \ + unotest \ + utl \ + tl \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,sw_writerfilter_filter)) + +$(eval $(call gb_CppunitTest_use_ure,sw_writerfilter_filter)) +$(eval $(call gb_CppunitTest_use_vcl,sw_writerfilter_filter)) + +$(eval $(call gb_CppunitTest_use_rdb,sw_writerfilter_filter,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,sw_writerfilter_filter,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,sw_writerfilter_filter)) + +# we need to explicitly depend on library sw_writerfilter because it is not implied +# by a link relation +$(call gb_CppunitTest_get_target,sw_writerfilter_filter) : $(call gb_Library_get_target,sw_writerfilter) + +ifneq ($(filter MORE_FONTS,$(BUILD_TYPE)),) +$(eval $(call gb_CppunitTest_set_non_application_font_use,sw_writerfilter_filter,abort)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk index 7d5679e52f..7556ecf337 100644 --- a/sw/Module_sw.mk +++ b/sw/Module_sw.mk @@ -168,6 +168,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\ CppunitTest_sw_a11y \ CppunitTest_sw_core_theme \ CppunitTest_sw_pdf_test \ + CppunitTest_sw_writerfilter_filter \ )) ifneq ($(DISABLE_GUI),TRUE) diff --git a/sw/inc/IDocumentSettingAccess.hxx b/sw/inc/IDocumentSettingAccess.hxx index 74b123dc46..264860b854 100644 --- a/sw/inc/IDocumentSettingAccess.hxx +++ b/sw/inc/IDocumentSettingAccess.hxx @@ -98,6 +98,8 @@ enum class DocumentSettingId ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK, // tdf#119908 new paragraph justification JUSTIFY_LINES_WITH_SHRINKING, + APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH, + DO_NOT_MIRROR_RTL_DRAW_OBJS, // COMPATIBILITY FLAGS END BROWSE_MODE, HTML_MODE, diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 99cb33c758..946c96fd29 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -1052,23 +1052,30 @@ public: bool GotoOutline(SwPosition& rPos, const OUString& rName, SwRootFrame const* = nullptr) const; - /** Accept changes of outline styles for OutlineRule. - @param bResetIndentAttrs Optional parameter - default value false: - If equals true, the indent attributes "before text" - and "first line indent" are additionally reset at the provided PaM, if - the list style makes use of the new list level attributes. - @param bCreateNewList indicates if a new list is created by applying the given list style. + enum class SetNumRuleMode { + Default = 0, + /// indicates if a new list is created by applying the given list style. + CreateNewList = 1, + DontSetItem = 2, + /** If enabled, the indent attributes "before text" and + "first line indent" are additionally reset at the provided PaM, + if the list style makes use of the new list level attributes. */ + ResetIndentAttrs = 4, + DontSetIfAlreadyApplied = 8 + }; + + /** Set or change numbering rule on text nodes, as direct formatting. @param sContinuedListId If bCreateNewList is false, may contain the list Id of a list which has to be continued by applying the given list style @return the set ListId if bSetItem is true */ OUString SetNumRule( const SwPaM&, const SwNumRule&, - bool bCreateNewList, + SetNumRuleMode mode, SwRootFrame const* pLayout = nullptr, const OUString& sContinuedListId = OUString(), - bool bSetItem = true, - const bool bResetIndentAttrs = false ); + SvxTextLeftMarginItem const* pTextLeftMarginToPropagate = nullptr, + SvxFirstLineIndentItem const* pFirstLineIndentToPropagate = nullptr); void SetCounted(const SwPaM&, bool bCounted, SwRootFrame const* pLayout); void MakeUniqueNumRules(const SwPaM & rPaM); @@ -1145,7 +1152,9 @@ public: int nNonEmptyAllowed, OUString& sListId, SwRootFrame const* pLayout, - const bool bInvestigateStartNode = false ); + const bool bInvestigateStartNode = false, + SvxTextLeftMarginItem const** o_ppTextLeftMargin = nullptr, + SvxFirstLineIndentItem const** o_ppFirstLineIndent = nullptr); // Paragraphs without numbering but with indents. bool NoNum( const SwPaM& ); @@ -1704,6 +1713,7 @@ private: namespace o3tl { template<> struct typed_flags : is_typed_flags {}; + template<> struct typed_flags : is_typed_flags {}; } // This method is called in Dtor of SwDoc and deletes cache of ContourObjects. diff --git a/sw/qa/core/frmedt/frmedt.cxx b/sw/qa/core/frmedt/frmedt.cxx index b2a53e60db..37425c1306 100644 --- a/sw/qa/core/frmedt/frmedt.cxx +++ b/sw/qa/core/frmedt/frmedt.cxx @@ -250,6 +250,41 @@ CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testSplitFlyUnfloat) CPPUNIT_ASSERT_EQUAL(static_cast(1), pDoc->GetTableFrameFormatCount(/*bUsed=*/true)); } +CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testInsertOnGrfNodeAsChar) +{ + // Given a selected as-char image: + createSwDoc(); + SwDoc* pDoc = getSwDocShell()->GetDoc(); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + { + SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items); + SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR); + aFrameSet.Put(aAnchor); + Graphic aGrf; + pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet); + } + + // When inserting another as-char image: + SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items); + SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR); + aFrameSet.Put(aAnchor); + Graphic aGrf; + // Without the accompanying fix in place, this call crashed, we try to set a graphic node as an + // anchor of an as-char image (which should be a text node). + pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet); + + // Then make sure that the anchor of the second image is next to the first anchor: + CPPUNIT_ASSERT(pDoc->GetSpzFrameFormats()); + sw::FrameFormats& rFormats = *pDoc->GetSpzFrameFormats(); + CPPUNIT_ASSERT_EQUAL(static_cast(2), rFormats.size()); + const sw::SpzFrameFormat& rFormat1 = *rFormats[0]; + const SwPosition* pAnchor1 = rFormat1.GetAnchor().GetContentAnchor(); + const sw::SpzFrameFormat& rFormat2 = *rFormats[1]; + const SwPosition* pAnchor2 = rFormat2.GetAnchor().GetContentAnchor(); + CPPUNIT_ASSERT_EQUAL(pAnchor1->nNode, pAnchor2->nNode); + CPPUNIT_ASSERT_EQUAL(pAnchor1->GetContentIndex() + 1, pAnchor2->GetContentIndex()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/layout/calcmove.cxx b/sw/qa/core/layout/calcmove.cxx new file mode 100644 index 0000000000..ad53df9bd0 --- /dev/null +++ b/sw/qa/core/layout/calcmove.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include + +namespace +{ +/// Covers sw/source/core/layout/calcmove.cxx fixes. +class Test : public SwModelTestBase +{ +public: + Test() + : SwModelTestBase("/sw/qa/core/layout/data/") + { + } +}; + +CPPUNIT_TEST_FIXTURE(Test, testIgnoreTopMargin) +{ + // Given a DOCX (>= Word 2013) file, with 2 pages: + // When loading that document: + createSwDoc("ignore-top-margin.docx"); + + // Then make sure that the paragraph on the 2nd page has no top margin: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + sal_Int32 nParaTopMargin + = getXPath(pXmlDoc, "/root/page[2]/body/txt/infos/prtBounds"_ostr, "top"_ostr).toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 + // - Actual : 2400 + // i.e. the top margin in the first para of a non-first page wasn't ignored, like in Word. + CPPUNIT_ASSERT_EQUAL(static_cast(0), nParaTopMargin); +} + +CPPUNIT_TEST_FIXTURE(Test, testIgnoreTopMarginTable) +{ + // Given a DOCX (>= Word 2013) file, with 2 pages: + // When loading that document: + createSwDoc("ignore-top-margin-table.docx"); + + // Then make sure that the paragraph on the 2nd page in B1 has a top margin: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + sal_Int32 nParaTopMargin + = getXPath(pXmlDoc, "/root/page[2]/body/tab/row/cell[2]/txt/infos/prtBounds"_ostr, + "top"_ostr) + .toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2000 + // - Actual : 0 + // i.e. the top margin in B1's first paragraph was ignored, but not in Word. + CPPUNIT_ASSERT_EQUAL(static_cast(2000), nParaTopMargin); +} + +CPPUNIT_TEST_FIXTURE(Test, testIgnoreTopMarginFly) +{ + // Given a document with compat flags like DOCX (>= Word 2013), 2 pages, multi-col fly frame on + // page 2: + createSwDoc("ignore-top-margin-fly.odt"); + + // When laying out that document: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + // Then make sure that the top margin is not ignored inside shape text: + sal_Int32 nParaTopMargin + = getXPath( + pXmlDoc, + "/root/page[2]/body/section/column[2]/body/txt/anchored/fly/column/body/txt/infos/prtBounds"_ostr, + "top"_ostr) + .toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 4000 + // - Actual : 0 + // i.e. the top margin was ignored inside shape text for Word compat, while multi-col shape text + // is a Writer feature. + CPPUNIT_ASSERT_EQUAL(static_cast(4000), nParaTopMargin); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/layout/data/bad-split-section.odt b/sw/qa/core/layout/data/bad-split-section.odt new file mode 100644 index 0000000000..6dbd07802d Binary files /dev/null and b/sw/qa/core/layout/data/bad-split-section.odt differ diff --git a/sw/qa/core/layout/data/ignore-top-margin-fly.odt b/sw/qa/core/layout/data/ignore-top-margin-fly.odt new file mode 100644 index 0000000000..51bda8fe40 Binary files /dev/null and b/sw/qa/core/layout/data/ignore-top-margin-fly.odt differ diff --git a/sw/qa/core/layout/data/ignore-top-margin-table.docx b/sw/qa/core/layout/data/ignore-top-margin-table.docx new file mode 100644 index 0000000000..c82f6d63c1 Binary files /dev/null and b/sw/qa/core/layout/data/ignore-top-margin-table.docx differ diff --git a/sw/qa/core/layout/data/ignore-top-margin.docx b/sw/qa/core/layout/data/ignore-top-margin.docx new file mode 100644 index 0000000000..d05a1358db Binary files /dev/null and b/sw/qa/core/layout/data/ignore-top-margin.docx differ diff --git a/sw/qa/core/layout/layact.cxx b/sw/qa/core/layout/layact.cxx index 8923d6b0e8..9de0c9ebfa 100644 --- a/sw/qa/core/layout/layact.cxx +++ b/sw/qa/core/layout/layact.cxx @@ -21,6 +21,7 @@ #include #include #include +#include namespace { @@ -108,6 +109,27 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInSection) CPPUNIT_ASSERT(pPage2); CPPUNIT_ASSERT(!pPage2->GetSortedObjs()); } + +CPPUNIT_TEST_FIXTURE(Test, testBadSplitSection) +{ + // Given a document with a section, containing 5 paragraphs: + createSwDoc("bad-split-section.odt"); + + // When laying out that document: + SwDoc* pDoc = getSwDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + + // Then make sure the entire section is on page 1: + auto pPage = pLayout->Lower()->DynCastPageFrame(); + CPPUNIT_ASSERT(pPage); + auto pBody = pPage->FindBodyCont(); + CPPUNIT_ASSERT(pBody); + auto pSection = dynamic_cast(pBody->GetLastLower()); + CPPUNIT_ASSERT(pSection); + // Without the fix in place, it would have failed, the section was split between page 1 and page + // 2. + CPPUNIT_ASSERT(!pSection->GetFollow()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/objectpositioning/objectpositioning.cxx b/sw/qa/core/objectpositioning/objectpositioning.cxx index bf560cbdaf..a1805bf450 100644 --- a/sw/qa/core/objectpositioning/objectpositioning.cxx +++ b/sw/qa/core/objectpositioning/objectpositioning.cxx @@ -24,6 +24,10 @@ #include #include #include +#include +#include +#include +#include namespace { @@ -404,6 +408,50 @@ CPPUNIT_TEST_FIXTURE(Test, testFloatingTableOverlapCell) CPPUNIT_ASSERT(pPage1); CPPUNIT_ASSERT(!pPage1->GetNext()); } + +CPPUNIT_TEST_FIXTURE(Test, testDoNotMirrorRtlDrawObjsLayout) +{ + // Given a document with an RTL paragraph, Word-style compat flag is enabled: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + auto& rIDSA = pDoc->getIDocumentSettingAccess(); + rIDSA.set(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS, true); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + SwView& rView = pWrtShell->GetView(); + SfxItemSetFixed aSet(rView.GetPool()); + SvxFrameDirectionItem aDirection(SvxFrameDirection::Horizontal_RL_TB, RES_FRAMEDIR); + aSet.Put(aDirection); + pWrtShell->SetAttrSet(aSet, SetAttrMode::DEFAULT, nullptr, /*bParagraphSetting=*/true); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPageFrame = pLayout->Lower()->DynCastPageFrame(); + SwFrame* pBodyFrame = pPageFrame->GetLower(); + + // When inserting a graphic on the middle of the right margin: + SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items); + SwFormatAnchor aAnchor(RndStdIds::FLY_AT_CHAR); + aFrameSet.Put(aAnchor); + // Default margin is 1440, this is 1440/2. + SwFormatFrameSize aSize(SwFrameSize::Fixed, 720, 720); + aFrameSet.Put(aSize); + // This is 1440/4. + SwFormatHoriOrient aOrient(pBodyFrame->getFrameArea().Right() + 360); + aFrameSet.Put(aOrient); + Graphic aGrf; + pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet); + + // Then make sure that the image is on the right margin: + SwTwips nBodyRight = pBodyFrame->getFrameArea().Right(); + CPPUNIT_ASSERT(pPageFrame->GetSortedObjs()); + const SwSortedObjs& rPageObjs = *pPageFrame->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast(1), rPageObjs.size()); + const SwAnchoredObject* pAnchored = rPageObjs[0]; + Point aAnchoredCenter = pAnchored->GetDrawObj()->GetLastBoundRect().Center(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected greater than: 11389 + // - Actual : 643 + // i.e. the graphic was on the left margin, not on the right margin. + CPPUNIT_ASSERT_GREATER(nBodyRight, aAnchoredCenter.getX()); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/core/text/data/A011-charheight.rtf b/sw/qa/core/text/data/A011-charheight.rtf new file mode 100644 index 0000000000..4b56ecdd6a --- /dev/null +++ b/sw/qa/core/text/data/A011-charheight.rtf @@ -0,0 +1,27 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang3079\deflangfe3079\themelang3079\themelangfe0\themelangcs0 +{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2Times New Roman{\*\falt Arial};} +} +{\*\defchp \f0\fs22\lang3079\langfe1033\langfenp1033 } +{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 } +\noqfpromote +{\stylesheet +{\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 +\f0\fs23\lang3079\langfe1033\cgrid\langnp3079\langfenp1033 \snext0 \sqformat \spriority0 Normal;} +{\s15\ql \li0\ri0\widctlpar\tqc\tx4536\tqr\tx9072\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f0\fs23\lang3079\langfe1033\cgrid\langnp3079\langfenp1033 +\sbasedon0 \snext15 \slink16 \sunhideused header;} +{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \sbasedon10 \slink15 \slocked Kopfzeile Zchn;} +} +\paperw11906\paperh16838\margl1417\margr1417\margt1417\margb1134\gutter0\ltrsect +\deftab708\widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0 +\showxmlerrors1\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1417\dgvorigin1417\dghshow1\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 +{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0 + +\ltrpar \pard\plain \ltrpar\s15\qc \li0\ri0\widctlpar +\tqc\tx4536\tqr\tx9072\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f0\fs23\lang3079\langfe1033\cgrid\langnp3079\langfenp1033 {\rtlch\fcs1 \af0\afs16 \ltrch\fcs0 +\fs16 \line \line \line +\par \line +\par } +} diff --git a/sw/qa/core/text/data/Broken indent demo.odt b/sw/qa/core/text/data/Broken indent demo.odt new file mode 100644 index 0000000000..af5928bfa5 Binary files /dev/null and b/sw/qa/core/text/data/Broken indent demo.odt differ diff --git a/sw/qa/core/text/data/tdf156146.fodt b/sw/qa/core/text/data/tdf156146.fodt new file mode 100644 index 0000000000..1587cd945e --- /dev/null +++ b/sw/qa/core/text/data/tdf156146.fodt @@ -0,0 +1,281 @@ + + + 2023-06-14T01:10:01.4975110772024-04-18T18:00:26.359273842PT14M47S2ZetaOffice/7.4.8.0.0$Linux_X86_64 LibreOffice_project/b82f1163cc2fc696cf86209d94d838d04998350f + + + false + false + false + true + true + true + true + true + false + 0 + false + false + false + true + false + false + true + false + false + false + false + true + true + true + false + false + false + false + false + false + false + false + true + false + false + true + false + false + false + true + 0 + 1 + true + false + + high-resolution + true + + + false + false + true + false + true + true + true + false + true + + true + 910346 + + true + false + true + true + 0 + + false + false + false + true + false + true + 0 + false + false + false + false + true + false + false + false + + false + false + true + false + false + false + false + false + false + false + false + false + 811422 + false + false + false + false + false + true + false + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + He heard quiet steps behind him. That didn't bode well. Who could be following him this late at night and in this deadbeat part of town? And at this particular moment, just after he pulled off the big time and was making off with the greenbacks. Was there another crook who'd had the same idea, and was now watching him and waiting for a chance to grab the fruit of his labor? Or did the steps behind him mean that one of many law officers in town was on to him and just waiting to pounce and snap those cuffs on his wrists? He nervously looked all around. Suddenly he saw the alley. Like lightning he darted off to the left and disappeared between the two warehouses almost falling over the trash can lying in the middle of the sidewalk. He tried to nervously tap his way along in the inky darkness and + + + + + diff --git a/sw/qa/core/text/itrform2.cxx b/sw/qa/core/text/itrform2.cxx index 6d59140f97..860b2197f7 100644 --- a/sw/qa/core/text/itrform2.cxx +++ b/sw/qa/core/text/itrform2.cxx @@ -88,6 +88,24 @@ CPPUNIT_TEST_FIXTURE(Test, testFloattableLegacyWrapEmptyParagraph) CPPUNIT_ASSERT_EQUAL(static_cast(1), rPageObjs2.size()); } +CPPUNIT_TEST_FIXTURE(Test, testApplyTextAttrToEmptyLineAtEndOfParagraph) +{ + createSwDoc("A011-charheight.rtf"); + + calcLayout(); + + SwDoc* pDoc = getSwDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage = dynamic_cast(pLayout->Lower()); + + SwContentFrame* pLastPara = pPage->FindLastBodyContent(); + // wrong was 449 (11.5pt) + CPPUNIT_ASSERT_EQUAL(static_cast(368), pLastPara->getFrameArea().Height()); + SwContentFrame* pFirstPara = pPage->FindFirstBodyContent(); + // wrong was 817 (11.5pt) + CPPUNIT_ASSERT_EQUAL(static_cast(736), pFirstPara->getFrameArea().Height()); +} + CPPUNIT_TEST_FIXTURE(Test, testFlyMinimalWrap) { // Given a document with a first page that has a shape and a table in it (not floating table), diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index 690fc333af..f18c4fd531 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,8 @@ #include #include #include +#include +#include /// Covers sw/source/core/text/ fixes. class SwCoreTextTest : public SwModelTestBase @@ -116,6 +119,65 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testLastBibliographyPdfExport) CPPUNIT_ASSERT(true); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf156146) +{ + createSwDoc("tdf156146.fodt"); + + uno::Reference const xLevels1( + getProperty>(getParagraph(1), "NumberingRules")); + uno::Reference const xNum1(xLevels1, uno::UNO_QUERY); + ::comphelper::SequenceAsHashMap props1(xLevels1->getByIndex(0)); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-700), props1["FirstLineIndent"].get()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1330), props1["IndentAt"].get()); + + // common style applies list-style-name and margin-left + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), + getProperty(getParagraph(1), "ParaFirstLineIndent")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(1), "ParaLeftMargin")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(1), "ParaRightMargin")); + + SwTextFrame* const pFrame(dynamic_cast( + static_cast(getSwDoc()->GetDocShell()->GetWrtShell()->GetLayout()->GetLower()) + ->FindFirstBodyContent())); + CPPUNIT_ASSERT(pFrame); + // this appears to be the only way to get the actual computed margins + SwTextSizeInfo info(pFrame); + SwTextMargin tm(pFrame, &info); + // this was wrong, 357 + CPPUNIT_ASSERT_EQUAL(SwTwips(0), tm.FirstLeft() - pFrame->getFrameArea().Left()); + // this was wrong, 754 + CPPUNIT_ASSERT_EQUAL(SwTwips(0), tm.Left() - pFrame->getFrameArea().Left()); +} + +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf159903) +{ + createSwDoc("Broken indent demo.odt"); + + uno::Reference const xLevels1( + getProperty>(getParagraph(1), "NumberingRules")); + uno::Reference const xNum1(xLevels1, uno::UNO_QUERY); + ::comphelper::SequenceAsHashMap props1(xLevels1->getByIndex(0)); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-4001), props1["FirstLineIndent"].get()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4001), props1["IndentAt"].get()); + + // common style applies list-style-name, parent style margin-left + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), + getProperty(getParagraph(1), "ParaFirstLineIndent")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(1), "ParaLeftMargin")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(1), "ParaRightMargin")); + + SwTextFrame* const pFrame(dynamic_cast( + static_cast(getSwDoc()->GetDocShell()->GetWrtShell()->GetLayout()->GetLower()) + ->FindFirstBodyContent())); + CPPUNIT_ASSERT(pFrame); + // this appears to be the only way to get the actual computed margins + SwTextSizeInfo info(pFrame); + SwTextMargin tm(pFrame, &info); + CPPUNIT_ASSERT_EQUAL(SwTwips(0), tm.FirstLeft() - pFrame->getFrameArea().Left()); + // left was wrong, was same as first + CPPUNIT_ASSERT_EQUAL(SwTwips(2268), tm.Left() - pFrame->getFrameArea().Left()); +} + CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testTdf159336) { createSwDoc("tdf159336.odt"); diff --git a/sw/qa/core/txtnode/data/plain-content-control-copy.docx b/sw/qa/core/txtnode/data/plain-content-control-copy.docx new file mode 100644 index 0000000000..80fecae26d Binary files /dev/null and b/sw/qa/core/txtnode/data/plain-content-control-copy.docx differ diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx index c2df8a407e..be4d971902 100644 --- a/sw/qa/core/txtnode/txtnode.cxx +++ b/sw/qa/core/txtnode/txtnode.cxx @@ -539,6 +539,29 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testSplitFlyAnchorSplit) CPPUNIT_ASSERT_EQUAL(OUString("PortionType::Fly"), aPortionType); } +CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testPlainContentControlCopy) +{ + // Given a document with a plain text content control, all text selected and copied to the + // clipboard: + createSwDoc("plain-content-control-copy.docx"); + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->SelAll(); + { + rtl::Reference xTransfer = new SwTransferable(*pWrtShell); + xTransfer->Copy(); + } + + // When closing that document, then make sure we don't crash on shutdown: + uno::Reference xModel(mxComponent, uno::UNO_QUERY); + uno::Reference xFrame(xModel->getCurrentController()->getFrame(), + uno::UNO_QUERY); + // Without the accompanying fix in place, this resulted in an assertion failure, a char style + // still had clients by the time it was deleted. + xFrame->close(false); + mxComponent.clear(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/odfexport/odfexport2.cxx b/sw/qa/extras/odfexport/odfexport2.cxx index f654821acf..fb45d5c8e9 100644 --- a/sw/qa/extras/odfexport/odfexport2.cxx +++ b/sw/qa/extras/odfexport/odfexport2.cxx @@ -885,9 +885,9 @@ DECLARE_ODFEXPORT_TEST(testTdf114287, "tdf114287.odt") xmlDocUniquePtr pXmlDoc = parseLayoutDump(); assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/infos/prtBounds"_ostr, "left"_ostr, "2268"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/infos/prtBounds"_ostr, "right"_ostr, "11339"); - // the problem was that the list style name of the list must override the - // paragraph style even though it's the same list style - assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/infos/prtBounds"_ostr, "left"_ostr, "357"); + // the list style name of the list is the same as the list style name of the + // paragraph, but in any case the margins of the paragraph take precedence + assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/infos/prtBounds"_ostr, "left"_ostr, "2268"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[9]/infos/prtBounds"_ostr, "right"_ostr, "11339"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/infos/prtBounds"_ostr, "left"_ostr, "357"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[16]/infos/prtBounds"_ostr, "right"_ostr, "11339"); diff --git a/sw/qa/extras/ooxmlexport/data/StyleRef-DE.docx b/sw/qa/extras/ooxmlexport/data/StyleRef-DE.docx new file mode 100644 index 0000000000..800ef6413f Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/StyleRef-DE.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index 49d974b011..dee26b595c 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -229,7 +229,7 @@ CPPUNIT_TEST_FIXTURE(Test, testParaStyleNumLevel) // - Expected: 1 // - Actual : 0 // i.e. a custom list level in a para style was lost on import+export. - assertXPath(pXmlDoc, "/w:styles/w:style[@w:styleId='Mystyle']/w:pPr/w:numPr/w:ilvl"_ostr, "val"_ostr, "1"); + assertXPath(pXmlDoc, "/w:styles/w:style[@w:styleId='mystyle']/w:pPr/w:numPr/w:ilvl"_ostr, "val"_ostr, "1"); } CPPUNIT_TEST_FIXTURE(Test, testClearingBreak) @@ -741,6 +741,16 @@ DECLARE_OOXMLEXPORT_TEST(testTdf153082_comma, "custom-styles-TOC-comma.docx") CPPUNIT_ASSERT(tocContent.indexOf("Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.") != -1); } +DECLARE_OOXMLEXPORT_TEST(testTdf160402, "StyleRef-DE.docx") +{ + xmlDocUniquePtr pLayout = parseLayoutDump(); + assertXPath(pLayout, "/root/page[1]/header/txt[1]/SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, "Heading 1"); + assertXPath(pLayout, "/root/page[2]/header/txt[1]/SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus."); + assertXPath(pLayout, "/root/page[3]/header/txt[1]/SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, "Cras faucibus condimentum odio. Sed ac ligula. Aliquam at eros."); + assertXPath(pLayout, "/root/page[4]/header/txt[1]/SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus."); + assertXPath(pLayout, "/root/page[5]/header/txt[1]/SwParaPortion/SwLineLayout/SwFieldPortion"_ostr, "expand"_ostr, "Aenean nec lorem. In porttitor. Donec laoreet nonummy augue."); +} + DECLARE_OOXMLEXPORT_TEST(testTdf142407, "tdf142407.docx") { uno::Reference xPageStyles = getStyles("PageStyles"); diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx index ad3bba49df..91f0deb4bb 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx @@ -563,7 +563,7 @@ DECLARE_OOXMLEXPORT_TEST(testTdf104061_tableSectionColumns,"tdf104061_tableSecti //tdf#95114 - follow style is Text Body - DOCX test uno::Reference< beans::XPropertySet > properties(getStyles("ParagraphStyles")->getByName("annotation subject"), uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(OUString("annotation text"), getProperty(properties, "FollowStyle")); + CPPUNIT_ASSERT_EQUAL(OUString("Marginalia"), getProperty(properties, "FollowStyle")); } DECLARE_OOXMLEXPORT_TEST(testTdf46940_dontEquallyDistributeColumns, "tdf46940_dontEquallyDistributeColumns.docx") @@ -690,7 +690,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf109310_endnoteStyleForMSO) xmlDocUniquePtr pXmlDoc = parseExport("word/endnotes.xml"); // Check w:rStyle element has w:val attribute - note that w: is not specified for attribute assertXPath(pXmlDoc, "/w:endnotes/w:endnote[@w:id='2']/w:p/w:r[1]/w:rPr/w:rStyle"_ostr, "val"_ostr, - "EndnoteCharacters"); + "EndnoteCharacters1"); } CPPUNIT_TEST_FIXTURE(Test, testTdf103389) diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx index a97d8e8b84..66faba5f3e 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx @@ -177,6 +177,8 @@ CPPUNIT_TEST_FIXTURE(Test, testFDO77715) xTOC->update(); OUString const tocContent(xTOC->getAnchor()->getString()); CPPUNIT_ASSERT(tocContent.startsWith("National Infrastructure Bank Aff/Neg Index")); + // check that 1st paragraph has outline level 1 + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty(getParagraph(1), "OutlineLevel")); } CPPUNIT_TEST_FIXTURE(Test, testTOCFlag_u) 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 0000000000..cb72fea62a --- /dev/null +++ b/sw/qa/extras/uiwriter/data/pagebreak-source.fodt @@ -0,0 +1,131 @@ + + + 2024-03-05T16:51:41.5265948182024-03-05T17:24:50.151189313PT24M14S5CIB_OfficeDev/6.4.0.24$Linux_X86_64 LibreOffice_project/2b8ce275a2d1aae9319dfc266e677412b611101d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000..1219cdd95e --- /dev/null +++ b/sw/qa/extras/uiwriter/data/pagebreak-target.fodt @@ -0,0 +1,137 @@ + + + 2024-03-05T16:57:01.1791494012024-03-05T16:58:35.972978544PT1M35S2CIB_OfficeDev/6.4.0.24$Linux_X86_64 LibreOffice_project/2b8ce275a2d1aae9319dfc266e677412b611101d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/qa/extras/uiwriter/data/stylewithlistandindents.fodt b/sw/qa/extras/uiwriter/data/stylewithlistandindents.fodt new file mode 100644 index 0000000000..afb7ae06aa --- /dev/null +++ b/sw/qa/extras/uiwriter/data/stylewithlistandindents.fodt @@ -0,0 +1,182 @@ + + + 2024-05-16T18:27:43.9131312122024-05-16T18:30:43.721654337PT3M1LibreOfficeDev/24.8.0.0.alpha0$Linux_X86_64 LibreOffice_project/a2e7a65b22857abc945af33c7673d9d8b50688d5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Item + + + more + + + + + + \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/data/table-in-table.fodt b/sw/qa/extras/uiwriter/data/table-in-table.fodt new file mode 100644 index 0000000000..e055d343b8 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/table-in-table.fodt @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/data/tdf160842.fodt b/sw/qa/extras/uiwriter/data/tdf160842.fodt new file mode 100644 index 0000000000..217b9c1a95 --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf160842.fodt @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQI12P2Ytilf46NeWX3 + a804HgAg7QTAqXPBTwAAAABJRU5ErkJggg== + + + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing. + + + + + + + + diff --git a/sw/qa/extras/uiwriter/data/tdf161172.fodt b/sw/qa/extras/uiwriter/data/tdf161172.fodt new file mode 100644 index 0000000000..08cea575cc --- /dev/null +++ b/sw/qa/extras/uiwriter/data/tdf161172.fodt @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + foo + + + + + \ No newline at end of file diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx index 21b7358c3d..b6913d4f6b 100644 --- a/sw/qa/extras/uiwriter/uiwriter.cxx +++ b/sw/qa/extras/uiwriter/uiwriter.cxx @@ -318,6 +318,127 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testTdf149548) dispatchCommand(mxComponent, ".uno:Paste", {}); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testPasteTableAtFlyAnchor) +{ + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR); + anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint()); + SfxItemSet flySet(pDoc->GetAttrPool(), svl::Items); + flySet.Put(anchor); + SwFlyFrameFormat const* pFly = dynamic_cast( + 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(); + + dispatchCommand(mxComponent, ".uno:Copy", {}); + + // move cursor back to body + pWrtShell->ClearMark(); + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->GetCurrFlyFrame()); + + dispatchCommand(mxComponent, ".uno:Paste", {}); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->GetNode().FindTableNode()); + + pWrtShell->Undo(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->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()->GetNode().FindTableNode()); + + pWrtShell->Undo(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + CPPUNIT_ASSERT(!pFly->GetAnchor().GetContentAnchor()->GetNode().FindTableNode()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testCopyPastePageBreak) +{ + { + createSwDoc("pagebreak-source.fodt"); + SwDoc* pDoc = getSwDoc(); + + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); + + pWrtShell->SelAll(); + dispatchCommand(mxComponent, ".uno:Copy", {}); + + mxComponent->dispose(); + mxComponent.clear(); + } + + createSwDoc("pagebreak-target.fodt"); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("WithMargin"), getProperty(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + // page style WithMargin is used + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); + + dispatchCommand(mxComponent, ".uno:Paste", {}); + + CPPUNIT_ASSERT_EQUAL(2, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("WithMargin"), getProperty(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()); + // page style WithMargin is used + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); + + pWrtShell->Undo(); + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("WithMargin"), getProperty(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + CPPUNIT_ASSERT_EQUAL(1, getPages()); + // page style WithMargin is used + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); + + pWrtShell->Redo(); + CPPUNIT_ASSERT_EQUAL(2, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("WithMargin"), getProperty(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()); + // page style WithMargin is used + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); + + pWrtShell->Undo(); + CPPUNIT_ASSERT_EQUAL(1, getParagraphs()); + CPPUNIT_ASSERT_EQUAL(OUString("WithMargin"), getProperty(getParagraph(1), "PageDescName")); + CPPUNIT_ASSERT_EQUAL(OUString("TargetSection"), pWrtShell->GetCurrSection()->GetSectionName()); + CPPUNIT_ASSERT_EQUAL(1, getPages()); + // page style WithMargin is used + CPPUNIT_ASSERT_EQUAL(tools::Long(5669), pWrtShell->GetLayout()->GetLower()->getFramePrintArea().Top()); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest, testBookmarkCopy) { createSwDoc(); diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx index af51954487..15852718f4 100644 --- a/sw/qa/extras/uiwriter/uiwriter2.cxx +++ b/sw/qa/extras/uiwriter/uiwriter2.cxx @@ -459,7 +459,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf134250) dispatchCommand(mxComponent, ".uno:Paste", {}); 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 dispatchCommand(mxComponent, ".uno:Undo", {}); @@ -474,7 +474,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf134250) dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); dispatchCommand(mxComponent, ".uno:Undo", {}); @@ -486,7 +486,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf134250) dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf134436) diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index 3027c65855..ba8d03ae7e 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -1333,7 +1333,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf131963) { createSwDoc("tdf131963.docx"); - CPPUNIT_ASSERT_EQUAL(11, getPages()); + CPPUNIT_ASSERT(11 == getPages() || 12 == getPages()); dispatchCommand(mxComponent, ".uno:SelectAll", {}); @@ -1348,7 +1348,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf131963) // Without the fix in place, this test would have crashed here // tdf#133169: without the fix in place, it would have been 2 instead of 11 - CPPUNIT_ASSERT_EQUAL(11, getPages()); + CPPUNIT_ASSERT(11 == getPages() || 12 == getPages()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf132596) @@ -1516,7 +1516,8 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135733) dispatchCommand(mxComponent, ".uno:Paste", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xIndexAccess->getCount()); - CPPUNIT_ASSERT_EQUAL(1, getPages()); + // the table is inserted before the first paragraph, which has a pagedesc + CPPUNIT_ASSERT_EQUAL(2, getPages()); CPPUNIT_ASSERT_EQUAL(2, getShapes()); dispatchCommand(mxComponent, ".uno:Undo", {}); diff --git a/sw/qa/extras/uiwriter/uiwriter6.cxx b/sw/qa/extras/uiwriter/uiwriter6.cxx index 5b4140e029..8e67d26e46 100644 --- a/sw/qa/extras/uiwriter/uiwriter6.cxx +++ b/sw/qa/extras/uiwriter/uiwriter6.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -1406,6 +1411,158 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf154771_MovingMultipleColumns) CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xTable1->getColumns()->getCount()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf44773) +{ + // allow resizing table rows, if cursor outside the table + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // insert an empty paragraph + pWrtShell->SplitNode(); + + // create a table + SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0); + (void)&pWrtShell->InsertTable(TableOpt, 2, 1); + + // the cursor is not inside the table + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + + uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference xTableNames = xTablesSupplier->getTextTables(); + CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); + uno::Reference xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable1->getColumns()->getCount()); + + Scheduler::ProcessEventsToIdle(); + + // set table row height by drag & drop + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + + SwFrame* pPage = pLayout->Lower(); + SwFrame* pBody = pPage->GetLower(); + SwFrame* pTable = pBody->GetLower()->GetNext(); + SwFrame* pRow1 = pTable->GetLower(); + CPPUNIT_ASSERT(pRow1->IsRowFrame()); + SwFrame* pCellA1 = pRow1->GetLower(); + const SwRect& rCellA1Rect = pCellA1->getFrameArea(); + auto nRowHeight = rCellA1Rect.Height(); + // select center of the bottom border of the first table cell + Point ptFrom(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + nRowHeight); + // double the row height + Point ptTo(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + 2 * nRowHeight); + vcl::Window& rEditWin = pDoc->GetDocShell()->GetView()->GetEditWin(); + Point aFrom = rEditWin.LogicToPixel(ptFrom); + MouseEvent aClickEvent(aFrom, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + rEditWin.MouseButtonDown(aClickEvent); + Point aTo = rEditWin.LogicToPixel(ptTo); + MouseEvent aMoveEvent(aTo, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + TrackingEvent aTEvt(aMoveEvent, TrackingEventFlags::Repeat); + // drag & drop of cell border inside the document (and outside the table) + // still based on the ruler code, use that to simulate dragging + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt); + TrackingEvent aTEvt2(aMoveEvent, TrackingEventFlags::End); + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt2); + Scheduler::ProcessEventsToIdle(); + rEditWin.CaptureMouse(); + rEditWin.ReleaseMouse(); + + // this was 396 (not modified row height previously) + CPPUNIT_ASSERT_EQUAL(tools::Long(810), pCellA1->getFrameArea().Height()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf160842) +{ + createSwDoc("tdf160842.fodt"); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + // the cursor is not in the table + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage = dynamic_cast(pLayout->Lower()); + CPPUNIT_ASSERT(pPage); + const SwSortedObjs& rPageObjs = *pPage->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast(1), rPageObjs.size()); + auto pPageFly = dynamic_cast(rPageObjs[0]); + CPPUNIT_ASSERT(pPageFly); + auto pTable = dynamic_cast(pPageFly->GetLower()); + CPPUNIT_ASSERT(pTable); + auto pRow2 = pTable->GetLower()->GetNext(); + CPPUNIT_ASSERT(pRow2->IsRowFrame()); + auto pCellA2 = pRow2->GetLower(); + CPPUNIT_ASSERT(pCellA2); + const SwRect& rCellA2Rect = pCellA2->getFrameArea(); + auto nRowHeight = rCellA2Rect.Height(); + // select center of the bottom cell + Point ptFrom(rCellA2Rect.Left() + rCellA2Rect.Width() / 2, rCellA2Rect.Top() + nRowHeight / 2); + vcl::Window& rEditWin = pDoc->GetDocShell()->GetView()->GetEditWin(); + Point aFrom = rEditWin.LogicToPixel(ptFrom); + MouseEvent aClickEvent(aFrom, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + rEditWin.MouseButtonDown(aClickEvent); + rEditWin.MouseButtonUp(aClickEvent); + + // the cursor is in the table + CPPUNIT_ASSERT(pWrtShell->IsCursorInTable()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf160836) +{ + createSwDoc("tdf160842.fodt"); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // set table row height by drag & drop at images cropped by the fixed row height + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage = dynamic_cast(pLayout->Lower()); + CPPUNIT_ASSERT(pPage); + const SwSortedObjs& rPageObjs = *pPage->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast(1), rPageObjs.size()); + auto pPageFly = dynamic_cast(rPageObjs[0]); + CPPUNIT_ASSERT(pPageFly); + auto pTable = dynamic_cast(pPageFly->GetLower()); + CPPUNIT_ASSERT(pTable); + auto pRow1 = pTable->GetLower(); + CPPUNIT_ASSERT(pRow1->IsRowFrame()); + auto pCellA1 = pRow1->GetLower(); + CPPUNIT_ASSERT(pCellA1); + const SwRect& rCellA1Rect = pCellA1->getFrameArea(); + auto nRowHeight = rCellA1Rect.Height(); + // select center of the bottom border of the first table cell + Point ptFrom(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + nRowHeight); + // halve the row height + Point ptTo(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + 0.5 * nRowHeight); + vcl::Window& rEditWin = pDoc->GetDocShell()->GetView()->GetEditWin(); + Point aFrom = rEditWin.LogicToPixel(ptFrom); + MouseEvent aClickEvent(aFrom, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + rEditWin.MouseButtonDown(aClickEvent); + Point aTo = rEditWin.LogicToPixel(ptTo); + MouseEvent aMoveEvent(aTo, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + TrackingEvent aTEvt(aMoveEvent, TrackingEventFlags::Repeat); + // drag & drop of cell border inside the document (and outside the table) + // still based on the ruler code, use that to simulate dragging + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt); + TrackingEvent aTEvt2(aMoveEvent, TrackingEventFlags::End); + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt2); + Scheduler::ProcessEventsToIdle(); + rEditWin.CaptureMouse(); + rEditWin.ReleaseMouse(); + + // this was 3910 (not modified row height previously) + CPPUNIT_ASSERT_EQUAL(tools::Long(1980), pCellA1->getFrameArea().Height()); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf115132) { createSwDoc(); diff --git a/sw/qa/extras/uiwriter/uiwriter8.cxx b/sw/qa/extras/uiwriter/uiwriter8.cxx index 4fa027b051..60b1bd8e9c 100644 --- a/sw/qa/extras/uiwriter/uiwriter8.cxx +++ b/sw/qa/extras/uiwriter/uiwriter8.cxx @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -870,6 +871,126 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf116315) } } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testInsertAutoTextIntoListFromParaStyle) +{ + createSwDoc("stylewithlistandindents.fodt"); + SwDoc* const pDoc = getSwDoc(); + SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + pWrtShell->FwdPara(); + pWrtShell->EndPara(/*bSelect=*/false); + // expands autotext (via F3) + pWrtShell->Insert(" jacr"); + + SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); + pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_F3); + Scheduler::ProcessEventsToIdle(); + + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->FwdPara(); + + SwNumRule* pNumRule; + SvxTextLeftMarginItem const* pTextLeftMargin; + SvxFirstLineIndentItem const* pFirstLineIndent; + + { + SwTextNode& rNode{ *pWrtShell->GetCursor()->GetPoint()->GetNode().GetTextNode() }; + // numrule from paragraph style, but not from direct formatting + auto pSet{ rNode.GetpSwAttrSet() }; + CPPUNIT_ASSERT(pSet); + // list id was set + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_LIST_ID, false)); + // the numrule is set on the paragraph style, not on the paragraph + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_PARATR_NUMRULE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_NUMRULE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, + pSet->GetItemState(RES_MARGIN_FIRSTLINE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_FIRSTLINE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_MARGIN_TEXTLEFT, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_TEXTLEFT, true)); + CPPUNIT_ASSERT_EQUAL(u"ListAndIndents"_ustr, rNode.GetTextColl()->GetName()); + CPPUNIT_ASSERT_EQUAL(u"Item We confirm receipt of your application material."_ustr, + rNode.GetText()); + pNumRule = rNode.GetNumRule(); + pTextLeftMargin = &rNode.GetAttr(RES_MARGIN_TEXTLEFT); + pFirstLineIndent = &rNode.GetAttr(RES_MARGIN_FIRSTLINE); + } + + pWrtShell->FwdPara(); + + { + SwTextNode& rNode{ *pWrtShell->GetCursor()->GetPoint()->GetNode().GetTextNode() }; + auto pSet{ rNode.GetpSwAttrSet() }; + CPPUNIT_ASSERT(pSet); + // list id was set + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_LIST_ID, false)); + // middle paragraph was pasted - has numrule and indents applied directly + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_NUMRULE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_FIRSTLINE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_TEXTLEFT, false)); + CPPUNIT_ASSERT_EQUAL(u"Default Paragraph Style"_ustr, rNode.GetTextColl()->GetName()); + CPPUNIT_ASSERT(rNode.GetText().startsWith("As more applicants applied")); + CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule()); + CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(), + rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft()); + CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(), + rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset()); + } + + pWrtShell->FwdPara(); + + { + SwTextNode& rNode{ *pWrtShell->GetCursor()->GetPoint()->GetNode().GetTextNode() }; + // numrule from paragraph style, but not from direct formatting + auto pSet{ rNode.GetpSwAttrSet() }; + CPPUNIT_ASSERT(pSet); + // list id was set + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_LIST_ID, false)); + // the numrule is set on the paragraph style, not on the paragraph + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_PARATR_NUMRULE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_NUMRULE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, + pSet->GetItemState(RES_MARGIN_FIRSTLINE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_FIRSTLINE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_MARGIN_TEXTLEFT, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_TEXTLEFT, true)); + CPPUNIT_ASSERT_EQUAL(u"ListAndIndents"_ustr, rNode.GetTextColl()->GetName()); + CPPUNIT_ASSERT(rNode.GetText().endsWith("as soon as we have come to a decision.")); + CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule()); + CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(), + rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft()); + CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(), + rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset()); + } + + pWrtShell->FwdPara(); + + { + SwTextNode& rNode{ *pWrtShell->GetCursor()->GetPoint()->GetNode().GetTextNode() }; + // numrule from paragraph style, but not from direct formatting + auto pSet{ rNode.GetpSwAttrSet() }; + CPPUNIT_ASSERT(pSet); + // list id was set + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_LIST_ID, false)); + // the numrule is set on the paragraph style, not on the paragraph + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_PARATR_NUMRULE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_PARATR_NUMRULE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, + pSet->GetItemState(RES_MARGIN_FIRSTLINE, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_FIRSTLINE, true)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, pSet->GetItemState(RES_MARGIN_TEXTLEFT, false)); + CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, pSet->GetItemState(RES_MARGIN_TEXTLEFT, true)); + CPPUNIT_ASSERT_EQUAL(u"ListAndIndents"_ustr, rNode.GetTextColl()->GetName()); + CPPUNIT_ASSERT_EQUAL(u"more"_ustr, rNode.GetText()); // pre-exising list item + CPPUNIT_ASSERT_EQUAL(pNumRule, rNode.GetNumRule()); + CPPUNIT_ASSERT_EQUAL(pTextLeftMargin->GetTextLeft(), + rNode.GetAttr(RES_MARGIN_TEXTLEFT).GetTextLeft()); + CPPUNIT_ASSERT_EQUAL(pFirstLineIndent->GetTextFirstLineOffset(), + rNode.GetAttr(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset()); + } +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf144364) { createSwDoc(); diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx b/sw/qa/extras/uiwriter/uiwriter9.cxx index 3772955dd9..2389aa6371 100644 --- a/sw/qa/extras/uiwriter/uiwriter9.cxx +++ b/sw/qa/extras/uiwriter/uiwriter9.cxx @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -200,6 +201,61 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159816) xTransfer->PrivateDrop(*pWrtShell, ptTo, /*bMove=*/true, /*bXSelection=*/true); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf160898) +{ + // Given a document with a 1-cell table in another 1-cell table: + createSwDoc("table-in-table.fodt"); + SwXTextDocument* pXTextDocument = dynamic_cast(mxComponent.get()); + SwDocShell* pDocShell = pXTextDocument->GetDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + + // Move to the normally hidden paragraph inside the outer table cell, following the inner table + pWrtShell->Down(false, 2); + // Without the fix, this would crash: + pWrtShell->SelAll(); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf161172) +{ + // Given a paragraph manually made a member of a list: + createSwDoc("tdf161172.fodt"); + auto para = getParagraph(1); + + // Check initial state: the first paragraph has "No_list" para style, "Num_1" numbering style, + // numbering level 0, and "Num1_lvl1_1" numbering label. + CPPUNIT_ASSERT_EQUAL(u"No_list"_ustr, getProperty(para, u"ParaStyleName"_ustr)); + CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty(para, u"NumberingStyleName"_ustr)); + CPPUNIT_ASSERT_EQUAL(u"Num1_lvl1_1"_ustr, getProperty(para, u"ListLabelString"_ustr)); + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty(para, u"NumberingLevel"_ustr)); + + // Assign "Num_1_lvl2" paragraph style to the first paragraph. The style is associated with + // "Num_1" numbering style, level 1. + dispatchCommand(mxComponent, u".uno:StyleApply"_ustr, + { comphelper::makePropertyValue(u"FamilyName"_ustr, u"ParagraphStyles"_ustr), + comphelper::makePropertyValue(u"Style"_ustr, u"Num_1_lvl2"_ustr) }); + + // Check that the respective properties got correctly applied + CPPUNIT_ASSERT_EQUAL(u"Num_1_lvl2"_ustr, getProperty(para, u"ParaStyleName"_ustr)); + CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty(para, u"NumberingStyleName"_ustr)); + CPPUNIT_ASSERT_EQUAL(u"Num1_lvl2_1"_ustr, getProperty(para, u"ListLabelString"_ustr)); + CPPUNIT_ASSERT_EQUAL(sal_Int16(1), getProperty(para, u"NumberingLevel"_ustr)); + + // Undo + dispatchCommand(mxComponent, u".uno:Undo"_ustr, {}); + + // Check that the numbering properties got correctly restored + CPPUNIT_ASSERT_EQUAL(u"No_list"_ustr, getProperty(para, u"ParaStyleName"_ustr)); + CPPUNIT_ASSERT_EQUAL(u"Num_1"_ustr, getProperty(para, u"NumberingStyleName"_ustr)); + // Without the fix, this would fail with + // - Expected: Num1_lvl1_1 + // - Actual : Num1_lvl2_1 + CPPUNIT_ASSERT_EQUAL(u"Num1_lvl1_1"_ustr, getProperty(para, u"ListLabelString"_ustr)); + // Without the fix, this would fail with + // - Expected: 0 + // - Actual : 1 + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty(para, u"NumberingLevel"_ustr)); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/unowriter/data/tdf161035.fodt b/sw/qa/extras/unowriter/data/tdf161035.fodt new file mode 100644 index 0000000000..8f1d53b083 --- /dev/null +++ b/sw/qa/extras/unowriter/data/tdf161035.fodt @@ -0,0 +1,9 @@ + + + + + + Lorem ipsum. + + + \ No newline at end of file diff --git a/sw/qa/extras/unowriter/unowriter.cxx b/sw/qa/extras/unowriter/unowriter.cxx index 80b9e556f7..7175e702d7 100644 --- a/sw/qa/extras/unowriter/unowriter.cxx +++ b/sw/qa/extras/unowriter/unowriter.cxx @@ -1222,6 +1222,33 @@ CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf160278) CPPUNIT_ASSERT_EQUAL(u"12test"_ustr, xText->getString()); } +CPPUNIT_TEST_FIXTURE(SwUnoWriter, testTdf161035) +{ + // Given a paragraph with a bookmark: + createSwDoc("tdf161035.fodt"); + auto xModel = mxComponent.queryThrow(); + + // Create a text view cursor in the paragraph. + auto xController = xModel->getCurrentController().queryThrow(); + auto xViewCursor = xController->getViewCursor(); + CPPUNIT_ASSERT(xViewCursor); + auto xText = xViewCursor->getText(); + CPPUNIT_ASSERT(xText); + // Create a text cursor from the text view cursor, and move it to the end of the paragraph + auto xTextCursor = xText->createTextCursorByRange(xViewCursor); + CPPUNIT_ASSERT(xTextCursor); + xTextCursor->gotoEnd(false); + // Get the first paragraph portion from the text cursor + auto xParaEnum = xTextCursor.queryThrow()->createEnumeration(); + CPPUNIT_ASSERT(xParaEnum); + auto xPara = xParaEnum->nextElement().queryThrow(); + // Try to enumerate text portions. Without the fix, it would fail an assertion in debug builds, + // and hang in release builds, because the paragraph portion started after the bookmark, and + // so the bookmark wasn't processed (expectedly): + auto xRunEnum = xPara->createEnumeration(); + CPPUNIT_ASSERT(!xRunEnum->hasMoreElements()); // Empty enumeration for empty selection +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/ww8export/data/draw-obj-rtl-no-mirror-vml.docx b/sw/qa/extras/ww8export/data/draw-obj-rtl-no-mirror-vml.docx new file mode 100644 index 0000000000..3b291901f7 Binary files /dev/null and b/sw/qa/extras/ww8export/data/draw-obj-rtl-no-mirror-vml.docx differ diff --git a/sw/qa/extras/ww8export/ww8export4.cxx b/sw/qa/extras/ww8export/ww8export4.cxx index d31bf17a31..3fb0ae7b03 100644 --- a/sw/qa/extras/ww8export/ww8export4.cxx +++ b/sw/qa/extras/ww8export/ww8export4.cxx @@ -252,6 +252,30 @@ CPPUNIT_TEST_FIXTURE(Test, testLegalNumbering) verify(); } +CPPUNIT_TEST_FIXTURE(Test, testDOCExportDoNotMirrorRtlDrawObjs) +{ + // Given a document with a shape, anchored in an RTL paragraph, loaded from DOCX: + createSwDoc("draw-obj-rtl-no-mirror-vml.docx"); + + // When saving that to DOC: + saveAndReload(mpFilter); + + // Then make sure the shape is on the right margin: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + sal_Int32 nPageRight = getXPath(pXmlDoc, "//page/infos/bounds"_ostr, "right"_ostr).toInt32(); + sal_Int32 nBodyRight = getXPath(pXmlDoc, "//body/infos/bounds"_ostr, "right"_ostr).toInt32(); + sal_Int32 nShapeLeft + = getXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds"_ostr, "left"_ostr).toInt32(); + CPPUNIT_ASSERT_GREATER(nBodyRight, nShapeLeft); + sal_Int32 nShapeRight + = getXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds"_ostr, "right"_ostr).toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected less than: 12523 + // - Actual : 12536 + // i.e. the shape was outside of the page right margin area, due to an unwanted mapping. + CPPUNIT_ASSERT_LESS(nPageRight, nShapeRight); +} + DECLARE_WW8EXPORT_TEST(testNonInlinePageBreakFirstLine, "nonInlinePageBreakFirstLine.doc") { SwDoc* pDoc = getSwDoc(); diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx index d38d0bbfc9..3e6d9c93d5 100644 --- a/sw/qa/uibase/uno/uno.cxx +++ b/sw/qa/uibase/uno/uno.cxx @@ -560,6 +560,30 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testAllowTextAfterFloatingTableBreak) CPPUNIT_ASSERT(bAllowTextAfterFloatingTableBreak); } +CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testDoNotMirrorRtlDrawObjs) +{ + // Given an empty document: + createSwDoc(); + + // When checking the state of the DoNotMirrorRtlDrawObjs compat flag: + uno::Reference xDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xSettings( + xDocument->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY); + bool bDoNotMirrorRtlDrawObjs{}; + // Without the accompanying fix in place, this test would have failed with: + // An uncaught exception of type com.sun.star.beans.UnknownPropertyException + // i.e. the compat flag was not recognized. + xSettings->getPropertyValue("DoNotMirrorRtlDrawObjs") >>= bDoNotMirrorRtlDrawObjs; + // Then make sure it's false by default: + CPPUNIT_ASSERT(!bDoNotMirrorRtlDrawObjs); + + // And when setting DoNotMirrorRtlDrawObjs=true: + xSettings->setPropertyValue("DoNotMirrorRtlDrawObjs", uno::Any(true)); + // Then make sure it gets enabled: + xSettings->getPropertyValue("DoNotMirrorRtlDrawObjs") >>= bDoNotMirrorRtlDrawObjs; + CPPUNIT_ASSERT(bDoNotMirrorRtlDrawObjs); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/writerfilter/cppunittests/filter/WriterFilter.cxx b/sw/qa/writerfilter/cppunittests/filter/WriterFilter.cxx new file mode 100644 index 0000000000..10b8cab57a --- /dev/null +++ b/sw/qa/writerfilter/cppunittests/filter/WriterFilter.cxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace +{ +/// Tests for sw/source/writerfilter/filter/WriterFilter.cxx. +class Test : public UnoApiXmlTest +{ +public: + Test() + : UnoApiXmlTest("/sw/qa/writerfilter/cppunittests/filter/data/") + { + } +}; + +CPPUNIT_TEST_FIXTURE(Test, testDoNotMirrorRtlDrawObjs) +{ + // Given a document with a shape, anchored in an RTL paragraph: + // When loading that document: + loadFromFile(u"draw-obj-rtl-no-mirror.docx"); + + // Then make sure the shape is on the right margin: + uno::Reference xModel(mxComponent, uno::UNO_QUERY); + css::uno::Reference xDumper(xModel->getCurrentController(), uno::UNO_QUERY); + OString aDump = xDumper->dump("layout").toUtf8(); + auto pCharBuffer = reinterpret_cast(aDump.getStr()); + xmlDocUniquePtr pXmlDoc(xmlParseDoc(pCharBuffer)); + sal_Int32 nBodyRight = getXPath(pXmlDoc, "//body/infos/bounds"_ostr, "right"_ostr).toInt32(); + sal_Int32 nShapeLeft + = getXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds"_ostr, "left"_ostr).toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected greater than: 11083 + // - Actual : 722 + // i.e. the shape was on the left margin. + CPPUNIT_ASSERT_GREATER(nBodyRight, nShapeLeft); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/writerfilter/cppunittests/filter/data/draw-obj-rtl-no-mirror.docx b/sw/qa/writerfilter/cppunittests/filter/data/draw-obj-rtl-no-mirror.docx new file mode 100644 index 0000000000..7988384b2c Binary files /dev/null and b/sw/qa/writerfilter/cppunittests/filter/data/draw-obj-rtl-no-mirror.docx differ diff --git a/sw/source/core/access/accfrmobj.cxx b/sw/source/core/access/accfrmobj.cxx index f78dc2d155..004ca4ab94 100644 --- a/sw/source/core/access/accfrmobj.cxx +++ b/sw/source/core/access/accfrmobj.cxx @@ -240,13 +240,16 @@ SwRect SwAccessibleChild::GetBox( const SwAccessibleMap& rAccMap ) const // by the mpFrame case above b) for genuine SdrObject this must be set // if it's connected to layout assert(dynamic_cast(pContact)); - SwPageFrame const*const pPage(const_cast( - pContact->GetAnchoredObj(mpDrawObj))->FindPageFrameOfAnchor()); - if (pPage) // may end up here with partial layout -> not visible + if (pContact) { - aBox = SwRect( mpDrawObj->GetCurrentBoundRect() ); - // tdf#91260 drawing object may be partially off-page - aBox.Intersection(pPage->getFrameArea()); + SwPageFrame const*const pPage(const_cast( + pContact->GetAnchoredObj(mpDrawObj))->FindPageFrameOfAnchor()); + if (pPage) // may end up here with partial layout -> not visible + { + aBox = SwRect( mpDrawObj->GetCurrentBoundRect() ); + // tdf#91260 drawing object may be partially off-page + aBox.Intersection(pPage->getFrameArea()); + } } } else if ( mpWindow ) diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index b7f6962982..33f11e9a28 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -738,9 +738,15 @@ bool SwCursorShell::MoveStartText() SwTableNode const*const pTable(pStartNode->FindTableNode()); m_pCurrentCursor->GetPoint()->Assign(*pStartNode); GetDoc()->GetNodes().GoNext(m_pCurrentCursor->GetPoint()); - while (m_pCurrentCursor->GetPoint()->GetNode().FindTableNode() != pTable - && (!pTable || pTable->GetIndex() < m_pCurrentCursor->GetPoint()->GetNode().FindTableNode()->GetIndex()) - && MoveOutOfTable()); + while (auto* pFoundTable = m_pCurrentCursor->GetPoint()->GetNode().FindTableNode()) + { + if (pFoundTable == pTable) + break; + if (pTable && pTable->GetIndex() >= pFoundTable->GetIndex()) + break; + if (!MoveOutOfTable()) + break; + } UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); return old != *m_pCurrentCursor->GetPoint(); } diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index 57b8e58310..234d73aecb 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -5065,16 +5066,14 @@ 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.GetNodeIndex() - pCopyPam->GetPoint()->GetNodeIndex()) > SwNodeOffset(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->GetNode().IsSectionNode() && !pStt->GetNode().IsTableNode()) { - // First go back to the original place - *(pCopyPam->GetPoint()) = rPos; - - bCanMoveBack = false; - bAfterTable = true; + bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); } if( !bCanMoveBack ) { @@ -5084,6 +5083,7 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() ); SwNodeIndex aInsPos( rPos.GetNode() ); + ::std::optional oInsContentIndex; const bool bOneNode = pStt->GetNode() == pEnd->GetNode(); SwTextNode* pSttTextNd = pStt->GetNode().GetTextNode(); SwTextNode* pEndTextNd = pEnd->GetNode().GetTextNode(); @@ -5107,12 +5107,16 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // bullet list. // Keep also the value for possible propagation. OUString aListIdToPropagate; + SvxTextLeftMarginItem const* pTextLeftMarginToPropagate{nullptr}; + SvxFirstLineIndentItem const* pFirstLineIndentToPropagate{nullptr}; const SwNumRule* pNumRuleToPropagate = - rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true ); + rDoc.SearchNumRule(rPos, false, true, false, 0, aListIdToPropagate, nullptr, + true, &pTextLeftMarginToPropagate, &pFirstLineIndentToPropagate); if ( !pNumRuleToPropagate ) { pNumRuleToPropagate = - rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true ); + rDoc.SearchNumRule(rPos, false, false, false, 0, aListIdToPropagate, nullptr, + true, &pTextLeftMarginToPropagate, &pFirstLineIndentToPropagate); } // #i86492# // Do not propagate previous found list, if @@ -5236,8 +5240,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.GetContentIndex() == pDestTextNd->Len() ) - { // Insertion at the last position of a textnode (empty or not) + if (pDestTextNd->Len() && rPos.GetContentIndex() == pDestTextNd->Len()) + { // Insertion at the last position of a textnode ++aInsPos; // The table will be inserted behind the text node } else if( rPos.GetContentIndex() ) @@ -5269,27 +5273,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()->Adjust(SwNodeOffset(-1)); - } + assert(!bCanMoveBack); } pDestTextNd = aInsPos.GetNode().GetTextNode(); if (pEndTextNd) { - SwContentIndex aDestIdx( aInsPos.GetNode().GetContentNode(), rPos.GetContentIndex() ); + oInsContentIndex.emplace(aInsPos.GetNode().GetContentNode(), rPos.GetContentIndex()); if( !pDestTextNd ) { pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode(), rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); - aDestIdx.Assign( pDestTextNd, 0 ); + oInsContentIndex->Assign(pDestTextNd, 0); --aInsPos; // if we have to insert an extra text node @@ -5307,8 +5302,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo PUSH_NUMRULE_STATE } - pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwContentIndex( pEndTextNd ), - pEnd->GetContentIndex() ); + pEndTextNd->CopyText(pDestTextNd, *oInsContentIndex, + SwContentIndex(pEndTextNd), pEnd->GetContentIndex()); // Also copy all format templates if( bCopyCollFormat && ( bOneNode || bEmptyDestNd )) @@ -5360,20 +5355,29 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo bCopyBookmarks = false; } + + // init *again* - because CopyWithFlyInFly moved startPos + SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1)); // 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.GetNode().IsTextNode() + || (pCopyPam->GetPoint()->GetNode().IsStartNode() + && startPos.GetNode().IsSectionNode()))) // not into table { - // init *again* - because CopyWithFlyInFly moved startPos - SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1)); 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.GetNode().IsSectionNode()) + { // probably on top-level start node, so no CheckNodesRange here; + GoNextPos(&startPos, false); // SwFEShell::Paste() deletes node + } assert(startPos.GetNode().IsContentNode()); SwPosition startPosAtPara(startPos); startPosAtPara.nContent.Assign(nullptr, 0); @@ -5456,26 +5460,31 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo } else // incremented in (!pSttTextNd && pDestTextNd) above { - pCopyPam->GetMark()->Assign(aInsPos); + // assign also content index in this case, see testSectionAnchorCopyTableAtStart + assert(oInsContentIndex); + assert(oInsContentIndex->GetContentNode() == &aInsPos.GetNode()); + pCopyPam->GetMark()->Assign(aInsPos, oInsContentIndex->GetIndex()); } rPos = *pCopyPam->GetMark(); } else *pCopyPam->GetMark() = rPos; - if ( !bAfterTable ) - pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode ); + if (bCanMoveBack) + { + pCopyPam->Move(fnMoveForward, GoInContent); + } else { // Reset the offset to 0 as it was before the insertion pCopyPam->GetPoint()->Adjust(SwNodeOffset(+1)); - // 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()->GetNode().IsStartNode()) pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1)); - } + oInsContentIndex.reset(); pCopyPam->Exchange(); // Also copy all bookmarks @@ -5513,8 +5522,12 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo // #i86492# - use , because it also handles the // Don't reset indent attributes, that would mean loss of direct // formatting. - rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr, - aListIdToPropagate, true, /*bResetIndentAttrs=*/false ); + // It could be that pNumRuleToPropagate is already applied via + // the paragraph style, in that case applying it again in mpAttrSet could + // override indents, so avoid that. + rDoc.SetNumRule(*pCopyPam, *pNumRuleToPropagate, + SwDoc::SetNumRuleMode::DontSetIfAlreadyApplied, nullptr, aListIdToPropagate, + pTextLeftMarginToPropagate, pFirstLineIndentToPropagate); } rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); diff --git a/sw/source/core/doc/DocumentSettingManager.cxx b/sw/source/core/doc/DocumentSettingManager.cxx index baa0f29326..14cfa5fd3c 100644 --- a/sw/source/core/doc/DocumentSettingManager.cxx +++ b/sw/source/core/doc/DocumentSettingManager.cxx @@ -250,10 +250,14 @@ bool sw::DocumentSettingManager::get(/*[in]*/ DocumentSettingId id) const case DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE: return mbAutoFirstLineIndentDisregardLineSpace; case DocumentSettingId::HYPHENATE_URLS: return mbHyphenateURLs; + case DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH: + return mbApplyTextAttrToEmptyLineAtEndOfParagraph; case DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES: return mbDoNotBreakWrappedTables; case DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK: return mbAllowTextAfterFloatingTableBreak; + case DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS: + return mbDoNotMirrorRtlDrawObjs; case DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING: return mbJustifyLinesWithShrinking; case DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY: return mbNoNumberingShowFollowBy; @@ -444,6 +448,14 @@ void sw::DocumentSettingManager::set(/*[in]*/ DocumentSettingId id, /*[in]*/ boo mbHyphenateURLs = value; break; + case DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH: + mbApplyTextAttrToEmptyLineAtEndOfParagraph = value; + break; + + case DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS: + mbDoNotMirrorRtlDrawObjs = value; + break; + case DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES: mbDoNotBreakWrappedTables = value; break; @@ -1091,7 +1103,16 @@ void sw::DocumentSettingManager::dumpAsXml(xmlTextWriterPtr pWriter) const (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mnImagePreferredDPI")); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(mnImagePreferredDPI).getStr())); + (void)xmlTextWriterEndElement(pWriter); + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbApplyTextAttrToEmptyLineAtEndOfParagraph")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbApplyTextAttrToEmptyLineAtEndOfParagraph).getStr())); + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotMirrorRtlDrawObjs")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbDoNotMirrorRtlDrawObjs).getStr())); (void)xmlTextWriterEndElement(pWriter); (void)xmlTextWriterEndElement(pWriter); diff --git a/sw/source/core/doc/docdraw.cxx b/sw/source/core/doc/docdraw.cxx index 521ca2b0ba..09b1d9afb6 100644 --- a/sw/source/core/doc/docdraw.cxx +++ b/sw/source/core/doc/docdraw.cxx @@ -68,6 +68,9 @@ static void lcl_AdjustPositioningAttr( SwDrawFrameFormat* _pFrameFormat, const SwContact* pContact = GetUserCall( &_rSdrObj ); OSL_ENSURE( pContact, " - missing contact object." ); + if (!pContact) + return; + // determine position of new group object relative to its anchor frame position SwTwips nHoriRelPos = 0; SwTwips nVertRelPos = 0; @@ -195,6 +198,9 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) // Revoke anchor attribute. SwDrawContact *pMyContact = static_cast(GetUserCall(pObj)); + if (!pMyContact) + return pNewContact; + const SwFormatAnchor aAnch( pMyContact->GetFormat()->GetAnchor() ); std::unique_ptr pUndo; @@ -216,6 +222,9 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + if (!pContact) + continue; + // #i53320# #if OSL_DEBUG_LEVEL > 0 SwAnchoredDrawObject* pAnchoredDrawObj = @@ -352,6 +361,9 @@ void SwDoc::UnGroupSelection( SdrView& rDrawView ) { SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + if (!pContact) + continue; + std::shared_ptr pTextBoxNode; if (auto pGroupFormat = pContact->GetFormat()) pTextBoxNode = pGroupFormat->GetOtherTextBoxFormats(); diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx index 1c696bebb6..3390a50605 100644 --- a/sw/source/core/doc/docfmt.cxx +++ b/sw/source/core/doc/docfmt.cxx @@ -1040,14 +1040,12 @@ static bool lcl_SetTextFormatColl( SwNode* pNode, void* pArgs ) } } + std::optional oRegH; + if (pPara->pHistory) + oRegH.emplace(&rTNd, rTNd, pPara->pHistory); + if ( bChangeOfListStyleAtParagraph ) { - std::unique_ptr< SwRegHistory > pRegH; - if ( pPara->pHistory ) - { - pRegH.reset(new SwRegHistory(&rTNd, rTNd, pPara->pHistory)); - } - pCNd->ResetAttr( RES_PARATR_NUMRULE ); // reset all list attributes diff --git a/sw/source/core/doc/docnum.cxx b/sw/source/core/doc/docnum.cxx index 0735380e5d..01c4a1b387 100644 --- a/sw/source/core/doc/docnum.cxx +++ b/sw/source/core/doc/docnum.cxx @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -860,11 +861,11 @@ static void lcl_ChgNumRule( SwDoc& rDoc, const SwNumRule& rRule ) OUString SwDoc::SetNumRule( const SwPaM& rPam, const SwNumRule& rRule, - const bool bCreateNewList, + SetNumRuleMode eMode, SwRootFrame const*const pLayout, const OUString& sContinuedListId, - bool bSetItem, - const bool bResetIndentAttrs ) + SvxTextLeftMarginItem const*const pTextLeftMarginToPropagate, + SvxFirstLineIndentItem const*const pFirstLineIndentToPropagate) { OUString sListId; @@ -902,9 +903,9 @@ OUString SwDoc::SetNumRule( const SwPaM& rPam, } } - if ( bSetItem ) + if (!(eMode & SetNumRuleMode::DontSetItem)) { - if ( bCreateNewList ) + if (eMode & SetNumRuleMode::CreateNewList) { if ( bNewNumRuleCreated ) { @@ -944,7 +945,7 @@ OUString SwDoc::SetNumRule( const SwPaM& rPam, if (pRule && pRule->GetName() == pNewOrChangedNumRule->GetName()) { - bSetItem = false; + eMode |= SetNumRuleMode::DontSetItem; if ( !pTextNd->IsInList() ) { pTextNd->AddToList(); @@ -961,21 +962,67 @@ OUString SwDoc::SetNumRule( const SwPaM& rPam, if ( pCollRule && pCollRule->GetName() == pNewOrChangedNumRule->GetName() ) { pTextNd->ResetAttr( RES_PARATR_NUMRULE ); - bSetItem = false; + eMode |= SetNumRuleMode::DontSetItem; } } } } } - if ( bSetItem ) + if (!(eMode & SetNumRuleMode::DontSetItem)) { - getIDocumentContentOperations().InsertPoolItem(aPam, - SwNumRuleItem(pNewOrChangedNumRule->GetName()), - SetAttrMode::DEFAULT, pLayout); + if (eMode & SetNumRuleMode::DontSetIfAlreadyApplied) + { + for (SwNodeIndex i = aPam.Start()->nNode; i <= aPam.End()->nNode; ++i) + { + if (SwTextNode const*const pNode = i.GetNode().GetTextNode()) + { + if (pNode->GetNumRule(true) != pNewOrChangedNumRule) + { + // only apply if it doesn't already have it - to + // avoid overriding indents from style + SwPaM const temp(*pNode, 0, *pNode, pNode->Len()); + getIDocumentContentOperations().InsertPoolItem(temp, + SwNumRuleItem(pNewOrChangedNumRule->GetName()), + SetAttrMode::DEFAULT, pLayout); + // apply provided margins to get visually same result + if (pTextLeftMarginToPropagate) + { + getIDocumentContentOperations().InsertPoolItem(temp, + *pTextLeftMarginToPropagate, + SetAttrMode::DEFAULT, pLayout); + } + if (pFirstLineIndentToPropagate) + { + getIDocumentContentOperations().InsertPoolItem(temp, + *pFirstLineIndentToPropagate, + SetAttrMode::DEFAULT, pLayout); + } + } + } + } + } + else + { + getIDocumentContentOperations().InsertPoolItem(aPam, + SwNumRuleItem(pNewOrChangedNumRule->GetName()), + SetAttrMode::DEFAULT, pLayout); + if (pTextLeftMarginToPropagate) + { + getIDocumentContentOperations().InsertPoolItem(aPam, + *pTextLeftMarginToPropagate, + SetAttrMode::DEFAULT, pLayout); + } + if (pFirstLineIndentToPropagate) + { + getIDocumentContentOperations().InsertPoolItem(aPam, + *pFirstLineIndentToPropagate, + SetAttrMode::DEFAULT, pLayout); + } + } } - if ( bResetIndentAttrs + if ((eMode & SetNumRuleMode::ResetIndentAttrs) && pNewOrChangedNumRule->Get( 0 ).GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) { const o3tl::sorted_vector attrs{ RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT, RES_MARGIN_RIGHT }; @@ -1275,7 +1322,7 @@ void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM) SetNumRule( aPam, *aListStyleData.pReplaceNumRule, - aListStyleData.bCreateNewList, + aListStyleData.bCreateNewList ? SetNumRuleMode::CreateNewList : SetNumRuleMode::Default, nullptr, aListStyleData.sListId ); if ( aListStyleData.bCreateNewList ) @@ -1624,7 +1671,9 @@ const SwNumRule * SwDoc::SearchNumRule(const SwPosition & rPos, int nNonEmptyAllowed, OUString& sListId, SwRootFrame const* pLayout, - const bool bInvestigateStartNode) + const bool bInvestigateStartNode, + SvxTextLeftMarginItem const** o_ppTextLeftMargin, + SvxFirstLineIndentItem const** o_ppFirstLineIndent) { const SwNumRule * pResult = nullptr; SwTextNode * pTextNd = rPos.GetNode().GetTextNode(); @@ -1661,9 +1710,28 @@ const SwNumRule * SwDoc::SearchNumRule(const SwPosition & rPos, ( ( bNum && pNumRule->Get(0).IsEnumeration()) || ( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560# { - pResult = pTextNd->GetNumRule(); + pResult = pNumRule; // provide also the list id, to which the text node belongs. sListId = pTextNd->GetListId(); + // also get the margins that override the numrule + int const nListLevel{pTextNd->GetActualListLevel()}; + if ((o_ppTextLeftMargin || o_ppFirstLineIndent) + && 0 <= nListLevel + && pNumRule->Get(o3tl::narrowing(nListLevel)) + .GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + ::sw::ListLevelIndents const indents{pTextNd->AreListLevelIndentsApplicable()}; + if (!(indents & ::sw::ListLevelIndents::LeftMargin) + && o_ppTextLeftMargin) + { + *o_ppTextLeftMargin = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT); + } + if (!(indents & ::sw::ListLevelIndents::FirstLine) + && o_ppFirstLineIndent) + { + *o_ppFirstLineIndent = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE); + } + } } break; diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx index d29223050b..f9533b37f6 100644 --- a/sw/source/core/doc/doctxm.cxx +++ b/sw/source/core/doc/doctxm.cxx @@ -1375,7 +1375,8 @@ void SwTOXBaseSection::UpdateTemplate(const SwTextNode* pOwnChapterNode, pTextNd->getLayoutFrame(pLayout) && pTextNd->GetNodes().IsDocNodes() && // tdf#40142 - consider level settings of the various text nodes - o3tl::make_unsigned(pTextNd->GetAttrOutlineLevel()) <= GetLevel() && + (TOX_CONTENT != SwTOXBase::GetType() || + o3tl::make_unsigned(pTextNd->GetAttrOutlineLevel()) <= GetLevel()) && (!pLayout || !pLayout->HasMergedParas() || static_cast(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) && (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd))) diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 3506dff230..850dc5a827 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1862,7 +1862,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/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx index aac8f2393c..a91e92569f 100644 --- a/sw/source/core/draw/dcontact.cxx +++ b/sw/source/core/draw/dcontact.cxx @@ -170,12 +170,16 @@ SwRect GetBoundRectOfAnchoredObj( const SdrObject* pObj ) /// Returns the UserCall if applicable from the group object SwContact* GetUserCall( const SdrObject* pObj ) { - SdrObject *pTmp; - while ( !pObj->GetUserCall() && nullptr != (pTmp = pObj->getParentSdrObjectFromSdrObject()) ) - pObj = pTmp; - assert((!pObj->GetUserCall() || nullptr != dynamic_cast(pObj->GetUserCall())) && - "<::GetUserCall(..)> - wrong type of found object user call." ); - return static_cast(pObj->GetUserCall()); + for (; pObj; pObj = pObj->getParentSdrObjectFromSdrObject()) + { + if (auto pUserCall = pObj->GetUserCall()) + { + assert(dynamic_cast(pUserCall) + && "<::GetUserCall(..)> - wrong type of found object user call."); + return static_cast(pUserCall); + } + } + return nullptr; } /// Returns true if the SrdObject is a Marquee-Object (scrolling text) @@ -2216,8 +2220,9 @@ namespace sdr::contact void VOCOfDrawVirtObj::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const { - // tdf#91260 have already checked top-level one is on the right page - assert(isPrimitiveVisible(rDisplayInfo)); + // this may be called for painting where it's a precondition that + // isPrimitiveVisible() is true, or for e.g. getObjectRange() (even + // during layout) where there are no preconditions... // nasty corner case: override to clear page frame to disable the // sub-objects' anchor check, because their anchor is always on // the first page that the page style is applied to diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx index fe0db61925..5499dad7c9 100644 --- a/sw/source/core/draw/dview.cxx +++ b/sw/source/core/draw/dview.cxx @@ -210,10 +210,14 @@ void SwDrawView::AddCustomHdl() { const SdrMarkList &rMrkList = GetMarkedObjectList(); - if(rMrkList.GetMarkCount() != 1 || !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj())) + if(rMrkList.GetMarkCount() != 1) return; SdrObject *pObj = rMrkList.GetMark(0)->GetMarkedSdrObj(); + SwContact* pContact = ::GetUserCall( pObj ); + if (!pContact) + return; + // make code robust SwFrameFormat* pFrameFormat( ::FindFrameFormat( pObj ) ); if ( !pFrameFormat ) @@ -236,7 +240,7 @@ void SwDrawView::AddCustomHdl() { // #i28701# - use last character rectangle saved at object // in order to avoid a format of the anchor frame - SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( pObj ); // Invalidate/recalc LastCharRect which can contain invalid frame offset because // of later frame changes @@ -360,7 +364,8 @@ void SwDrawView::MoveRepeatedObjs( const SwAnchoredObject& _rMovedAnchoredObj, { const SwContact* pContact = ::GetUserCall( _rMovedAnchoredObj.GetDrawObj() ); assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash."); - pContact->GetAnchoredObjs( aAnchoredObjs ); + if (pContact) + pContact->GetAnchoredObjs( aAnchoredObjs ); } // check, if 'repeated' objects exists. @@ -402,7 +407,8 @@ void SwDrawView::MoveRepeatedObjs( const SwAnchoredObject& _rMovedAnchoredObj, { const SwContact* pContact = ::GetUserCall( pChildObj ); assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash."); - pContact->GetAnchoredObjs( aAnchoredObjs ); + if (pContact) + pContact->GetAnchoredObjs( aAnchoredObjs ); } // move 'repeated' ones to the same order number as the already moved one. const size_t nTmpNewPos = pChildObj->GetOrdNum(); @@ -449,8 +455,11 @@ void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos, pDrawPage->RecalcObjOrdNums(); const size_t nObjCount = pDrawPage->GetObjCount(); - SwAnchoredObject* pMovedAnchoredObj = - ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwContact* pContact = ::GetUserCall( pObj ); + if (!pContact) + return; + + SwAnchoredObject* pMovedAnchoredObj = pContact->GetAnchoredObj( pObj ); const SwFlyFrame* pParentAnchoredObj = pMovedAnchoredObj->GetAnchorFrame()->FindFlyFrame(); @@ -493,21 +502,22 @@ void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos, if ( pTmpObj ) { size_t nTmpNewPos( nNewPos ); - if ( bMovedForward ) - { - // move before the top 'repeated' object - const sal_uInt32 nTmpMaxOrdNum = - ::GetUserCall( pTmpObj )->GetMaxOrdNum(); - if ( nTmpMaxOrdNum > nNewPos ) - nTmpNewPos = nTmpMaxOrdNum; - } - else + if (const SwContact* pContact2 = ::GetUserCall( pTmpObj )) { - // move behind the bottom 'repeated' object - const sal_uInt32 nTmpMinOrdNum = - ::GetUserCall( pTmpObj )->GetMinOrdNum(); - if ( nTmpMinOrdNum < nNewPos ) - nTmpNewPos = nTmpMinOrdNum; + if ( bMovedForward ) + { + // move before the top 'repeated' object + const sal_uInt32 nTmpMaxOrdNum = pContact2->GetMaxOrdNum(); + if ( nTmpMaxOrdNum > nNewPos ) + nTmpNewPos = nTmpMaxOrdNum; + } + else + { + // move behind the bottom 'repeated' object + const sal_uInt32 nTmpMinOrdNum = pContact2->GetMinOrdNum(); + if ( nTmpMinOrdNum < nNewPos ) + nTmpNewPos = nTmpMinOrdNum; + } } if ( nTmpNewPos != nNewPos ) { @@ -529,18 +539,25 @@ void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos, { // determine position before the object before its top 'child' object const SdrObject* pTmpObj = pDrawPage->GetObj( nMaxChildOrdNum ); - size_t nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum() + 1; - if ( nTmpNewPos >= nObjCount ) + if (SwContact* pContact2 = ::GetUserCall( pTmpObj )) { - --nTmpNewPos; + size_t nTmpNewPos = pContact2->GetMaxOrdNum() + 1; + if ( nTmpNewPos >= nObjCount ) + { + --nTmpNewPos; + } + // assure, that determined position isn't between 'repeated' objects + pTmpObj = pDrawPage->GetObj( nTmpNewPos ); + pContact2 = ::GetUserCall( pTmpObj ); + if (pContact2) + { + nTmpNewPos = pContact2->GetMaxOrdNum(); + // apply new position + pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos ); + nNewPos = nTmpNewPos; + pDrawPage->RecalcObjOrdNums(); + } } - // assure, that determined position isn't between 'repeated' objects - pTmpObj = pDrawPage->GetObj( nTmpNewPos ); - nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum(); - // apply new position - pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos ); - nNewPos = nTmpNewPos; - pDrawPage->RecalcObjOrdNums(); } } @@ -564,16 +581,18 @@ void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos, if ( pTmpParentObj && pTmpParentObj->GetFrameFormat() != pParentFrameFormat ) { - if ( bMovedForward ) + if (const SwContact* pContact2 = ::GetUserCall( pTmpObj )) { - nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum(); - pTmpObj = pDrawPage->GetObj( nTmpNewPos + 1 ); - } - else - { - nTmpNewPos = ::GetUserCall( pTmpParentObj->GetDrawObj() ) - ->GetMinOrdNum(); - pTmpObj = pTmpParentObj->GetDrawObj(); + if ( bMovedForward ) + { + nTmpNewPos = pContact2->GetMaxOrdNum(); + pTmpObj = pDrawPage->GetObj( nTmpNewPos + 1 ); + } + else + { + nTmpNewPos = pContact2->GetMinOrdNum(); + pTmpObj = pTmpParentObj->GetDrawObj(); + } } } else @@ -690,7 +709,7 @@ const SwFrame* SwDrawView::CalcAnchor() //Search for paragraph bound objects, otherwise only the //current anchor. Search only if we currently drag. - const SwFrame* pAnch; + const SwFrame* pAnch = nullptr; tools::Rectangle aMyRect; auto pFlyDrawObj = dynamic_cast( pObj ); if ( pFlyDrawObj ) @@ -700,16 +719,18 @@ const SwFrame* SwDrawView::CalcAnchor() } else { - SwDrawContact *pC = static_cast(GetUserCall(pObj)); // determine correct anchor position for 'virtual' drawing objects. // #i26791# - pAnch = pC->GetAnchorFrame( pObj ); - if( !pAnch ) + if (SwDrawContact* pContact = static_cast(GetUserCall(pObj))) { - pC->ConnectToLayout(); - // determine correct anchor position for 'virtual' drawing objects. - // #i26791# - pAnch = pC->GetAnchorFrame( pObj ); + pAnch = pContact->GetAnchorFrame( pObj ); + if( !pAnch ) + { + pContact->ConnectToLayout(); + // determine correct anchor position for 'virtual' drawing objects. + // #i26791# + pAnch = pContact->GetAnchorFrame( pObj ); + } } aMyRect = pObj->GetSnapRect(); } @@ -744,12 +765,14 @@ const SwFrame* SwDrawView::CalcAnchor() { const SwRect aRect( aPt.getX(), aPt.getY(), 1, 1 ); - SwDrawContact* pContact = static_cast(GetUserCall(pObj)); - if ( pContact->GetAnchorFrame( pObj ) && - pContact->GetAnchorFrame( pObj )->IsPageFrame() ) - pAnch = pContact->GetPageFrame(); - else - pAnch = pContact->FindPage( aRect ); + if (SwDrawContact* pContact = static_cast(GetUserCall(pObj))) + { + if ( pContact->GetAnchorFrame( pObj ) && + pContact->GetAnchorFrame( pObj )->IsPageFrame() ) + pAnch = pContact->GetPageFrame(); + else + pAnch = pContact->FindPage( aRect ); + } } } if( pAnch && !pAnch->IsProtected() ) diff --git a/sw/source/core/edit/autofmt.cxx b/sw/source/core/edit/autofmt.cxx index 6fafe1310d..2082fa3ae4 100644 --- a/sw/source/core/edit/autofmt.cxx +++ b/sw/source/core/edit/autofmt.cxx @@ -1710,7 +1710,7 @@ void SwAutoFormat::BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel ) const_cast(m_pCurTextFrame->GetTextNodeForParaProps())->SetAttrListLevel(nLvl); // start new list - m_pDoc->SetNumRule(m_aDelPam, aRule, true, m_pEditShell->GetLayout()); + m_pDoc->SetNumRule(m_aDelPam, aRule, SwDoc::SetNumRuleMode::CreateNewList, m_pEditShell->GetLayout()); m_aDelPam.DeleteMark(); *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0)); diff --git a/sw/source/core/edit/edglss.cxx b/sw/source/core/edit/edglss.cxx index 80310bab4f..b5dc871a6e 100644 --- a/sw/source/core/edit/edglss.cxx +++ b/sw/source/core/edit/edglss.cxx @@ -229,6 +229,14 @@ bool SwEditShell::CopySelToDoc( SwDoc& rInsDoc ) // but we want to copy the table and the start node before // the first cell as well. aPaM.Start()->Assign(*oSelectAll->first); + if (SwSectionNode const* pSection = oSelectAll->first->GetSectionNode()) + { + if (aPaM.End()->GetNodeIndex() < pSection->EndOfSectionIndex()) + { + // include section end so that section is copied + aPaM.End()->Assign(*oSelectAll->first->GetNodes()[pSection->EndOfSectionIndex() + 1]); + } + } } bRet = GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, SwCopyFlags::CheckPosInFly) || bRet; diff --git a/sw/source/core/edit/ednumber.cxx b/sw/source/core/edit/ednumber.cxx index c272aa5f8e..65391d376b 100644 --- a/sw/source/core/edit/ednumber.cxx +++ b/sw/source/core/edit/ednumber.cxx @@ -355,7 +355,7 @@ void SwEditShell::SetIndent(short nIndent, const SwPosition & rPos) // change numbering rule - changed numbering rule is not applied at SwPaM aPaM(pos); - GetDoc()->SetNumRule(aPaM, aRule, false, GetLayout(), OUString(), false); + GetDoc()->SetNumRule(aPaM, aRule, SwDoc::SetNumRuleMode::DontSetItem, GetLayout(), OUString()); } EndAllAction(); @@ -751,8 +751,9 @@ void SwEditShell::SetCurNumRule( const SwNumRule& rRule, for (SwPaM& rPaM : pCursor->GetRingContainer()) { OUString sListId = GetDoc()->SetNumRule(rPaM, rRule, - bCreateNewList, GetLayout(), sContinuedListId, - true, bResetIndentAttrs ); + (bCreateNewList ? SwDoc::SetNumRuleMode::CreateNewList : SwDoc::SetNumRuleMode::Default) + | (bResetIndentAttrs ? SwDoc::SetNumRuleMode::ResetIndentAttrs : SwDoc::SetNumRuleMode::Default), + GetLayout(), sContinuedListId); //tdf#87548 On creating a new list for a multi-selection only //create a single new list for the multi-selection, not one per selection @@ -768,8 +769,9 @@ void SwEditShell::SetCurNumRule( const SwNumRule& rRule, else { GetDoc()->SetNumRule( *pCursor, rRule, - bCreateNewList, GetLayout(), rContinuedListId, - true, bResetIndentAttrs ); + (bCreateNewList ? SwDoc::SetNumRuleMode::CreateNewList : SwDoc::SetNumRuleMode::Default) + | (bResetIndentAttrs ? SwDoc::SetNumRuleMode::ResetIndentAttrs : SwDoc::SetNumRuleMode::Default), + GetLayout(), rContinuedListId); GetDoc()->SetCounted( *pCursor, true, GetLayout() ); } GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSATTR, nullptr ); diff --git a/sw/source/core/fields/fldbas.cxx b/sw/source/core/fields/fldbas.cxx index b3fab811f1..69f08a4eab 100644 --- a/sw/source/core/fields/fldbas.cxx +++ b/sw/source/core/fields/fldbas.cxx @@ -48,13 +48,12 @@ using namespace ::com::sun::star; using namespace nsSwDocInfoSubType; -static LanguageType lcl_GetLanguageOfFormat( LanguageType nLng, sal_uLong nFormat, - const SvNumberFormatter& rFormatter ) +static LanguageType lcl_GetLanguageOfFormat(LanguageType nLng, sal_uLong nFormat) { if( nLng == LANGUAGE_NONE ) // Bug #60010 nLng = LANGUAGE_SYSTEM; else if( nLng == ::GetAppLanguage() ) - switch( rFormatter.GetIndexTableOffset( nFormat )) + switch( SvNumberFormatter::GetIndexTableOffset( nFormat )) { case NF_NUMBER_SYSTEM: case NF_DATE_SYSTEM_SHORT: @@ -583,7 +582,7 @@ OUString SwValueFieldType::ExpandValue( const double& rVal, const Color* pCol = nullptr; // Bug #60010 - LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, nFormat, *pFormatter ); + LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, nFormat ); if( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && LANGUAGE_SYSTEM != nFormatLng ) { @@ -752,8 +751,7 @@ void SwValueField::SetLanguage( LanguageType nLng ) { // Bug #60010 SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); - LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, GetFormat(), - *pFormatter ); + LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, GetFormat() ); if( (GetFormat() >= SV_COUNTRY_LANGUAGE_OFFSET || LANGUAGE_SYSTEM != nFormatLng ) && diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx index 0ee26771bb..85a18c7892 100644 --- a/sw/source/core/fields/reffld.cxx +++ b/sw/source/core/fields/reffld.cxx @@ -1220,7 +1220,9 @@ namespace /// Picks the first text node with a matching style from a double ended queue, starting at the front /// This allows us to use the deque either as a stack or as a queue depending on whether we want to search up or down SwTextNode* SearchForStyleAnchor(SwTextNode* pSelf, const std::deque& pToSearch, - std::u16string_view rStyleName, bool bCaseSensitive = true) + std::u16string_view rStyleName, + sal_Int32 *const pStart, sal_Int32 *const pEnd, + bool bCaseSensitive = true) { std::deque pSearching(pToSearch); while (!pSearching.empty()) @@ -1235,15 +1237,38 @@ namespace if (!pTextNode) continue; - if (bCaseSensitive) + if (bCaseSensitive + ? pTextNode->GetFormatColl()->GetName() == rStyleName + : pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName)) { - if (pTextNode->GetFormatColl()->GetName() == rStyleName) - return pTextNode; + *pStart = 0; + if (pEnd) + { + *pEnd = pTextNode->GetText().getLength(); + } + return pTextNode; } - else + + if (auto const pHints = pTextNode->GetpSwpHints()) { - if (pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName)) - return pTextNode; + for (size_t i = 0; i < pHints->Count(); ++i) + { + auto const*const pHint(pHints->Get(i)); + if (pHint->Which() == RES_TXTATR_CHARFMT) + { + if (bCaseSensitive + ? pHint->GetCharFormat().GetCharFormat()->HasName(rStyleName) + : pHint->GetCharFormat().GetCharFormat()->GetName().equalsIgnoreAsciiCase(rStyleName)) + { + *pStart = pHint->GetStart(); + if (pEnd) + { + *pEnd = *pHint->End(); + } + return pTextNode; + } + } + } } } @@ -1505,21 +1530,21 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, pSearchThird.push_back(nodes[n]); } - pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark); + pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark, pStt, pEnd); if (pTextNd) { break; } // 2. Search up from the top of the page - pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark); + pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, pStt, pEnd); if (pTextNd) { break; } // 3. Search down from the bottom of the page - pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark); + pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark, pStt, pEnd); if (pTextNd) { break; @@ -1528,21 +1553,21 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, // Word has case insensitive styles. LO has case sensitive styles. If we didn't find // it yet, maybe we could with a case insensitive search. Let's do that - pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark, + pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark, pStt, pEnd, false /* bCaseSensitive */); if (pTextNd) { break; } - pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, + pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, pStt, pEnd, false /* bCaseSensitive */); if (pTextNd) { break; } - pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark, + pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark, pStt, pEnd, false /* bCaseSensitive */); break; } @@ -1574,7 +1599,7 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, // 1. Search up until we hit the top of the document - pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark); + pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark, pStt, pEnd); if (pTextNd) { break; @@ -1582,7 +1607,7 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, // 2. Search down until we hit the bottom of the document - pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark); + pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, pStt, pEnd); if (pTextNd) { break; @@ -1590,14 +1615,14 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, // Again, we need to remember that Word styles are not case sensitive - pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark, + pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark, pStt, pEnd, false /* bCaseSensitive */); if (pTextNd) { break; } - pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, + pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark, pStt, pEnd, false /* bCaseSensitive */); break; } @@ -1605,13 +1630,6 @@ SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, OSL_FAIL(" - unknown getref element type"); } - if (pTextNd) - { - *pStt = 0; - if (pEnd) - *pEnd = pTextNd->GetText().getLength(); - } - break; } diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx index 1d8b0ef680..01eee5072f 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -195,18 +195,20 @@ void SwFEShell::Copy( SwDoc& rClpDoc, const OUString* pNewClpText ) } else { - SwDrawContact *pContact = static_cast(GetUserCall( pObj )); - SwFrameFormat *pFormat = pContact->GetFormat(); - SwFormatAnchor aAnchor( pFormat->GetAnchor() ); - if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || - (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || - (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || - (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + if (SwDrawContact *pContact = static_cast(GetUserCall( pObj ))) { - aAnchor.SetAnchor( &aPos ); - } + SwFrameFormat *pFormat = pContact->GetFormat(); + SwFormatAnchor aAnchor( pFormat->GetAnchor() ); + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + aAnchor.SetAnchor( &aPos ); + } - rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true ); + rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true ); + } } } } @@ -297,6 +299,9 @@ bool SwFEShell::CopyDrawSel( SwFEShell& rDestShell, const Point& rSttPt, SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj(); SwDrawContact *pContact = static_cast(GetUserCall( pObj )); + if (!pContact) + continue; + SwFrameFormat *pFormat = pContact->GetFormat(); const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); @@ -1088,9 +1093,24 @@ bool SwFEShell::Paste(SwDoc& rClpDoc, 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()->GetNode().IsSectionNode() + && aCpyPam.End()->GetNodeIndex() == aCpyPam.Start()->GetNode().EndOfSectionIndex() + 1 + && aCpyPam.End()->GetNode().IsTextNode() + && aCpyPam.End()->GetNode().GetTextNode()->Len() == 0); + rClpDoc.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly); // Note: aCpyPam is invalid now + if (isSourceSection + && aIndexBefore.GetNode().IsStartNode() + && rInsPos.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(aIndexBefore.GetNode(), rInsPos.GetNode()); diff --git a/sw/source/core/frmedt/fefly1.cxx b/sw/source/core/frmedt/fefly1.cxx index 93ad4212cf..d6ff8b2969 100644 --- a/sw/source/core/frmedt/fefly1.cxx +++ b/sw/source/core/frmedt/fefly1.cxx @@ -382,11 +382,13 @@ const SwFrameFormat* SwFEShell::IsFlyInFly() return nullptr; return pFly->GetFormat(); } - else if ( rMrkList.GetMarkCount() != 1 || - !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) ) + else if ( rMrkList.GetMarkCount() != 1 ) return nullptr; SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + SwContact* pContact = GetUserCall( pObj ); + if (!pContact) + return nullptr; SwFrameFormat *pFormat = FindFrameFormat( pObj ); if( pFormat && RndStdIds::FLY_AT_FLY == pFormat->GetAnchor().GetAnchorId() ) @@ -398,7 +400,7 @@ const SwFrameFormat* SwFEShell::IsFlyInFly() } else { - pFly = static_cast(GetUserCall(pObj))->GetAnchorFrame(pObj); + pFly = static_cast(pContact)->GetAnchorFrame(pObj); } OSL_ENSURE( pFly, "IsFlyInFly: Where's my anchor?" ); @@ -491,11 +493,12 @@ Point SwFEShell::FindAnchorPos( const Point& rAbsPos, bool bMoveIt ) SdrObject* pObj = rMrkList.GetMark(0)->GetMarkedSdrObj(); - if (!GetUserCall(pObj)) + SwContact* pContact = ::GetUserCall( pObj ); + if (!pContact) return aRet; // #i28701# - SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( pObj ); SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat(); const RndStdIds nAnchorId = pFormat->GetAnchor().GetAnchorId(); @@ -890,6 +893,43 @@ const SwFrameFormat *SwFEShell::NewFlyFrame( const SfxItemSet& rSet, bool bAnchV return pRet; } +namespace +{ +/// If pCursor points to an as-char anchored graphic node, then set the node's anchor position on +/// pAnchor and rPam. +bool SetAnchorOnGrfNodeForAsChar(SwShellCursor *pCursor, SwFormatAnchor* pAnchor, std::optional& rPam) +{ + const SwPosition* pPoint = pCursor->GetPoint(); + if (pAnchor->GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + return false; + } + + if (!pPoint->GetNode().IsGrfNode()) + { + return false; + } + + SwFrameFormat* pFrameFormat = pPoint->GetNode().GetFlyFormat(); + if (!pFrameFormat) + { + return false; + } + + const SwPosition* pContentAnchor = pFrameFormat->GetAnchor().GetContentAnchor(); + if (!pContentAnchor) + { + return false; + } + + SwPosition aPosition(*pContentAnchor); + ++aPosition.nContent; + pAnchor->SetAnchor(&aPosition); + rPam.emplace(aPosition); + return true; +} +} + void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, const Graphic* pGraphic, const SfxItemSet* pFlyAttrSet ) @@ -905,6 +945,7 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, break; // Has the anchor not been set or been set incompletely? + std::optional oPam; if( pFlyAttrSet ) { if( const SwFormatAnchor* pItem = pFlyAttrSet->GetItemIfSet( RES_ANCHOR, false ) ) @@ -917,6 +958,13 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, case RndStdIds::FLY_AS_CHAR: if( !pAnchor->GetAnchorNode() ) { + if (SetAnchorOnGrfNodeForAsChar(pCursor, pAnchor, oPam)) + { + // Don't anchor the image on the previous image, rather insert them next + // to each other. + break; + } + pAnchor->SetAnchor( pCursor->GetPoint() ); } break; @@ -940,7 +988,7 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, } } pFormat = GetDoc()->getIDocumentContentOperations().InsertGraphic( - *pCursor, rGrfName, + oPam.has_value() ? *oPam : *pCursor, rGrfName, rFltName, pGraphic, pFlyAttrSet, nullptr, nullptr ); diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx index 9154758364..353cb5214e 100644 --- a/sw/source/core/frmedt/feshview.cxx +++ b/sw/source/core/frmedt/feshview.cxx @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -376,22 +377,27 @@ bool SwFEShell::MoveAnchor( SwMove nDir ) const SdrMarkList& pMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); if (1 != pMrkList.GetMarkCount()) return false; + + SdrObject *pObj = pMrkList.GetMark( 0 )->GetMarkedSdrObj(); + SwDrawContact* pContact = static_cast(GetUserCall(pObj)); + if (!pContact) + return false; + SwFrame* pOld; SwFlyFrame* pFly = nullptr; - SdrObject *pObj = pMrkList.GetMark( 0 )->GetMarkedSdrObj(); if (SwVirtFlyDrawObj* pVirtO = dynamic_cast(pObj)) { pFly = pVirtO->GetFlyFrame(); pOld = pFly->AnchorFrame(); } else - pOld = static_cast(GetUserCall(pObj))->GetAnchorFrame( pObj ); + pOld = pContact->GetAnchorFrame( pObj ); bool bRet = false; if( pOld ) { SwFrame* pNew = pOld; // #i28701# - SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( pObj ); SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat(); SwFormatAnchor aAnch( pFormat->GetAnchor() ); RndStdIds nAnchorId = aAnch.GetAnchorId(); @@ -874,7 +880,11 @@ static void lcl_NotifyNeighbours( const SdrMarkList *pLst ) } else { - SwFrame* pAnch = static_cast( GetUserCall(pO) )->GetAnchorFrame( pO ); + SwDrawContact* pContact = static_cast(GetUserCall(pO)); + if (!pContact) + continue; + + SwFrame* pAnch = pContact->GetAnchorFrame( pO ); if( !pAnch ) continue; pPage = pAnch->FindPageFrame(); @@ -1316,12 +1326,15 @@ bool SwFEShell::ShouldObjectBeSelected(const Point& rPt) { if ( pObj->GetLayer() == rIDDMA.GetHellId() ) { - const SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); - const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat(); - const SwFormatSurround& rSurround = pFormat->GetSurround(); - if ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) + if (const SwContact* pContact = ::GetUserCall( pObj )) { - bObjInBackground = true; + const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( pObj ); + const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat(); + const SwFormatSurround& rSurround = pFormat->GetSurround(); + if ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) + { + bObjInBackground = true; + } } } } @@ -1392,6 +1405,27 @@ bool SwFEShell::ShouldObjectBeSelected(const Point& rPt) } } } + + // within table row, where image cropped by the fixed table row height, + // click position must be in the cell, where the image anchored as character + if ( bRet && pContact && pContact->ObjAnchoredAsChar() ) + { + if ( const SwTableBox *pBox = pContact->GetAnchorNode().GetTableBox() ) + { + SwIterator aIter( *pBox->GetUpper()->GetFrameFormat() ); + bool bContainsClickPosition = false; + for (SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if ( pFrame->getFrameArea().Contains( rPt ) ) + { + bContainsClickPosition = true; + break; + } + } + if ( !bContainsClickPosition ) + bRet = false; + } + } } } @@ -2330,7 +2364,7 @@ bool SwFEShell::IsGroupSelected(bool bAllowDiagams) // Thus, use corresponding method instead of checking type. if ( pObj->IsGroupObject() && // --> #i38505# No ungroup allowed for 3d objects - !pObj->Is3DObj() && + !pObj->Is3DObj() && GetUserCall(pObj) && RndStdIds::FLY_AS_CHAR != static_cast(GetUserCall(pObj))-> GetFormat()->GetAnchor().GetAnchorId() ) { @@ -2659,7 +2693,6 @@ bool SwFEShell::GetObjAttr( SfxItemSet &rSet ) const { SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); SwDrawContact *pContact = static_cast(GetUserCall(pObj)); - // --> make code robust OSL_ENSURE( pContact, " - missing ." ); if ( pContact ) { @@ -2689,8 +2722,8 @@ void SwFEShell::SetObjAttr( const SfxItemSet& rSet ) for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) { SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); - SwDrawContact *pContact = static_cast(GetUserCall(pObj)); - GetDoc()->SetAttr( rSet, *pContact->GetFormat() ); + if (SwDrawContact *pContact = static_cast(GetUserCall(pObj))) + GetDoc()->SetAttr( rSet, *pContact->GetFormat() ); } EndUndo( SwUndoId::INSATTR ); diff --git a/sw/source/core/inc/DocumentSettingManager.hxx b/sw/source/core/inc/DocumentSettingManager.hxx index 3e5be92c64..6599c9bf2a 100644 --- a/sw/source/core/inc/DocumentSettingManager.hxx +++ b/sw/source/core/inc/DocumentSettingManager.hxx @@ -178,6 +178,8 @@ class DocumentSettingManager final : bool mbDoNotBreakWrappedTables = false; bool mbAllowTextAfterFloatingTableBreak = false; bool mbJustifyLinesWithShrinking = false; + bool mbApplyTextAttrToEmptyLineAtEndOfParagraph = true; + bool mbDoNotMirrorRtlDrawObjs = false; // If this is on as_char flys wrapping will be handled the same like in Word bool mbNoNumberingShowFollowBy; bool mbDropCapPunctuation; // tdf#150200, tdf#150438 diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx index 604488a18c..a863585081 100644 --- a/sw/source/core/inc/frame.hxx +++ b/sw/source/core/inc/frame.hxx @@ -948,6 +948,9 @@ public: virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const; void dumpChildrenAsXml(xmlTextWriterPtr writer) const; bool IsCollapse() const; + + /// Determines if the upper margin of this frame should be ignored. + bool IsCollapseUpper() const; }; inline bool SwFrame::IsInDocBody() const diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx index 69158b3358..3debf367f0 100644 --- a/sw/source/core/inc/sectfrm.hxx +++ b/sw/source/core/inc/sectfrm.hxx @@ -46,7 +46,7 @@ namespace o3tl { template<> struct typed_flags : is_typed_flags {}; } -class SwSectionFrame final: public SwLayoutFrame, public SwFlowFrame +class SW_DLLPUBLIC SwSectionFrame final: public SwLayoutFrame, public SwFlowFrame , public SvtListener // TODO? { SwSection* m_pSection; diff --git a/sw/source/core/layout/anchoreddrawobject.cxx b/sw/source/core/layout/anchoreddrawobject.cxx index 5a9e1245af..fe01bbc3b3 100644 --- a/sw/source/core/layout/anchoreddrawobject.cxx +++ b/sw/source/core/layout/anchoreddrawobject.cxx @@ -266,6 +266,8 @@ void SwAnchoredDrawObject::MakeObjPos() SwDrawContact* pDrawContact = static_cast(::GetUserCall( GetDrawObj() )); + if (!pDrawContact) + return; // --> #i28749# - if anchored drawing object hasn't been yet // positioned, convert its positioning attributes, if its positioning @@ -849,6 +851,8 @@ void SwAnchoredDrawObject::SetPositioningAttr() { SwDrawContact* pDrawContact = static_cast(GetUserCall( GetDrawObj() )); + if (!pDrawContact) + return; SwFrameFormat* pObjFormat = GetFrameFormat(); if ( !pDrawContact->ObjAnchoredAsChar() ) diff --git a/sw/source/core/layout/anchoredobject.cxx b/sw/source/core/layout/anchoredobject.cxx index a74438afb3..43c5baa396 100644 --- a/sw/source/core/layout/anchoredobject.cxx +++ b/sw/source/core/layout/anchoredobject.cxx @@ -44,10 +44,13 @@ SwObjPositioningInProgress::SwObjPositioningInProgress( SdrObject& _rSdrObj ) : // --> #i52904# mbOldObjPositioningInProgress( false ) { - mpAnchoredObj = ::GetUserCall( &_rSdrObj )->GetAnchoredObj( &_rSdrObj ); - // --> #i52904# - mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress(); - mpAnchoredObj->SetPositioningInProgress( true ); + if (SwContact* pContact = ::GetUserCall( &_rSdrObj )) + { + mpAnchoredObj = pContact->GetAnchoredObj( &_rSdrObj ); + // --> #i52904# + mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress(); + mpAnchoredObj->SetPositioningInProgress( true ); + } } SwObjPositioningInProgress::SwObjPositioningInProgress( SwAnchoredObject& _rAnchoredObj ) : mpAnchoredObj( &_rAnchoredObj ), diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx index e13fdf0121..f358d74af0 100644 --- a/sw/source/core/layout/calcmove.cxx +++ b/sw/source/core/layout/calcmove.cxx @@ -1076,6 +1076,44 @@ bool SwFrame::IsCollapse() const return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse(); } +bool SwFrame::IsCollapseUpper() const +{ + const SwTextFrame* pTextFrame = DynCastTextFrame(); + if (!pTextFrame) + { + return false; + } + + const SwDoc& rDoc = pTextFrame->GetDoc(); + const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess(); + if (!rIDSA.get(DocumentSettingId::TAB_OVER_SPACING) || rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN)) + { + // Writer or Word Word <= 2010 style: upper margin is never ignored. + return false; + } + + if (IsInFly()) + { + // Not in a page's body. + return false; + } + + // Word >= 2013 style: when we're at the top of the page's body, but not on the first page, then + // ignore the upper margin for paragraphs. + if (GetPrev() || !GetUpper() || !GetUpper()->IsBodyFrame()) + { + return false; + } + + const SwPageFrame* pPageFrame = FindPageFrame(); + if (!pPageFrame || !pPageFrame->GetPrev()) + { + return false; + } + + return true; +} + void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs ) { if ( isFramePrintAreaValid() ) diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx index 88158161c5..37fd20b323 100644 --- a/sw/source/core/layout/flowfrm.cxx +++ b/sw/source/core/layout/flowfrm.cxx @@ -1658,6 +1658,11 @@ SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs, CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) ) { nUpper = pAttrs->GetULSpace().GetUpper(); + + if (m_rThis.IsCollapseUpper()) + { + nUpper = 0; + } } } diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index db50a42de0..b454bd9591 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -2528,8 +2528,8 @@ void SwFrame::AppendDrawObj( SwAnchoredObject& _rNewObj ) assert(!m_pDrawObjs || m_pDrawObjs->is_sorted()); // perform disconnect from layout, if 'master' drawing object is appended // to a new frame. - static_cast(::GetUserCall( _rNewObj.GetDrawObj() ))-> - DisconnectFromLayout( false ); + if (SwDrawContact* pContact = static_cast(::GetUserCall( _rNewObj.GetDrawObj() ))) + pContact->DisconnectFromLayout( false ); assert(!m_pDrawObjs || m_pDrawObjs->is_sorted()); } diff --git a/sw/source/core/layout/flylay.cxx b/sw/source/core/layout/flylay.cxx index 7ac95d65e0..b0ef08a01f 100644 --- a/sw/source/core/layout/flylay.cxx +++ b/sw/source/core/layout/flylay.cxx @@ -1430,27 +1430,30 @@ bool CalcClipRect( const SdrObject *pSdrObj, SwRect &rRect, bool bMove ) } tools::Long nHeight = (9*aRectFnSet.GetHeight(rRect))/10; tools::Long nTop; - const SwFormat *pFormat = GetUserCall(pSdrObj)->GetFormat(); - const SvxULSpaceItem &rUL = pFormat->GetULSpace(); - if( bMove ) + if (const SwContact* pContact = ::GetUserCall( pSdrObj )) { - nTop = aRectFnSet.IsVert() ? static_cast(pFly)->GetRefPoint().X() : - static_cast(pFly)->GetRefPoint().Y(); - nTop = aRectFnSet.YInc( nTop, -nHeight ); - tools::Long nWidth = aRectFnSet.GetWidth(pFly->getFrameArea()); - aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ? - static_cast(pFly)->GetRefPoint().Y() : - static_cast(pFly)->GetRefPoint().X(), nWidth ); - nHeight = 2*nHeight - rUL.GetLower() - rUL.GetUpper(); - } - else - { - nTop = aRectFnSet.YInc( aRectFnSet.GetBottom(pFly->getFrameArea()), - rUL.GetLower() - nHeight ); - nHeight = 2*nHeight - aRectFnSet.GetHeight(pFly->getFrameArea()) - - rUL.GetLower() - rUL.GetUpper(); + const SwFormat *pFormat = pContact->GetFormat(); + const SvxULSpaceItem &rUL = pFormat->GetULSpace(); + if( bMove ) + { + nTop = aRectFnSet.IsVert() ? static_cast(pFly)->GetRefPoint().X() : + static_cast(pFly)->GetRefPoint().Y(); + nTop = aRectFnSet.YInc( nTop, -nHeight ); + tools::Long nWidth = aRectFnSet.GetWidth(pFly->getFrameArea()); + aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ? + static_cast(pFly)->GetRefPoint().Y() : + static_cast(pFly)->GetRefPoint().X(), nWidth ); + nHeight = 2*nHeight - rUL.GetLower() - rUL.GetUpper(); + } + else + { + nTop = aRectFnSet.YInc( aRectFnSet.GetBottom(pFly->getFrameArea()), + rUL.GetLower() - nHeight ); + nHeight = 2*nHeight - aRectFnSet.GetHeight(pFly->getFrameArea()) + - rUL.GetLower() - rUL.GetUpper(); + } + aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight ); } - aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight ); } } else diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index baf632d6eb..38667bd5c4 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -297,6 +297,8 @@ void SwFrameNotify::ImplDestroy() bool bNotify = false; bool bNotifySize = false; SwContact* pContact = ::GetUserCall( pObj->GetDrawObj() ); + if (!pContact) + continue; const bool bAnchoredAsChar = pContact->ObjAnchoredAsChar(); if ( !bAnchoredAsChar ) { @@ -1067,22 +1069,23 @@ void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *c InsertObject(pSdrObj, pSdrObj->GetOrdNumDirect()); } - SwDrawContact* pNew = - static_cast(GetUserCall( pSdrObj )); - if ( !pNew->GetAnchorFrame() ) - { - pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( nullptr )) ); - } - // OD 19.06.2003 #108784# - add 'virtual' drawing object, - // if necessary. But control objects have to be excluded. - else if ( !::CheckControlLayer( pSdrObj ) && - pNew->GetAnchorFrame() != pFrame && - !pNew->GetDrawObjectByAnchorFrame( *pFrame ) ) + if (SwDrawContact* pNew = static_cast(GetUserCall( pSdrObj ))) { - SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame); - pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) ); + if ( !pNew->GetAnchorFrame() ) + { + pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( nullptr )) ); + } + // OD 19.06.2003 #108784# - add 'virtual' drawing object, + // if necessary. But control objects have to be excluded. + else if ( !::CheckControlLayer( pSdrObj ) && + pNew->GetAnchorFrame() != pFrame && + !pNew->GetDrawObjectByAnchorFrame( *pFrame ) ) + { + SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame); + pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) ); - pDrawVirtObj->ActionChanged(); + pDrawVirtObj->ActionChanged(); + } } } else @@ -3598,8 +3601,13 @@ bool Is_Lower_Of(const SwFrame *pCurrFrame, const SdrObject* pObj) } else { - pFrame = static_cast(GetUserCall(pObj))->GetAnchorFrame(pObj); - aPos = pObj->GetCurrentBoundRect().TopLeft(); + if (SwDrawContact* pC = static_cast(GetUserCall(pObj))) + { + pFrame = pC->GetAnchorFrame(pObj); + aPos = pObj->GetCurrentBoundRect().TopLeft(); + } + else + return false; } OSL_ENSURE( pFrame, "8-( Fly is lost in Space." ); pFrame = GetVirtualUpper( pFrame, aPos ); diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index 1a0a1260a1..ad437f9852 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -1439,9 +1439,9 @@ bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLa PopFormatLayout(); } } - else if (pLay->IsSctFrame() && pLow->IsTextFrame() && pLow == pLay->GetLastLower()) + else if (pLay->IsSctFrame() && pLay->GetNext() && pLay->GetNext()->IsSctFrame() && pLow->IsTextFrame() && pLow == pLay->GetLastLower()) { - // else: only calc the last text lower of sections + // else: only calc the last text lower of sections, followed by sections pLow->OptCalc(); } diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx index f8c029ea12..2575c98278 100644 --- a/sw/source/core/layout/pagechg.cxx +++ b/sw/source/core/layout/pagechg.cxx @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -445,22 +446,21 @@ static void lcl_MakeObjs(const sw::FrameFormats& rSpzs, SwP if ( bSdrObj ) { // OD 23.06.2003 #108784# - consider 'virtual' drawing objects - SwDrawContact *pContact = - static_cast(::GetUserCall(pSdrObj)); - if ( auto pDrawVirtObj = dynamic_cast( pSdrObj ) ) + if (SwDrawContact *pContact = + static_cast(::GetUserCall(pSdrObj))) { - if ( pContact ) + if ( auto pDrawVirtObj = dynamic_cast( pSdrObj ) ) { pDrawVirtObj->RemoveFromWriterLayout(); pDrawVirtObj->RemoveFromDrawingPage(); pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) ); } - } - else - { - if ( pContact->GetAnchorFrame() ) - pContact->DisconnectFromLayout( false ); - pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) ); + else + { + if ( pContact->GetAnchorFrame() ) + pContact->DisconnectFromLayout( false ); + pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) ); + } } } else @@ -790,8 +790,11 @@ SwPageDesc *SwPageFrame::FindPageDesc() } SwContentFrame* pFirstContent = FindFirstBodyContent(); - while (pFirstContent && pFirstContent->IsHiddenNow()) + while (pFirstContent && pFirstContent->IsInSct() + && pFirstContent->FindSctFrame()->IsHiddenNow()) + { pFirstContent = pFirstContent->GetNextContentFrame(); + } SwFrame* pFlow = pFirstContent; if ( pFlow && pFlow->IsInTab() ) pFlow = pFlow->FindTabFrame(); diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx index 89a5f03302..0e3d135706 100644 --- a/sw/source/core/layout/trvlfrm.cxx +++ b/sw/source/core/layout/trvlfrm.cxx @@ -70,26 +70,29 @@ namespace { { const SwVirtFlyDrawObj* pObj = static_cast(aIter()); - const SwAnchoredObject* pAnchoredObj = GetUserCall( aIter() )->GetAnchoredObj( aIter() ); - const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat(); - const SwFormatSurround& rSurround = pObjFormat->GetSurround(); - const SvxOpaqueItem& rOpaque = pObjFormat->GetOpaque(); - bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue(); - - bool bBackgroundMatches = bInBackground == bSearchBackground; - - const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr; - if ( pFly && bBackgroundMatches && - ( ( pCMS && pCMS->m_bSetInReadOnly ) || - !pFly->IsProtected() ) && - pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) ) + if (const SwContact* pContact = ::GetUserCall( aIter() )) { - bRet = true; - break; - } + const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( aIter() ); + const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat(); + const SwFormatSurround& rSurround = pObjFormat->GetSurround(); + const SvxOpaqueItem& rOpaque = pObjFormat->GetOpaque(); + bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue(); + + bool bBackgroundMatches = bInBackground == bSearchBackground; + + const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr; + if ( pFly && bBackgroundMatches && + ( ( pCMS && pCMS->m_bSetInReadOnly ) || + !pFly->IsProtected() ) && + pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) ) + { + bRet = true; + break; + } - if ( pCMS && pCMS->m_bStop ) - return false; + if ( pCMS && pCMS->m_bStop ) + return false; + } aIter.Prev(); } return bRet; diff --git a/sw/source/core/objectpositioning/anchoredobjectposition.cxx b/sw/source/core/objectpositioning/anchoredobjectposition.cxx index ab35ae7af7..4af3af542b 100644 --- a/sw/source/core/objectpositioning/anchoredobjectposition.cxx +++ b/sw/source/core/objectpositioning/anchoredobjectposition.cxx @@ -852,7 +852,10 @@ SwTwips SwAnchoredObjectPosition::CalcRelPosX( if ( _rHoriOrient.GetHoriOrient() == text::HoriOrientation::NONE ) { // 'manual' horizontal position - const bool bR2L = rAnchorFrame.IsRightToLeft(); + const IDocumentSettingAccess& rIDSA = mpFrameFormat->getIDocumentSettingAccess(); + // If compat flag is active, then disable automatic mirroring for RTL. + bool bMirrorRtlDrawObjs = !rIDSA.get(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS); + const bool bR2L = rAnchorFrame.IsRightToLeft() && bMirrorRtlDrawObjs; if( IsAnchoredToChar() && text::RelOrientation::CHAR == eRelOrient ) { if( bR2L ) diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx index 2ddaee7b2e..cb11d02b1e 100644 --- a/sw/source/core/text/inftxt.hxx +++ b/sw/source/core/text/inftxt.hxx @@ -71,8 +71,8 @@ class SwLineInfo void CtorInitLineInfo( const SwAttrSet& rAttrSet, const SwTextNode& rTextNode ); - SwLineInfo(); - ~SwLineInfo(); + SW_DLLPUBLIC SwLineInfo(); + SW_DLLPUBLIC ~SwLineInfo(); public: // #i24363# tab stops relative to indent - returns the tab stop following nSearchPos or NULL const SvxTabStop* GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const; @@ -183,7 +183,7 @@ public: SwTextSizeInfo( const SwTextSizeInfo &rInf ); SwTextSizeInfo( const SwTextSizeInfo &rInf, const OUString* pText, TextFrameIndex nIdx = TextFrameIndex(0) ); - SwTextSizeInfo(SwTextFrame *pTextFrame, TextFrameIndex nIndex = TextFrameIndex(0)); + SW_DLLPUBLIC SwTextSizeInfo(SwTextFrame *pTextFrame, TextFrameIndex nIndex = TextFrameIndex(0)); // GetMultiAttr returns the text attribute of the multiportion, // if rPos is inside any multi-line part. diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx index eb15400cf5..9a5f0b3f01 100644 --- a/sw/source/core/text/itratr.hxx +++ b/sw/source/core/text/itratr.hxx @@ -29,7 +29,7 @@ class SwRedlineItr; class SwViewShell; class SwTextFrame; -class SwAttrIter +class SW_DLLPUBLIC SwAttrIter { friend class SwFontSave; protected: diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 6623000c33..dad32bf4fc 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -852,6 +852,7 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor ) // In empty lines the attributes are switched on via SeekStart const bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); + if ( pPor->IsQuoVadisPortion() ) bChg = SeekStartAndChg( rInf, true ); else @@ -860,10 +861,16 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor ) { if( !rInf.GetText().isEmpty() ) { - if ( pPor->GetLen() || !rInf.GetIdx() - || ( m_pCurr != pLast && !pLast->IsFlyPortion() ) - || !m_pCurr->IsRest() ) // instead of !rInf.GetRest() + if ((rInf.GetIdx() != TextFrameIndex(rInf.GetText().getLength()) + || rInf.GetRest() // field continued - not empty + || !GetTextFrame()->GetDoc().getIDocumentSettingAccess().get( + DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH)) + && (pPor->GetLen() || !rInf.GetIdx() + || (m_pCurr != pLast && !pLast->IsFlyPortion()) + || !m_pCurr->IsRest())) // instead of !rInf.GetRest() + { bChg = SeekAndChg( rInf ); + } else bChg = SeekAndChgBefore( rInf ); } diff --git a/sw/source/core/text/itrtxt.hxx b/sw/source/core/text/itrtxt.hxx index 8db063d616..b5ea78369d 100644 --- a/sw/source/core/text/itrtxt.hxx +++ b/sw/source/core/text/itrtxt.hxx @@ -156,7 +156,7 @@ protected: // For FormatQuoVadis void Right( const SwTwips nNew ) { mnRight = nNew; } - void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf ); + SW_DLLPUBLIC void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf ); explicit SwTextMargin(SwTextNode const * pTextNode) : SwTextIter(pTextNode) , mnLeft(0) diff --git a/sw/source/core/txtnode/atrflyin.cxx b/sw/source/core/txtnode/atrflyin.cxx index 28eb7b38e5..fbefb76fa8 100644 --- a/sw/source/core/txtnode/atrflyin.cxx +++ b/sw/source/core/txtnode/atrflyin.cxx @@ -197,7 +197,8 @@ void SwTextFlyCnt::SetAnchor( const SwTextNode *pNode ) { if (SdrObject const*const pObj = pFormat->FindSdrObject()) { // tdf#123259 disconnect with *old* anchor position - static_cast(::GetUserCall(pObj))->DisconnectFromLayout(false); + if (SwDrawContact* pContact = static_cast(::GetUserCall( pObj ))) + pContact->DisconnectFromLayout(false); } } pFormat->SetFormatAttr( aAnchor ); // only set the anchor diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx index cf5e1ff1cc..c499294f4f 100644 --- a/sw/source/core/txtnode/thints.cxx +++ b/sw/source/core/txtnode/thints.cxx @@ -1700,7 +1700,9 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode ) // for all of its content. auto* pTextContentControl = static_txtattr_cast( GetTextAttrAt(pAttr->GetStart(), RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent)); - if (pTextContentControl) + // If the caller is SwTextNode::CopyText, we just copy an existing attribute, no need to + // correct it. + if (pTextContentControl && !(nMode & SetAttrMode::NOTXTATRCHR)) { auto& rFormatContentControl = static_cast(pTextContentControl->GetAttr()); diff --git a/sw/source/core/undo/undraw.cxx b/sw/source/core/undo/undraw.cxx index 134c686297..73a58a0f52 100644 --- a/sw/source/core/undo/undraw.cxx +++ b/sw/source/core/undo/undraw.cxx @@ -274,6 +274,8 @@ void SwUndoDrawGroup::RedoImpl(::sw::UndoRedoContext &) SdrObject* pObj = rSave.pObj; SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + if (!pContact) + continue; // Save the textboxes if (auto pOldTextBoxNode = rSave.pFormat->GetOtherTextBoxFormats()) @@ -350,23 +352,25 @@ SwUndoDrawUnGroup::SwUndoDrawUnGroup( SdrObjGroup* pObj, const SwDoc& rDoc ) m_nSize = o3tl::narrowing(pObj->GetSubList()->GetObjCount()) + 1; m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] ); - SwDrawContact *pContact = static_cast(GetUserCall(pObj)); - SwDrawFrameFormat* pFormat = static_cast(pContact->GetFormat()); + if (SwDrawContact *pContact = static_cast(GetUserCall(pObj))) + { + SwDrawFrameFormat* pFormat = static_cast(pContact->GetFormat()); - m_pObjArray[0].pObj = pObj; - m_pObjArray[0].pFormat = pFormat; + m_pObjArray[0].pObj = pObj; + m_pObjArray[0].pFormat = pFormat; - // object will destroy itself - pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); - pObj->SetUserCall( nullptr ); + // object will destroy itself + pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); - ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); + ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); - pFormat->RemoveAllUnos(); + pFormat->RemoveAllUnos(); - // remove from array - sw::SpzFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats(); - rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + // remove from array + sw::SpzFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + } } SwUndoDrawUnGroup::~SwUndoDrawUnGroup() @@ -622,6 +626,9 @@ void SwUndoDrawDelete::RedoImpl(::sw::UndoRedoContext & rContext) SwUndoGroupObjImpl& rSave = m_pObjArray[n]; SdrObject *pObj = rSave.pObj; SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + if (!pContact) + continue; + SwDrawFrameFormat *pFormat = static_cast(pContact->GetFormat()); // object will destroy itself diff --git a/sw/source/core/undo/unnum.cxx b/sw/source/core/undo/unnum.cxx index 1251635a31..a0aa39f3fc 100644 --- a/sw/source/core/undo/unnum.cxx +++ b/sw/source/core/undo/unnum.cxx @@ -112,7 +112,7 @@ void SwUndoInsNum::RedoImpl(::sw::UndoRedoContext & rContext) else { // #i42921# - adapt to changed signature - rDoc.SetNumRule(rPam, m_aNumRule, false); + rDoc.SetNumRule(rPam, m_aNumRule, SwDoc::SetNumRuleMode::Default); } } } @@ -131,7 +131,7 @@ void SwUndoInsNum::RepeatImpl(::sw::RepeatContext & rContext) if( m_sReplaceRule.isEmpty() ) { // #i42921# - adapt to changed signature - rDoc.SetNumRule(rContext.GetRepeatPaM(), m_aNumRule, false); + rDoc.SetNumRule(rContext.GetRepeatPaM(), m_aNumRule, SwDoc::SetNumRuleMode::Default); } } else diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx index fd5c3239c0..c8967178c2 100644 --- a/sw/source/core/undo/untblk.cxx +++ b/sw/source/core/undo/untblk.cxx @@ -390,6 +390,10 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext) rPam.GetPoint()->GetNodeIndex())); ::std::optional oMvBkwrd = MovePtBackward(rPam); + bool const isMoveFlyAnchors(!oMvBkwrd // equivalent to bCanMoveBack + || m_oUndoNodeIndex->GetNode().IsTextNode() + || (oMvBkwrd->GetNode().IsStartNode() + && m_oUndoNodeIndex->GetNode().IsSectionNode())); // re-insert content again (first detach m_oUndoNodeIndex!) SwNodeOffset const nMvNd = m_oUndoNodeIndex->GetIndex(); @@ -403,7 +407,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/unocrsrhelper.cxx b/sw/source/core/unocore/unocrsrhelper.cxx index 30f6d6e619..deb0d443b5 100644 --- a/sw/source/core/unocore/unocrsrhelper.cxx +++ b/sw/source/core/unocore/unocrsrhelper.cxx @@ -905,14 +905,14 @@ void setNumberingProperty(const Any& rValue, SwPaM& rPam) for ( size_t n = 0; n < aRangeArr.Count(); ++n ) { // no start of a new list - rDoc.SetNumRule( aRangeArr.SetPam( n, aPam ), aRule, false ); + rDoc.SetNumRule(aRangeArr.SetPam( n, aPam ), aRule, SwDoc::SetNumRuleMode::Default); } rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); } else { // no start of a new list - rDoc.SetNumRule( rPam, aRule, false ); + rDoc.SetNumRule(rPam, aRule, SwDoc::SetNumRuleMode::Default); } } @@ -923,7 +923,7 @@ void setNumberingProperty(const Any& rValue, SwPaM& rPam) if ( !pRule ) throw RuntimeException(); // no start of a new list - rDoc.SetNumRule( rPam, *pRule, false ); + rDoc.SetNumRule(rPam, *pRule, SwDoc::SetNumRuleMode::Default); } else { @@ -933,7 +933,7 @@ void setNumberingProperty(const Any& rValue, SwPaM& rPam) SwNumRule* pRule = rDoc.GetOutlineNumRule(); if(!pRule) throw RuntimeException(); - rDoc.SetNumRule( rPam, *pRule, false ); + rDoc.SetNumRule(rPam, *pRule, SwDoc::SetNumRuleMode::Default); } } } diff --git a/sw/source/core/unocore/unoportenum.cxx b/sw/source/core/unocore/unoportenum.cxx index 709d79ef4d..9177765c0e 100644 --- a/sw/source/core/unocore/unoportenum.cxx +++ b/sw/source/core/unocore/unoportenum.cxx @@ -608,7 +608,9 @@ static void lcl_ExportBookmark( const SwXBookmarkPortion_ImplSharedPtr& pPtr = *aIter; if ( nIndex > pPtr->getIndex() ) { - assert(!"Some bookmarks were not consumed earlier"); + // We may get here, if SwXTextPortionEnumeration ctor was called with nStart greater + // than this bookmark's index. Just drop it. + aIter = rBkmArr.erase(aIter); continue; } if ( nIndex < pPtr->getIndex() ) diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx index 93603d6d40..3f720e452f 100644 --- a/sw/source/core/unocore/unotext.cxx +++ b/sw/source/core/unocore/unotext.cxx @@ -2337,12 +2337,6 @@ SwXText::copyText( // us, even if we have only a single paragraph. m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, SwCopyFlags::CheckPosInFly); } - 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(*GetStartNode(), SwNodeOffset(1)))); - m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pDelCursor); - } } SwXBodyText::SwXBodyText(SwDoc *const pDoc) diff --git a/sw/source/filter/basflt/fltshell.cxx b/sw/source/filter/basflt/fltshell.cxx index 901614897f..c8b7ac2058 100644 --- a/sw/source/filter/basflt/fltshell.cxx +++ b/sw/source/filter/basflt/fltshell.cxx @@ -538,7 +538,7 @@ void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, { SwPaM aTmpPam( aTmpStart, aTmpEnd ); // no start of a new list - m_rDoc.SetNumRule( aTmpPam, *pNumRule, false ); + m_rDoc.SetNumRule(aTmpPam, *pNumRule, SwDoc::SetNumRuleMode::Default); aTmpStart = aTmpEnd; // here starts the next range ++aTmpStart; diff --git a/sw/source/filter/ww8/wrtw8esh.cxx b/sw/source/filter/ww8/wrtw8esh.cxx index 19ac50ccf5..272e3bd277 100644 --- a/sw/source/filter/ww8/wrtw8esh.cxx +++ b/sw/source/filter/ww8/wrtw8esh.cxx @@ -577,6 +577,12 @@ void WW8Export::MiserableRTLFrameFormatHack(SwTwips &rLeft, SwTwips &rRight, if (SvxFrameDirection::Horizontal_RL_TB != m_rDoc.GetTextDirection(rFrameFormat.GetPosition())) return; + if (m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS)) + { + // Swap is handled at a layout-level, no need to compensate for it at export time. + return; + } + SwTwips nWidth = rRight - rLeft; SwTwips nPageLeft, nPageRight; SwTwips nPageSize = CurrentPageWidth(nPageLeft, nPageRight); diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx index 976a68b88a..a2d959d7d3 100644 --- a/sw/source/filter/ww8/ww8par.cxx +++ b/sw/source/filter/ww8/ww8par.cxx @@ -1946,6 +1946,7 @@ void SwWW8ImplReader::ImportDop() m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true); m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true); // rely on default for HYPHENATE_URLS=false + // rely on default for APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH=true IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess(); if (m_xWDop->fDontBreakWrappedTables) diff --git a/sw/source/filter/xml/xmlexp.cxx b/sw/source/filter/xml/xmlexp.cxx index 434eb8c073..fa6f0d1dda 100644 --- a/sw/source/filter/xml/xmlexp.cxx +++ b/sw/source/filter/xml/xmlexp.cxx @@ -397,6 +397,7 @@ void SwXMLExport::GetConfigurationSettings( Sequence < PropertyValue >& rProps) static const std::initializer_list vOmitFalseValues = { u"DoNotBreakWrappedTables", u"AllowTextAfterFloatingTableBreak", + u"DoNotMirrorRtlDrawObjs", }; SvXMLUnitConverter::convertPropertySet( rProps, xProps, &vOmitFalseValues ); diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx index 81ddfbbb5f..aa670c8f92 100644 --- a/sw/source/filter/xml/xmlimp.cxx +++ b/sw/source/filter/xml/xmlimp.cxx @@ -1302,9 +1302,11 @@ void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aC bool bCollapseEmptyCellPara = false; bool bAutoFirstLineIndentDisregardLineSpace = false; bool bHyphenateURLs = false; + bool bApplyTextAttrToEmptyLineAtEndOfParagraph = false; bool bDoNotBreakWrappedTables = false; bool bAllowTextAfterFloatingTableBreak = false; bool bDropCapPunctuation = false; + bool bDoNotMirrorRtlDrawObjs = false; const PropertyValue* currentDatabaseDataSource = nullptr; const PropertyValue* currentDatabaseCommand = nullptr; @@ -1402,6 +1404,10 @@ void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aC { bHyphenateURLs = true; } + else if (rValue.Name == "ApplyTextAttrToEmptyLineAtEndOfParagraph") + { + bApplyTextAttrToEmptyLineAtEndOfParagraph = true; + } else if (rValue.Name == "DoNotBreakWrappedTables") { rValue.Value >>= bDoNotBreakWrappedTables; @@ -1412,6 +1418,10 @@ void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aC } else if ( rValue.Name == "DropCapPunctuation" ) bDropCapPunctuation = true; + else if (rValue.Name == "DoNotMirrorRtlDrawObjs") + { + rValue.Value >>= bDoNotMirrorRtlDrawObjs; + } } catch( Exception& ) { @@ -1579,6 +1589,16 @@ void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aC xProps->setPropertyValue("HyphenateURLs", Any(true)); } + if (!bApplyTextAttrToEmptyLineAtEndOfParagraph) + { + xProps->setPropertyValue("ApplyTextAttrToEmptyLineAtEndOfParagraph", Any(false)); + } + + if (bDoNotMirrorRtlDrawObjs) + { + xProps->setPropertyValue("DoNotMirrorRtlDrawObjs", Any(true)); + } + if (bDoNotBreakWrappedTables) { xProps->setPropertyValue("DoNotBreakWrappedTables", Any(true)); diff --git a/sw/source/ui/frmdlg/frmpage.cxx b/sw/source/ui/frmdlg/frmpage.cxx index b3e2a873ad..8a7ec8d4ff 100644 --- a/sw/source/ui/frmdlg/frmpage.cxx +++ b/sw/source/ui/frmdlg/frmpage.cxx @@ -2306,6 +2306,8 @@ void SwFramePage::Init(const SfxItemSet& rSet) if (SfxItemState::SET == rSet.GetItemState(FN_KEEP_ASPECT_RATIO)) m_xFixedRatioCB->set_active(rSet.Get(FN_KEEP_ASPECT_RATIO).GetValue()); + // see tdf#132591 and tdf#151382 for some examples of FN_KEEP_ASPECT_RATIO cases + m_xFixedRatioCB->save_state(); // columns SwFormatCol aCol( rSet.Get(RES_COL) ); @@ -2364,7 +2366,6 @@ void SwFramePage::Init(const SfxItemSet& rSet) if (rSize.GetWidthPercent() == SwFormatFrameSize::SYNCED || rSize.GetHeightPercent() == SwFormatFrameSize::SYNCED) m_xFixedRatioCB->set_active(true); - m_xFixedRatioCB->save_state(); if (rSize.GetWidthPercent() && rSize.GetWidthPercent() != SwFormatFrameSize::SYNCED && !m_xRelWidthCB->get_active()) { diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 073b6b5964..c062760275 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -3113,9 +3113,12 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt) SwTab nMouseTabCol = SwTab::COL_NONE; const bool bTmp = !rSh.IsDrawCreate() && !m_pApplyTempl && !rSh.IsInSelect() && aMEvt.GetClicks() == 1 && MOUSE_LEFT == aMEvt.GetButtons(); + if ( bTmp && SwTab::COL_NONE != (nMouseTabCol = rSh.WhichMouseTabCol( aDocPos ) ) && - !rSh.IsObjSelectable( aDocPos ) ) + ( !rSh.IsObjSelectable( aDocPos ) || + // allow resizing row height, if the image is anchored as character in the cell + !( SwTab::COL_VERT == nMouseTabCol || SwTab::COL_HORI == nMouseTabCol ) ) ) { // Enhanced table selection if ( SwTab::SEL_HORI <= nMouseTabCol && SwTab::COLSEL_VERT >= nMouseTabCol ) @@ -3984,8 +3987,11 @@ bool SwEditWin::changeMousePointer(Point const & rDocPoint) SwWrtShell & rShell = m_rView.GetWrtShell(); SwTab nMouseTabCol; + if ( SwTab::COL_NONE != (nMouseTabCol = rShell.WhichMouseTabCol( rDocPoint ) ) && - !rShell.IsObjSelectable( rDocPoint ) ) + ( !rShell.IsObjSelectable( rDocPoint ) || + // allow resizing row height, if the image is anchored as character in the cell + !( SwTab::COL_VERT == nMouseTabCol || SwTab::COL_HORI == nMouseTabCol ) ) ) { PointerStyle nPointer = PointerStyle::Null; bool bChkTableSel = false; diff --git a/sw/source/uibase/shells/drwbassh.cxx b/sw/source/uibase/shells/drwbassh.cxx index d5eec21320..2255841607 100644 --- a/sw/source/uibase/shells/drwbassh.cxx +++ b/sw/source/uibase/shells/drwbassh.cxx @@ -60,6 +60,8 @@ #include #include #include +#include +#include using namespace ::com::sun::star; using namespace css::beans; @@ -235,6 +237,14 @@ void SwDrawBaseShell::Execute(SfxRequest const &rReq) aSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_HORI_MIRROR, aHOrient.IsPosToggle())); aSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_HORI_POSITION, aHOrient.GetPos())); + const IDocumentSettingAccess& rIDSA = pFrameFormat->getIDocumentSettingAccess(); + if (rIDSA.get(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS)) + { + SfxGrabBagItem aItem(RES_CHRATR_GRABBAG); + aItem.GetGrabBag()["DoNotMirrorRtlDrawObjs"] <<= true; + aSet.Put(aItem); + } + aSet.Put(SfxUInt16Item(SID_HTML_MODE, nHtmlMode)); pDlg->SetInputSet( &aSet ); diff --git a/sw/source/uibase/uiview/viewtab.cxx b/sw/source/uibase/uiview/viewtab.cxx index 52a26eabb6..eb7f65dc96 100644 --- a/sw/source/uibase/uiview/viewtab.cxx +++ b/sw/source/uibase/uiview/viewtab.cxx @@ -1034,7 +1034,7 @@ void SwView::ExecTabWin( SfxRequest const & rReq ) { SvxColumnItem aColItem(static_cast(pReqArgs->Get(nSlot))); - if( m_bSetTabColFromDoc || (!bSect && rSh.GetTableFormat()) ) + if( m_bSetTabColFromDoc || m_bSetTabRowFromDoc || (!bSect && rSh.GetTableFormat()) ) { OSL_ENSURE(aColItem.Count(), "ColDesc is empty!!"); diff --git a/sw/source/uibase/uno/SwXDocumentSettings.cxx b/sw/source/uibase/uno/SwXDocumentSettings.cxx index 66f3eef7db..3c0a8bbbf2 100644 --- a/sw/source/uibase/uno/SwXDocumentSettings.cxx +++ b/sw/source/uibase/uno/SwXDocumentSettings.cxx @@ -159,6 +159,8 @@ enum SwDocumentSettingsPropertyHandles HANDLE_NO_NUMBERING_SHOW_FOLLOWBY, HANDLE_DROP_CAP_PUNCTUATION, HANDLE_USE_VARIABLE_WIDTH_NBSP, + HANDLE_APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH, + HANDLE_DO_NOT_MIRROR_RTL_DRAW_OBJS, }; } @@ -264,6 +266,8 @@ static rtl::Reference lcl_createSettingsInfo() { OUString("NoNumberingShowFollowBy"), HANDLE_NO_NUMBERING_SHOW_FOLLOWBY, cppu::UnoType::get(), 0 }, { OUString("DropCapPunctuation"), HANDLE_DROP_CAP_PUNCTUATION, cppu::UnoType::get(), 0 }, { OUString("UseVariableWidthNBSP"), HANDLE_USE_VARIABLE_WIDTH_NBSP, cppu::UnoType::get(), 0 }, + { OUString("ApplyTextAttrToEmptyLineAtEndOfParagraph"), HANDLE_APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH, cppu::UnoType::get(), 0 }, + { OUString("DoNotMirrorRtlDrawObjs"), HANDLE_DO_NOT_MIRROR_RTL_DRAW_OBJS, cppu::UnoType::get(), 0 }, /* * As OS said, we don't have a view when we need to set this, so I have to @@ -1074,6 +1078,26 @@ void SwXDocumentSettings::_setSingleValue( const comphelper::PropertyInfo & rInf } } break; + case HANDLE_APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH, bTmp); + } + } + break; + case HANDLE_DO_NOT_MIRROR_RTL_DRAW_OBJS: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS, bTmp); + } + } + break; case HANDLE_DO_NOT_BREAK_WRAPPED_TABLES: { bool bTmp; @@ -1656,6 +1680,18 @@ void SwXDocumentSettings::_getSingleValue( const comphelper::PropertyInfo & rInf DocumentSettingId::HYPHENATE_URLS); } break; + case HANDLE_APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH); + } + break; + case HANDLE_DO_NOT_MIRROR_RTL_DRAW_OBJS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS); + } + break; case HANDLE_DO_NOT_BREAK_WRAPPED_TABLES: { rValue <<= mpDoc->getIDocumentSettingAccess().get( diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 4e67041403..c75f08430c 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -1010,7 +1010,7 @@ void SwContentType::FillMemberList(bool* pbContentChanged) for (const rtl::Reference& pTemp : *pPage) { // #i51726# - all drawing objects can be named now - if (!pTemp->GetName().isEmpty()) + if (!pTemp->IsVirtualObj() && !pTemp->GetName().isEmpty()) { tools::Long nYPos = LONG_MIN; const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer()); -- cgit v1.2.3