sw/qa/core/layout/flycnt.cxx | 45 +++++++++++++++++++++++++++++++++++++-- sw/source/core/layout/fly.cxx | 9 +++++++ sw/source/core/layout/flycnt.cxx | 5 ++-- sw/source/core/layout/tabfrm.cxx | 18 +++++++++++++-- 4 files changed, 71 insertions(+), 6 deletions(-)
New commits: commit 9dda7a9e6551c204e8e709a0873d2b1865de3bf6 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri May 5 08:09:35 2023 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon May 15 10:11:33 2023 +0200 sw floattable: fix UI for turning fly split off Given a multi-page floating table of 2 pages, turning the "allow frame to split across pages" checkbox off in the frame properties dialog resulted in a 1 page document, but with 2 fly frames instead of just 1. There were 3 problems here: 1) SwTabFrame::MakeAll() should join the follow table frames, similar to what happens then the split bit on the table itself is turned off. Note that this is only safe if the frame is not chained, either. 2) SwTabFrame::Cut() should mark the follow fly for deletion. The check here was broken, since we didn't do anything in case the split bit is off for the fly frame. This looks OK, but we still need to handle the case when it was on, but it's now off. 3) Finally SwFlyFrame::Format() always increases the height of flys to at least MINFLY, so drop the HasArea() condition that prevented deletion. Checking if the fly has content is probably enough. With this, disabling the "split" checkbox on the frame dialog results in a single fly frame, as expected. For the testcase, improve Create1x2SplitFly() a bit, so it's closer to what floattable.docx import would create (to help debugging) and also improve SwFlyFrame::UpdateAttr_(), because previously that only worked by accident, due to some rounding error on the UI. (So the fly size changed even when that was not the intent.) Change-Id: I7b33e6557282032c5d8ed2c3aea21b78a33bd6d3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151408 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins (cherry picked from commit f7e1cdf951f7f9cbb5822c49b86ba8a77a2fa878) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151758 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx index bf1068635a72..5f042b1958e6 100644 --- a/sw/qa/core/layout/flycnt.cxx +++ b/sw/qa/core/layout/flycnt.cxx @@ -52,9 +52,9 @@ void Test::Create1x2SplitFly() SwDoc* pDoc = getSwDoc(); SwPageDesc aStandard(pDoc->GetPageDesc(0)); SwFormatFrameSize aPageSize(aStandard.GetMaster().GetFrameSize()); - // 5cm for the page height, 2cm are the top and bottom margins, so 1cm remains for the body + // 10 cm for the page height, 2cm are the top and bottom margins, so 6cm remains for the body // frame: - aPageSize.SetHeight(2834); + aPageSize.SetHeight(5669); aStandard.GetMaster().SetFormatAttr(aPageSize); pDoc->ChgPageDesc(0, aStandard); // Insert a table: @@ -64,8 +64,13 @@ void Test::Create1x2SplitFly() pWrtShell->MoveTable(GotoPrevTable, fnTableStart); pWrtShell->GoPrevCell(); pWrtShell->Insert("A1"); + SwFormatFrameSize aRowSize(SwFrameSize::Minimum); + // 4 cm, so 2 rows don't fit 1 page. + aRowSize.SetHeight(2267); + pWrtShell->SetRowHeight(aRowSize); pWrtShell->GoNextCell(); pWrtShell->Insert("A2"); + pWrtShell->SetRowHeight(aRowSize); // Select cell: pWrtShell->SelAll(); // Select table: @@ -294,6 +299,42 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyEnable) CPPUNIT_ASSERT(pPage2); } +CPPUNIT_TEST_FIXTURE(Test, testSplitFlyDisable) +{ + // Given a document with a floating talbe, table split on 2 pages: + Create1x2SplitFly(); + SwDoc* pDoc = getSwDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower()); + CPPUNIT_ASSERT(pPage1); + CPPUNIT_ASSERT(pPage1->GetNext()); + const SwSortedObjs& rPage1Objs = *pPage1->GetSortedObjs(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage1Objs.size()); + auto pPage1Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage1Objs[0]); + CPPUNIT_ASSERT(pPage1Fly); + CPPUNIT_ASSERT(pPage1Fly->GetFollow()); + + // When turning the "split fly" checkbox off: + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + pWrtShell->StartAllAction(); + auto& rFlys = *pDoc->GetSpzFrameFormats(); + auto pFly = rFlys[0]; + SwAttrSet aSet(pFly->GetAttrSet()); + // Note how the UI puts a SwFormatFrameSize into this item set with a slightly different size + // (e.g. 3823 twips width -> 3821). This means that by accident the UI works even without the + // explicit RES_FLY_SPLIT handling in SwFlyFrame::UpdateAttr_(), but this test will fail when + // that is missing. + aSet.Put(SwFormatFlySplit(false)); + pDoc->SetAttr(aSet, *pFly); + pWrtShell->EndAllAction(); + + // Then make sure the extra page and follow fly frame is joined: + CPPUNIT_ASSERT(!pPage1->GetNext()); + // Without the accompanying fix in place, this test would have failed, the follow fly frame was + // moved to page 1, but it wasn't deleted. + CPPUNIT_ASSERT(!pPage1Fly->GetFollow()); +} + CPPUNIT_TEST_FIXTURE(Test, testSplitFlyFooter) { // Given a document with a floattable, table split on 2 pages with headers/footers: diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 5de34f544894..c5ee8815a71b 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -954,6 +954,15 @@ void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew)); else if (nWhich == RES_FMT_CHG) pOldFormatChg = const_cast<SwFormatChg*>(static_cast<const SwFormatChg*>(pOld)); + else if (nWhich == RES_FLY_SPLIT) + { + // If the fly frame has a table lower, invalidate that, so it joins its follow tab + // frames and re-splits according to the new fly split rule. + if (Lower() && Lower()->IsTabFrame()) + { + Lower()->InvalidateAll_(); + } + } if (aURL.GetMap() && (pNewFormatFrameSize || pOldFormatChg)) { diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx index 3821ac66b1df..de26ee63a18a 100644 --- a/sw/source/core/layout/flycnt.cxx +++ b/sw/source/core/layout/flycnt.cxx @@ -1651,8 +1651,9 @@ void SwRootFrame::DeleteEmptyFlys_() { SwFlyFrame* pFly = *mpFlyDestroy->begin(); mpFlyDestroy->erase( mpFlyDestroy->begin() ); - if (!pFly->getFrameArea().HasArea() && !pFly->ContainsContent() - && !pFly->IsDeleteForbidden()) + // Allow deletion of non-empty flys: a fly with no content is still formatted to have a + // height of MINLAY. + if (!pFly->ContainsContent() && !pFly->IsDeleteForbidden()) { SwFrame::DestroyFrame(pFly); } diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 50e02cec3a7a..647bee556368 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1971,7 +1971,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) const SwBorderAttrs *pAttrs = oAccess->Get(); // All rows should keep together - const bool bDontSplit = !IsFollow() && + bool bDontSplit = !IsFollow() && ( !GetFormat()->GetLayoutSplit().GetValue() ); // The number of repeated headlines @@ -1991,6 +1991,12 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // Ignore the above text node -> row inheritance for floating tables. bTableRowKeep = false; } + else if (!pFly->GetNextLink()) + { + // If the fly is not allowed to split and is not chained, then it makes no sense to + // split the table. + bDontSplit = true; + } } // The Magic Move: Used for the table row keep feature. @@ -3848,7 +3854,15 @@ void SwTabFrame::Cut() !(pFly = pUp->FindFlyFrame())->ContainsContent() && !pFly->ContainsAny()) { - if (pUp == pFly && pFly->IsFlySplitAllowed()) + bool bSplitFly = pFly->IsFlySplitAllowed(); + if (!bSplitFly && pFly->IsFlyAtContentFrame()) + { + // If the fly is not allowed to split, it's still possible that it was allowed to + // split. That is definitely the case when the fly is a follow. + auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly); + bSplitFly = pFlyAtContent->IsFollow(); + } + if (pUp == pFly && bSplitFly) { auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly); pFlyAtContent->DelEmpty();