summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2023-05-24 20:16:49 +0200
committerMichael Stahl <michael.stahl@allotropia.de>2023-05-24 22:29:15 +0200
commitb0e2b60fa45f236f6a993cc1295a7afddabb5098 (patch)
tree18ff6911f47a90dc548c7cfc9f8e0d1f0c094111
parent35f7f1ad9c2aba1644a46dfb4da0a86f72c6a224 (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.hxx4
-rw-r--r--sw/source/core/inc/txtfrm.hxx3
-rw-r--r--sw/source/core/text/frmform.cxx100
-rw-r--r--sw/source/core/text/txtfly.cxx29
-rw-r--r--sw/source/core/view/viewsh.cxx3
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 )