Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / core / layout / flowfrm.cxx
bloba60e51962541c254e8bead152cb9e45c1f8d95f2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
23 #include <svx/svdobj.hxx>
25 #include <anchoredobject.hxx>
26 #include <bodyfrm.hxx>
27 #include <swtable.hxx>
28 #include <rootfrm.hxx>
29 #include <pagefrm.hxx>
30 #include <viewimp.hxx>
31 #include <viewopt.hxx>
32 #include <frmatr.hxx>
33 #include <frmtool.hxx>
34 #include <IDocumentFieldsAccess.hxx>
35 #include <editeng/formatbreakitem.hxx>
36 #include <editeng/keepitem.hxx>
37 #include <fmtanchr.hxx>
38 #include <fmtsrnd.hxx>
39 #include <fmtpdsc.hxx>
40 #include <editeng/ulspitem.hxx>
41 #include <tgrditem.hxx>
42 #include <txtftn.hxx>
43 #include <fmtftn.hxx>
44 #include <editeng/pgrditem.hxx>
45 #include <paratr.hxx>
46 #include <ftnfrm.hxx>
47 #include <txtfrm.hxx>
48 #include <notxtfrm.hxx>
49 #include <tabfrm.hxx>
50 #include <rowfrm.hxx>
51 #include <pagedesc.hxx>
52 #include <layact.hxx>
53 #include <flyfrm.hxx>
54 #include <sectfrm.hxx>
55 #include <section.hxx>
56 #include <dbg_lay.hxx>
57 #include <lineinfo.hxx>
58 #include <fmtclbl.hxx>
59 #include <sortedobjs.hxx>
60 #include <layouter.hxx>
61 #include <fmtfollowtextflow.hxx>
62 #include <calbck.hxx>
63 #include <IDocumentSettingAccess.hxx>
64 #include <IDocumentDrawModelAccess.hxx>
65 #include <pam.hxx>
66 #include <ndtxt.hxx>
67 #include <flyfrms.hxx>
69 bool SwFlowFrame::s_bMoveBwdJump = false;
71 SwFlowFrame::SwFlowFrame( SwFrame &rFrame ) :
72 m_rThis( rFrame ),
73 m_pFollow( nullptr ),
74 m_pPrecede( nullptr ),
75 m_bLockJoin( false ),
76 m_bUndersized( false ),
77 m_bFlyLock( false )
80 SwFlowFrame::~SwFlowFrame()
82 if (m_pFollow)
84 m_pFollow->m_pPrecede = nullptr;
86 if (m_pPrecede)
88 m_pPrecede->m_pFollow = nullptr;
92 void SwFlowFrame::SetFollow(SwFlowFrame *const pFollow)
94 if (m_pFollow)
96 assert(this == m_pFollow->m_pPrecede);
97 m_pFollow->m_pPrecede = nullptr;
99 m_pFollow = pFollow;
100 if (m_pFollow != nullptr)
102 if (m_pFollow->m_pPrecede) // re-chaining pFollow?
104 assert(m_pFollow == m_pFollow->m_pPrecede->m_pFollow);
105 m_pFollow->m_pPrecede->m_pFollow = nullptr;
107 m_pFollow->m_pPrecede = this;
111 /// @return true if any follow has the JoinLocked flag
112 bool SwFlowFrame::HasLockedFollow() const
114 const SwFlowFrame* pFrame = GetFollow();
115 while( pFrame )
117 if( pFrame->IsJoinLocked() )
118 return true;
119 pFrame = pFrame->GetFollow();
121 return false;
124 bool SwFlowFrame::IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue )
126 // If all the predecessors up to the first of the chain have
127 // the 'keep' attribute set, and the first of the chain's
128 // IsFwdMoveAllowed returns false, then we're not allowed to move.
129 SwFrame *pFrame = &m_rThis;
130 if ( !pFrame->IsInFootnote() ) {
131 if ( bIgnoreMyOwnKeepValue && pFrame->GetIndPrev() )
132 pFrame = pFrame->GetIndPrev();
135 if (pFrame->GetAttrSet()->GetKeep().GetValue()
136 || pFrame->IsHiddenNow())
137 pFrame = pFrame->GetIndPrev();
138 else
139 return true;
140 } while ( pFrame );
142 //See IsFwdMoveAllowed()
143 bool bRet = false;
144 if ( pFrame && pFrame->GetIndPrev() )
145 bRet = true;
146 return bRet;
149 void SwFlowFrame::CheckKeep()
151 // Kick off the "last" predecessor with a 'keep' attribute, because
152 // it's possible for the whole troop to move back.
153 SwFrame *pPre = m_rThis.GetIndPrev();
154 assert(pPre);
155 while (pPre && pPre->IsHiddenNow())
157 pPre = pPre->GetIndPrev();
159 if (!pPre)
161 return;
163 if( pPre->IsSctFrame() )
165 SwFrame *pLast = static_cast<SwSectionFrame*>(pPre)->FindLastContent();
166 while (pLast && pLast->IsHiddenNow())
168 pLast = pLast->GetIndPrev();
170 if( pLast && pLast->FindSctFrame() == pPre )
171 pPre = pLast;
172 else
173 return;
175 SwFrame* pTmp{pPre};
176 bool bKeep;
177 while ( (bKeep = pPre->GetAttrSet()->GetKeep().GetValue()) &&
178 nullptr != (pTmp = pTmp->GetIndPrev()) )
180 if (pTmp->IsHiddenNow())
182 continue;
184 if( pTmp->IsSctFrame() )
186 SwFrame *pLast = static_cast<SwSectionFrame*>(pTmp)->FindLastContent();
187 while (pLast && pLast->IsHiddenNow())
189 pLast = pLast->GetIndPrev();
191 if( pLast && pLast->FindSctFrame() == pTmp )
192 pTmp = pLast;
193 else
194 break;
196 pPre = pTmp;
198 if ( bKeep )
199 pPre->InvalidatePos();
202 namespace
205 * Determines if the next content frame after rThis will require the full area of the parent body
206 * frame.
208 bool IsNextContentFullPage(const SwFrame& rThis)
210 const SwFrame* pNext = rThis.FindNextCnt();
211 if (!pNext)
213 return false;
216 const SwSortedObjs* pNextDrawObjs = pNext->GetDrawObjs();
217 if (!pNextDrawObjs || !pNextDrawObjs->size())
219 return false;
222 for (const auto& pDrawObj : *pNextDrawObjs)
224 if (!pDrawObj)
226 continue;
229 SwTwips nDrawObjHeight = pDrawObj->GetObjRectWithSpaces().Height();
230 const SwPageFrame* pPageFrame = pDrawObj->GetPageFrame();
231 if (!pPageFrame)
233 continue;
236 SwTwips nBodyHeight = pPageFrame->GetLower()->getFrameArea().Height();
237 if (nDrawObjHeight < nBodyHeight)
239 continue;
242 const SwFormatSurround& rSurround = pDrawObj->GetFrameFormat()->GetSurround();
243 if (rSurround.GetSurround() != text::WrapTextMode_NONE)
245 continue;
248 // At this point the height of the draw object will use all the vertical available space,
249 // and also no wrapping will be performed, so all horizontal space will be taken as well.
250 return true;
253 return false;
257 bool SwFlowFrame::IsKeep(SvxFormatKeepItem const& rKeep,
258 SvxFormatBreakItem const& rBreak,
259 bool const bCheckIfLastRowShouldKeep) const
261 assert(m_rThis.IsTextFrame() ? !static_cast<SwTextFrame const&>(m_rThis).IsHiddenNowImpl() : !m_rThis.IsHiddenNow()); // check it before?
262 // 1. The keep attribute is ignored inside footnotes
263 // 2. For compatibility reasons, the keep attribute is
264 // ignored for frames inside table cells
265 // 3. If bBreakCheck is set to true, this function only checks
266 // if there are any break after attributes set at rAttrs
267 // or break before attributes set for the next content (or next table)
268 // 4. Keep is ignored if the next frame will require its own page.
269 bool bKeep = bCheckIfLastRowShouldKeep ||
270 ( !m_rThis.IsInFootnote() &&
271 ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) &&
272 rKeep.GetValue() && !IsNextContentFullPage(m_rThis));
274 if (bKeep && m_rThis.IsTextFrame())
276 auto& rTextFrame = static_cast<SwTextFrame&>(m_rThis);
277 if (rTextFrame.HasNonLastSplitFlyDrawObj())
279 // Allow split for the non-last anchors of a split fly, even if rKeep.GetValue() is
280 // true.
281 bKeep = false;
285 OSL_ENSURE( !bCheckIfLastRowShouldKeep || m_rThis.IsTabFrame(),
286 "IsKeep with bCheckIfLastRowShouldKeep should only be used for tabfrms" );
288 // Ignore keep attribute if there are break situations:
289 if ( bKeep )
291 switch (rBreak.GetBreak())
293 case SvxBreak::ColumnAfter:
294 case SvxBreak::ColumnBoth:
295 case SvxBreak::PageAfter:
296 case SvxBreak::PageBoth:
298 bKeep = false;
299 break;
301 default: break;
303 if ( bKeep )
305 SwFrame *pNxt;
306 if( nullptr != (pNxt = m_rThis.FindNextCnt()) &&
307 (!m_pFollow || pNxt != &m_pFollow->GetFrame()))
309 // The last row of a table only keeps with the next content
310 // it they are in the same section:
311 if ( bCheckIfLastRowShouldKeep )
313 const SwSection* pThisSection = nullptr;
314 const SwSection* pNextSection = nullptr;
315 const SwSectionFrame* pThisSectionFrame = m_rThis.FindSctFrame();
316 const SwSectionFrame* pNextSectionFrame = pNxt->FindSctFrame();
318 if ( pThisSectionFrame )
319 pThisSection = pThisSectionFrame->GetSection();
321 if ( pNextSectionFrame )
322 pNextSection = pNextSectionFrame->GetSection();
324 if ( pThisSection != pNextSection )
325 bKeep = false;
328 if ( bKeep )
330 SvxFormatBreakItem const* pBreak;
331 SwFormatPageDesc const* pPageDesc;
332 SwTabFrame* pTab = pNxt->IsInTab() ? pNxt->FindTabFrame() : nullptr;
333 if (pTab && (!m_rThis.IsInTab() || m_rThis.FindTabFrame() != pTab))
335 const SwAttrSet *const pSet = &pTab->GetFormat()->GetAttrSet();
336 pBreak = &pSet->GetBreak();
337 pPageDesc = &pSet->GetPageDesc();
339 else
341 pBreak = &pNxt->GetBreakItem();
342 pPageDesc = &pNxt->GetPageDescItem();
345 if (pPageDesc->GetPageDesc())
346 bKeep = false;
347 else switch (pBreak->GetBreak())
349 case SvxBreak::ColumnBefore:
350 case SvxBreak::ColumnBoth:
351 case SvxBreak::PageBefore:
352 case SvxBreak::PageBoth:
353 bKeep = false;
354 break;
355 default: break;
361 return bKeep;
364 SwFrame * SwFlowFrame::FindPrevIgnoreHidden() const
366 SwFrame * pRet{m_rThis.FindPrev()};
367 while (pRet && pRet->IsHiddenNow())
369 pRet = pRet->FindPrev();
371 return pRet;
374 SwFrame * SwFlowFrame::FindNextIgnoreHidden() const
376 SwFrame * pRet{m_rThis.FindNext()};
377 while (pRet && pRet->IsHiddenNow())
379 pRet = pRet->FindNext();
381 return pRet;
384 sal_uInt8 SwFlowFrame::BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect )
386 // The return value helps deciding whether we need to flow back (3),
387 // or whether we can use the good old WouldFit (0, 1), or if
388 // it's reasonable to relocate and test-format (2).
390 // Bit 1 in this case means that there are objects anchored to myself,
391 // bit 2 means that I have to evade other objects.
393 // If a SurroundObj that desires to be wrapped around overlaps with the
394 // Rect, it's required to flow (because we can't guess the relationships).
395 // However it's possible for a test formatting to happen.
396 // If the SurroundObj is a Fly and I'm a Lower, or the Fly is a Lower of
397 // mine, then it doesn't matter.
398 // If the SurroundObj is anchored in a character bound Fly, and I'm not
399 // a Lower of that character bound Fly myself, then the Fly doesn't matter.
401 // If the object is anchored with me, i can ignore it, because
402 // it's likely that it will follow me with the flow. A test formatting is
403 // not allowed in that case, however!
404 sal_uInt8 nRet = 0;
405 SwFlowFrame *pTmp = this;
407 { // If there are objects hanging either on me or on a follow, we can't
408 // do a test formatting, because paragraph bound objects wouldn't
409 // be properly considered, and character bound objects shouldn't
410 // be test formatted at all.
411 if( pTmp->GetFrame().GetDrawObjs() )
412 nRet = 1;
413 pTmp = pTmp->GetFollow();
414 } while ( !nRet && pTmp );
415 const SwSortedObjs *pObjs = pPage ? pPage->GetSortedObjs() : nullptr;
416 if (pObjs)
419 const SwSortedObjs &rObjs = *pObjs;
420 SwNodeOffset nIndex = NODE_OFFSET_MAX;
421 for ( size_t i = 0; nRet < 3 && i < rObjs.size(); ++i )
424 SwAnchoredObject* pObj = rObjs[i];
425 const SwFrameFormat* pFormat = pObj->GetFrameFormat();
426 const SwRect aRect( pObj->GetObjRect() );
427 if ( aRect.Overlaps( rRect ) &&
428 pFormat->GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
430 if( m_rThis.IsLayoutFrame() && //Fly Lower of This?
431 Is_Lower_Of( &m_rThis, pObj->GetDrawObj() ) )
432 continue;
433 if( auto pFly = pObj->DynCastFlyFrame() )
435 if ( pFly->IsAnLower( &m_rThis ) )//This Lower of Fly?
436 continue;
439 const SwFrame* pAnchor = pObj->GetAnchorFrame();
440 if ( pAnchor == &m_rThis )
442 nRet |= 1;
443 continue;
446 // Don't do this if the object is anchored behind me in the text
447 // flow, because then I wouldn't evade it.
448 if ( ::IsFrameInSameContext( pAnchor, &m_rThis ) )
450 if ( pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA )
452 // The index of the other one can be retrieved using the anchor attribute.
453 SwNodeOffset nTmpIndex = pFormat->GetAnchor().GetAnchorNode()->GetIndex();
454 // Now we're going to check whether the current paragraph before
455 // the anchor of the displacing object sits in the text. If this
456 // is the case, we don't try to evade it.
457 // The index is being determined via SwFormatAnchor, because it's
458 // getting quite expensive otherwise.
459 if( NODE_OFFSET_MAX == nIndex )
461 const SwNode *pNode;
462 if (m_rThis.IsTextFrame())
463 pNode = static_cast<SwTextFrame&>(m_rThis).GetTextNodeFirst();
464 else if (m_rThis.IsNoTextFrame())
465 pNode = static_cast<SwNoTextFrame&>(m_rThis).GetNode();
466 else if( m_rThis.IsSctFrame() )
467 pNode = static_cast<SwSectionFormat*>(static_cast<SwSectionFrame&>(m_rThis).
468 GetFormat())->GetSectionNode();
469 else
471 assert(!m_rThis.IsContentFrame());
472 OSL_ENSURE( m_rThis.IsTabFrame(), "new FowFrame?" );
473 pNode = static_cast<SwTabFrame&>(m_rThis).GetTable()->
474 GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
476 nIndex = pNode->GetIndex();
478 if (nIndex < nTmpIndex &&
479 (!m_rThis.IsTextFrame() ||
480 !FrameContainsNode(static_cast<SwTextFrame&>(m_rThis), nTmpIndex)))
482 continue;
486 else
487 continue;
489 nRet |= 2;
493 return nRet;
496 /// A specialized form of Cut(), which relocates a whole chain (this and the following,
497 /// in particular). During this process, only the minimum operations and notifications are done.
498 SwLayoutFrame *SwFlowFrame::CutTree( SwFrame *pStart )
500 // Cut the Start and all the neighbours; they are chained together and
501 // a handle to the first one is returned. Residuals are invalidated
502 // as appropriate.
504 SwLayoutFrame *pLay = pStart->GetUpper();
505 if ( pLay->IsInFootnote() )
506 pLay = pLay->FindFootnoteFrame();
508 // i#58846
509 // <pPrepare( PrepareHint::QuoVadis )> only for frames in footnotes
510 if( pStart->IsInFootnote() )
512 SwFrame* pTmp = pStart->GetIndPrev();
513 if( pTmp )
514 pTmp->Prepare( PrepareHint::QuoVadis );
517 // Just cut quickly and take care that we don't cause problems with the
518 // left-behinds. The pointers of the chain being cut can point who-knows where.
519 if ( pStart == pStart->GetUpper()->Lower() )
520 pStart->GetUpper()->m_pLower = nullptr;
521 if ( pStart->GetPrev() )
523 pStart->GetPrev()->mpNext = nullptr;
524 pStart->mpPrev = nullptr;
527 if ( pLay->IsFootnoteFrame() )
529 if ( !pLay->Lower() && !pLay->IsColLocked() &&
530 !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked() )
532 // tdf#101821 don't delete it while iterating over it
533 if (!pLay->IsDeleteForbidden())
535 pLay->Cut();
536 SwFrame::DestroyFrame(pLay);
538 // else: assume there is code on the stack to clean up empty
539 // footnote frames
540 // (don't go into the else branch below, it produces a disconnected
541 // footnote with null upper that can be returned by
542 // SwFootnoteBossFrame::FindFootnote() causing null pointer deref
543 // in SwTextFrame::ConnectFootnote()
545 else
547 bool bUnlock = !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked();
548 static_cast<SwFootnoteFrame*>(pLay)->LockBackMove();
549 pLay->InvalidateSize();
550 pLay->Calc(pLay->getRootFrame()->GetCurrShell()->GetOut());
551 SwContentFrame *pCnt = pLay->ContainsContent();
552 while ( pCnt && pLay->IsAnLower( pCnt ) )
554 // It's possible for the ContentFrame to be locked, and we don't want
555 // to end up in an endless page migration, so we're not even
556 // going to call Calc!
557 OSL_ENSURE( pCnt->IsTextFrame(), "The Graphic has landed." );
558 if ( static_cast<SwTextFrame*>(pCnt)->IsLocked() ||
559 static_cast<SwTextFrame*>(pCnt)->GetFollow() == pStart )
560 break;
561 pCnt->Calc(pCnt->getRootFrame()->GetCurrShell()->GetOut());
562 pCnt = pCnt->GetNextContentFrame();
564 if( bUnlock )
565 static_cast<SwFootnoteFrame*>(pLay)->UnlockBackMove();
567 pLay = nullptr;
569 return pLay;
572 /// A specialized form of Paste(), which relocates a whole chain (this and the following,
573 /// in particular). During this process, only the minimum operations and notifications are done.
574 bool SwFlowFrame::PasteTree( SwFrame *pStart, SwLayoutFrame *pParent, SwFrame *pSibling,
575 SwFrame *pOldParent )
577 // returns true if there's a LayoutFrame in the chain.
578 bool bRet = false;
580 // The chain beginning with pStart is inserted before pSibling
581 // under the parent. We take care to invalidate as required.
583 // I'm receiving a finished chain. We need to update the pointers for
584 // the beginning of the chain, then all the uppers and finally the end.
585 // On the way there, we invalidate as required.
586 if ( pSibling )
588 pStart->mpPrev = pSibling->GetPrev();
589 if ( nullptr != pStart->mpPrev )
590 pStart->GetPrev()->mpNext = pStart;
591 else
592 pParent->m_pLower = pStart;
593 pSibling->InvalidatePos_();
594 pSibling->InvalidatePrt_();
596 else
598 pStart->mpPrev = pParent->Lower();
599 if ( nullptr == pStart->mpPrev )
600 pParent->m_pLower = pStart;
601 else
602 //i#100782
603 //If the pParent has more than 1 child nodes, former design will
604 //ignore them directly without any collection work. It will make some
605 //dangling pointers. This lead the crash...
606 //The new design will find the last child of pParent in loop way, and
607 //add the pStart after the last child.
608 // pParent->Lower()->pNext = pStart;
610 SwFrame* pTemp = pParent->m_pLower;
611 while (pTemp)
613 if (pTemp->mpNext)
614 pTemp = pTemp->mpNext;
615 else
617 pStart->mpPrev = pTemp;
618 pTemp->mpNext = pStart;
619 break;
625 // i#27145
626 if ( pParent->IsSctFrame() )
628 // We have no sibling because pParent is a section frame and
629 // has just been created to contain some content. The printing
630 // area of the frame behind pParent has to be invalidated, so
631 // that the correct distance between pParent and the next frame
632 // can be calculated.
633 pParent->InvalidateNextPrtArea();
636 SwFrame *pFloat = pStart;
637 SwFrame *pLst = nullptr;
638 SwRectFnSet aRectFnSet(pParent);
639 SwTwips nGrowVal = 0;
641 { pFloat->mpUpper = pParent;
642 pFloat->InvalidateAll_();
643 pFloat->CheckDirChange();
645 // I'm a friend of the TextFrame and thus am allowed to do many things.
646 // The CacheIdx idea seems to be a bit risky!
647 if ( pFloat->IsTextFrame() )
649 if ( static_cast<SwTextFrame*>(pFloat)->GetCacheIdx() != USHRT_MAX )
650 static_cast<SwTextFrame*>(pFloat)->Init(); // I'm his friend.
652 else
653 bRet = true;
655 nGrowVal = o3tl::saturating_add(nGrowVal, aRectFnSet.GetHeight(pFloat->getFrameArea()));
656 if ( pFloat->GetNext() )
657 pFloat = pFloat->GetNext();
658 else
660 pLst = pFloat;
661 pFloat = nullptr;
663 } while ( pFloat );
665 if ( pSibling )
667 pLst->mpNext = pSibling;
668 pSibling->mpPrev = pLst;
669 if( pSibling->IsInFootnote() )
671 if( pSibling->IsSctFrame() )
672 pSibling = static_cast<SwSectionFrame*>(pSibling)->ContainsAny();
673 if( pSibling )
674 pSibling->Prepare( PrepareHint::ErgoSum );
677 if ( nGrowVal )
679 if ( pOldParent && pOldParent->IsBodyFrame() ) // For variable page height while browsing
680 pOldParent->Shrink( nGrowVal );
681 pParent->Grow( nGrowVal );
684 if ( pParent->IsFootnoteFrame() )
685 static_cast<SwFootnoteFrame*>(pParent)->InvalidateNxtFootnoteCnts( pParent->FindPageFrame() );
686 return bRet;
689 void SwFlowFrame::MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling )
691 OSL_ENSURE( pParent, "No parent given." );
692 OSL_ENSURE( m_rThis.GetUpper(), "Where are we coming from?" );
694 // Be economical with notifications if an action is running.
695 SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
696 const SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
697 const bool bComplete = pImp && pImp->IsAction() && pImp->GetLayAction().IsComplete();
699 if ( !bComplete )
701 SwFrame *pPre = m_rThis.GetIndPrev();
702 if ( pPre )
704 pPre->SetRetouche();
705 // follow-up of i#26250
706 // invalidate printing area of previous frame, if it's in a table
707 if ( pPre->GetUpper()->IsInTab() )
709 pPre->InvalidatePrt_();
711 pPre->InvalidatePage();
713 else
715 m_rThis.GetUpper()->SetCompletePaint();
716 m_rThis.GetUpper()->InvalidatePage();
720 SwPageFrame *pOldPage = m_rThis.FindPageFrame();
722 SwLayoutFrame *pOldParent;
723 bool bInvaLay;
726 //JoinLock pParent for the lifetime of the Cut/Paste call to avoid
727 //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
728 //into
729 FlowFrameJoinLockGuard aJoinGuard(pParent);
730 SwFrameDeleteGuard aDeleteGuard(pParent);
731 pOldParent = CutTree( &m_rThis );
732 bInvaLay = PasteTree( &m_rThis, pParent, pSibling, pOldParent );
735 // If, by cutting & pasting, an empty SectionFrame came into existence, it should
736 // disappear automatically.
737 SwSectionFrame *pSct;
739 SwFlyFrame* pFly = nullptr;
740 if ( pOldParent && !pOldParent->Lower() &&
741 ( pOldParent->IsInSct() &&
742 !(pSct = pOldParent->FindSctFrame())->ContainsContent() &&
743 !pSct->ContainsAny( true ) ) )
745 pSct->DelEmpty( false );
747 else if (pOldParent && !pOldParent->Lower()
748 && (pOldParent->IsInFly() && !(pFly = pOldParent->FindFlyFrame())->ContainsContent()
749 && !pFly->ContainsAny()))
751 if (pFly->IsFlySplitAllowed())
753 // Master fly is empty now that we pasted the content to the follow, mark it for
754 // deletion.
755 auto pFlyAtContent = static_cast<SwFlyAtContentFrame*>(pFly);
756 pFlyAtContent->DelEmpty();
760 // If we're in a column section, we'd rather not call Calc "from below"
761 if( !m_rThis.IsInSct() &&
762 ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) )
763 m_rThis.GetUpper()->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
764 else if( m_rThis.GetUpper()->IsSctFrame() )
766 SwSectionFrame* pTmpSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
767 bool bOld = pTmpSct->IsContentLocked();
768 pTmpSct->SetContentLock( true );
769 pTmpSct->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
770 if( !bOld )
771 pTmpSct->SetContentLock( false );
773 SwPageFrame *pPage = m_rThis.FindPageFrame();
775 if ( pOldPage != pPage )
777 m_rThis.InvalidatePage( pPage );
778 if ( m_rThis.IsLayoutFrame() )
780 SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(&m_rThis)->ContainsContent();
781 if ( pCnt )
782 pCnt->InvalidatePage( pPage );
784 else if ( pSh && pSh->GetDoc()->GetLineNumberInfo().IsRestartEachPage()
785 && pPage->FindFirstBodyContent() == &m_rThis )
787 m_rThis.InvalidateLineNum_();
790 if ( bInvaLay || (pSibling && pSibling->IsLayoutFrame()) )
791 m_rThis.GetUpper()->InvalidatePage( pPage );
794 bool SwFlowFrame::IsAnFollow( const SwFlowFrame *pAssumed ) const
796 const SwFlowFrame *pFoll = this;
798 { if ( pAssumed == pFoll )
799 return true;
800 pFoll = pFoll->GetFollow();
801 } while ( pFoll );
802 return false;
805 SwTextFrame* SwContentFrame::FindMaster() const
807 OSL_ENSURE( IsFollow(), "SwContentFrame::FindMaster(): !IsFollow" );
809 const SwContentFrame* pPrec = static_cast<const SwContentFrame*>(SwFlowFrame::GetPrecede());
811 if ( pPrec && pPrec->HasFollow() && pPrec->GetFollow() == this )
813 OSL_ENSURE( pPrec->IsTextFrame(), "NoTextFrame with follow found" );
814 return const_cast<SwTextFrame*>(static_cast< const SwTextFrame* >(pPrec));
817 OSL_FAIL( "Follow is lost in Space." );
818 return nullptr;
821 SwSectionFrame* SwSectionFrame::FindMaster() const
823 OSL_ENSURE( IsFollow(), "SwSectionFrame::FindMaster(): !IsFollow" );
825 if (!m_pSection)
826 return nullptr;
828 SwIterator<SwSectionFrame,SwFormat> aIter( *m_pSection->GetFormat() );
829 SwSectionFrame* pSect = aIter.First();
830 while ( pSect )
832 if (pSect->GetFollow() == this)
833 return pSect;
834 pSect = aIter.Next();
837 OSL_FAIL( "Follow is lost in Space." );
838 return nullptr;
841 SwTabFrame* SwTabFrame::FindMaster( bool bFirstMaster ) const
843 OSL_ENSURE( IsFollow(), "SwTabFrame::FindMaster(): !IsFollow" );
845 SwIterator<SwTabFrame,SwFormat> aIter( *GetTable()->GetFrameFormat() );
846 SwTabFrame* pTab = aIter.First();
847 while ( pTab )
849 if ( bFirstMaster )
851 // Optimization. This makes code like this obsolete:
852 // while ( pTab->IsFollow() )
853 // pTab = pTab->FindMaster();
855 if ( !pTab->IsFollow() )
857 SwTabFrame* pNxt = pTab;
858 while ( pNxt )
860 if ( pNxt->GetFollow() == this )
861 return pTab;
862 pNxt = pNxt->GetFollow();
866 else
868 if ( pTab->GetFollow() == this )
869 return pTab;
872 pTab = aIter.Next();
875 OSL_FAIL( "Follow is lost in Space." );
876 return nullptr;
880 * Returns the next/previous Layout leaf that's NOT below this (or even is this itself).
881 * Also, that leaf must be in the same text flow as the pAnch origin frame (Body, Footnote)
883 const SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd,
884 const SwFrame *pAnch ) const
886 // No flow, no joy...
887 if ( !(IsInDocBody() || IsInFootnote() || IsInFly()) )
888 return nullptr;
890 const SwFrame *pLeaf = this;
891 bool bFound = false;
894 { pLeaf = const_cast<SwFrame*>(pLeaf)->GetLeaf( eMakePage, bFwd );
896 if ( pLeaf &&
897 (!IsLayoutFrame() || !static_cast<const SwLayoutFrame*>(this)->IsAnLower( pLeaf )))
899 if ( pAnch->IsInDocBody() == pLeaf->IsInDocBody() &&
900 pAnch->IsInFootnote() == pLeaf->IsInFootnote() )
902 bFound = true;
905 } while ( !bFound && pLeaf );
907 return static_cast<const SwLayoutFrame*>(pLeaf);
910 SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd )
912 if ( IsInFootnote() )
913 return bFwd ? GetNextFootnoteLeaf( eMakePage ) : GetPrevFootnoteLeaf( eMakePage );
915 // i#53323
916 // A frame could be inside a table AND inside a section.
917 // Thus, it has to be determined, which is the first parent.
918 bool bInTab( IsInTab() );
919 bool bInSct( IsInSct() );
920 if ( bInTab && bInSct )
922 const SwFrame* pUpperFrame( GetUpper() );
923 while ( pUpperFrame )
925 if ( pUpperFrame->IsTabFrame() )
927 // the table is the first.
928 bInSct = false;
929 break;
931 else if ( pUpperFrame->IsSctFrame() )
933 // the section is the first.
934 bInTab = false;
935 break;
938 pUpperFrame = pUpperFrame->GetUpper();
942 if ( bInTab && ( !IsTabFrame() || GetUpper()->IsCellFrame() ) ) // TABLE IN TABLE
943 return bFwd ? GetNextCellLeaf() : GetPrevCellLeaf();
945 if ( bInSct )
946 return bFwd ? GetNextSctLeaf( eMakePage ) : GetPrevSctLeaf();
948 if (IsInFly() && FindFlyFrame()->IsFlySplitAllowed())
950 if (bFwd)
952 return GetNextFlyLeaf(eMakePage);
954 else
956 return GetPrevFlyLeaf();
960 return bFwd ? GetNextLeaf( eMakePage ) : GetPrevLeaf();
963 namespace sw {
965 bool HasPageBreakBefore(SwPageFrame const& rPage)
967 SwFrame const* pFlow(rPage.FindFirstBodyContent());
968 if (!pFlow)
970 return false;
972 while (pFlow->GetUpper()->IsInTab())
974 pFlow = pFlow->GetUpper()->FindTabFrame();
976 return pFlow->GetPageDescItem().GetPageDesc()
977 || pFlow->GetBreakItem().GetBreak() == SvxBreak::PageBefore
978 || pFlow->GetBreakItem().GetBreak() == SvxBreak::PageBoth;
981 } // namespace sw
983 bool SwFrame::WrongPageDesc( SwPageFrame* pNew )
985 // Now it's getting a bit complicated:
987 // Maybe I'm bringing a Pagedesc myself; in that case,
988 // the pagedesc of the next page needs to correspond.
989 // Otherwise, I'll have to dig a bit deeper to see where
990 // the following Pagedesc is coming from.
991 // If the following page itself tells me that it's pagedesc
992 // is wrong, I can happily exchange it.
993 // If the page however thinks that it's pagedesc is correct,
994 // this doesn't mean it's useful to me:
995 // If the first BodyContent asks for a PageDesc or a PageBreak,
996 // I'll have to insert a new page - except the desired page is
997 // the correct one.
998 // If I inserted a new page, the problems only get started:
999 // because then it's likely for the next page to have been
1000 // wrong and having been swapped because of that.
1001 // This in turn means that I have a new (and correct) page,
1002 // but the conditions to swap still apply.
1003 // Way out of the situation: Try to preliminarily insert a
1004 // new page once (empty pages are already inserted by InsertPage()
1005 // if required)
1007 //My Pagedesc doesn't count if I'm a follow!
1008 const SwPageDesc *pDesc = nullptr;
1009 std::optional<sal_uInt16> oTmp;
1010 SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( this );
1011 if ( !pFlow || !pFlow->IsFollow() )
1013 const SwFormatPageDesc &rFormatDesc = GetPageDescItem();
1014 pDesc = rFormatDesc.GetPageDesc();
1015 if( pDesc )
1017 if( !pDesc->GetRightFormat() )
1018 oTmp = 2;
1019 else if( !pDesc->GetLeftFormat() )
1020 oTmp = 1;
1021 else if( rFormatDesc.GetNumOffset() )
1022 oTmp = rFormatDesc.GetNumOffset();
1026 // Does the Content bring a Pagedesc or do we need the
1027 // virtual page number of the new layout leaf?
1028 // PageDesc isn't allowed with Follows
1029 const bool isRightPage = oTmp ? sw::IsRightPageByNumber(*mpRoot, *oTmp) : pNew->OnRightPage();
1030 if ( !pDesc )
1031 pDesc = pNew->FindPageDesc();
1033 bool bFirst = pNew->OnFirstPage();
1035 const SwFlowFrame *pNewFlow = pNew->FindFirstBodyContent();
1036 // Did we find ourselves?
1037 if( pNewFlow == pFlow )
1038 pNewFlow = nullptr;
1039 if ( pNewFlow && pNewFlow->GetFrame().IsInTab() )
1040 pNewFlow = pNewFlow->GetFrame().FindTabFrame();
1041 const SwPageDesc *pNewDesc= ( pNewFlow && !pNewFlow->IsFollow() )
1042 ? pNewFlow->GetFrame().GetPageDescItem().GetPageDesc()
1043 : nullptr;
1045 SAL_INFO( "sw.pageframe", "WrongPageDesc p: " << pNew << " phys: " << pNew->GetPhyPageNum() );
1046 SAL_INFO( "sw.pageframe", "WrongPageDesc " << pNew->GetPageDesc() << " " << pDesc );
1047 SAL_INFO( "sw.pageframe", "WrongPageDesc right: " << isRightPage
1048 << " first: " << bFirst << " " << pNew->GetFormat() << " == "
1049 << (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)) << " "
1050 << (isRightPage ? pDesc->GetLeftFormat(bFirst) : pDesc->GetRightFormat(bFirst)) );
1052 return (pNew->GetPageDesc() != pDesc) // own desc ?
1053 || (pNew->GetFormat() !=
1054 (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)))
1055 || (pNewDesc && pNewDesc == pDesc);
1058 /// Returns the next layout leaf in which we can move the frame.
1059 SwLayoutFrame *SwFrame::GetNextLeaf( MakePageType eMakePage )
1061 OSL_ENSURE( !IsInFootnote(), "GetNextLeaf(), don't call me for Footnote." );
1062 OSL_ENSURE( !IsInSct(), "GetNextLeaf(), don't call me for Sections." );
1064 const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
1065 // I want to end up in the body.
1067 // It doesn't make sense to insert pages, as we only want to search the
1068 // chain.
1069 if( IsInFly() )
1070 eMakePage = MAKEPAGE_NONE;
1072 // For tables, we just take the big leap. A simple GetNext would
1073 // iterate through the first cells and, in turn, all other cells.
1074 SwLayoutFrame *pLayLeaf = nullptr;
1075 if ( IsTabFrame() )
1077 SwFrame *const pTmp = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
1078 if ( pTmp )
1079 pLayLeaf = pTmp->GetUpper();
1081 if ( !pLayLeaf )
1082 pLayLeaf = GetNextLayoutLeaf();
1084 SwLayoutFrame *pOldLayLeaf = nullptr; // Make sure that we don't have to
1085 // start searching from top when we
1086 // have a freshly created page.
1087 bool bNewPg = false; // Only insert a new page once.
1089 while ( true )
1091 if ( pLayLeaf )
1093 // There's yet another LayoutFrame. Let's see if it's ready to host
1094 // me as well.
1095 // It only needs to be of the same kind like my starting point
1096 // (DocBody or Footnote respectively)
1097 if ( pLayLeaf->FindPageFrame()->IsFootnotePage() )
1098 { // If I ended up at the end note pages, we're done.
1099 pLayLeaf = nullptr;
1100 continue;
1102 if ( (bBody && !pLayLeaf->IsInDocBody()) || pLayLeaf->IsInTab()
1103 || pLayLeaf->IsInSct() )
1105 // They don't want me! Try again
1106 pOldLayLeaf = pLayLeaf;
1107 pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
1108 continue;
1111 // I'm wanted, therefore I'm done. However, it may still be that,
1112 // during a page break, the page type isn't the desired one. In that
1113 // case we have to insert a page of the correct type.
1115 if( !IsFlowFrame() && ( eMakePage == MAKEPAGE_NONE ||
1116 eMakePage==MAKEPAGE_APPEND || eMakePage==MAKEPAGE_NOSECTION ) )
1117 return pLayLeaf;
1119 SwPageFrame *pNew = pLayLeaf->FindPageFrame();
1120 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
1121 // The pagedesc check does not make sense for frames in fly frames
1122 if ( pNew != FindPageFrame() && !bNewPg && !IsInFly() &&
1123 // i#46683
1124 // Do not consider page descriptions in browse mode (since
1125 // MoveBwd ignored them)
1126 !(pSh && pSh->GetViewOptions()->getBrowseMode() ) )
1128 if( WrongPageDesc( pNew ) )
1130 SwFootnoteContFrame *pCont = pNew->FindFootnoteCont();
1131 if( pCont )
1133 // If the reference of the first footnote of this page
1134 // lies before the page, we'd rather not insert a new page.
1136 SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
1137 if( pFootnote && pFootnote->GetRef() )
1139 const sal_uInt16 nRefNum = pNew->GetPhyPageNum();
1140 if( pFootnote->GetRef()->GetPhyPageNum() < nRefNum )
1141 break;
1144 //Gotcha! The following page is wrong, therefore we need to
1145 //insert a new one.
1146 if ( eMakePage == MAKEPAGE_INSERT )
1148 bNewPg = true;
1150 SwPageFrame *pPg = pOldLayLeaf ?
1151 pOldLayLeaf->FindPageFrame() : nullptr;
1152 if ( pPg && pPg->IsEmptyPage() )
1153 // Don't insert behind. Insert before the EmptyPage.
1154 pPg = static_cast<SwPageFrame*>(pPg->GetPrev());
1156 if ( !pPg || pPg == pNew )
1157 pPg = FindPageFrame();
1159 InsertPage( pPg, false );
1160 pLayLeaf = GetNextLayoutLeaf();
1161 pOldLayLeaf = nullptr;
1162 continue;
1164 else
1165 pLayLeaf = nullptr;
1168 break;
1170 else
1172 // There's no other matching LayoutFrame, so we have to insert
1173 // a new page.
1174 if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
1176 InsertPage(
1177 pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
1178 false );
1180 // And again from the start.
1181 pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
1183 else
1184 break;
1187 return pLayLeaf;
1190 /// Returns the previous layout leaf where we can move the frame.
1191 SwLayoutFrame *SwFrame::GetPrevLeaf()
1193 OSL_ENSURE( !IsInFootnote(), "GetPrevLeaf(), don't call me for Footnote." );
1195 const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
1196 // I want to end up in the body.
1197 const bool bFly = IsInFly();
1199 SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
1200 SwLayoutFrame *pPrevLeaf = nullptr;
1202 while ( pLayLeaf )
1204 if ( pLayLeaf->IsInTab() || // Never go into tables.
1205 pLayLeaf->IsInSct() ) // Same goes for sections!
1206 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1207 else if ( bBody && pLayLeaf->IsInDocBody() )
1209 if ( pLayLeaf->Lower() )
1210 break;
1211 pPrevLeaf = pLayLeaf;
1212 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1213 if ( pLayLeaf )
1214 SwFlowFrame::SetMoveBwdJump( true );
1216 else if ( bFly )
1217 break; //Contents in Flys should accept any layout leaf.
1218 else
1219 pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1221 return pLayLeaf ? pLayLeaf : pPrevLeaf;
1224 bool SwFlowFrame::IsPrevObjMove() const
1226 // true: The FlowFrame must respect the a border of the predecessor, also needs
1227 // to insert a break if required.
1229 //!!!!!!!!!!!Hack!!!!!!!!!!!
1230 const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
1231 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
1232 return false;
1234 SwFrame *const pPre{FindPrevIgnoreHidden()};
1236 if ( pPre && pPre->GetDrawObjs() )
1238 OSL_ENSURE( SwFlowFrame::CastFlowFrame( pPre ), "new flowfrm?" );
1239 if( SwFlowFrame::CastFlowFrame( pPre )->IsAnFollow( this ) )
1240 return false;
1241 if (SwFlowFrame::CastFlowFrame(pPre)->IsJoinLocked())
1243 SwBorderAttrAccess baa(SwFrame::GetCache(), pPre);
1244 SwBorderAttrs const& rAttrs(*baa.Get());
1245 if (SwFlowFrame::CastFlowFrame(pPre)->IsKeep(rAttrs.GetAttrSet().GetKeep(), pPre->GetBreakItem()))
1246 { // pPre is currently being formatted - maybe it moved back but
1247 // its objects still have the old page's body as
1248 // mpVertPosOrientFrame and SwContentFrame::MakeAll() is calling
1249 // pNxt->Calc() in this case so allow this frame to move back
1250 return false; // too, else pPre is forced to move forward again.
1253 SwLayoutFrame* pPreUp = pPre->GetUpper();
1254 // If the upper is a SectionFrame, or a column of a SectionFrame, we're
1255 // allowed to protrude out of it. However, we need to respect the
1256 // Upper of the SectionFrame.
1257 if( pPreUp->IsInSct() )
1259 if( pPreUp->IsSctFrame() )
1260 pPreUp = pPreUp->GetUpper();
1261 else if( pPreUp->IsColBodyFrame() &&
1262 pPreUp->GetUpper()->GetUpper()->IsSctFrame() )
1263 pPreUp = pPreUp->GetUpper()->GetUpper()->GetUpper();
1265 // i#26945 - re-factoring
1266 // use <GetVertPosOrientFrame()> to determine, if object has followed the
1267 // text flow to the next layout frame
1268 for (SwAnchoredObject* pObj : *pPre->GetDrawObjs())
1270 const SwFrameFormat* pObjFormat = pObj->GetFrameFormat();
1271 // Do not consider hidden objects
1272 // i#26945 - do not consider object, which
1273 // doesn't follow the text flow.
1274 if ( pObjFormat->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
1275 pObj->GetDrawObj()->GetLayer() ) &&
1276 pObjFormat->GetFollowTextFlow().GetValue() )
1278 const SwLayoutFrame* pVertPosOrientFrame = pObj->GetVertPosOrientFrame();
1279 if ( pVertPosOrientFrame &&
1280 pPreUp != pVertPosOrientFrame &&
1281 !pPreUp->IsAnLower( pVertPosOrientFrame ) )
1283 return true;
1288 return false;
1292 |* If there's a hard page break before the Frame AND there's a
1293 |* predecessor on the same page, true is returned (we need to create a
1294 |* new PageBreak). Otherwise, returns false.
1295 |* If bAct is set to true, this function returns true if
1296 |* there's a PageBreak.
1297 |* Of course, we don't evaluate the hard page break for follows.
1298 |* The page break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
1299 |* predecessor (AFTER). If there's no predecessor on the page, we don't
1300 |* need to think further.
1301 |* Also, a page break (or the need for one) is also present if
1302 |* the FrameFormat contains a PageDesc.
1303 |* The implementation works only on ContentFrames! - the definition
1304 |* of the predecessor is not clear for LayoutFrames.
1306 bool SwFlowFrame::IsPageBreak( bool bAct ) const
1308 if ( !IsFollow() && m_rThis.IsInDocBody() &&
1309 ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) ) // i66968
1311 const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
1312 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
1313 return false;
1315 // Determine predecessor
1316 const SwFrame *pPrev = m_rThis.FindPrev();
1317 while (pPrev && (!pPrev->IsInDocBody() || pPrev->IsHiddenNow()))
1318 pPrev = pPrev->FindPrev();
1320 if ( pPrev )
1322 OSL_ENSURE( pPrev->IsInDocBody(), "IsPageBreak: Not in DocBody?" );
1323 if ( bAct )
1324 { if ( m_rThis.FindPageFrame() == pPrev->FindPageFrame() )
1325 return false;
1327 else
1328 { if ( m_rThis.FindPageFrame() != pPrev->FindPageFrame() )
1329 return false;
1332 //for compatibility, also break at column break if no columns exist
1333 const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1334 const bool bTreatSingleColumnBreakAsPageBreak = rIDSA.get(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK);
1335 const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
1336 if ( eBreak == SvxBreak::PageBefore ||
1337 eBreak == SvxBreak::PageBoth ||
1338 ( bTreatSingleColumnBreakAsPageBreak && eBreak == SvxBreak::ColumnBefore && !m_rThis.FindColFrame() ))
1339 return true;
1340 else
1342 const SvxBreak ePrB = pPrev->GetBreakItem().GetBreak();
1343 if ( ePrB == SvxBreak::PageAfter ||
1344 ePrB == SvxBreak::PageBoth ||
1345 m_rThis.GetPageDescItem().GetPageDesc())
1347 return true;
1352 return false;
1356 |* If there's a hard column break before the Frame AND there is
1357 |* a predecessor in the same column, we return true (we need to create
1358 |* a ColBreak). Otherwise, we return false.
1359 |* If bAct is set to true, we return true if there's a ColBreak.
1360 |* Of course, we don't evaluate the hard column break for follows.
1362 |* The column break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
1363 |* predecessor (AFTER). If there's no predecessor in the column, we don't
1364 |* need to think further.
1365 |* The implementation works only on ContentFrames! - the definition
1366 |* of the predecessor is not clear for LayoutFrames.
1368 bool SwFlowFrame::IsColBreak( bool bAct ) const
1370 if ( !IsFollow() && (m_rThis.IsMoveable() || bAct) )
1372 const SwFrame *pCol = m_rThis.FindColFrame();
1373 if ( pCol )
1375 // Determine predecessor
1376 const SwFrame *pPrev = m_rThis.FindPrev();
1377 while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) ||
1378 pPrev->IsHiddenNow() ) )
1379 pPrev = pPrev->FindPrev();
1381 if ( pPrev )
1383 if ( bAct )
1384 { if ( pCol == pPrev->FindColFrame() )
1385 return false;
1387 else
1388 { if ( pCol != pPrev->FindColFrame() )
1389 return false;
1392 const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
1393 if ( eBreak == SvxBreak::ColumnBefore ||
1394 eBreak == SvxBreak::ColumnBoth )
1395 return true;
1396 else
1398 const SvxBreak ePrB = pPrev->GetBreakItem().GetBreak();
1399 if ( ePrB == SvxBreak::ColumnAfter ||
1400 ePrB == SvxBreak::ColumnBoth )
1401 return true;
1406 return false;
1409 // Skip hidden paragraphs and empty sections on the same level
1410 static const SwFrame* skipHiddenSiblingFrames_(const SwFrame* pFrame)
1412 while (pFrame && pFrame->IsHiddenNow())
1413 pFrame = pFrame->GetPrev();
1414 return pFrame;
1417 bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const
1419 if( m_rThis.IsInSct() )
1421 const SwFrame* pTmp = m_rThis.GetUpper();
1422 while( pTmp )
1424 if( pTmp->IsCellFrame() || pTmp->IsFlyFrame() ||
1425 pTmp->IsFooterFrame() || pTmp->IsHeaderFrame() ||
1426 ( pTmp->IsFootnoteFrame() && !static_cast<const SwFootnoteFrame*>(pTmp)->GetMaster() ) )
1427 return true;
1428 if( pTmp->IsPageFrame() )
1429 return !pTmp->GetPrev() || IsPageBreak(true);
1430 if( pTmp->IsColumnFrame() && pTmp->GetPrev() )
1431 return IsColBreak( true );
1432 if (pTmp->IsSctFrame() && (!bSct || skipHiddenSiblingFrames_(pTmp->GetPrev())))
1433 return false;
1434 pTmp = pTmp->GetUpper();
1436 OSL_FAIL( "HasParaSpaceAtPages: Where's my page?" );
1437 return false;
1439 if( !m_rThis.IsInDocBody() || ( m_rThis.IsInTab() && !m_rThis.IsTabFrame()) ||
1440 IsPageBreak( true ) || ( m_rThis.FindColFrame() && IsColBreak( true ) ) )
1441 return true;
1442 const SwFrame* pTmp = m_rThis.FindColFrame();
1443 if( pTmp )
1445 if( pTmp->GetPrev() )
1446 return false;
1448 else
1449 pTmp = &m_rThis;
1450 pTmp = pTmp->FindPageFrame();
1451 return pTmp && !pTmp->GetPrev();
1454 // Skip hidden paragraphs and empty sections
1455 static const SwFrame* skipHiddenFrames_(const SwFrame* pFrame)
1459 pFrame = skipHiddenSiblingFrames_(pFrame);
1460 if (!pFrame || !pFrame->IsSctFrame())
1461 return pFrame;
1462 // Special case: found previous frame is a section
1463 // Search for the last content in the section
1464 auto pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
1465 pFrame = pSectFrame->FindLastContent();
1466 // If the last content is in a table _inside_ the section,
1467 // take the table herself.
1468 // Correction: Check directly, if table is inside table, instead of indirectly
1469 // by checking, if section isn't inside a table
1470 if (pFrame && pFrame->IsInTab())
1472 const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
1473 if (pSectFrame->IsAnLower(pTableFrame))
1474 return pTableFrame;
1476 } while (true);
1479 /** helper method to determine previous frame for calculation of the
1480 upper space
1482 i#11860
1484 const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const
1486 const SwFrame* pPrevFrame
1487 = skipHiddenFrames_(_pProposedPrevFrame ? _pProposedPrevFrame : m_rThis.GetPrev());
1488 if (pPrevFrame || !m_rThis.IsInFootnote()
1489 || !(m_rThis.IsSctFrame() || !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote()))
1490 return pPrevFrame;
1492 // Special case: no direct previous frame is found but frame is in footnote
1493 // Search for a previous frame in previous footnote,
1494 // if frame isn't in a section, which is also in the footnote
1495 const SwFootnoteFrame* pPrevFootnoteFrame =
1496 static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev());
1497 if ( pPrevFootnoteFrame )
1498 return skipHiddenFrames_(pPrevFootnoteFrame->GetLastLower());
1500 return nullptr;
1503 // This should be renamed to something like lcl_UseULSpacing
1504 /// Compare styles attached to these text frames.
1505 static bool lcl_IdenticalStyles(const SwFrame* pPrevFrame, const SwFrame* pFrame)
1507 if (!pFrame || !pFrame->IsTextFrame())
1508 return false;
1510 // Identical styles only applies if "the paragraphs belong to the same content area".
1511 if (pPrevFrame && pPrevFrame->FindSctFrame() != pFrame->FindSctFrame())
1512 return false;
1514 SwTextFormatColl *pPrevFormatColl = nullptr;
1515 if (pPrevFrame && pPrevFrame->IsTextFrame())
1517 const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pPrevFrame );
1518 pPrevFormatColl = dynamic_cast<SwTextFormatColl*>(
1519 pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
1522 const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pFrame);
1523 SwTextFormatColl* const pFormatColl
1524 = dynamic_cast<SwTextFormatColl*>(pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
1525 return pPrevFormatColl == pFormatColl;
1528 static bool lcl_getContextualSpacing(const SwFrame* pPrevFrame)
1530 bool bRet;
1531 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pPrevFrame);
1532 const SwBorderAttrs *pAttrs = aAccess.Get();
1534 bRet = pAttrs->GetULSpace().GetContext();
1536 return bRet;
1540 SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
1541 const SwFrame* pPr,
1542 const bool _bConsiderGrid ) const
1544 if (m_rThis.IsHiddenNow())
1545 return 0;
1547 const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_( pPr );
1549 std::optional<SwBorderAttrAccess> oAccess;
1550 SwFrame* pOwn;
1551 if( !pAttrs )
1553 if( m_rThis.IsSctFrame() )
1555 SwSectionFrame* pFoll = &static_cast<SwSectionFrame&>(m_rThis);
1557 pOwn = pFoll->ContainsAny();
1558 while( !pOwn && nullptr != ( pFoll = pFoll->GetFollow() ) );
1559 if( !pOwn )
1560 return 0;
1562 else
1563 pOwn = &m_rThis;
1564 oAccess.emplace(SwFrame::GetCache(), pOwn);
1565 pAttrs = oAccess->Get();
1567 else
1569 pOwn = &m_rThis;
1571 SwTwips nUpper = 0;
1574 const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1575 if( pPrevFrame )
1577 const bool bUseFormerLineSpacing = rIDSA.get(DocumentSettingId::OLD_LINE_SPACING);
1578 const bool bContextualSpacingThis = pAttrs->GetULSpace().GetContext();
1579 const bool bContextualSpacingPrev = lcl_getContextualSpacing(pPrevFrame);
1580 bool bIdenticalStyles = lcl_IdenticalStyles(pPrevFrame, &m_rThis);
1582 const bool bContextualSpacing = bContextualSpacingThis
1583 && bContextualSpacingPrev
1584 && bIdenticalStyles;
1586 // tdf#125893 always ignore own top margin setting of the actual paragraph
1587 // with contextual spacing, if the previous paragraph is identical
1588 const bool bHalfContextualSpacing = !bContextualSpacing
1589 && bContextualSpacingThis
1590 && !bContextualSpacingPrev
1591 && bIdenticalStyles;
1593 // tdf#134463 always ignore own bottom margin setting of the previous paragraph
1594 // with contextual spacing, if the actual paragraph is identical
1595 const bool bHalfContextualSpacingPrev = !bContextualSpacing
1596 && !bContextualSpacingThis
1597 && bContextualSpacingPrev
1598 && bIdenticalStyles;
1600 // i#11860 - use new method to determine needed spacing
1601 // values of found previous frame and use these values.
1602 SwTwips nPrevLowerSpace = 0;
1603 SwTwips nPrevLineSpacing = 0;
1604 // i#102458
1605 bool bPrevLineSpacingProportional = false;
1606 GetSpacingValuesOfFrame( (*pPrevFrame),
1607 nPrevLowerSpace, nPrevLineSpacing,
1608 bPrevLineSpacingProportional,
1609 bIdenticalStyles);
1610 if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) )
1612 // FIXME: apply bHalfContextualSpacing for better portability?
1613 nUpper = bContextualSpacing ? 0 : nPrevLowerSpace + pAttrs->GetULSpace().GetUpper();
1614 SwTwips nAdd = nPrevLineSpacing;
1615 // i#11859 - consideration of the line spacing
1616 // for the upper spacing of a text frame
1617 if ( bUseFormerLineSpacing )
1619 // former consideration
1620 if ( pOwn->IsTextFrame() )
1622 nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
1624 nUpper += nAdd;
1626 else
1628 // new consideration:
1629 // Only the proportional line spacing of the previous
1630 // text frame is considered for the upper spacing and
1631 // the line spacing values are add up instead of
1632 // building its maximum.
1633 if ( pOwn->IsTextFrame() )
1635 // i#102458
1636 // Correction:
1637 // A proportional line spacing of the previous text frame
1638 // is added up to an own leading line spacing.
1639 // Otherwise, the maximum of the leading line spacing
1640 // of the previous text frame and the own leading line
1641 // spacing is built.
1642 if ( bPrevLineSpacingProportional )
1644 nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
1646 else
1648 nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
1651 nUpper += nAdd;
1654 else
1656 nUpper = bContextualSpacing ? 0 : std::max(
1657 bHalfContextualSpacingPrev ? 0 : static_cast<tools::Long>(nPrevLowerSpace),
1658 bHalfContextualSpacing ? 0 : static_cast<tools::Long>(pAttrs->GetULSpace().GetUpper()) );
1660 // i#11859 - consideration of the line spacing
1661 // for the upper spacing of a text frame
1662 if ( bUseFormerLineSpacing )
1664 // former consideration
1665 if ( pOwn->IsTextFrame() )
1666 nUpper = std::max( nUpper, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
1667 if ( nPrevLineSpacing != 0 )
1669 nUpper = std::max( nUpper, nPrevLineSpacing );
1672 else
1674 // new consideration:
1675 // Only the proportional line spacing of the previous
1676 // text frame is considered for the upper spacing and
1677 // the line spacing values are add up and added to
1678 // the paragraph spacing instead of building the
1679 // maximum of the line spacings and the paragraph spacing.
1680 SwTwips nAdd = nPrevLineSpacing;
1681 if ( pOwn->IsTextFrame() )
1683 // i#102458
1684 // Correction:
1685 // A proportional line spacing of the previous text frame
1686 // is added up to an own leading line spacing.
1687 // Otherwise, the maximum of the leading line spacing
1688 // of the previous text frame and the own leading line
1689 // spacing is built.
1690 if ( bPrevLineSpacingProportional )
1692 nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
1694 else
1696 nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
1699 nUpper += nAdd;
1703 else if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) &&
1704 CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) )
1706 nUpper = pAttrs->GetULSpace().GetUpper();
1708 if (m_rThis.IsCollapseUpper())
1710 nUpper = 0;
1715 // i#25029 - pass previous frame <pPrevFrame>
1716 // to method <GetTopLine(..)>, if parameter <pPr> is set.
1717 // Note: parameter <pPr> is set, if method is called from <SwTextFrame::WouldFit(..)>
1718 nUpper += pAttrs->GetTopLine( m_rThis, (pPr ? pPrevFrame : nullptr) );
1720 // i#11860 - consider value of new parameter <_bConsiderGrid>
1721 // and use new method <GetUpperSpaceAmountConsideredForPageGrid(..)>
1723 //consider grid in square page mode
1724 if ( _bConsiderGrid && m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode() )
1726 nUpper += GetUpperSpaceAmountConsideredForPageGrid_( nUpper );
1728 return nUpper;
1731 /** method to determine the upper space amount, which is considered for
1732 the page grid
1734 i#11860
1735 Precondition: Position of frame is valid.
1737 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid_(
1738 const SwTwips _nUpperSpaceWithoutGrid ) const
1740 SwTwips nUpperSpaceAmountConsideredForPageGrid = 0;
1742 if ( m_rThis.IsInDocBody() && m_rThis.GetAttrSet()->GetParaGrid().GetValue() )
1744 const SwPageFrame* pPageFrame = m_rThis.FindPageFrame();
1745 SwTextGridItem const*const pGrid(GetGridItem(pPageFrame));
1746 if( pGrid )
1748 const SwFrame* pBodyFrame = pPageFrame->FindBodyCont();
1749 if ( pBodyFrame )
1751 const tools::Long nGridLineHeight =
1752 pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
1754 SwRectFnSet aRectFnSet(&m_rThis);
1755 const SwTwips nBodyPrtTop = aRectFnSet.GetPrtTop(*pBodyFrame);
1756 const SwTwips nProposedPrtTop =
1757 aRectFnSet.YInc( aRectFnSet.GetTop(m_rThis.getFrameArea()),
1758 _nUpperSpaceWithoutGrid );
1760 const SwTwips nSpaceAbovePrtTop =
1761 aRectFnSet.YDiff( nProposedPrtTop, nBodyPrtTop );
1762 const SwTwips nSpaceOfCompleteLinesAbove =
1763 nGridLineHeight * ( nSpaceAbovePrtTop / nGridLineHeight );
1764 SwTwips nNewPrtTop =
1765 aRectFnSet.YInc( nBodyPrtTop, nSpaceOfCompleteLinesAbove );
1766 if ( aRectFnSet.YDiff( nProposedPrtTop, nNewPrtTop ) > 0 )
1768 nNewPrtTop = aRectFnSet.YInc( nNewPrtTop, nGridLineHeight );
1771 const SwTwips nNewUpperSpace =
1772 aRectFnSet.YDiff( nNewPrtTop,
1773 aRectFnSet.GetTop(m_rThis.getFrameArea()) );
1775 nUpperSpaceAmountConsideredForPageGrid =
1776 nNewUpperSpace - _nUpperSpaceWithoutGrid;
1778 OSL_ENSURE( nUpperSpaceAmountConsideredForPageGrid >= 0,
1779 "<SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid(..)> - negative space considered for page grid!" );
1783 return nUpperSpaceAmountConsideredForPageGrid;
1786 /** method to determine the upper space amount, which is considered for
1787 the previous frame
1789 i#11860
1791 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrame() const
1793 SwTwips nUpperSpaceAmountOfPrevFrame = 0;
1795 const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_();
1796 if ( pPrevFrame )
1798 SwTwips nPrevLowerSpace = 0;
1799 SwTwips nPrevLineSpacing = 0;
1800 // i#102458
1801 bool bDummy = false;
1802 GetSpacingValuesOfFrame( (*pPrevFrame), nPrevLowerSpace, nPrevLineSpacing, bDummy, lcl_IdenticalStyles(pPrevFrame, &m_rThis));
1803 if ( nPrevLowerSpace > 0 || nPrevLineSpacing > 0 )
1805 const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1806 if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
1807 !rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) )
1809 nUpperSpaceAmountOfPrevFrame = nPrevLowerSpace + nPrevLineSpacing;
1811 else
1813 nUpperSpaceAmountOfPrevFrame = std::max( nPrevLowerSpace, nPrevLineSpacing );
1818 return nUpperSpaceAmountOfPrevFrame;
1821 /** method to determine the upper space amount, which is considered for
1822 the previous frame and the page grid, if option 'Use former object
1823 positioning' is OFF
1825 i#11860
1827 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const
1829 SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid = 0;
1831 if (!m_rThis.GetUpper() || !m_rThis.GetUpper()->GetFormat())
1833 return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
1836 if ( !m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
1838 nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
1839 GetUpperSpaceAmountConsideredForPrevFrame() +
1840 ( m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode()
1841 ? GetUpperSpaceAmountConsideredForPageGrid_( CalcUpperSpace( nullptr, nullptr, false ) )
1842 : 0 );
1845 return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
1848 // Calculation of lower space
1850 SwTwips SwFlowFrame::CalcLowerSpace( const SwBorderAttrs* _pAttrs ) const
1852 if (m_rThis.IsHiddenNow())
1853 return 0;
1855 SwTwips nLowerSpace = 0;
1857 std::optional<SwBorderAttrAccess> oAttrAccess;
1858 if ( !_pAttrs )
1860 oAttrAccess.emplace(SwFrame::GetCache(), &m_rThis);
1861 _pAttrs = oAttrAccess->Get();
1864 bool bCommonBorder = true;
1865 if ( m_rThis.IsInSct() && m_rThis.GetUpper()->IsColBodyFrame() )
1867 const SwSectionFrame* pSectFrame = m_rThis.FindSctFrame();
1868 bCommonBorder = pSectFrame->GetFormat()->GetBalancedColumns().GetValue();
1870 nLowerSpace = bCommonBorder ?
1871 _pAttrs->GetBottomLine( m_rThis ) :
1872 _pAttrs->CalcBottomLine();
1874 // i#26250
1875 // - correct consideration of table frames
1876 // - use new method <CalcAddLowerSpaceAsLastInTableCell(..)>
1877 if ( ( ( m_rThis.IsTabFrame() && m_rThis.GetUpper()->IsInTab() ) ||
1878 // No lower spacing, if frame has a follow
1879 ( m_rThis.IsInTab() && !GetFollow() ) ) &&
1880 !m_rThis.GetIndNext() )
1882 nLowerSpace += CalcAddLowerSpaceAsLastInTableCell( _pAttrs );
1885 // tdf#128195 Consider para spacing below last paragraph in header
1886 bool bHasSpacingBelowPara = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(
1887 DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA);
1888 if (bHasSpacingBelowPara && !m_rThis.IsInTab() && !m_rThis.IsInFly()
1889 && m_rThis.FindFooterOrHeader() && !GetFollow() && !m_rThis.GetIndNext())
1891 nLowerSpace += _pAttrs->GetULSpace().GetLower() + _pAttrs->CalcLineSpacing();
1894 return nLowerSpace;
1897 /** calculation of the additional space to be considered, if flow frame
1898 is the last inside a table cell
1900 i#26250
1902 SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell(
1903 const SwBorderAttrs* _pAttrs ) const
1905 if (m_rThis.IsHiddenNow())
1906 return 0;
1908 SwTwips nAdditionalLowerSpace = 0;
1910 IDocumentSettingAccess const& rIDSA(m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess());
1911 if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
1913 const SwFrame* pFrame = &m_rThis;
1914 if ( pFrame->IsSctFrame() )
1916 const SwSectionFrame* pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
1917 pFrame = pSectFrame->FindLastContent();
1918 if ( pFrame && pFrame->IsInTab() )
1920 const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
1921 if ( pSectFrame->IsAnLower( pTableFrame ) )
1923 pFrame = pTableFrame;
1928 std::optional<SwBorderAttrAccess> oAttrAccess;
1929 if (pFrame && (!_pAttrs || pFrame != &m_rThis))
1931 oAttrAccess.emplace(SwFrame::GetCache(), pFrame);
1932 _pAttrs = oAttrAccess->Get();
1935 if (_pAttrs)
1937 nAdditionalLowerSpace += _pAttrs->GetULSpace().GetLower();
1939 if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
1941 nAdditionalLowerSpace += _pAttrs->CalcLineSpacing();
1946 return nAdditionalLowerSpace;
1949 /// Moves the Frame forward if it seems necessary regarding the current conditions and attributes.
1950 bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue )
1952 if (m_rThis.IsHiddenNow())
1953 return false;
1954 const SwFrame* pNxt = m_rThis.GetIndNext();
1956 if ( bKeep && //!bMovedBwd &&
1957 ( !pNxt || ( pNxt->IsTextFrame() && static_cast<const SwTextFrame*>(pNxt)->IsEmptyMaster() ) ) &&
1958 ( nullptr != (pNxt = m_rThis.FindNext()) ) && IsKeepFwdMoveAllowed(bIgnoreMyOwnKeepValue) )
1960 if( pNxt->IsSctFrame() )
1961 { // Don't get fooled by empty SectionFrames
1962 const SwFrame* pTmp = nullptr;
1963 while( pNxt && pNxt->IsSctFrame() &&
1964 ( !static_cast<const SwSectionFrame*>(pNxt)->GetSection() ||
1965 nullptr == ( pTmp = static_cast<const SwSectionFrame*>(pNxt)->ContainsAny() ) ) )
1967 pNxt = pNxt->FindNext();
1968 pTmp = nullptr;
1970 if( pTmp )
1971 pNxt = pTmp; // the content of the next notempty sectionfrm
1973 if( pNxt && pNxt->isFrameAreaPositionValid() )
1975 bool bMove = false;
1976 const SwSectionFrame *pSct = m_rThis.FindSctFrame();
1977 if( pSct && !pSct->isFrameAreaSizeValid() )
1979 const SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
1980 if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
1981 bMove = true;
1983 else
1984 bMove = true;
1985 if( bMove )
1987 //Keep together with the following frame
1988 MoveFwd( rbMakePage, false );
1989 return true;
1994 bool bMovedFwd = false;
1996 if ( m_rThis.GetIndPrev() )
1998 if ( IsPrevObjMove() ) // Should we care about objects of the Prev?
2000 bMovedFwd = true;
2001 if ( !MoveFwd( rbMakePage, false ) )
2002 rbMakePage = false;
2004 else
2006 if ( IsPageBreak( false ) )
2008 while ( MoveFwd( rbMakePage, true ) )
2009 /* do nothing */;
2010 rbMakePage = false;
2011 bMovedFwd = true;
2013 else if ( IsColBreak ( false ) )
2015 const SwPageFrame *pPage = m_rThis.FindPageFrame();
2016 SwFrame *pCol = m_rThis.FindColFrame();
2018 { MoveFwd( rbMakePage, false );
2019 SwFrame *pTmp = m_rThis.FindColFrame();
2020 if( pTmp != pCol )
2022 bMovedFwd = true;
2023 pCol = pTmp;
2025 else
2026 break;
2027 } while ( IsColBreak( false ) );
2028 if ( pPage != m_rThis.FindPageFrame() )
2029 rbMakePage = false;
2033 return bMovedFwd;
2036 bool SwFlowFrame::ForbiddenForFootnoteCntFwd() const
2038 return m_rThis.IsTabFrame() || m_rThis.IsInTab();
2041 /// Return value guarantees that a new page was not created,
2042 /// although false does not NECESSARILY indicate that a new page was created.
2043 /// Either false or true(MoveFootnoteCntFwd) can be returned if no changes were made
2044 bool SwFlowFrame::MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways )
2046 //!!!!MoveFootnoteCntFwd might need to be updated as well.
2047 SwFootnoteBossFrame *pOldBoss = m_rThis.FindFootnoteBossFrame();
2048 if (m_rThis.IsInFootnote())
2050 assert(!ForbiddenForFootnoteCntFwd()); // prevented by IsMoveable()
2051 if (!m_rThis.IsContentFrame() || !pOldBoss)
2053 SAL_WARN("sw.core", "Tables in footnotes are not truly supported");
2054 return false;
2056 return static_cast<SwContentFrame&>(m_rThis).MoveFootnoteCntFwd( bMakePage, pOldBoss );
2059 if( !IsFwdMoveAllowed() && !bMoveAlways )
2061 bool bNoFwd = true;
2062 if( m_rThis.IsInSct() )
2064 SwFootnoteBossFrame* pBoss = m_rThis.FindFootnoteBossFrame();
2065 bNoFwd = !pBoss->IsInSct() || ( !pBoss->Lower()->GetNext() &&
2066 !pBoss->GetPrev() );
2069 // Allow the MoveFwd even if we do not have an IndPrev in these cases:
2070 if ( m_rThis.IsInTab() &&
2071 ( !m_rThis.IsTabFrame() ||
2072 m_rThis.GetUpper()->IsInTab() ) &&
2073 nullptr != m_rThis.GetNextCellLeaf() )
2075 bNoFwd = false;
2078 if( bNoFwd )
2080 // It's allowed to move PageBreaks if the Frame isn't the first
2081 // one on the page.
2082 if ( !bPageBreak )
2083 return false;
2085 const SwFrame *pCol = m_rThis.FindColFrame();
2086 if ( !pCol || !pCol->GetPrev() )
2087 return false;
2091 // prevent -Werror=maybe-uninitialized under gcc 11.2.0
2092 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
2093 #pragma GCC diagnostic push
2094 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
2095 #endif
2096 std::optional<SwFrameDeleteGuard> oDeleteGuard;
2097 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
2098 #pragma GCC diagnostic pop
2099 #endif
2100 if (bMakePage)
2101 oDeleteGuard.emplace(pOldBoss);
2103 bool bSamePage = true;
2104 SwLayoutFrame *pNewUpper =
2105 m_rThis.GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
2107 if ( pNewUpper )
2109 PROTOCOL_ENTER( &m_rThis, PROT::MoveFwd, DbgAction::NONE, nullptr );
2110 SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
2111 // We move ourself and all the direct successors before the
2112 // first ContentFrame below the new Upper.
2114 // If our NewUpper lies in a SectionFrame, we need to make sure
2115 // that it won't destroy itself in Calc.
2116 SwSectionFrame* pSect = pNewUpper->FindSctFrame();
2117 if( pSect )
2119 // If we only switch column within our SectionFrame, we better don't
2120 // call Calc, as this would format the SectionFrame, which in turn would
2121 // call us again, etc.
2122 if( pSect != m_rThis.FindSctFrame() )
2124 bool bUnlock = !pSect->IsColLocked();
2125 pSect->ColLock();
2126 pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2127 if( bUnlock )
2128 pSect->ColUnlock();
2131 // Do not calculate split cell frames.
2132 else if ( !pNewUpper->IsCellFrame() || pNewUpper->Lower() )
2133 pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2135 SwFootnoteBossFrame *pNewBoss = pNewUpper->FindFootnoteBossFrame();
2136 bool bBossChg = pNewBoss != pOldBoss;
2137 pNewBoss = pNewBoss->FindFootnoteBossFrame( true );
2138 pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
2139 SwPageFrame* pNewPage = pOldPage;
2141 oDeleteGuard.reset();
2143 // First, we move the footnotes.
2144 bool bFootnoteMoved = false;
2146 // i#26831
2147 // If pSect has just been created, the printing area of pSect has
2148 // been calculated based on the first content of its follow.
2149 // In this case we prefer to call a SimpleFormat for this new
2150 // section after we inserted the contents. Otherwise the section
2151 // frame will invalidate its lowers, if its printing area changes
2152 // in SwSectionFrame::Format, which can cause loops.
2153 const bool bForceSimpleFormat = pSect && pSect->HasFollow() &&
2154 !pSect->ContainsAny();
2156 if ( pNewBoss != pOldBoss )
2158 pNewPage = pNewBoss->FindPageFrame();
2159 bSamePage = pNewPage == pOldPage;
2160 // Set deadline, so the footnotes don't think up
2161 // silly things...
2162 SwRectFnSet aRectFnSet(pOldBoss);
2163 SwSaveFootnoteHeight aHeight( pOldBoss,
2164 aRectFnSet.GetBottom(pOldBoss->getFrameArea()) );
2165 SwContentFrame* pStart = m_rThis.IsContentFrame() ?
2166 static_cast<SwContentFrame*>(&m_rThis) : static_cast<SwLayoutFrame&>(m_rThis).ContainsContent();
2167 OSL_ENSURE( pStart || ( m_rThis.IsTabFrame() && !static_cast<SwTabFrame&>(m_rThis).Lower() ),
2168 "MoveFwd: Missing Content" );
2169 SwLayoutFrame* pBody = pStart ? ( pStart->IsTextFrame() ?
2170 const_cast<SwBodyFrame *>(static_cast<SwTextFrame*>(pStart)->FindBodyFrame()) : nullptr ) : nullptr;
2171 if( pBody )
2172 bFootnoteMoved = pBody->MoveLowerFootnotes( pStart, pOldBoss, pNewBoss,
2173 false);
2175 // It's possible when dealing with SectionFrames that we have been moved
2176 // by pNewUpper->Calc(), for instance into the pNewUpper.
2177 // MoveSubTree or PasteTree respectively is not prepared to handle such a
2178 // situation.
2179 if( pNewUpper != m_rThis.GetUpper() )
2181 // i#27145
2182 SwSectionFrame* pOldSct = nullptr;
2183 if ( m_rThis.GetUpper()->IsSctFrame() )
2185 pOldSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
2188 MoveSubTree( pNewUpper, pNewUpper->Lower() );
2190 // i#27145
2191 if ( pOldSct && pOldSct->GetSection() )
2193 // Prevent loops by setting the new height at
2194 // the section frame if footnotes have been moved.
2195 // Otherwise the call of SwLayNotify::~SwLayNotify() for
2196 // the (invalid) section frame will invalidate the first
2197 // lower of its follow, because it grows due to the removed
2198 // footnotes.
2199 // Note: If pOldSct has become empty during MoveSubTree, it
2200 // has already been scheduled for removal. No SimpleFormat
2201 // for these.
2202 pOldSct->SimpleFormat();
2205 // i#26831
2206 if ( bForceSimpleFormat )
2208 pSect->SimpleFormat();
2211 if ( bFootnoteMoved && !bSamePage )
2213 pOldPage->UpdateFootnoteNum();
2214 pNewPage->UpdateFootnoteNum();
2217 if( bBossChg )
2219 m_rThis.Prepare( PrepareHint::BossChanged, nullptr, false );
2220 if( !bSamePage )
2222 SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2223 if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
2224 pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on!
2226 pNewPage->InvalidateSpelling();
2227 pNewPage->InvalidateSmartTags();
2228 pNewPage->InvalidateAutoCompleteWords();
2229 pNewPage->InvalidateWordCount();
2233 // No <CheckPageDesc(..)> in online layout
2234 const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2236 if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
2238 // i#106452
2239 // check page description not only in situation with sections.
2240 if ( !bSamePage &&
2241 ((!IsFollow() && m_rThis.GetPageDescItem().GetPageDesc()) ||
2242 pOldPage->GetPageDesc()->GetFollow() != pNewPage->GetPageDesc() ) )
2244 SwFrame::CheckPageDescs( pNewPage, false );
2248 return bSamePage;
2251 /** Return value tells whether any changes have been made.
2252 * If true, the frame has moved backwards to an earlier column/section/frame/page etc.
2254 * @note This should be called by derived classes.
2255 * @note The actual moving must be implemented in the subclasses via Cut()/Paste().
2257 bool SwFlowFrame::MoveBwd( bool &rbReformat )
2259 SwFlowFrame::SetMoveBwdJump( false );
2261 SwFootnoteFrame* pFootnote = m_rThis.FindFootnoteFrame();
2262 if ( pFootnote && pFootnote->IsBackMoveLocked() )
2263 return false;
2265 // Text frames, which are directly inside
2266 // tables aren't allowed to move backward.
2267 if ( m_rThis.IsTextFrame() && m_rThis.IsInTab() )
2269 const SwLayoutFrame* pUpperFrame = m_rThis.GetUpper();
2270 while ( pUpperFrame )
2272 if ( pUpperFrame->IsTabFrame() || pUpperFrame->IsRowFrame() )
2274 return false;
2276 // If the text frame is a follow-section-in-table, that can move
2277 // backward as well.
2278 bool bIsFollowSection = pUpperFrame->IsSctFrame() && static_cast<const SwSectionFrame*>(pUpperFrame)->GetPrecede();
2280 // If the text frame is a follow-in-table, that can move
2281 // backward as well.
2282 bool bIsFollow = const_cast<SwLayoutFrame*>(pUpperFrame)->GetPrevCellLeaf();
2284 if ( ( pUpperFrame->IsColumnFrame() && pUpperFrame->IsInSct() ) || bIsFollowSection || bIsFollow )
2286 break;
2288 pUpperFrame = pUpperFrame->GetUpper();
2292 SwFootnoteBossFrame * pOldBoss = m_rThis.FindFootnoteBossFrame();
2293 if (!pOldBoss)
2294 return false;
2296 SwPageFrame * const pOldPage = pOldBoss->FindPageFrame();
2297 SwLayoutFrame *pNewUpper = nullptr;
2298 bool bCheckPageDescs = false;
2299 bool bCheckPageDescOfNextPage = false;
2301 if ( pFootnote )
2303 // If the footnote already sits on the same page/column as the reference,
2304 // we can't flow back. The breaks don't need to be checked for footnotes.
2306 // i#37084 FindLastContent does not necessarily
2307 // have to have a result != 0
2308 SwFrame* pRef = nullptr;
2309 const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
2310 const IDocumentSettingAccess& rSettings
2311 = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
2312 bool bContEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
2313 if( bEndnote && pFootnote->IsInSct() && !bContEndnotes)
2315 SwSectionFrame* pSect = pFootnote->FindSctFrame();
2316 if( pSect->IsEndnAtEnd() )
2317 // Endnotes at the end of the section.
2318 pRef = pSect->FindLastContent( SwFindMode::LastCnt );
2320 else if (bEndnote && bContEndnotes)
2322 // Endnotes at the end of the document.
2323 SwSectionFrame* pSect = pFootnote->FindSctFrame();
2324 if (!pSect->GetPrev() && !pSect->FindMaster())
2326 // The section is at the top of the page, and is not a follow of a master section.
2327 // See if there is a previous body frame.
2328 SwLayoutFrame* pPrev = pSect->GetPrevLayoutLeaf();
2329 if (pPrev && pPrev->IsBodyFrame())
2331 // There is, then see if that's on a different page.
2332 SwPageFrame* pSectPage = pSect->FindPageFrame();
2333 SwPageFrame* pPage = pPrev->FindPageFrame();
2334 if (pPage != pSectPage)
2336 // It is, then create a new section frame at the end of that previous body
2337 // frame.
2338 auto pNew = new SwSectionFrame(*pSect, /*bMaster=*/true);
2339 pNew->InsertBehind(pPage->FindBodyCont(), pPage->FindLastBodyContent());
2340 pNew->Init();
2341 SwFrame* pLower = pNew->GetLower();
2342 if (pLower->IsColumnFrame())
2344 pRef = pLower;
2349 else
2351 pRef = pFootnote->FindFootnoteBossFrame();
2354 if( !pRef )
2355 // Endnotes on a separate page.
2356 pRef = pFootnote->GetRef();
2358 OSL_ENSURE( pRef, "MoveBwd: Endnote for an empty section?" );
2360 if( !bEndnote )
2361 pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
2362 SwFootnoteBossFrame *pRefBoss = pRef->FindFootnoteBossFrame( !bEndnote );
2363 if ( pOldBoss != pRefBoss &&
2365 ( !bEndnote ||
2366 pRefBoss->IsBefore( pOldBoss ) )
2368 pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false );
2370 // Do we have to respect a PageBreak?
2371 else if (IsPageBreak(true) && (!m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsHiddenNow()))
2373 // If the previous page doesn't have a Frame in the body,
2374 // flowing back makes sense despite the PageBreak (otherwise,
2375 // we'd get an empty page).
2376 // Of course we need to overlook empty pages!
2377 const SwFrame *pFlow = &m_rThis;
2380 pFlow = pFlow->FindPrev();
2381 } while ( pFlow &&
2382 ( pFlow->FindPageFrame() == pOldPage ||
2383 !pFlow->IsInDocBody() ) );
2384 if ( pFlow )
2386 tools::Long nDiff = pOldPage->GetPhyPageNum() - pFlow->GetPhyPageNum();
2387 if ( nDiff > 1 )
2389 if ( static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage() )
2390 nDiff -= 1;
2391 if ( nDiff > 1 )
2393 pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2394 // i#53139
2395 // Now <pNewUpper> is a previous layout frame, which contains
2396 // content. But the new upper layout frame has to be the next one.
2397 // Thus, hack for issue i14206 no longer needed, but fix for issue 114442
2398 // Correct fix for i53139
2399 // Check for wrong page description before using next new upper.
2400 // i#66051 - further correction of fix for i53139
2401 // Check for correct type of new next upper layout frame
2402 // Another correction of fix for i53139
2403 // Assumption, that in all cases <pNewUpper> is a previous
2404 // layout frame, which contains content, is wrong.
2405 // Another correction of fix for i53139
2406 // Beside type check, check also, if proposed new next upper
2407 // frame is inside the same frame types.
2408 // i#73194 - and yet another correction
2409 // of fix for i53139:
2410 // Assure that the new next upper layout frame doesn't
2411 // equal the current one.
2412 // E.g.: content is on page 3, on page 2 is only a 'ghost'
2413 // section and on page 1 is normal content. Method <FindPrev(..)>
2414 // will find the last content of page 1, but <GetLeaf(..)>
2415 // returns new upper on page 2.
2416 if (pNewUpper && pNewUpper->Lower())
2418 SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
2419 if ( pNewNextUpper &&
2420 pNewNextUpper != m_rThis.GetUpper() &&
2421 pNewNextUpper->GetType() == pNewUpper->GetType() &&
2422 pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2423 pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2424 pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2425 pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2426 !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2428 pNewUpper = pNewNextUpper;
2429 bCheckPageDescOfNextPage = true;
2433 bCheckPageDescs = true;
2438 else if (IsColBreak(true))
2440 // If the previous column doesn't contain a ContentFrame, flowing back
2441 // makes sense despite the ColumnBreak, as otherwise we'd get
2442 // an empty column.
2443 if( m_rThis.IsInSct() )
2445 pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2446 if( pNewUpper && !SwFlowFrame::IsMoveBwdJump() &&
2447 ( pNewUpper->ContainsContent() ||
2448 ( ( !pNewUpper->IsColBodyFrame() ||
2449 !pNewUpper->GetUpper()->GetPrev() ) &&
2450 !pNewUpper->FindSctFrame()->GetPrev() ) ) )
2452 pNewUpper = nullptr;
2454 // i#53139
2455 // i#69409 - check <pNewUpper>
2456 // i#71065 - check <SwFlowFrame::IsMoveBwdJump()>
2457 else if ( pNewUpper && !SwFlowFrame::IsMoveBwdJump() )
2459 // Now <pNewUpper> is a previous layout frame, which
2460 // contains content. But the new upper layout frame
2461 // has to be the next one.
2462 // Correct fix for i53139
2463 // Check for wrong page description before using next new upper.
2464 // i#66051 - further correction of fix for i53139
2465 // Check for correct type of new next upper layout frame
2466 // Another correction of fix for i53139
2467 // Beside type check, check also, if proposed new next upper
2468 // frame is inside the same frame types.
2469 SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NOSECTION, true );
2470 if ( pNewNextUpper &&
2471 pNewNextUpper->GetType() == pNewUpper->GetType() &&
2472 pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2473 pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2474 pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2475 pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2476 !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2478 pNewUpper = pNewNextUpper;
2482 else
2484 const SwFrame *pCol = m_rThis.FindColFrame();
2485 assert(pCol);
2486 bool bGoOn = true;
2487 bool bJump = false;
2490 if ( pCol->GetPrev() )
2491 pCol = pCol->GetPrev();
2492 else
2494 bGoOn = false;
2495 pCol = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2497 if ( pCol )
2499 // ColumnFrames now with BodyFrame
2500 SwLayoutFrame* pColBody = pCol->IsColumnFrame() ?
2501 const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())) :
2502 const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol));
2503 if ( pColBody->ContainsContent() )
2505 bGoOn = false; // We have content here! we accept this
2506 // only if GetLeaf() has set the MoveBwdJump.
2507 if( SwFlowFrame::IsMoveBwdJump() )
2509 pNewUpper = pColBody;
2510 // i#53139
2511 // Now <pNewUpper> is a previous layout frame, which
2512 // contains content. But the new upper layout frame
2513 // has to be the next one.
2514 // Correct fix for i53139
2515 // Check for wrong page description before using next new upper.
2516 // i#66051 - further correction of fix for i53139
2517 // Check for correct type of new next upper layout frame
2518 // Another correction of fix for i53139
2519 // Beside type check, check also, if proposed new next upper
2520 // frame is inside the same frame types.
2521 // i#71065
2522 // Check that the proposed new next upper layout
2523 // frame isn't the current one.
2524 SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
2525 if ( pNewNextUpper &&
2526 pNewNextUpper != m_rThis.GetUpper() &&
2527 pNewNextUpper->GetType() == pNewUpper->GetType() &&
2528 pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2529 pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2530 pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2531 pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2532 !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2534 pNewUpper = pNewNextUpper;
2538 else
2540 if( pNewUpper ) // We already had an empty column, in other
2541 bJump = true; // words we skipped one.
2542 pNewUpper = pColBody; // this empty column could be considered,
2543 // but we continue searching nevertheless.
2546 } while( bGoOn );
2547 if( bJump )
2548 SwFlowFrame::SetMoveBwdJump( true );
2551 else // No breaks - we can flow back.
2552 pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2554 // i#27801 - no move backward of 'master' text frame,
2555 // if - due to its object positioning - it isn't allowed to be on the new page frame
2556 // i#44049 - add another condition for not moving backward
2557 // If one of its objects has restarted the layout process, moving backward
2558 // isn't sensible either.
2559 // i#47697 - refine condition made for issue i44049
2560 // - allow move backward as long as the anchored object is only temporarily
2561 // positions considering its wrapping style.
2562 if ( pNewUpper &&
2563 m_rThis.IsTextFrame() && !IsFollow() )
2565 sal_uInt32 nToPageNum( 0 );
2566 const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
2567 *(pOldPage->GetFormat()->GetDoc()),
2568 static_cast<SwTextFrame&>(m_rThis),
2569 nToPageNum );
2570 if ( bMoveFwdByObjPos &&
2571 pNewUpper->FindPageFrame()->GetPhyPageNum() < nToPageNum )
2573 pNewUpper = nullptr;
2575 // i#44049 - check, if one of its anchored objects
2576 // has restarted the layout process.
2577 else if ( m_rThis.GetDrawObjs() )
2579 for (SwAnchoredObject* pAnchoredObj : *m_rThis.GetDrawObjs())
2581 // i#47697 - refine condition - see above
2582 if ( pAnchoredObj->RestartLayoutProcess() &&
2583 !pAnchoredObj->IsTmpConsiderWrapInfluence() )
2585 pNewUpper = nullptr;
2586 break;
2592 // With Follows, it's only allowed to flow back if there's no neighbor
2593 // in the new environment (because that would be the Master).
2594 // (6677) If however we skipped empty pages, we still have to move.
2595 if ( pNewUpper && IsFollow() && pNewUpper->Lower() )
2597 // i#79774
2598 // neglect empty sections in proposed new upper frame
2599 bool bProposedNewUpperContainsOnlyEmptySections( true );
2601 const SwFrame* pLower( pNewUpper->Lower() );
2602 while ( pLower )
2604 if ( pLower->IsSctFrame() &&
2605 !dynamic_cast<const SwSectionFrame&>(*pLower).GetSection() )
2607 pLower = pLower->GetNext();
2608 continue;
2610 else
2612 bProposedNewUpperContainsOnlyEmptySections = false;
2613 break;
2617 if ( !bProposedNewUpperContainsOnlyEmptySections )
2619 if ( SwFlowFrame::IsMoveBwdJump() )
2621 // Don't move after the Master, but into the next empty page.
2622 SwFrame *pFrame = pNewUpper->Lower();
2623 while ( pFrame->GetNext() )
2624 pFrame = pFrame->GetNext();
2625 pNewUpper = pFrame->GetLeaf( MAKEPAGE_INSERT, true );
2626 if( pNewUpper == m_rThis.GetUpper() ) // Did we end up in the same place?
2627 pNewUpper = nullptr; // If so, moving is not needed.
2629 else
2630 pNewUpper = nullptr;
2633 if ( pNewUpper && !ShouldBwdMoved( pNewUpper, rbReformat ) )
2635 if( !pNewUpper->Lower() )
2637 if( pNewUpper->IsFootnoteContFrame() )
2639 pNewUpper->Cut();
2640 SwFrame::DestroyFrame(pNewUpper);
2642 else
2644 SwSectionFrame* pSectFrame = pNewUpper->FindSctFrame();
2646 if ( pSectFrame && !pSectFrame->IsColLocked() &&
2647 !pSectFrame->ContainsContent() && !pSectFrame->ContainsAny( true ) )
2649 pSectFrame->DelEmpty( true );
2650 SwFrame::DestroyFrame(pSectFrame);
2651 m_rThis.setFrameAreaPositionValid(true);
2655 pNewUpper = nullptr;
2658 // i#21478 - don't move backward, if flow frame wants to
2659 // keep with next frame and next frame is locked.
2660 // i#38232 - If next frame is a table, do *not* check,
2661 // if it's locked.
2662 if ( pNewUpper && !IsFollow() && !m_rThis.IsHiddenNow() &&
2663 m_rThis.GetAttrSet()->GetKeep().GetValue() && m_rThis.GetIndNext() )
2665 SwFrame* pIndNext = m_rThis.GetIndNext();
2666 // i#38232
2667 if ( !pIndNext->IsTabFrame() )
2669 // get first content of section, while empty sections are skipped
2670 while ( pIndNext && pIndNext->IsSctFrame() )
2672 if( static_cast<SwSectionFrame*>(pIndNext)->GetSection() )
2674 SwFrame* pTmp = static_cast<SwSectionFrame*>(pIndNext)->ContainsAny();
2675 if ( pTmp )
2677 pIndNext = pTmp;
2678 break;
2681 pIndNext = pIndNext->GetIndNext();
2683 OSL_ENSURE( !pIndNext || dynamic_cast<const SwTextFrame*>( pIndNext) != nullptr,
2684 "<SwFlowFrame::MovedBwd(..)> - incorrect next found." );
2685 if ( pIndNext && pIndNext->IsFlowFrame() &&
2686 SwFlowFrame::CastFlowFrame(pIndNext)->IsJoinLocked() )
2688 pNewUpper = nullptr;
2693 // i#65250
2694 // layout loop control for flowing content again and again moving
2695 // backward under the same layout condition.
2696 if ( pNewUpper && !IsFollow() &&
2697 pNewUpper != m_rThis.GetUpper() &&
2698 SwLayouter::MoveBwdSuppressed( *(pOldPage->GetFormat()->GetDoc()),
2699 *this, *pNewUpper ) )
2701 SwLayoutFrame* pNextNewUpper = pNewUpper->GetLeaf(
2702 ( !m_rThis.IsSctFrame() && m_rThis.IsInSct() )
2703 ? MAKEPAGE_NOSECTION
2704 : MAKEPAGE_NONE,
2705 true );
2706 // i#73194 - make code robust
2707 OSL_ENSURE( pNextNewUpper, "<SwFlowFrame::MoveBwd(..)> - missing next new upper" );
2708 if ( pNextNewUpper &&
2709 ( pNextNewUpper == m_rThis.GetUpper() ||
2710 pNextNewUpper->GetType() != m_rThis.GetUpper()->GetType() ) )
2712 // tdf#107398 do not leave empty footnote container around
2713 if (!pNewUpper->Lower() && pNewUpper->IsFootnoteContFrame())
2715 pNewUpper->Cut();
2716 SwFrame::DestroyFrame(pNewUpper);
2718 pNewUpper = nullptr;
2719 OSL_FAIL( "<SwFlowFrame::MoveBwd(..)> - layout loop control for layout action <Move Backward> applied!" );
2723 OSL_ENSURE( pNewUpper != m_rThis.GetUpper(),
2724 "<SwFlowFrame::MoveBwd(..)> - moving backward to the current upper frame!?" );
2725 if ( pNewUpper )
2727 PROTOCOL_ENTER( &m_rThis, PROT::MoveBack, DbgAction::NONE, nullptr );
2728 if ( pNewUpper->IsFootnoteContFrame() )
2730 // I may have gotten a Container
2731 SwFootnoteFrame *pNew = SwFootnoteContFrame::PrependChained(&m_rThis, false);
2732 pNew->Paste( pNewUpper );
2733 pNewUpper = pNew;
2735 if( pNewUpper->IsFootnoteFrame() && m_rThis.IsInSct() )
2737 SwSectionFrame* pSct = m_rThis.FindSctFrame();
2738 // If we're in a section of a footnote, we may need to create
2739 // a SwSectionFrame in the new upper
2740 if( pSct->IsInFootnote() )
2742 SwFrame* pTmp = pNewUpper->Lower();
2743 if( pTmp )
2745 while( pTmp->GetNext() )
2746 pTmp = pTmp->GetNext();
2747 if( !pTmp->IsSctFrame() ||
2748 static_cast<SwSectionFrame*>(pTmp)->GetFollow() != pSct )
2749 pTmp = nullptr;
2751 if( pTmp )
2752 pNewUpper = static_cast<SwSectionFrame*>(pTmp);
2753 else
2755 pSct = new SwSectionFrame( *pSct, true );
2756 pSct->Paste( pNewUpper );
2757 pSct->Init();
2758 pNewUpper = pSct;
2759 pSct->SimpleFormat();
2763 bool bUnlock = false;
2764 bool bFollow = false;
2765 // Lock section. Otherwise, it could get destroyed if the only Content
2766 // moves e.g. from the second into the first column.
2767 SwSectionFrame* pSect = pNewUpper->FindSctFrame();
2768 if( pSect )
2770 bUnlock = !pSect->IsColLocked();
2771 pSect->ColLock();
2772 bFollow = pSect->HasFollow();
2776 auto const pOld = m_rThis.GetUpper();
2777 ::std::optional<SwFrameDeleteGuard> g;
2778 if (m_rThis.GetUpper()->IsCellFrame())
2780 // note: IsFollowFlowRow() is never set for new-style tables
2781 SwTabFrame const*const pTabFrame(m_rThis.FindTabFrame());
2782 if ( pTabFrame->IsFollow()
2783 && static_cast<SwTabFrame const*>(pTabFrame->GetPrecede())->HasFollowFlowLine()
2784 && pTabFrame->GetFirstNonHeadlineRow() == m_rThis.GetUpper()->GetUpper())
2786 // lock follow-flow-row (similar to sections above)
2787 g.emplace(m_rThis.GetUpper()->GetUpper());
2788 assert(m_rThis.GetUpper()->GetUpper()->IsDeleteForbidden());
2791 pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2792 SAL_WARN_IF(pOld != m_rThis.GetUpper(), "sw.core",
2793 "MoveBwd(): pNewUpper->Calc() moved this frame?");
2796 m_rThis.Cut();
2798 // optimization: format section, if its size is invalidated and if it's
2799 // the new parent of moved backward frame.
2800 bool bFormatSect( false );
2801 if( bUnlock )
2803 pSect->ColUnlock();
2804 if( pSect->HasFollow() != bFollow )
2806 pSect->InvalidateSize();
2807 // - optimization
2808 if ( pSect == pNewUpper )
2809 bFormatSect = true;
2813 m_rThis.Paste( pNewUpper );
2814 // - optimization
2815 if ( bFormatSect )
2816 pSect->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2818 SwPageFrame *pNewPage = m_rThis.FindPageFrame();
2819 if( pNewPage != pOldPage )
2821 m_rThis.Prepare( PrepareHint::BossChanged, static_cast<const void*>(pOldPage), false );
2822 SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2823 if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
2824 pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on
2826 pNewPage->InvalidateSpelling();
2827 pNewPage->InvalidateSmartTags();
2828 pNewPage->InvalidateAutoCompleteWords();
2829 pNewPage->InvalidateWordCount();
2831 // No <CheckPageDesc(..)> in online layout
2832 if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
2834 if ( bCheckPageDescs && pNewPage->GetNext() )
2836 SwPageFrame* pStartPage = bCheckPageDescOfNextPage ?
2837 pNewPage :
2838 static_cast<SwPageFrame*>(pNewPage->GetNext());
2839 SwFrame::CheckPageDescs( pStartPage, false);
2841 else if (m_rThis.GetPageDescItem().GetPageDesc())
2843 // First page could get empty for example by disabling
2844 // a section
2845 SwFrame::CheckPageDescs( pNewPage, false);
2850 return pNewUpper != nullptr;
2853 SwFlowFrame *SwFlowFrame::CastFlowFrame( SwFrame *pFrame )
2855 if ( pFrame->IsContentFrame() )
2856 return static_cast<SwContentFrame*>(pFrame);
2857 if ( pFrame->IsTabFrame() )
2858 return static_cast<SwTabFrame*>(pFrame);
2859 if ( pFrame->IsSctFrame() )
2860 return static_cast<SwSectionFrame*>(pFrame);
2861 return nullptr;
2864 const SwFlowFrame *SwFlowFrame::CastFlowFrame( const SwFrame *pFrame )
2866 if ( pFrame->IsContentFrame() )
2867 return static_cast<const SwContentFrame*>(pFrame);
2868 if ( pFrame->IsTabFrame() )
2869 return static_cast<const SwTabFrame*>(pFrame);
2870 if ( pFrame->IsSctFrame() )
2871 return static_cast<const SwSectionFrame*>(pFrame);
2872 return nullptr;
2875 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */