diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2023-05-24 20:16:49 +0200 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2023-05-24 22:29:15 +0200 |
commit | b0e2b60fa45f236f6a993cc1295a7afddabb5098 (patch) | |
tree | 18ff6911f47a90dc548c7cfc9f8e0d1f0c094111 | |
parent | 35f7f1ad9c2aba1644a46dfb4da0a86f72c6a224 (diff) |
tdf#148897 tdf#143239 sw: move flys off the page in SwTextFrame::Format()
The bugfix regresses testTdf143239, so we add some scary additional hacks:
* testTdf143239: turns out that this was already moving back and forth
many times but then ran into some loop control - now with the fix for
tdf#148897 this still happens, but the result is now bad, where
previously it was good (by accident?).
Move the flys off the page also in SwTextFrame::Format() - there is
already code there to detect that footnotes have been moved off by
Format_() - this is similar, the formatting should be redone immediately
if there is a fly that created SwFlyPortions in the current frame, but
the fly was actually moved to the next page by the very same
Format_() splitting the frame.
* testKeepWithNextPlusFlyFollowTextFlow: here the problem was that the
para with the fly didn't move back to page 1; need to clear the
SwLayouter when toggling FieldNames.
* testKeepwithnextFullheight: this test document will loop creating
infinite pages if the newly add move code is run during
SwObjectFormatterTextFrame - the latter must move the flys if it is
active, not SwTextFrame::Format().
This is all a bit experimental but i failed to have a better idea...
(regression from previous commit)
Change-Id: Icfcbd270137116198128d549b34e7f49a47b6911
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152246
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
-rw-r--r-- | sw/source/core/inc/txtfly.hxx | 4 | ||||
-rw-r--r-- | sw/source/core/inc/txtfrm.hxx | 3 | ||||
-rw-r--r-- | sw/source/core/text/frmform.cxx | 100 | ||||
-rw-r--r-- | sw/source/core/text/txtfly.cxx | 29 | ||||
-rw-r--r-- | sw/source/core/view/viewsh.cxx | 3 |
5 files changed, 120 insertions, 19 deletions
diff --git a/sw/source/core/inc/txtfly.hxx b/sw/source/core/inc/txtfly.hxx index 37d78e3c822f..95d70198f858 100644 --- a/sw/source/core/inc/txtfly.hxx +++ b/sw/source/core/inc/txtfly.hxx @@ -152,7 +152,9 @@ class SwTextFly SwAnchoredObjList* InitAnchoredObjList(); +public: SwAnchoredObjList* GetAnchoredObjList() const; +private: /** Look for the first object which overlaps with the rectangle. @@ -302,6 +304,8 @@ public: void SetIgnoreContour( bool bNew ); void SetIgnoreObjsInHeaderFooter( const bool bNew ); + + SwRect GetFrameArea() const; }; inline SwAnchoredObjList* SwTextFly::GetAnchoredObjList() const diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index 2376153b40d3..c4c51503ec98 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -268,7 +268,8 @@ class SW_DLLPUBLIC SwTextFrame final : public SwContentFrame // In order to safe stack space, we split this method: // Format_ calls Format_ with parameters - void Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara ); + void FormatImpl( vcl::RenderContext* pRenderContext, SwParaPortion *pPara, + ::std::vector<SwAnchoredObject *> & rIntersectingObjs); void Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf, const bool bAdjust = false ); void FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf ); diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx index 912047487a64..5fdf320d4eee 100644 --- a/sw/source/core/text/frmform.cxx +++ b/sw/source/core/text/frmform.cxx @@ -50,6 +50,8 @@ #include <redline.hxx> #include <comphelper/lok.hxx> #include <flyfrms.hxx> +#include <frmtool.hxx> +#include <layouter.hxx> // Tolerance in formatting and text output #define SLOPPY_TWIPS 5 @@ -1775,7 +1777,8 @@ void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf } } -void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara ) +void SwTextFrame::FormatImpl(vcl::RenderContext* pRenderContext, SwParaPortion *pPara, + std::vector<SwAnchoredObject *> & rIntersectingObjs) { const bool bIsEmpty = GetText().isEmpty(); @@ -1810,6 +1813,18 @@ void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pP if( aLine.IsOnceMore() ) FormatOnceMore( aLine, aInf ); + if (aInf.GetTextFly().IsOn()) + { + SwRect const aRect(aInf.GetTextFly().GetFrameArea()); + for (SwAnchoredObject *const pObj : *aInf.GetTextFly().GetAnchoredObjList()) + { + if (!aInf.GetTextFly().AnchoredObjToRect(pObj, aRect).IsEmpty()) + { + rIntersectingObjs.push_back(pObj); + } + } + } + if ( IsVertical() ) SwapWidthAndHeight(); @@ -1991,7 +2006,13 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr } do { - Format_( pRenderContext, aAccess.GetPara() ); + ::std::vector<SwAnchoredObject *> intersectingObjs; + ::std::vector<SwFrame const*> nexts; + for (SwFrame const* pNext = GetNext(); pNext; pNext = pNext->GetNext()) + { + nexts.push_back(pNext); + } + FormatImpl(pRenderContext, aAccess.GetPara(), intersectingObjs); if( pFootnoteBoss && nFootnoteHeight ) { const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont(); @@ -1999,12 +2020,79 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr // If we lost some footnotes, we may have more space // for our main text, so we have to format again ... if( nNewHeight < nFootnoteHeight ) + { nFootnoteHeight = nNewHeight; - else - break; + continue; + } } - else - break; + if (!intersectingObjs.empty()) + { + // assumption is that FormatImpl() only moves frames + // in the next-chain to next page + SwPageFrame *const pPage(FindPageFrame()); + SwTextFrame * pLastMovedAnchor(nullptr); + auto lastIter(nexts.end()); + for (SwAnchoredObject *const pObj : intersectingObjs) + { + SwFrame *const pAnchor(pObj->AnchorFrame()); + SwPageFrame *const pAnchorPage(pAnchor->FindPageFrame()); + if (pAnchorPage != pPage) + { + auto const iter(::std::find(nexts.begin(), nexts.end(), pAnchor)); + if (iter != nexts.end()) + { + assert(pAnchor->IsTextFrame()); + // (can't check SwOszControl::IsInProgress()?) + // called in loop in FormatAnchorFrameAndItsPrevs() + if (static_cast<SwTextFrame const*>(pAnchor)->IsJoinLocked() + // called in loop in SwFrame::PrepareMake() + || pAnchor->IsDeleteForbidden()) + { + // when called via FormatAnchorFrameAndItsPrevs(): + // don't do anything, caller will handle it + pLastMovedAnchor = nullptr; + break; + } + assert(pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()); // how could it move backward? + + if (!pLastMovedAnchor || iter < lastIter) + { + pLastMovedAnchor = static_cast<SwTextFrame *>(pAnchor); + lastIter = iter; + } + } + } + } + SwPageFrame const*const pPrevPage(static_cast<SwPageFrame const*>(pPage->GetPrev())); + if (pLastMovedAnchor) + { + for (SwAnchoredObject *const pObj : intersectingObjs) + { + if (pObj->AnchorFrame() == pLastMovedAnchor) + { + SwPageFrame *const pAnchorPage(pLastMovedAnchor->FindPageFrame()); + SAL_INFO("sw.layout", "SwTextFrame::Format: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum()); + pObj->RegisterAtPage(*pAnchorPage); + // tdf#143239 if the position remains valid, it may not be + // positioned again so would remain on the wrong page! + pObj->InvalidateObjPos(); + ::Notify_Background(pObj->GetDrawObj(), pPage, + pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false); + pObj->SetForceNotifyNewBackground(true); + } + } + if (GetFollow() // this frame was split + && (!pPrevPage // prev page is still valid + || (!pPrevPage->IsInvalid() + && (!pPrevPage->GetSortedObjs() || !pPrevPage->IsInvalidFly())))) + { // this seems a bit risky... + SwLayouter::InsertMovedFwdFrame(GetTextNodeFirst()->GetDoc(), + *pLastMovedAnchor, FindPageFrame()->GetPhyPageNum() + 1); + } + continue; // try again without the fly + } + } + break; } while ( pFootnoteBoss ); if( bOrphan ) { diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx index 2c7f5aeadb4f..cb5105763e73 100644 --- a/sw/source/core/text/txtfly.cxx +++ b/sw/source/core/text/txtfly.cxx @@ -823,6 +823,22 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj, return false; } +SwRect SwTextFly::GetFrameArea() const +{ + // i#28701 - consider complete frame area for new text wrapping + SwRect aRect; + if (m_pCurrFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING)) + { + aRect = m_pCurrFrame->getFramePrintArea(); + aRect += m_pCurrFrame->getFrameArea().Pos(); + } + else + { + aRect = m_pCurrFrame->getFrameArea(); + } + return aRect; +} + // #i68520# SwAnchoredObjList* SwTextFly::InitAnchoredObjList() { @@ -853,18 +869,7 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList() // #i68520# mpAnchoredObjList.reset(new SwAnchoredObjList ); - // #i28701# - consider complete frame area for new - // text wrapping - SwRect aRect; - if ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ) - { - aRect = m_pCurrFrame->getFramePrintArea(); - aRect += m_pCurrFrame->getFrameArea().Pos(); - } - else - { - aRect = m_pCurrFrame->getFrameArea(); - } + SwRect const aRect(GetFrameArea()); // Make ourselves a little smaller than we are, // so that 1-Twip-overlappings are ignored (#49532) SwRectFnSet aRectFnSet(m_pCurrFrame); diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx index e501545e7396..b514990bfed3 100644 --- a/sw/source/core/view/viewsh.cxx +++ b/sw/source/core/view/viewsh.cxx @@ -66,6 +66,7 @@ #include <anchoredobject.hxx> #include <DocumentSettingManager.hxx> #include <DocumentRedlineManager.hxx> +#include <DocumentLayoutManager.hxx> #include <unotxdoc.hxx> #include <view.hxx> @@ -2425,6 +2426,8 @@ void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt ) } } } + // the layout changes but SetModified() wasn't called so do it here: + mxDoc->GetDocumentLayoutManager().ClearSwLayouterEntries(); } if( !bOnlineSpellChgd ) |