1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <svx/svdobj.hxx>
25 
26 #include <anchoredobject.hxx>
27 #include <bodyfrm.hxx>
28 #include <swtable.hxx>
29 #include <rootfrm.hxx>
30 #include <pagefrm.hxx>
31 #include <viewimp.hxx>
32 #include <viewopt.hxx>
33 #include <frmatr.hxx>
34 #include <frmtool.hxx>
35 #include <IDocumentFieldsAccess.hxx>
36 #include <editeng/formatbreakitem.hxx>
37 #include <editeng/keepitem.hxx>
38 #include <fmtanchr.hxx>
39 #include <fmtsrnd.hxx>
40 #include <fmtpdsc.hxx>
41 #include <editeng/ulspitem.hxx>
42 #include <tgrditem.hxx>
43 #include <txtftn.hxx>
44 #include <fmtftn.hxx>
45 #include <editeng/pgrditem.hxx>
46 #include <paratr.hxx>
47 #include <ftnfrm.hxx>
48 #include <txtfrm.hxx>
49 #include <notxtfrm.hxx>
50 #include <tabfrm.hxx>
51 #include <rowfrm.hxx>
52 #include <pagedesc.hxx>
53 #include <layact.hxx>
54 #include <flyfrm.hxx>
55 #include <sectfrm.hxx>
56 #include <section.hxx>
57 #include <dbg_lay.hxx>
58 #include <lineinfo.hxx>
59 #include <fmtclbl.hxx>
60 #include <sortedobjs.hxx>
61 #include <layouter.hxx>
62 #include <fmtfollowtextflow.hxx>
63 #include <calbck.hxx>
64 #include <IDocumentSettingAccess.hxx>
65 #include <IDocumentDrawModelAccess.hxx>
66 #include <pam.hxx>
67 #include <ndtxt.hxx>
68 
69 bool SwFlowFrame::s_bMoveBwdJump = false;
70 
SwFlowFrame(SwFrame & rFrame)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 )
78 {}
79 
~SwFlowFrame()80 SwFlowFrame::~SwFlowFrame()
81 {
82     if (m_pFollow)
83     {
84         m_pFollow->m_pPrecede = nullptr;
85     }
86     if (m_pPrecede)
87     {
88         m_pPrecede->m_pFollow = nullptr;
89     }
90 }
91 
SetFollow(SwFlowFrame * const pFollow)92 void SwFlowFrame::SetFollow(SwFlowFrame *const pFollow)
93 {
94     if (m_pFollow)
95     {
96         assert(this == m_pFollow->m_pPrecede);
97         m_pFollow->m_pPrecede = nullptr;
98     }
99     m_pFollow = pFollow;
100     if (m_pFollow != nullptr)
101     {
102         if (m_pFollow->m_pPrecede) // re-chaining pFollow?
103         {
104             assert(m_pFollow == m_pFollow->m_pPrecede->m_pFollow);
105             m_pFollow->m_pPrecede->m_pFollow = nullptr;
106         }
107         m_pFollow->m_pPrecede = this;
108     }
109 }
110 
111 /// @return true if any follow has the JoinLocked flag
HasLockedFollow() const112 bool SwFlowFrame::HasLockedFollow() const
113 {
114     const SwFlowFrame* pFrame = GetFollow();
115     while( pFrame )
116     {
117         if( pFrame->IsJoinLocked() )
118             return true;
119         pFrame = pFrame->GetFollow();
120     }
121     return false;
122 }
123 
IsKeepFwdMoveAllowed(bool bIgnoreMyOwnKeepValue)124 bool SwFlowFrame::IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue )
125 {
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();
133         do
134         {   if ( pFrame->GetAttrSet()->GetKeep().GetValue() )
135                 pFrame = pFrame->GetIndPrev();
136             else
137                 return true;
138         } while ( pFrame );
139     }
140                   //See IsFwdMoveAllowed()
141     bool bRet = false;
142     if ( pFrame && pFrame->GetIndPrev() )
143         bRet = true;
144     return bRet;
145 }
146 
CheckKeep()147 void SwFlowFrame::CheckKeep()
148 {
149     // Kick off the "last" predecessor with a 'keep' attribute, because
150     // it's possible for the whole troop to move back.
151     SwFrame *pPre = m_rThis.GetIndPrev();
152     assert(pPre);
153     if( pPre->IsSctFrame() )
154     {
155         SwFrame *pLast = static_cast<SwSectionFrame*>(pPre)->FindLastContent();
156         if( pLast && pLast->FindSctFrame() == pPre )
157             pPre = pLast;
158         else
159             return;
160     }
161     SwFrame* pTmp;
162     bool bKeep;
163     while ( (bKeep = pPre->GetAttrSet()->GetKeep().GetValue()) &&
164             nullptr != ( pTmp = pPre->GetIndPrev() ) )
165     {
166         if( pTmp->IsSctFrame() )
167         {
168             SwFrame *pLast = static_cast<SwSectionFrame*>(pTmp)->FindLastContent();
169             if( pLast && pLast->FindSctFrame() == pTmp )
170                 pTmp = pLast;
171             else
172                 break;
173         }
174         pPre = pTmp;
175     }
176     if ( bKeep )
177         pPre->InvalidatePos();
178 }
179 
180 namespace
181 {
182 /**
183  * Determines if the next content frame after rThis will require the full area of the parent body
184  * frame.
185  */
IsNextContentFullPage(const SwFrame & rThis)186 bool IsNextContentFullPage(const SwFrame& rThis)
187 {
188     const SwFrame* pNext = rThis.FindNextCnt();
189     if (!pNext)
190     {
191         return false;
192     }
193 
194     const SwSortedObjs* pNextDrawObjs = pNext->GetDrawObjs();
195     if (!pNextDrawObjs || !pNextDrawObjs->size())
196     {
197         return false;
198     }
199 
200     for (const auto& pDrawObj : *pNextDrawObjs)
201     {
202         if (!pDrawObj)
203         {
204             continue;
205         }
206 
207         SwTwips nDrawObjHeight = pDrawObj->GetObjRectWithSpaces().Height();
208         const SwPageFrame* pPageFrame = pDrawObj->GetPageFrame();
209         if (!pPageFrame)
210         {
211             continue;
212         }
213 
214         SwTwips nBodyHeight = pPageFrame->GetLower()->getFrameArea().Height();
215         if (nDrawObjHeight < nBodyHeight)
216         {
217             continue;
218         }
219 
220         const SwFormatSurround& rSurround = pDrawObj->GetFrameFormat().GetSurround();
221         if (rSurround.GetSurround() != text::WrapTextMode_NONE)
222         {
223             continue;
224         }
225 
226         // At this point the height of the draw object will use all the vertical available space,
227         // and also no wrapping will be performed, so all horizontal space will be taken as well.
228         return true;
229     }
230 
231     return false;
232 }
233 }
234 
IsKeep(SvxFormatKeepItem const & rKeep,SvxFormatBreakItem const & rBreak,bool const bCheckIfLastRowShouldKeep) const235 bool SwFlowFrame::IsKeep(SvxFormatKeepItem const& rKeep,
236         SvxFormatBreakItem const& rBreak,
237         bool const bCheckIfLastRowShouldKeep) const
238 {
239     // 1. The keep attribute is ignored inside footnotes
240     // 2. For compatibility reasons, the keep attribute is
241     //    ignored for frames inside table cells
242     // 3. If bBreakCheck is set to true, this function only checks
243     //    if there are any break after attributes set at rAttrs
244     //    or break before attributes set for the next content (or next table)
245     // 4. Keep is ignored if the next frame will require its own page.
246     bool bKeep = bCheckIfLastRowShouldKeep ||
247                  (  !m_rThis.IsInFootnote() &&
248                     ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) &&
249                     rKeep.GetValue() && !IsNextContentFullPage(m_rThis));
250 
251     OSL_ENSURE( !bCheckIfLastRowShouldKeep || m_rThis.IsTabFrame(),
252             "IsKeep with bCheckIfLastRowShouldKeep should only be used for tabfrms" );
253 
254     // Ignore keep attribute if there are break situations:
255     if ( bKeep )
256     {
257         switch (rBreak.GetBreak())
258         {
259             case SvxBreak::ColumnAfter:
260             case SvxBreak::ColumnBoth:
261             case SvxBreak::PageAfter:
262             case SvxBreak::PageBoth:
263             {
264                 bKeep = false;
265                 break;
266             }
267             default: break;
268         }
269         if ( bKeep )
270         {
271             SwFrame *pNxt;
272             if( nullptr != (pNxt = m_rThis.FindNextCnt()) &&
273                 (!m_pFollow || pNxt != &m_pFollow->GetFrame()))
274             {
275                 // The last row of a table only keeps with the next content
276                 // it they are in the same section:
277                 if ( bCheckIfLastRowShouldKeep )
278                 {
279                     const SwSection* pThisSection = nullptr;
280                     const SwSection* pNextSection = nullptr;
281                     const SwSectionFrame* pThisSectionFrame = m_rThis.FindSctFrame();
282                     const SwSectionFrame* pNextSectionFrame = pNxt->FindSctFrame();
283 
284                     if ( pThisSectionFrame )
285                         pThisSection = pThisSectionFrame->GetSection();
286 
287                     if ( pNextSectionFrame )
288                         pNextSection = pNextSectionFrame->GetSection();
289 
290                     if ( pThisSection != pNextSection )
291                         bKeep = false;
292                 }
293 
294                 if ( bKeep )
295                 {
296                     SvxFormatBreakItem const* pBreak;
297                     SwFormatPageDesc const* pPageDesc;
298                     SwTabFrame* pTab = pNxt->IsInTab() ? pNxt->FindTabFrame() : nullptr;
299                     if (pTab && (!m_rThis.IsInTab() || m_rThis.FindTabFrame() != pTab))
300                     {
301                         const SwAttrSet *const pSet = &pTab->GetFormat()->GetAttrSet();
302                         pBreak = &pSet->GetBreak();
303                         pPageDesc = &pSet->GetPageDesc();
304                     }
305                     else
306                     {
307                         pBreak = &pNxt->GetBreakItem();
308                         pPageDesc = &pNxt->GetPageDescItem();
309                     }
310 
311                     if (pPageDesc->GetPageDesc())
312                         bKeep = false;
313                     else switch (pBreak->GetBreak())
314                     {
315                         case SvxBreak::ColumnBefore:
316                         case SvxBreak::ColumnBoth:
317                         case SvxBreak::PageBefore:
318                         case SvxBreak::PageBoth:
319                             bKeep = false;
320                             break;
321                         default: break;
322                     }
323                 }
324             }
325         }
326     }
327     return bKeep;
328 }
329 
BwdMoveNecessary(const SwPageFrame * pPage,const SwRect & rRect)330 sal_uInt8 SwFlowFrame::BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect )
331 {
332     // The return value helps deciding whether we need to flow back (3),
333     // or whether we can use the good old WouldFit (0, 1), or if
334     // it's reasonable to relocate and test-format (2).
335 
336     // Bit 1 in this case means that there are objects anchored to myself,
337     // bit 2 means that I have to evade other objects.
338 
339     // If a SurroundObj that desires to be wrapped around overlaps with the
340     // Rect, it's required to flow (because we can't guess the relationships).
341     // However it's possible for a test formatting to happen.
342     // If the SurroundObj is a Fly and I'm a Lower, or the Fly is a Lower of
343     // mine, then it doesn't matter.
344     // If the SurroundObj is anchored in a character bound Fly, and I'm not
345     // a Lower of that character bound Fly myself, then the Fly doesn't matter.
346 
347     // If the object is anchored with me, i can ignore it, because
348     // it's likely that it will follow me with the flow. A test formatting is
349     // not allowed in that case, however!
350     sal_uInt8 nRet = 0;
351     SwFlowFrame *pTmp = this;
352     do
353     {   // If there are objects hanging either on me or on a follow, we can't
354         // do a test formatting, because paragraph bound objects wouldn't
355         // be properly considered, and character bound objects shouldn't
356         // be test formatted at all.
357         if( pTmp->GetFrame().GetDrawObjs() )
358             nRet = 1;
359         pTmp = pTmp->GetFollow();
360     } while ( !nRet && pTmp );
361     const SwSortedObjs *pObjs = pPage ? pPage->GetSortedObjs() : nullptr;
362     if (pObjs)
363     {
364 
365         const SwSortedObjs &rObjs = *pObjs;
366         sal_uLong nIndex = ULONG_MAX;
367         for ( size_t i = 0; nRet < 3 && i < rObjs.size(); ++i )
368         {
369 
370             SwAnchoredObject* pObj = rObjs[i];
371             const SwFrameFormat& rFormat = pObj->GetFrameFormat();
372             const SwRect aRect( pObj->GetObjRect() );
373             if ( aRect.IsOver( rRect ) &&
374                  rFormat.GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
375             {
376                 if( m_rThis.IsLayoutFrame() && //Fly Lower of This?
377                     Is_Lower_Of( &m_rThis, pObj->GetDrawObj() ) )
378                     continue;
379                 if( auto pFly = dynamic_cast<const SwFlyFrame*>(pObj) )
380                 {
381                     if ( pFly->IsAnLower( &m_rThis ) )//This Lower of Fly?
382                         continue;
383                 }
384 
385                 const SwFrame* pAnchor = pObj->GetAnchorFrame();
386                 if ( pAnchor == &m_rThis )
387                 {
388                     nRet |= 1;
389                     continue;
390                 }
391 
392                 // Don't do this if the object is anchored behind me in the text
393                 // flow, because then I wouldn't evade it.
394                 if ( ::IsFrameInSameContext( pAnchor, &m_rThis ) )
395                 {
396                     if ( rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA )
397                     {
398                         // The index of the other one can be retrieved using the anchor attribute.
399                         sal_uLong nTmpIndex = rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex();
400                         // Now we're going to check whether the current paragraph before
401                         // the anchor of the displacing object sits in the text. If this
402                         // is the case, we don't try to evade it.
403                         // The index is being determined via SwFormatAnchor, because it's
404                         // getting quite expensive otherwise.
405                         if( ULONG_MAX == nIndex )
406                         {
407                             const SwNode *pNode;
408                             if (m_rThis.IsTextFrame())
409                                 pNode = static_cast<SwTextFrame&>(m_rThis).GetTextNodeFirst();
410                             else if (m_rThis.IsNoTextFrame())
411                                 pNode = static_cast<SwNoTextFrame&>(m_rThis).GetNode();
412                             else if( m_rThis.IsSctFrame() )
413                                 pNode = static_cast<SwSectionFormat*>(static_cast<SwSectionFrame&>(m_rThis).
414                                         GetFormat())->GetSectionNode();
415                             else
416                             {
417                                 assert(!m_rThis.IsContentFrame());
418                                 OSL_ENSURE( m_rThis.IsTabFrame(), "new FowFrame?" );
419                                 pNode = static_cast<SwTabFrame&>(m_rThis).GetTable()->
420                                     GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
421                             }
422                             nIndex = pNode->GetIndex();
423                         }
424                         if (nIndex < nTmpIndex &&
425                             (!m_rThis.IsTextFrame() ||
426                              !FrameContainsNode(static_cast<SwTextFrame&>(m_rThis), nTmpIndex)))
427                         {
428                             continue;
429                         }
430                     }
431                 }
432                 else
433                     continue;
434 
435                 nRet |= 2;
436             }
437         }
438     }
439     return nRet;
440 }
441 
442 /// A specialized form of Cut(), which relocates a whole chain (this and the following,
443 /// in particular). During this process, only the minimum operations and notifications are done.
CutTree(SwFrame * pStart)444 SwLayoutFrame *SwFlowFrame::CutTree( SwFrame *pStart )
445 {
446     // Cut the Start and all the neighbours; they are chained together and
447     // a handle to the first one is returned. Residuals are invalidated
448     // as appropriate.
449 
450     SwLayoutFrame *pLay = pStart->GetUpper();
451     if ( pLay->IsInFootnote() )
452         pLay = pLay->FindFootnoteFrame();
453 
454     // i#58846
455     // <pPrepare( PrepareHint::QuoVadis )> only for frames in footnotes
456     if( pStart->IsInFootnote() )
457     {
458         SwFrame* pTmp = pStart->GetIndPrev();
459         if( pTmp )
460             pTmp->Prepare( PrepareHint::QuoVadis );
461     }
462 
463     // Just cut quickly and take care that we don't cause problems with the
464     // left-behinds. The pointers of the chain being cut can point who-knows where.
465     if ( pStart == pStart->GetUpper()->Lower() )
466         pStart->GetUpper()->m_pLower = nullptr;
467     if ( pStart->GetPrev() )
468     {
469         pStart->GetPrev()->mpNext = nullptr;
470         pStart->mpPrev = nullptr;
471     }
472 
473     if ( pLay->IsFootnoteFrame() )
474     {
475         if ( !pLay->Lower() && !pLay->IsColLocked() &&
476              !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked() )
477         {
478             // tdf#101821 don't delete it while iterating over it
479             if (!pLay->IsDeleteForbidden())
480             {
481                 pLay->Cut();
482                 SwFrame::DestroyFrame(pLay);
483             }
484             // else: assume there is code on the stack to clean up empty
485             // footnote frames
486             // (don't go into the else branch below, it produces a disconnected
487             // footnote with null upper that can be returned by
488             // SwFootnoteBossFrame::FindFootnote() causing null pointer deref
489             // in SwTextFrame::ConnectFootnote()
490         }
491         else
492         {
493             bool bUnlock = !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked();
494             static_cast<SwFootnoteFrame*>(pLay)->LockBackMove();
495             pLay->InvalidateSize();
496             pLay->Calc(pLay->getRootFrame()->GetCurrShell()->GetOut());
497             SwContentFrame *pCnt = pLay->ContainsContent();
498             while ( pCnt && pLay->IsAnLower( pCnt ) )
499             {
500                 // It's possible for the ContentFrame to be locked, and we don't want
501                 // to end up in an endless page migration, so we're not even
502                 // going to call Calc!
503                 OSL_ENSURE( pCnt->IsTextFrame(), "The Graphic has landed." );
504                 if ( static_cast<SwTextFrame*>(pCnt)->IsLocked() ||
505                      static_cast<SwTextFrame*>(pCnt)->GetFollow() == pStart )
506                     break;
507                 pCnt->Calc(pCnt->getRootFrame()->GetCurrShell()->GetOut());
508                 pCnt = pCnt->GetNextContentFrame();
509             }
510             if( bUnlock )
511                 static_cast<SwFootnoteFrame*>(pLay)->UnlockBackMove();
512         }
513         pLay = nullptr;
514     }
515     return pLay;
516 }
517 
518 /// A specialized form of Paste(), which relocates a whole chain (this and the following,
519 /// in particular). During this process, only the minimum operations and notifications are done.
PasteTree(SwFrame * pStart,SwLayoutFrame * pParent,SwFrame * pSibling,SwFrame * pOldParent)520 bool SwFlowFrame::PasteTree( SwFrame *pStart, SwLayoutFrame *pParent, SwFrame *pSibling,
521                            SwFrame *pOldParent )
522 {
523     // returns true if there's a LayoutFrame in the chain.
524     bool bRet = false;
525 
526     // The chain beginning with pStart is inserted before pSibling
527     // under the parent. We take care to invalidate as required.
528 
529     // I'm receiving a finished chain. We need to update the pointers for
530     // the beginning of the chain, then all the uppers and finally the end.
531     // On the way there, we invalidate as required.
532     if ( pSibling )
533     {
534         pStart->mpPrev = pSibling->GetPrev();
535         if ( nullptr != pStart->mpPrev )
536             pStart->GetPrev()->mpNext = pStart;
537         else
538             pParent->m_pLower = pStart;
539         pSibling->InvalidatePos_();
540         pSibling->InvalidatePrt_();
541     }
542     else
543     {
544         pStart->mpPrev = pParent->Lower();
545         if ( nullptr == pStart->mpPrev )
546             pParent->m_pLower = pStart;
547         else
548         //i#100782
549         //If the pParent has more than 1 child nodes, former design will
550         //ignore them directly without any collection work. It will make some
551         //dangling pointers. This lead the crash...
552         //The new design will find the last child of pParent in loop way, and
553         //add the pStart after the last child.
554         //  pParent->Lower()->pNext = pStart;
555         {
556             SwFrame* pTemp = pParent->m_pLower;
557             while (pTemp)
558             {
559                 if (pTemp->mpNext)
560                     pTemp = pTemp->mpNext;
561                 else
562                 {
563                     pStart->mpPrev = pTemp;
564                     pTemp->mpNext = pStart;
565                     break;
566                 }
567             }
568         }
569 
570 
571         // i#27145
572         if ( pParent->IsSctFrame() )
573         {
574             // We have no sibling because pParent is a section frame and
575             // has just been created to contain some content. The printing
576             // area of the frame behind pParent has to be invalidated, so
577             // that the correct distance between pParent and the next frame
578             // can be calculated.
579             pParent->InvalidateNextPrtArea();
580         }
581     }
582     SwFrame *pFloat = pStart;
583     SwFrame *pLst = nullptr;
584     SwRectFnSet aRectFnSet(pParent);
585     SwTwips nGrowVal = 0;
586     do
587     {   pFloat->mpUpper = pParent;
588         pFloat->InvalidateAll_();
589         pFloat->CheckDirChange();
590 
591         // I'm a friend of the TextFrame and thus am allowed to do many things.
592         // The CacheIdx idea seems to be a bit risky!
593         if ( pFloat->IsTextFrame() )
594         {
595             if ( static_cast<SwTextFrame*>(pFloat)->GetCacheIdx() != USHRT_MAX )
596                 static_cast<SwTextFrame*>(pFloat)->Init();    // I'm his friend.
597         }
598         else
599             bRet = true;
600 
601         nGrowVal += aRectFnSet.GetHeight(pFloat->getFrameArea());
602         if ( pFloat->GetNext() )
603             pFloat = pFloat->GetNext();
604         else
605         {
606             pLst = pFloat;
607             pFloat = nullptr;
608         }
609     } while ( pFloat );
610 
611     if ( pSibling )
612     {
613         pLst->mpNext = pSibling;
614         pSibling->mpPrev = pLst;
615         if( pSibling->IsInFootnote() )
616         {
617             if( pSibling->IsSctFrame() )
618                 pSibling = static_cast<SwSectionFrame*>(pSibling)->ContainsAny();
619             if( pSibling )
620                 pSibling->Prepare( PrepareHint::ErgoSum );
621         }
622     }
623     if ( nGrowVal )
624     {
625         if ( pOldParent && pOldParent->IsBodyFrame() ) // For variable page height while browsing
626             pOldParent->Shrink( nGrowVal );
627         pParent->Grow( nGrowVal );
628     }
629 
630     if ( pParent->IsFootnoteFrame() )
631         static_cast<SwFootnoteFrame*>(pParent)->InvalidateNxtFootnoteCnts( pParent->FindPageFrame() );
632     return bRet;
633 }
634 
MoveSubTree(SwLayoutFrame * pParent,SwFrame * pSibling)635 void SwFlowFrame::MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling )
636 {
637     OSL_ENSURE( pParent, "No parent given." );
638     OSL_ENSURE( m_rThis.GetUpper(), "Where are we coming from?" );
639 
640     // Be economical with notifications if an action is running.
641     SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
642     const SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
643     const bool bComplete = pImp && pImp->IsAction() && pImp->GetLayAction().IsComplete();
644 
645     if ( !bComplete )
646     {
647         SwFrame *pPre = m_rThis.GetIndPrev();
648         if ( pPre )
649         {
650             pPre->SetRetouche();
651             // follow-up of i#26250
652             // invalidate printing area of previous frame, if it's in a table
653             if ( pPre->GetUpper()->IsInTab() )
654             {
655                 pPre->InvalidatePrt_();
656             }
657             pPre->InvalidatePage();
658         }
659         else
660         {
661             m_rThis.GetUpper()->SetCompletePaint();
662             m_rThis.GetUpper()->InvalidatePage();
663         }
664     }
665 
666     SwPageFrame *pOldPage = m_rThis.FindPageFrame();
667 
668     SwLayoutFrame *pOldParent;
669     bool bInvaLay;
670 
671     {
672         //JoinLock pParent for the lifetime of the Cut/Paste call to avoid
673         //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
674         //into
675         FlowFrameJoinLockGuard aJoinGuard(pParent);
676         SwFrameDeleteGuard aDeleteGuard(pParent);
677         pOldParent = CutTree( &m_rThis );
678         bInvaLay = PasteTree( &m_rThis, pParent, pSibling, pOldParent );
679     }
680 
681     // If, by cutting & pasting, an empty SectionFrame came into existence, it should
682     // disappear automatically.
683     SwSectionFrame *pSct;
684 
685     if ( pOldParent && !pOldParent->Lower() &&
686          ( pOldParent->IsInSct() &&
687            !(pSct = pOldParent->FindSctFrame())->ContainsContent() &&
688            !pSct->ContainsAny( true ) ) )
689     {
690             pSct->DelEmpty( false );
691     }
692 
693     // If we're in a column section, we'd rather not call Calc "from below"
694     if( !m_rThis.IsInSct() &&
695         ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) )
696         m_rThis.GetUpper()->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
697     else if( m_rThis.GetUpper()->IsSctFrame() )
698     {
699         SwSectionFrame* pTmpSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
700         bool bOld = pTmpSct->IsContentLocked();
701         pTmpSct->SetContentLock( true );
702         pTmpSct->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
703         if( !bOld )
704             pTmpSct->SetContentLock( false );
705     }
706     SwPageFrame *pPage = m_rThis.FindPageFrame();
707 
708     if ( pOldPage != pPage )
709     {
710         m_rThis.InvalidatePage( pPage );
711         if ( m_rThis.IsLayoutFrame() )
712         {
713             SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(&m_rThis)->ContainsContent();
714             if ( pCnt )
715                 pCnt->InvalidatePage( pPage );
716         }
717         else if ( pSh && pSh->GetDoc()->GetLineNumberInfo().IsRestartEachPage()
718                   && pPage->FindFirstBodyContent() == &m_rThis )
719         {
720             m_rThis.InvalidateLineNum_();
721         }
722     }
723     if ( bInvaLay || (pSibling && pSibling->IsLayoutFrame()) )
724         m_rThis.GetUpper()->InvalidatePage( pPage );
725 }
726 
IsAnFollow(const SwFlowFrame * pAssumed) const727 bool SwFlowFrame::IsAnFollow( const SwFlowFrame *pAssumed ) const
728 {
729     const SwFlowFrame *pFoll = this;
730     do
731     {   if ( pAssumed == pFoll )
732             return true;
733         pFoll = pFoll->GetFollow();
734     } while ( pFoll );
735     return false;
736 }
737 
FindMaster() const738 SwTextFrame* SwContentFrame::FindMaster() const
739 {
740     OSL_ENSURE( IsFollow(), "SwContentFrame::FindMaster(): !IsFollow" );
741 
742     const SwContentFrame* pPrec = static_cast<const SwContentFrame*>(SwFlowFrame::GetPrecede());
743 
744     if ( pPrec && pPrec->HasFollow() && pPrec->GetFollow() == this )
745     {
746         OSL_ENSURE( pPrec->IsTextFrame(), "NoTextFrame with follow found" );
747         return const_cast<SwTextFrame*>(static_cast< const SwTextFrame* >(pPrec));
748     }
749 
750     OSL_FAIL( "Follow is lost in Space." );
751     return nullptr;
752 }
753 
FindMaster() const754 SwSectionFrame* SwSectionFrame::FindMaster() const
755 {
756     OSL_ENSURE( IsFollow(), "SwSectionFrame::FindMaster(): !IsFollow" );
757 
758     if (!m_pSection)
759         return nullptr;
760 
761     SwIterator<SwSectionFrame,SwFormat> aIter( *m_pSection->GetFormat() );
762     SwSectionFrame* pSect = aIter.First();
763     while ( pSect )
764     {
765         if (pSect->GetFollow() == this)
766             return pSect;
767         pSect = aIter.Next();
768     }
769 
770     OSL_FAIL( "Follow is lost in Space." );
771     return nullptr;
772 }
773 
FindMaster(bool bFirstMaster) const774 SwTabFrame* SwTabFrame::FindMaster( bool bFirstMaster ) const
775 {
776     OSL_ENSURE( IsFollow(), "SwTabFrame::FindMaster(): !IsFollow" );
777 
778     SwIterator<SwTabFrame,SwFormat> aIter( *GetTable()->GetFrameFormat() );
779     SwTabFrame* pTab = aIter.First();
780     while ( pTab )
781     {
782             if ( bFirstMaster )
783             {
784                 // Optimization. This makes code like this obsolete:
785                 // while ( pTab->IsFollow() )
786                 //     pTab = pTab->FindMaster();
787 
788                 if ( !pTab->IsFollow() )
789                 {
790                     SwTabFrame* pNxt = pTab;
791                     while ( pNxt )
792                     {
793                         if ( pNxt->GetFollow() == this )
794                             return pTab;
795                         pNxt = pNxt->GetFollow();
796                     }
797                 }
798             }
799             else
800             {
801                 if ( pTab->GetFollow() == this )
802                     return pTab;
803             }
804 
805             pTab = aIter.Next();
806     }
807 
808     OSL_FAIL( "Follow is lost in Space." );
809     return nullptr;
810 }
811 
812 /**
813  * Returns the next/previous Layout leaf that's NOT below this (or even is this itself).
814  * Also, that leaf must be in the same text flow as the pAnch origin frame (Body, Footnote)
815  */
GetLeaf(MakePageType eMakePage,bool bFwd,const SwFrame * pAnch) const816 const SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd,
817                                    const SwFrame *pAnch ) const
818 {
819     // No flow, no joy...
820     if ( !(IsInDocBody() || IsInFootnote() || IsInFly()) )
821         return nullptr;
822 
823     const SwFrame *pLeaf = this;
824     bool bFound = false;
825 
826     do
827     {   pLeaf = const_cast<SwFrame*>(pLeaf)->GetLeaf( eMakePage, bFwd );
828 
829         if ( pLeaf &&
830             (!IsLayoutFrame() || !static_cast<const SwLayoutFrame*>(this)->IsAnLower( pLeaf )))
831         {
832             if ( pAnch->IsInDocBody() == pLeaf->IsInDocBody() &&
833                  pAnch->IsInFootnote()     == pLeaf->IsInFootnote() )
834             {
835                 bFound = true;
836             }
837         }
838     } while ( !bFound && pLeaf );
839 
840     return static_cast<const SwLayoutFrame*>(pLeaf);
841 }
842 
GetLeaf(MakePageType eMakePage,bool bFwd)843 SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd )
844 {
845     if ( IsInFootnote() )
846         return bFwd ? GetNextFootnoteLeaf( eMakePage ) : GetPrevFootnoteLeaf( eMakePage );
847 
848     // i#53323
849     // A frame could be inside a table AND inside a section.
850     // Thus, it has to be determined, which is the first parent.
851     bool bInTab( IsInTab() );
852     bool bInSct( IsInSct() );
853     if ( bInTab && bInSct )
854     {
855         const SwFrame* pUpperFrame( GetUpper() );
856         while ( pUpperFrame )
857         {
858             if ( pUpperFrame->IsTabFrame() )
859             {
860                 // the table is the first.
861                 bInSct = false;
862                 break;
863             }
864             else if ( pUpperFrame->IsSctFrame() )
865             {
866                 // the section is the first.
867                 bInTab = false;
868                 break;
869             }
870 
871             pUpperFrame = pUpperFrame->GetUpper();
872         }
873     }
874 
875     if ( bInTab && ( !IsTabFrame() || GetUpper()->IsCellFrame() ) ) // TABLE IN TABLE
876         return bFwd ? GetNextCellLeaf() : GetPrevCellLeaf();
877 
878     if ( bInSct )
879         return bFwd ? GetNextSctLeaf( eMakePage ) : GetPrevSctLeaf();
880 
881     return bFwd ? GetNextLeaf( eMakePage ) : GetPrevLeaf();
882 }
883 
WrongPageDesc(SwPageFrame * pNew)884 bool SwFrame::WrongPageDesc( SwPageFrame* pNew )
885 {
886     // Now it's getting a bit complicated:
887 
888     // Maybe I'm bringing a Pagedesc myself; in that case,
889     // the pagedesc of the next page needs to correspond.
890     // Otherwise, I'll have to dig a bit deeper to see where
891     // the following Pagedesc is coming from.
892     // If the following page itself tells me that it's pagedesc
893     // is wrong, I can happily exchange it.
894     // If the page however thinks that it's pagedesc is correct,
895     // this doesn't mean it's useful to me:
896     // If the first BodyContent asks for a PageDesc or a PageBreak,
897     // I'll have to insert a new page - except the desired page is
898     // the correct one.
899     // If I inserted a new page, the problems only get started:
900     // because then it's likely for the next page to have been
901     // wrong and having been swapped because of that.
902     // This in turn means that I have a new (and correct) page,
903     // but the conditions to swap still apply.
904     // Way out of the situation: Try to preliminarily insert a
905     // new page once (empty pages are already inserted by InsertPage()
906     // if required)
907 
908     //My Pagedesc doesn't count if I'm a follow!
909     const SwPageDesc *pDesc = nullptr;
910     std::optional<sal_uInt16> oTmp;
911     SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( this );
912     if ( !pFlow || !pFlow->IsFollow() )
913     {
914         const SwFormatPageDesc &rFormatDesc = GetPageDescItem();
915         pDesc = rFormatDesc.GetPageDesc();
916         if( pDesc )
917         {
918             if( !pDesc->GetRightFormat() )
919                 oTmp = 2;
920             else if( !pDesc->GetLeftFormat() )
921                 oTmp = 1;
922             else if( rFormatDesc.GetNumOffset() )
923                 oTmp = rFormatDesc.GetNumOffset();
924         }
925     }
926 
927     // Does the Content bring a Pagedesc or do we need the
928     // virtual page number of the new layout leaf?
929     // PageDesc isn't allowed with Follows
930     const bool isRightPage = oTmp ? sw::IsRightPageByNumber(*mpRoot, *oTmp) : pNew->OnRightPage();
931     if ( !pDesc )
932         pDesc = pNew->FindPageDesc();
933 
934     bool bFirst = pNew->OnFirstPage();
935 
936     const SwFlowFrame *pNewFlow = pNew->FindFirstBodyContent();
937     // Did we find ourselves?
938     if( pNewFlow == pFlow )
939         pNewFlow = nullptr;
940     if ( pNewFlow && pNewFlow->GetFrame().IsInTab() )
941         pNewFlow = pNewFlow->GetFrame().FindTabFrame();
942     const SwPageDesc *pNewDesc= ( pNewFlow && !pNewFlow->IsFollow() )
943             ? pNewFlow->GetFrame().GetPageDescItem().GetPageDesc()
944             : nullptr;
945 
946     SAL_INFO( "sw.pageframe", "WrongPageDesc p: " << pNew << " phys: " << pNew->GetPhyPageNum() );
947     SAL_INFO( "sw.pageframe", "WrongPageDesc " << pNew->GetPageDesc() << " " << pDesc );
948     SAL_INFO( "sw.pageframe", "WrongPageDesc right: " << isRightPage
949               << " first: " << bFirst << " " << pNew->GetFormat() << " == "
950               << (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)) << " "
951               << (isRightPage ? pDesc->GetLeftFormat(bFirst) : pDesc->GetRightFormat(bFirst)) );
952 
953     return (pNew->GetPageDesc() != pDesc)   //  own desc ?
954         || (pNew->GetFormat() !=
955               (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)))
956         || (pNewDesc && pNewDesc == pDesc);
957 }
958 
959 /// Returns the next layout leaf in which we can move the frame.
GetNextLeaf(MakePageType eMakePage)960 SwLayoutFrame *SwFrame::GetNextLeaf( MakePageType eMakePage )
961 {
962     OSL_ENSURE( !IsInFootnote(), "GetNextLeaf(), don't call me for Footnote." );
963     OSL_ENSURE( !IsInSct(), "GetNextLeaf(), don't call me for Sections." );
964 
965     const bool bBody = IsInDocBody();  // If I'm coming from the DocBody,
966                                            // I want to end up in the body.
967 
968     // It doesn't make sense to insert pages, as we only want to search the
969     // chain.
970     if( IsInFly() )
971         eMakePage = MAKEPAGE_NONE;
972 
973     // For tables, we just take the big leap. A simple GetNext would
974     // iterate through the first cells and, in turn, all other cells.
975     SwLayoutFrame *pLayLeaf = nullptr;
976     if ( IsTabFrame() )
977     {
978         SwFrame *const pTmp = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
979         if ( pTmp )
980             pLayLeaf = pTmp->GetUpper();
981     }
982     if ( !pLayLeaf )
983         pLayLeaf = GetNextLayoutLeaf();
984 
985     SwLayoutFrame *pOldLayLeaf = nullptr;           // Make sure that we don't have to
986                                             // start searching from top when we
987                                             // have a freshly created page.
988     bool bNewPg = false;    // Only insert a new page once.
989 
990     while ( true )
991     {
992         if ( pLayLeaf )
993         {
994             // There's yet another LayoutFrame. Let's see if it's ready to host
995             // me as well.
996             // It only needs to be of the same kind like my starting point
997             // (DocBody or Footnote respectively)
998             if ( pLayLeaf->FindPageFrame()->IsFootnotePage() )
999             {   // If I ended up at the end note pages, we're done.
1000                 pLayLeaf = nullptr;
1001                 continue;
1002             }
1003             if ( (bBody && !pLayLeaf->IsInDocBody()) || pLayLeaf->IsInTab()
1004                  || pLayLeaf->IsInSct() )
1005             {
1006                 // They don't want me! Try again
1007                 pOldLayLeaf = pLayLeaf;
1008                 pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
1009                 continue;
1010             }
1011 
1012             // I'm wanted, therefore I'm done. However, it may still be that,
1013             // during a page break, the page type isn't the desired one. In that
1014             // case we have to insert a page of the correct type.
1015 
1016             if( !IsFlowFrame() && ( eMakePage == MAKEPAGE_NONE ||
1017                 eMakePage==MAKEPAGE_APPEND || eMakePage==MAKEPAGE_NOSECTION ) )
1018                 return pLayLeaf;
1019 
1020             SwPageFrame *pNew = pLayLeaf->FindPageFrame();
1021             const SwViewShell *pSh = getRootFrame()->GetCurrShell();
1022             // The pagedesc check does not make sense for frames in fly frames
1023             if ( pNew != FindPageFrame() && !bNewPg && !IsInFly() &&
1024                  // i#46683
1025                  // Do not consider page descriptions in browse mode (since
1026                  // MoveBwd ignored them)
1027                  !(pSh && pSh->GetViewOptions()->getBrowseMode() ) )
1028             {
1029                 if( WrongPageDesc( pNew ) )
1030                 {
1031                     SwFootnoteContFrame *pCont = pNew->FindFootnoteCont();
1032                     if( pCont )
1033                     {
1034                         // If the reference of the first footnote of this page
1035                         // lies before the page, we'd rather not insert a new page.
1036 
1037                         SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
1038                         if( pFootnote && pFootnote->GetRef() )
1039                         {
1040                             const sal_uInt16 nRefNum = pNew->GetPhyPageNum();
1041                             if( pFootnote->GetRef()->GetPhyPageNum() < nRefNum )
1042                                 break;
1043                         }
1044                     }
1045                     //Gotcha! The following page is wrong, therefore we need to
1046                     //insert a new one.
1047                     if ( eMakePage == MAKEPAGE_INSERT )
1048                     {
1049                         bNewPg = true;
1050 
1051                         SwPageFrame *pPg = pOldLayLeaf ?
1052                                     pOldLayLeaf->FindPageFrame() : nullptr;
1053                         if ( pPg && pPg->IsEmptyPage() )
1054                             // Don't insert behind. Insert before the EmptyPage.
1055                             pPg = static_cast<SwPageFrame*>(pPg->GetPrev());
1056 
1057                         if ( !pPg || pPg == pNew )
1058                             pPg = FindPageFrame();
1059 
1060                         InsertPage( pPg, false );
1061                         pLayLeaf = GetNextLayoutLeaf();
1062                         pOldLayLeaf = nullptr;
1063                         continue;
1064                     }
1065                     else
1066                         pLayLeaf = nullptr;
1067                 }
1068             }
1069             break;
1070         }
1071         else
1072         {
1073             // There's no other matching LayoutFrame, so we have to insert
1074             // a new page.
1075             if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
1076             {
1077                 InsertPage(
1078                     pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
1079                     false );
1080 
1081                 // And again from the start.
1082                 pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
1083             }
1084             else
1085                 break;
1086         }
1087     }
1088     return pLayLeaf;
1089 }
1090 
1091 /// Returns the previous layout leaf where we can move the frame.
GetPrevLeaf()1092 SwLayoutFrame *SwFrame::GetPrevLeaf()
1093 {
1094     OSL_ENSURE( !IsInFootnote(), "GetPrevLeaf(), don't call me for Footnote." );
1095 
1096     const bool bBody = IsInDocBody();  // If I'm coming from the DocBody,
1097                                            // I want to end up in the body.
1098     const bool bFly  = IsInFly();
1099 
1100     SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
1101     SwLayoutFrame *pPrevLeaf = nullptr;
1102 
1103     while ( pLayLeaf )
1104     {
1105         if ( pLayLeaf->IsInTab() ||     // Never go into tables.
1106              pLayLeaf->IsInSct() )      // Same goes for sections!
1107             pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1108         else if ( bBody && pLayLeaf->IsInDocBody() )
1109         {
1110             if ( pLayLeaf->Lower() )
1111                 break;
1112             pPrevLeaf = pLayLeaf;
1113             pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1114             if ( pLayLeaf )
1115                 SwFlowFrame::SetMoveBwdJump( true );
1116         }
1117         else if ( bFly )
1118             break;  //Contents in Flys should accept any layout leaf.
1119         else
1120             pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
1121     }
1122     return pLayLeaf ? pLayLeaf : pPrevLeaf;
1123 }
1124 
IsPrevObjMove() const1125 bool SwFlowFrame::IsPrevObjMove() const
1126 {
1127     // true:   The FlowFrame must respect the a border of the predecessor, also needs
1128     //         to insert a break if required.
1129 
1130     //!!!!!!!!!!!Hack!!!!!!!!!!!
1131     const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
1132     if( pSh && pSh->GetViewOptions()->getBrowseMode() )
1133         return false;
1134 
1135     SwFrame *pPre = m_rThis.FindPrev();
1136 
1137     if ( pPre && pPre->GetDrawObjs() )
1138     {
1139         OSL_ENSURE( SwFlowFrame::CastFlowFrame( pPre ), "new flowfrm?" );
1140         if( SwFlowFrame::CastFlowFrame( pPre )->IsAnFollow( this ) )
1141             return false;
1142         SwLayoutFrame* pPreUp = pPre->GetUpper();
1143         // If the upper is a SectionFrame, or a column of a SectionFrame, we're
1144         // allowed to protrude out of it.  However, we need to respect the
1145         // Upper of the SectionFrame.
1146         if( pPreUp->IsInSct() )
1147         {
1148             if( pPreUp->IsSctFrame() )
1149                 pPreUp = pPreUp->GetUpper();
1150             else if( pPreUp->IsColBodyFrame() &&
1151                      pPreUp->GetUpper()->GetUpper()->IsSctFrame() )
1152                 pPreUp = pPreUp->GetUpper()->GetUpper()->GetUpper();
1153         }
1154         // i#26945 - re-factoring
1155         // use <GetVertPosOrientFrame()> to determine, if object has followed the
1156         // text flow to the next layout frame
1157         for (SwAnchoredObject* pObj : *pPre->GetDrawObjs())
1158         {
1159 
1160             // Do not consider hidden objects
1161             // i#26945 - do not consider object, which
1162             // doesn't follow the text flow.
1163             if ( pObj->GetFrameFormat().GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
1164                                             pObj->GetDrawObj()->GetLayer() ) &&
1165                  pObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
1166             {
1167                 const SwLayoutFrame* pVertPosOrientFrame = pObj->GetVertPosOrientFrame();
1168                 if ( pVertPosOrientFrame &&
1169                      pPreUp != pVertPosOrientFrame &&
1170                      !pPreUp->IsAnLower( pVertPosOrientFrame ) )
1171                 {
1172                     return true;
1173                 }
1174             }
1175         }
1176     }
1177     return false;
1178 }
1179 
1180 /**
1181 |*      If there's a hard page break before the Frame AND there's a
1182 |*      predecessor on the same page, true is returned (we need to create a
1183 |*      new PageBreak). Otherwise, returns false.
1184 |*      If bAct is set to true, this function returns true if
1185 |*      there's a PageBreak.
1186 |*      Of course, we don't evaluate the hard page break for follows.
1187 |*      The page break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
1188 |*      predecessor (AFTER). If there's no predecessor on the page, we don't
1189 |*      need to think further.
1190 |*      Also, a page break (or the need for one) is also present if
1191 |*      the FrameFormat contains a PageDesc.
1192 |*      The implementation works only on ContentFrames! - the definition
1193 |*      of the predecessor is not clear for LayoutFrames.
1194 |*/
IsPageBreak(bool bAct) const1195 bool SwFlowFrame::IsPageBreak( bool bAct ) const
1196 {
1197     if ( !IsFollow() && m_rThis.IsInDocBody() &&
1198          ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) ) // i66968
1199     {
1200         const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
1201         if( pSh && pSh->GetViewOptions()->getBrowseMode() )
1202             return false;
1203 
1204         // Determine predecessor
1205         const SwFrame *pPrev = m_rThis.FindPrev();
1206         while ( pPrev && ( !pPrev->IsInDocBody() ||
1207                 ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
1208             pPrev = pPrev->FindPrev();
1209 
1210         if ( pPrev )
1211         {
1212             OSL_ENSURE( pPrev->IsInDocBody(), "IsPageBreak: Not in DocBody?" );
1213             if ( bAct )
1214             {   if ( m_rThis.FindPageFrame() == pPrev->FindPageFrame() )
1215                     return false;
1216             }
1217             else
1218             {   if ( m_rThis.FindPageFrame() != pPrev->FindPageFrame() )
1219                     return false;
1220             }
1221 
1222             //for compatibility, also break at column break if no columns exist
1223             const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1224             const bool bTreatSingleColumnBreakAsPageBreak = rIDSA.get(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK);
1225             const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
1226             if ( eBreak == SvxBreak::PageBefore ||
1227                  eBreak == SvxBreak::PageBoth ||
1228                  ( bTreatSingleColumnBreakAsPageBreak && eBreak == SvxBreak::ColumnBefore && !m_rThis.FindColFrame() ))
1229                 return true;
1230             else
1231             {
1232                 const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
1233                 if ( ePrB == SvxBreak::PageAfter ||
1234                      ePrB == SvxBreak::PageBoth  ||
1235                     m_rThis.GetPageDescItem().GetPageDesc())
1236                 {
1237                     return true;
1238                 }
1239             }
1240         }
1241     }
1242     return false;
1243 }
1244 
1245 /**
1246 |*      If there's a hard column break before the Frame AND there is
1247 |*      a predecessor in the same column, we return true (we need to create
1248 |*      a ColBreak). Otherwise, we return false.
1249 |*      If bAct is set to true, we return true if there's a ColBreak.
1250 |*      Of course, we don't evaluate the hard column break for follows.
1251 |*
1252 |*      The column break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
1253 |*      predecessor (AFTER). If there's no predecessor in the column, we don't
1254 |*      need to think further.
1255 |*      The implementation works only on ContentFrames! - the definition
1256 |*      of the predecessor is not clear for LayoutFrames.
1257 |*/
IsColBreak(bool bAct) const1258 bool SwFlowFrame::IsColBreak( bool bAct ) const
1259 {
1260     if ( !IsFollow() && (m_rThis.IsMoveable() || bAct) )
1261     {
1262         const SwFrame *pCol = m_rThis.FindColFrame();
1263         if ( pCol )
1264         {
1265             // Determine predecessor
1266             const SwFrame *pPrev = m_rThis.FindPrev();
1267             while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) ||
1268                    ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
1269                     pPrev = pPrev->FindPrev();
1270 
1271             if ( pPrev )
1272             {
1273                 if ( bAct )
1274                 {   if ( pCol == pPrev->FindColFrame() )
1275                         return false;
1276                 }
1277                 else
1278                 {   if ( pCol != pPrev->FindColFrame() )
1279                         return false;
1280                 }
1281 
1282                 const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
1283                 if ( eBreak == SvxBreak::ColumnBefore ||
1284                      eBreak == SvxBreak::ColumnBoth )
1285                     return true;
1286                 else
1287                 {
1288                     const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
1289                     if ( ePrB == SvxBreak::ColumnAfter ||
1290                          ePrB == SvxBreak::ColumnBoth )
1291                         return true;
1292                 }
1293             }
1294         }
1295     }
1296     return false;
1297 }
1298 
HasParaSpaceAtPages(bool bSct) const1299 bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const
1300 {
1301     if( m_rThis.IsInSct() )
1302     {
1303         const SwFrame* pTmp = m_rThis.GetUpper();
1304         while( pTmp )
1305         {
1306             if( pTmp->IsCellFrame() || pTmp->IsFlyFrame() ||
1307                 pTmp->IsFooterFrame() || pTmp->IsHeaderFrame() ||
1308                 ( pTmp->IsFootnoteFrame() && !static_cast<const SwFootnoteFrame*>(pTmp)->GetMaster() ) )
1309                 return true;
1310             if( pTmp->IsPageFrame() )
1311                 return !pTmp->GetPrev() || IsPageBreak(true);
1312             if( pTmp->IsColumnFrame() && pTmp->GetPrev() )
1313                 return IsColBreak( true );
1314             if( pTmp->IsSctFrame() && ( !bSct || pTmp->GetPrev() ) )
1315                 return false;
1316             pTmp = pTmp->GetUpper();
1317         }
1318         OSL_FAIL( "HasParaSpaceAtPages: Where's my page?" );
1319         return false;
1320     }
1321     if( !m_rThis.IsInDocBody() || ( m_rThis.IsInTab() && !m_rThis.IsTabFrame()) ||
1322         IsPageBreak( true ) || ( m_rThis.FindColFrame() && IsColBreak( true ) ) )
1323         return true;
1324     const SwFrame* pTmp = m_rThis.FindColFrame();
1325     if( pTmp )
1326     {
1327         if( pTmp->GetPrev() )
1328             return false;
1329     }
1330     else
1331         pTmp = &m_rThis;
1332     pTmp = pTmp->FindPageFrame();
1333     return pTmp && !pTmp->GetPrev();
1334 }
1335 
1336 /** helper method to determine previous frame for calculation of the
1337     upper space
1338 
1339      i#11860
1340 */
GetPrevFrameForUpperSpaceCalc_(const SwFrame * _pProposedPrevFrame) const1341 const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const
1342 {
1343     const SwFrame* pPrevFrame = _pProposedPrevFrame
1344                             ? _pProposedPrevFrame
1345                             : m_rThis.GetPrev();
1346 
1347     // Skip hidden paragraphs and empty sections
1348     while ( pPrevFrame &&
1349             ( ( pPrevFrame->IsTextFrame() &&
1350                 static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
1351               ( pPrevFrame->IsSctFrame() &&
1352                 !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
1353     {
1354         pPrevFrame = pPrevFrame->GetPrev();
1355     }
1356 
1357     // Special case: no direct previous frame is found but frame is in footnote
1358     // Search for a previous frame in previous footnote,
1359     // if frame isn't in a section, which is also in the footnote
1360     if ( !pPrevFrame && m_rThis.IsInFootnote() &&
1361          ( m_rThis.IsSctFrame() ||
1362            !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote() ) )
1363     {
1364         const SwFootnoteFrame* pPrevFootnoteFrame =
1365                 static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev());
1366         if ( pPrevFootnoteFrame )
1367         {
1368             pPrevFrame = pPrevFootnoteFrame->GetLastLower();
1369 
1370             // Skip hidden paragraphs and empty sections
1371             while ( pPrevFrame &&
1372                     ( ( pPrevFrame->IsTextFrame() &&
1373                         static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
1374                       ( pPrevFrame->IsSctFrame() &&
1375                         !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
1376             {
1377                 pPrevFrame = pPrevFrame->GetPrev();
1378             }
1379         }
1380     }
1381     // Special case: found previous frame is a section
1382     // Search for the last content in the section
1383     if( pPrevFrame && pPrevFrame->IsSctFrame() )
1384     {
1385         const SwSectionFrame* pPrevSectFrame =
1386                                     static_cast<const SwSectionFrame*>(pPrevFrame);
1387         pPrevFrame = pPrevSectFrame->FindLastContent();
1388         // If the last content is in a table _inside_ the section,
1389         // take the table herself.
1390         // Correction: Check directly, if table is inside table, instead of indirectly
1391         // by checking, if section isn't inside a table
1392         if ( pPrevFrame && pPrevFrame->IsInTab() )
1393         {
1394             const SwTabFrame* pTableFrame = pPrevFrame->FindTabFrame();
1395             if ( pPrevSectFrame->IsAnLower( pTableFrame ) )
1396             {
1397                 pPrevFrame = pTableFrame;
1398             }
1399         }
1400         // Correction: skip hidden text frames
1401         while ( pPrevFrame &&
1402                 pPrevFrame->IsTextFrame() &&
1403                 static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() )
1404         {
1405             pPrevFrame = pPrevFrame->GetPrev();
1406         }
1407     }
1408 
1409     return pPrevFrame;
1410 }
1411 
1412 /// Compare styles attached to these text frames.
lcl_IdenticalStyles(const SwFrame * pPrevFrame,const SwFrame * pFrame)1413 static bool lcl_IdenticalStyles(const SwFrame* pPrevFrame, const SwFrame* pFrame)
1414 {
1415     SwTextFormatColl *pPrevFormatColl = nullptr;
1416     if (pPrevFrame && pPrevFrame->IsTextFrame())
1417     {
1418         const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pPrevFrame );
1419         pPrevFormatColl = dynamic_cast<SwTextFormatColl*>(
1420             pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
1421     }
1422 
1423     bool bIdenticalStyles = false;
1424     if (pFrame && pFrame->IsTextFrame())
1425     {
1426         const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pFrame );
1427         SwTextFormatColl *const pFormatColl = dynamic_cast<SwTextFormatColl*>(
1428             pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
1429         bIdenticalStyles = pPrevFormatColl == pFormatColl;
1430     }
1431     return bIdenticalStyles;
1432 }
1433 
lcl_getContextualSpacing(const SwFrame * pPrevFrame)1434 static bool lcl_getContextualSpacing(const SwFrame* pPrevFrame)
1435 {
1436     bool bRet;
1437     auto pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pPrevFrame);
1438     const SwBorderAttrs *pAttrs = pAccess->Get();
1439 
1440     bRet = pAttrs->GetULSpace().GetContext();
1441 
1442     return bRet;
1443 }
1444 
1445 
CalcUpperSpace(const SwBorderAttrs * pAttrs,const SwFrame * pPr,const bool _bConsiderGrid) const1446 SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
1447                                    const SwFrame* pPr,
1448                                    const bool _bConsiderGrid ) const
1449 {
1450     const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_( pPr );
1451 
1452     std::unique_ptr<SwBorderAttrAccess> pAccess;
1453     SwFrame* pOwn;
1454     if( !pAttrs )
1455     {
1456         if( m_rThis.IsSctFrame() )
1457         {
1458             SwSectionFrame* pFoll = &static_cast<SwSectionFrame&>(m_rThis);
1459             do
1460                 pOwn = pFoll->ContainsAny();
1461             while( !pOwn && nullptr != ( pFoll = pFoll->GetFollow() ) );
1462             if( !pOwn )
1463                 return 0;
1464         }
1465         else
1466             pOwn = &m_rThis;
1467         pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pOwn);
1468         pAttrs = pAccess->Get();
1469     }
1470     else
1471     {
1472         pOwn = &m_rThis;
1473     }
1474     SwTwips nUpper = 0;
1475 
1476     {
1477         const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1478         if( pPrevFrame )
1479         {
1480             const bool bUseFormerLineSpacing = rIDSA.get(DocumentSettingId::OLD_LINE_SPACING);
1481             const bool bContextualSpacingThis = pAttrs->GetULSpace().GetContext();
1482             const bool bContextualSpacingPrev = lcl_getContextualSpacing(pPrevFrame);
1483             bool bIdenticalStyles = lcl_IdenticalStyles(pPrevFrame, &m_rThis);
1484 
1485             const bool bContextualSpacing = bContextualSpacingThis
1486                                          && bContextualSpacingPrev
1487                                          && bIdenticalStyles;
1488 
1489             // tdf#125893 always ignore own top margin setting of the actual paragraph
1490             // with contextual spacing, if the previous paragraph is identical
1491             const bool bHalfContextualSpacing = !bContextualSpacing
1492                                          && bContextualSpacingThis
1493                                          && !bContextualSpacingPrev
1494                                          && bIdenticalStyles;
1495 
1496             // tdf#134463 always ignore own bottom margin setting of the previous paragraph
1497             // with contextual spacing, if the actual paragraph is identical
1498             const bool bHalfContextualSpacingPrev = !bContextualSpacing
1499                                          && !bContextualSpacingThis
1500                                          && bContextualSpacingPrev
1501                                          && bIdenticalStyles;
1502 
1503             // i#11860 - use new method to determine needed spacing
1504             // values of found previous frame and use these values.
1505             SwTwips nPrevLowerSpace = 0;
1506             SwTwips nPrevLineSpacing = 0;
1507             // i#102458
1508             bool bPrevLineSpacingProportional = false;
1509             GetSpacingValuesOfFrame( (*pPrevFrame),
1510                                    nPrevLowerSpace, nPrevLineSpacing,
1511                                    bPrevLineSpacingProportional,
1512                                    bIdenticalStyles);
1513             if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) )
1514             {
1515                 // FIXME: apply bHalfContextualSpacing for better portability?
1516                 nUpper = bContextualSpacing ? 0 : nPrevLowerSpace + pAttrs->GetULSpace().GetUpper();
1517                 SwTwips nAdd = nPrevLineSpacing;
1518                 // i#11859 - consideration of the line spacing
1519                 //      for the upper spacing of a text frame
1520                 if ( bUseFormerLineSpacing )
1521                 {
1522                     // former consideration
1523                     if ( pOwn->IsTextFrame() )
1524                     {
1525                         nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
1526                     }
1527                     nUpper += nAdd;
1528                 }
1529                 else
1530                 {
1531                     // new consideration:
1532                     //      Only the proportional line spacing of the previous
1533                     //      text frame is considered for the upper spacing and
1534                     //      the line spacing values are add up instead of
1535                     //      building its maximum.
1536                     if ( pOwn->IsTextFrame() )
1537                     {
1538                         // i#102458
1539                         // Correction:
1540                         // A proportional line spacing of the previous text frame
1541                         // is added up to an own leading line spacing.
1542                         // Otherwise, the maximum of the leading line spacing
1543                         // of the previous text frame and the own leading line
1544                         // spacing is built.
1545                         if ( bPrevLineSpacingProportional )
1546                         {
1547                             nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
1548                         }
1549                         else
1550                         {
1551                             nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
1552                         }
1553                     }
1554                     nUpper += nAdd;
1555                 }
1556             }
1557             else
1558             {
1559                 nUpper = bContextualSpacing ? 0 : std::max(
1560                                 bHalfContextualSpacingPrev ? 0 : static_cast<tools::Long>(nPrevLowerSpace),
1561                                 bHalfContextualSpacing     ? 0 : static_cast<tools::Long>(pAttrs->GetULSpace().GetUpper()) );
1562 
1563                 // i#11859 - consideration of the line spacing
1564                 //      for the upper spacing of a text frame
1565                 if ( bUseFormerLineSpacing )
1566                 {
1567                     // former consideration
1568                     if ( pOwn->IsTextFrame() )
1569                         nUpper = std::max( nUpper, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
1570                     if ( nPrevLineSpacing != 0 )
1571                     {
1572                         nUpper = std::max( nUpper, nPrevLineSpacing );
1573                     }
1574                 }
1575                 else
1576                 {
1577                     // new consideration:
1578                     //      Only the proportional line spacing of the previous
1579                     //      text frame is considered for the upper spacing and
1580                     //      the line spacing values are add up and added to
1581                     //      the paragraph spacing instead of building the
1582                     //      maximum of the line spacings and the paragraph spacing.
1583                     SwTwips nAdd = nPrevLineSpacing;
1584                     if ( pOwn->IsTextFrame() )
1585                     {
1586                         // i#102458
1587                         // Correction:
1588                         // A proportional line spacing of the previous text frame
1589                         // is added up to an own leading line spacing.
1590                         // Otherwise, the maximum of the leading line spacing
1591                         // of the previous text frame and the own leading line
1592                         // spacing is built.
1593                         if ( bPrevLineSpacingProportional )
1594                         {
1595                             nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
1596                         }
1597                         else
1598                         {
1599                             nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
1600                         }
1601                     }
1602                     nUpper += nAdd;
1603                 }
1604             }
1605         }
1606         else if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) &&
1607                   CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) )
1608         {
1609             nUpper = pAttrs->GetULSpace().GetUpper();
1610         }
1611     }
1612 
1613     // i#25029 - pass previous frame <pPrevFrame>
1614     // to method <GetTopLine(..)>, if parameter <pPr> is set.
1615     // Note: parameter <pPr> is set, if method is called from <SwTextFrame::WouldFit(..)>
1616     nUpper += pAttrs->GetTopLine( m_rThis, (pPr ? pPrevFrame : nullptr) );
1617 
1618     // i#11860 - consider value of new parameter <_bConsiderGrid>
1619     // and use new method <GetUpperSpaceAmountConsideredForPageGrid(..)>
1620 
1621     //consider grid in square page mode
1622     if ( _bConsiderGrid && m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode() )
1623     {
1624         nUpper += GetUpperSpaceAmountConsideredForPageGrid_( nUpper );
1625     }
1626     return nUpper;
1627 }
1628 
1629 /** method to determine the upper space amount, which is considered for
1630     the page grid
1631 
1632     i#11860
1633     Precondition: Position of frame is valid.
1634 */
GetUpperSpaceAmountConsideredForPageGrid_(const SwTwips _nUpperSpaceWithoutGrid) const1635 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid_(
1636                             const SwTwips _nUpperSpaceWithoutGrid ) const
1637 {
1638     SwTwips nUpperSpaceAmountConsideredForPageGrid = 0;
1639 
1640     if ( m_rThis.IsInDocBody() && m_rThis.GetAttrSet()->GetParaGrid().GetValue() )
1641     {
1642         const SwPageFrame* pPageFrame = m_rThis.FindPageFrame();
1643         SwTextGridItem const*const pGrid(GetGridItem(pPageFrame));
1644         if( pGrid )
1645         {
1646             const SwFrame* pBodyFrame = pPageFrame->FindBodyCont();
1647             if ( pBodyFrame )
1648             {
1649                 const tools::Long nGridLineHeight =
1650                         pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
1651 
1652                 SwRectFnSet aRectFnSet(&m_rThis);
1653                 const SwTwips nBodyPrtTop = aRectFnSet.GetPrtTop(*pBodyFrame);
1654                 const SwTwips nProposedPrtTop =
1655                         aRectFnSet.YInc( aRectFnSet.GetTop(m_rThis.getFrameArea()),
1656                                            _nUpperSpaceWithoutGrid );
1657 
1658                 const SwTwips nSpaceAbovePrtTop =
1659                         aRectFnSet.YDiff( nProposedPrtTop, nBodyPrtTop );
1660                 const SwTwips nSpaceOfCompleteLinesAbove =
1661                         nGridLineHeight * ( nSpaceAbovePrtTop / nGridLineHeight );
1662                 SwTwips nNewPrtTop =
1663                         aRectFnSet.YInc( nBodyPrtTop, nSpaceOfCompleteLinesAbove );
1664                 if ( aRectFnSet.YDiff( nProposedPrtTop, nNewPrtTop ) > 0 )
1665                 {
1666                     nNewPrtTop = aRectFnSet.YInc( nNewPrtTop, nGridLineHeight );
1667                 }
1668 
1669                 const SwTwips nNewUpperSpace =
1670                         aRectFnSet.YDiff( nNewPrtTop,
1671                                             aRectFnSet.GetTop(m_rThis.getFrameArea()) );
1672 
1673                 nUpperSpaceAmountConsideredForPageGrid =
1674                         nNewUpperSpace - _nUpperSpaceWithoutGrid;
1675 
1676                 OSL_ENSURE( nUpperSpaceAmountConsideredForPageGrid >= 0,
1677                         "<SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid(..)> - negative space considered for page grid!" );
1678             }
1679         }
1680     }
1681     return nUpperSpaceAmountConsideredForPageGrid;
1682 }
1683 
1684 /** method to determine the upper space amount, which is considered for
1685     the previous frame
1686 
1687     i#11860
1688 */
GetUpperSpaceAmountConsideredForPrevFrame() const1689 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrame() const
1690 {
1691     SwTwips nUpperSpaceAmountOfPrevFrame = 0;
1692 
1693     const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_();
1694     if ( pPrevFrame )
1695     {
1696         SwTwips nPrevLowerSpace = 0;
1697         SwTwips nPrevLineSpacing = 0;
1698         // i#102458
1699         bool bDummy = false;
1700         GetSpacingValuesOfFrame( (*pPrevFrame), nPrevLowerSpace, nPrevLineSpacing, bDummy, lcl_IdenticalStyles(pPrevFrame, &m_rThis));
1701         if ( nPrevLowerSpace > 0 || nPrevLineSpacing > 0 )
1702         {
1703             const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
1704             if (  rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
1705                  !rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) )
1706             {
1707                 nUpperSpaceAmountOfPrevFrame = nPrevLowerSpace + nPrevLineSpacing;
1708             }
1709             else
1710             {
1711                 nUpperSpaceAmountOfPrevFrame = std::max( nPrevLowerSpace, nPrevLineSpacing );
1712             }
1713         }
1714     }
1715 
1716     return nUpperSpaceAmountOfPrevFrame;
1717 }
1718 
1719 /** method to determine the upper space amount, which is considered for
1720     the previous frame and the page grid, if option 'Use former object
1721     positioning' is OFF
1722 
1723     i#11860
1724 */
GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const1725 SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const
1726 {
1727     SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid = 0;
1728 
1729     if ( !m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
1730     {
1731         nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
1732             GetUpperSpaceAmountConsideredForPrevFrame() +
1733             ( m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode()
1734               ? GetUpperSpaceAmountConsideredForPageGrid_( CalcUpperSpace( nullptr, nullptr, false ) )
1735               : 0 );
1736     }
1737 
1738     return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
1739 }
1740 
1741 // Calculation of lower space
1742 
CalcLowerSpace(const SwBorderAttrs * _pAttrs) const1743 SwTwips SwFlowFrame::CalcLowerSpace( const SwBorderAttrs* _pAttrs ) const
1744 {
1745     SwTwips nLowerSpace = 0;
1746 
1747     std::unique_ptr<SwBorderAttrAccess> pAttrAccess;
1748     if ( !_pAttrs )
1749     {
1750         pAttrAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), &m_rThis);
1751         _pAttrs = pAttrAccess->Get();
1752     }
1753 
1754     bool bCommonBorder = true;
1755     if ( m_rThis.IsInSct() && m_rThis.GetUpper()->IsColBodyFrame() )
1756     {
1757         const SwSectionFrame* pSectFrame = m_rThis.FindSctFrame();
1758         bCommonBorder = pSectFrame->GetFormat()->GetBalancedColumns().GetValue();
1759     }
1760     nLowerSpace = bCommonBorder ?
1761                   _pAttrs->GetBottomLine( m_rThis ) :
1762                   _pAttrs->CalcBottomLine();
1763 
1764     // i#26250
1765     // - correct consideration of table frames
1766     // - use new method <CalcAddLowerSpaceAsLastInTableCell(..)>
1767     if ( ( ( m_rThis.IsTabFrame() && m_rThis.GetUpper()->IsInTab() ) ||
1768            // No lower spacing, if frame has a follow
1769            ( m_rThis.IsInTab() && !GetFollow() ) ) &&
1770          !m_rThis.GetIndNext() )
1771     {
1772         nLowerSpace += CalcAddLowerSpaceAsLastInTableCell( _pAttrs );
1773     }
1774 
1775     // tdf#128195 Consider para spacing below last paragraph in header
1776     bool bHasSpacingBelowPara = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(
1777         DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA);
1778     if (bHasSpacingBelowPara && !m_rThis.IsInFly() && m_rThis.FindFooterOrHeader() && !GetFollow()
1779         && !m_rThis.GetIndNext())
1780         nLowerSpace += _pAttrs->GetULSpace().GetLower() + _pAttrs->CalcLineSpacing();
1781 
1782     return nLowerSpace;
1783 }
1784 
1785 /** calculation of the additional space to be considered, if flow frame
1786     is the last inside a table cell
1787 
1788     i#26250
1789 */
CalcAddLowerSpaceAsLastInTableCell(const SwBorderAttrs * _pAttrs) const1790 SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell(
1791                                             const SwBorderAttrs* _pAttrs ) const
1792 {
1793     SwTwips nAdditionalLowerSpace = 0;
1794 
1795     IDocumentSettingAccess const& rIDSA(m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess());
1796     if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
1797     {
1798         const SwFrame* pFrame = &m_rThis;
1799         if ( pFrame->IsSctFrame() )
1800         {
1801             const SwSectionFrame* pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
1802             pFrame = pSectFrame->FindLastContent();
1803             if ( pFrame && pFrame->IsInTab() )
1804             {
1805                 const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
1806                 if ( pSectFrame->IsAnLower( pTableFrame ) )
1807                 {
1808                     pFrame = pTableFrame;
1809                 }
1810             }
1811         }
1812 
1813         std::unique_ptr<SwBorderAttrAccess> pAttrAccess;
1814         if (pFrame && (!_pAttrs || pFrame != &m_rThis))
1815         {
1816             pAttrAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pFrame);
1817             _pAttrs = pAttrAccess->Get();
1818         }
1819 
1820         if (_pAttrs)
1821         {
1822             nAdditionalLowerSpace += _pAttrs->GetULSpace().GetLower();
1823 
1824             if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
1825             {
1826                 nAdditionalLowerSpace += _pAttrs->CalcLineSpacing();
1827             }
1828         }
1829     }
1830 
1831     return nAdditionalLowerSpace;
1832 }
1833 
1834 /// Moves the Frame forward if it seems necessary regarding the current conditions and attributes.
CheckMoveFwd(bool & rbMakePage,bool bKeep,bool bIgnoreMyOwnKeepValue)1835 bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue )
1836 {
1837     const SwFrame* pNxt = m_rThis.GetIndNext();
1838 
1839     if ( bKeep && //!bMovedBwd &&
1840          ( !pNxt || ( pNxt->IsTextFrame() && static_cast<const SwTextFrame*>(pNxt)->IsEmptyMaster() ) ) &&
1841          ( nullptr != (pNxt = m_rThis.FindNext()) ) && IsKeepFwdMoveAllowed(bIgnoreMyOwnKeepValue) )
1842     {
1843         if( pNxt->IsSctFrame() )
1844         {   // Don't get fooled by empty SectionFrames
1845             const SwFrame* pTmp = nullptr;
1846             while( pNxt && pNxt->IsSctFrame() &&
1847                    ( !static_cast<const SwSectionFrame*>(pNxt)->GetSection() ||
1848                      nullptr == ( pTmp = static_cast<const SwSectionFrame*>(pNxt)->ContainsAny() ) ) )
1849             {
1850                 pNxt = pNxt->FindNext();
1851                 pTmp = nullptr;
1852             }
1853             if( pTmp )
1854                 pNxt = pTmp; // the content of the next notempty sectionfrm
1855         }
1856         if( pNxt && pNxt->isFrameAreaPositionValid() )
1857         {
1858             bool bMove = false;
1859             const SwSectionFrame *pSct = m_rThis.FindSctFrame();
1860             if( pSct && !pSct->isFrameAreaSizeValid() )
1861             {
1862                 const SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
1863                 if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
1864                     bMove = true;
1865             }
1866             else
1867                 bMove = true;
1868             if( bMove )
1869             {
1870                 //Keep together with the following frame
1871                 MoveFwd( rbMakePage, false );
1872                 return true;
1873             }
1874         }
1875     }
1876 
1877     bool bMovedFwd = false;
1878 
1879     if ( m_rThis.GetIndPrev() )
1880     {
1881         if ( IsPrevObjMove() ) // Should we care about objects of the Prev?
1882         {
1883             bMovedFwd = true;
1884             if ( !MoveFwd( rbMakePage, false ) )
1885                 rbMakePage = false;
1886         }
1887         else
1888         {
1889             if ( IsPageBreak( false ) )
1890             {
1891                 while ( MoveFwd( rbMakePage, true ) )
1892                         /* do nothing */;
1893                 rbMakePage = false;
1894                 bMovedFwd = true;
1895             }
1896             else if ( IsColBreak ( false ) )
1897             {
1898                 const SwPageFrame *pPage = m_rThis.FindPageFrame();
1899                 SwFrame *pCol = m_rThis.FindColFrame();
1900                 do
1901                 {   MoveFwd( rbMakePage, false );
1902                     SwFrame *pTmp = m_rThis.FindColFrame();
1903                     if( pTmp != pCol )
1904                     {
1905                         bMovedFwd = true;
1906                         pCol = pTmp;
1907                     }
1908                     else
1909                         break;
1910                 } while ( IsColBreak( false ) );
1911                 if ( pPage != m_rThis.FindPageFrame() )
1912                     rbMakePage = false;
1913             }
1914         }
1915     }
1916     return bMovedFwd;
1917 }
1918 
ForbiddenForFootnoteCntFwd() const1919 bool SwFlowFrame::ForbiddenForFootnoteCntFwd() const
1920 {
1921     return m_rThis.IsTabFrame() || m_rThis.IsInTab();
1922 }
1923 
1924 /// Return value guarantees that a new page was not created,
1925 /// although false does not NECESSARILY indicate that a new page was created.
1926 /// Either false or true(MoveFootnoteCntFwd) can be returned if no changes were made
MoveFwd(bool bMakePage,bool bPageBreak,bool bMoveAlways)1927 bool SwFlowFrame::MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways )
1928 {
1929 //!!!!MoveFootnoteCntFwd might need to be updated as well.
1930     SwFootnoteBossFrame *pOldBoss = m_rThis.FindFootnoteBossFrame();
1931     if (m_rThis.IsInFootnote())
1932     {
1933         assert(!ForbiddenForFootnoteCntFwd()); // prevented by IsMoveable()
1934         if (!m_rThis.IsContentFrame() || !pOldBoss)
1935         {
1936             SAL_WARN("sw.core", "Tables in footnotes are not truly supported");
1937             return false;
1938         }
1939         return static_cast<SwContentFrame&>(m_rThis).MoveFootnoteCntFwd( bMakePage, pOldBoss );
1940     }
1941 
1942     if( !IsFwdMoveAllowed() && !bMoveAlways )
1943     {
1944         bool bNoFwd = true;
1945         if( m_rThis.IsInSct() )
1946         {
1947             SwFootnoteBossFrame* pBoss = m_rThis.FindFootnoteBossFrame();
1948             bNoFwd = !pBoss->IsInSct() || ( !pBoss->Lower()->GetNext() &&
1949                      !pBoss->GetPrev() );
1950         }
1951 
1952         // Allow the MoveFwd even if we do not have an IndPrev in these cases:
1953         if ( m_rThis.IsInTab() &&
1954             ( !m_rThis.IsTabFrame() ||
1955               m_rThis.GetUpper()->IsInTab() ) &&
1956              nullptr != m_rThis.GetNextCellLeaf() )
1957         {
1958             bNoFwd = false;
1959         }
1960 
1961         if( bNoFwd )
1962         {
1963             // It's allowed to move PageBreaks if the Frame isn't the first
1964             // one on the page.
1965             if ( !bPageBreak )
1966                 return false;
1967 
1968             const SwFrame *pCol = m_rThis.FindColFrame();
1969             if ( !pCol || !pCol->GetPrev() )
1970                 return false;
1971         }
1972     }
1973 
1974     std::unique_ptr<SwFrameDeleteGuard> xDeleteGuard(bMakePage ? new SwFrameDeleteGuard(pOldBoss) : nullptr);
1975 
1976     bool bSamePage = true;
1977     SwLayoutFrame *pNewUpper =
1978             m_rThis.GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
1979 
1980     if ( pNewUpper )
1981     {
1982         PROTOCOL_ENTER( &m_rThis, PROT::MoveFwd, DbgAction::NONE, nullptr );
1983         SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
1984         // We move ourself and all the direct successors before the
1985         // first ContentFrame below the new Upper.
1986 
1987         // If our NewUpper lies in a SectionFrame, we need to make sure
1988         // that it won't destroy itself in Calc.
1989         SwSectionFrame* pSect = pNewUpper->FindSctFrame();
1990         if( pSect )
1991         {
1992             // If we only switch column within our SectionFrame, we better don't
1993             // call Calc, as this would format the SectionFrame, which in turn would
1994             // call us again, etc.
1995             if( pSect != m_rThis.FindSctFrame() )
1996             {
1997                 bool bUnlock = !pSect->IsColLocked();
1998                 pSect->ColLock();
1999                 pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2000                 if( bUnlock )
2001                     pSect->ColUnlock();
2002             }
2003         }
2004         // Do not calculate split cell frames.
2005         else if ( !pNewUpper->IsCellFrame() || pNewUpper->Lower() )
2006             pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2007 
2008         SwFootnoteBossFrame *pNewBoss = pNewUpper->FindFootnoteBossFrame();
2009         bool bBossChg = pNewBoss != pOldBoss;
2010         pNewBoss = pNewBoss->FindFootnoteBossFrame( true );
2011         pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
2012         SwPageFrame* pNewPage = pOldPage;
2013 
2014         xDeleteGuard.reset();
2015 
2016         // First, we move the footnotes.
2017         bool bFootnoteMoved = false;
2018 
2019         // i#26831
2020         // If pSect has just been created, the printing area of pSect has
2021         // been calculated based on the first content of its follow.
2022         // In this case we prefer to call a SimpleFormat for this new
2023         // section after we inserted the contents. Otherwise the section
2024         // frame will invalidate its lowers, if its printing area changes
2025         // in SwSectionFrame::Format, which can cause loops.
2026         const bool bForceSimpleFormat = pSect && pSect->HasFollow() &&
2027                                        !pSect->ContainsAny();
2028 
2029         if ( pNewBoss != pOldBoss )
2030         {
2031             pNewPage = pNewBoss->FindPageFrame();
2032             bSamePage = pNewPage == pOldPage;
2033             // Set deadline, so the footnotes don't think up
2034             // silly things...
2035             SwRectFnSet aRectFnSet(pOldBoss);
2036             SwSaveFootnoteHeight aHeight( pOldBoss,
2037                 aRectFnSet.GetBottom(pOldBoss->getFrameArea()) );
2038             SwContentFrame* pStart = m_rThis.IsContentFrame() ?
2039                 static_cast<SwContentFrame*>(&m_rThis) : static_cast<SwLayoutFrame&>(m_rThis).ContainsContent();
2040             OSL_ENSURE( pStart || ( m_rThis.IsTabFrame() && !static_cast<SwTabFrame&>(m_rThis).Lower() ),
2041                     "MoveFwd: Missing Content" );
2042             SwLayoutFrame* pBody = pStart ? ( pStart->IsTextFrame() ?
2043                 const_cast<SwBodyFrame *>(static_cast<SwTextFrame*>(pStart)->FindBodyFrame()) : nullptr ) : nullptr;
2044             if( pBody )
2045                 bFootnoteMoved = pBody->MoveLowerFootnotes( pStart, pOldBoss, pNewBoss,
2046                                                   false);
2047         }
2048         // It's possible when dealing with SectionFrames that we have been moved
2049         // by pNewUpper->Calc(), for instance into the pNewUpper.
2050         // MoveSubTree or PasteTree respectively is not prepared to handle such a
2051         // situation.
2052         if( pNewUpper != m_rThis.GetUpper() )
2053         {
2054             // i#27145
2055             SwSectionFrame* pOldSct = nullptr;
2056             if ( m_rThis.GetUpper()->IsSctFrame() )
2057             {
2058                 pOldSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
2059             }
2060 
2061             MoveSubTree( pNewUpper, pNewUpper->Lower() );
2062 
2063             // i#27145
2064             if ( pOldSct && pOldSct->GetSection() )
2065             {
2066                 // Prevent loops by setting the new height at
2067                 // the section frame if footnotes have been moved.
2068                 // Otherwise the call of SwLayNotify::~SwLayNotify() for
2069                 // the (invalid) section frame will invalidate the first
2070                 // lower of its follow, because it grows due to the removed
2071                 // footnotes.
2072                 // Note: If pOldSct has become empty during MoveSubTree, it
2073                 // has already been scheduled for removal. No SimpleFormat
2074                 // for these.
2075                 pOldSct->SimpleFormat();
2076             }
2077 
2078             // i#26831
2079             if ( bForceSimpleFormat )
2080             {
2081                 pSect->SimpleFormat();
2082             }
2083 
2084             if ( bFootnoteMoved && !bSamePage )
2085             {
2086                 pOldPage->UpdateFootnoteNum();
2087                 pNewPage->UpdateFootnoteNum();
2088             }
2089 
2090             if( bBossChg )
2091             {
2092                 m_rThis.Prepare( PrepareHint::BossChanged, nullptr, false );
2093                 if( !bSamePage )
2094                 {
2095                     SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2096                     if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
2097                         pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true);  // Will be done by CalcLayout() later on!
2098 
2099                     pNewPage->InvalidateSpelling();
2100                     pNewPage->InvalidateSmartTags();
2101                     pNewPage->InvalidateAutoCompleteWords();
2102                     pNewPage->InvalidateWordCount();
2103                 }
2104             }
2105         }
2106         // No <CheckPageDesc(..)> in online layout
2107         const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2108 
2109         if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
2110         {
2111             // i#106452
2112             // check page description not only in situation with sections.
2113             if ( !bSamePage &&
2114                  ( m_rThis.GetPageDescItem().GetPageDesc() ||
2115                    pOldPage->GetPageDesc()->GetFollow() != pNewPage->GetPageDesc() ) )
2116             {
2117                 SwFrame::CheckPageDescs( pNewPage, false );
2118             }
2119         }
2120     }
2121     return bSamePage;
2122 }
2123 
2124 /** Return value tells whether any changes have been made.
2125  *  If true, the frame has moved backwards to an earlier column/section/frame/page etc.
2126  *
2127  * @note This should be called by derived classes.
2128  * @note The actual moving must be implemented in the subclasses via Cut()/Paste().
2129  */
MoveBwd(bool & rbReformat)2130 bool SwFlowFrame::MoveBwd( bool &rbReformat )
2131 {
2132     SwFlowFrame::SetMoveBwdJump( false );
2133 
2134     SwFootnoteFrame* pFootnote = m_rThis.FindFootnoteFrame();
2135     if ( pFootnote && pFootnote->IsBackMoveLocked() )
2136         return false;
2137 
2138     // Text frames, which are directly inside
2139     // tables aren't allowed to move backward.
2140     if ( m_rThis.IsTextFrame() && m_rThis.IsInTab() )
2141     {
2142         const SwLayoutFrame* pUpperFrame = m_rThis.GetUpper();
2143         while ( pUpperFrame )
2144         {
2145             if ( pUpperFrame->IsTabFrame() || pUpperFrame->IsRowFrame() )
2146             {
2147                 return false;
2148             }
2149             // If the text frame is a follow-section-in-table, that can move
2150             // backward as well.
2151             bool bIsFollowSection = pUpperFrame->IsSctFrame() && static_cast<const SwSectionFrame*>(pUpperFrame)->GetPrecede();
2152 
2153             // If the text frame is a follow-in-table, that can move
2154             // backward as well.
2155             bool bIsFollow = const_cast<SwLayoutFrame*>(pUpperFrame)->GetPrevCellLeaf();
2156 
2157             if ( ( pUpperFrame->IsColumnFrame() && pUpperFrame->IsInSct() ) || bIsFollowSection || bIsFollow )
2158             {
2159                 break;
2160             }
2161             pUpperFrame = pUpperFrame->GetUpper();
2162         }
2163     }
2164 
2165     SwFootnoteBossFrame * pOldBoss = m_rThis.FindFootnoteBossFrame();
2166     if (!pOldBoss)
2167         return false;
2168 
2169     SwPageFrame * const pOldPage = pOldBoss->FindPageFrame();
2170     SwLayoutFrame *pNewUpper = nullptr;
2171     bool bCheckPageDescs = false;
2172     bool bCheckPageDescOfNextPage = false;
2173 
2174     if ( pFootnote )
2175     {
2176         // If the footnote already sits on the same page/column as the reference,
2177         // we can't flow back. The breaks don't need to be checked for footnotes.
2178 
2179         // i#37084 FindLastContent does not necessarily
2180         // have to have a result != 0
2181         SwFrame* pRef = nullptr;
2182         const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
2183         const IDocumentSettingAccess& rSettings
2184             = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
2185         if( bEndnote && pFootnote->IsInSct() )
2186         {
2187             SwSectionFrame* pSect = pFootnote->FindSctFrame();
2188             if( pSect->IsEndnAtEnd() )
2189                 // Endnotes at the end of the section.
2190                 pRef = pSect->FindLastContent( SwFindMode::LastCnt );
2191         }
2192         else if (bEndnote && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
2193         {
2194             // Endnotes at the end of the document.
2195             SwPageFrame* pPage = m_rThis.getRootFrame()->GetLastPage();
2196             pRef = pPage->FindLastBodyContent();
2197         }
2198         if( !pRef )
2199             // Endnotes on a separate page.
2200             pRef = pFootnote->GetRef();
2201 
2202         OSL_ENSURE( pRef, "MoveBwd: Endnote for an empty section?" );
2203 
2204         if( !bEndnote )
2205             pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
2206         SwFootnoteBossFrame *pRefBoss = pRef->FindFootnoteBossFrame( !bEndnote );
2207         if ( pOldBoss != pRefBoss &&
2208 
2209              ( !bEndnote ||
2210                pRefBoss->IsBefore( pOldBoss ) )
2211            )
2212             pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false );
2213     }
2214     else if ( IsPageBreak( true ) ) // Do we have to respect a PageBreak?
2215     {
2216         // If the previous page doesn't have a Frame in the body,
2217         // flowing back makes sense despite the PageBreak (otherwise,
2218         // we'd get an empty page).
2219         // Of course we need to overlook empty pages!
2220         const SwFrame *pFlow = &m_rThis;
2221         do
2222         {
2223             pFlow = pFlow->FindPrev();
2224         } while ( pFlow &&
2225                   ( pFlow->FindPageFrame() == pOldPage ||
2226                     !pFlow->IsInDocBody() ) );
2227         if ( pFlow )
2228         {
2229             tools::Long nDiff = pOldPage->GetPhyPageNum() - pFlow->GetPhyPageNum();
2230             if ( nDiff > 1 )
2231             {
2232                 if ( static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage() )
2233                     nDiff -= 1;
2234                 if ( nDiff > 1 )
2235                 {
2236                     pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2237                     // i#53139
2238                     // Now <pNewUpper> is a previous layout frame, which contains
2239                     // content. But the new upper layout frame has to be the next one.
2240                     // Thus, hack for issue i14206 no longer needed, but fix for issue 114442
2241                     // Correct fix for i53139
2242                     // Check for wrong page description before using next new upper.
2243                     // i#66051 - further correction of fix for i53139
2244                     // Check for correct type of new next upper layout frame
2245                     // Another correction of fix for i53139
2246                     // Assumption, that in all cases <pNewUpper> is a previous
2247                     // layout frame, which contains content, is wrong.
2248                     // Another correction of fix for i53139
2249                     // Beside type check, check also, if proposed new next upper
2250                     // frame is inside the same frame types.
2251                     // i#73194 - and yet another correction
2252                     // of fix for i53139:
2253                     // Assure that the new next upper layout frame doesn't
2254                     // equal the current one.
2255                     // E.g.: content is on page 3, on page 2 is only a 'ghost'
2256                     // section and on page 1 is normal content. Method <FindPrev(..)>
2257                     // will find the last content of page 1, but <GetLeaf(..)>
2258                     // returns new upper on page 2.
2259                     if (pNewUpper && pNewUpper->Lower())
2260                     {
2261                         SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
2262                         if ( pNewNextUpper &&
2263                              pNewNextUpper != m_rThis.GetUpper() &&
2264                              pNewNextUpper->GetType() == pNewUpper->GetType() &&
2265                              pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2266                              pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2267                              pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2268                              pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2269                              !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2270                         {
2271                             pNewUpper = pNewNextUpper;
2272                             bCheckPageDescOfNextPage = true;
2273                         }
2274                     }
2275 
2276                     bCheckPageDescs = true;
2277                 }
2278             }
2279         }
2280     }
2281     else if ( IsColBreak( true ) )
2282     {
2283         // If the previous column doesn't contain a ContentFrame, flowing back
2284         // makes sense despite the ColumnBreak, as otherwise we'd get
2285         // an empty column.
2286         if( m_rThis.IsInSct() )
2287         {
2288             pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2289             if( pNewUpper && !SwFlowFrame::IsMoveBwdJump() &&
2290                 ( pNewUpper->ContainsContent() ||
2291                   ( ( !pNewUpper->IsColBodyFrame() ||
2292                       !pNewUpper->GetUpper()->GetPrev() ) &&
2293                     !pNewUpper->FindSctFrame()->GetPrev() ) ) )
2294             {
2295                 pNewUpper = nullptr;
2296             }
2297             // i#53139
2298             // i#69409 - check <pNewUpper>
2299             // i#71065 - check <SwFlowFrame::IsMoveBwdJump()>
2300             else if ( pNewUpper && !SwFlowFrame::IsMoveBwdJump() )
2301             {
2302                 // Now <pNewUpper> is a previous layout frame, which
2303                 // contains content. But the new upper layout frame
2304                 // has to be the next one.
2305                 // Correct fix for i53139
2306                 // Check for wrong page description before using next new upper.
2307                 // i#66051 - further correction of fix for i53139
2308                 // Check for correct type of new next upper layout frame
2309                 // Another correction of fix for i53139
2310                 // Beside type check, check also, if proposed new next upper
2311                 // frame is inside the same frame types.
2312                 SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NOSECTION, true );
2313                 if ( pNewNextUpper &&
2314                      pNewNextUpper->GetType() == pNewUpper->GetType() &&
2315                      pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2316                      pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2317                      pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2318                      pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2319                      !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2320                 {
2321                     pNewUpper = pNewNextUpper;
2322                 }
2323             }
2324         }
2325         else
2326         {
2327             const SwFrame *pCol = m_rThis.FindColFrame();
2328             bool bGoOn = true;
2329             bool bJump = false;
2330             do
2331             {
2332                 if ( pCol->GetPrev() )
2333                     pCol = pCol->GetPrev();
2334                 else
2335                 {
2336                     bGoOn = false;
2337                     pCol = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2338                 }
2339                 if ( pCol )
2340                 {
2341                     // ColumnFrames now with BodyFrame
2342                     SwLayoutFrame* pColBody = pCol->IsColumnFrame() ?
2343                         const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())) :
2344                         const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol));
2345                     if ( pColBody->ContainsContent() )
2346                     {
2347                         bGoOn = false; // We have content here! we accept this
2348                         // only if GetLeaf() has set the MoveBwdJump.
2349                         if( SwFlowFrame::IsMoveBwdJump() )
2350                         {
2351                             pNewUpper = pColBody;
2352                             // i#53139
2353                             // Now <pNewUpper> is a previous layout frame, which
2354                             // contains content. But the new upper layout frame
2355                             // has to be the next one.
2356                             // Correct fix for i53139
2357                             // Check for wrong page description before using next new upper.
2358                             // i#66051 - further correction of fix for i53139
2359                             // Check for correct type of new next upper layout frame
2360                             // Another correction of fix for i53139
2361                             // Beside type check, check also, if proposed new next upper
2362                             // frame is inside the same frame types.
2363                             // i#71065
2364                             // Check that the proposed new next upper layout
2365                             // frame isn't the current one.
2366                             SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
2367                             if ( pNewNextUpper &&
2368                                  pNewNextUpper != m_rThis.GetUpper() &&
2369                                  pNewNextUpper->GetType() == pNewUpper->GetType() &&
2370                                  pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
2371                                  pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
2372                                  pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
2373                                  pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
2374                                  !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
2375                             {
2376                                 pNewUpper = pNewNextUpper;
2377                             }
2378                         }
2379                     }
2380                     else
2381                     {
2382                         if( pNewUpper )        // We already had an empty column, in other
2383                             bJump = true;      // words we skipped one.
2384                         pNewUpper = pColBody;  // this empty column could be considered,
2385                                                // but we continue searching nevertheless.
2386                     }
2387                 }
2388             } while( bGoOn );
2389             if( bJump )
2390                 SwFlowFrame::SetMoveBwdJump( true );
2391         }
2392     }
2393     else // No breaks - we can flow back.
2394         pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
2395 
2396     // i#27801 - no move backward of 'master' text frame,
2397     // if - due to its object positioning - it isn't allowed to be on the new page frame
2398     // i#44049 - add another condition for not moving backward
2399     // If one of its objects has restarted the layout process, moving backward
2400     // isn't sensible either.
2401     // i#47697 - refine condition made for issue i44049
2402     // - allow move backward as long as the anchored object is only temporarily
2403     //   positions considering its wrapping style.
2404     if ( pNewUpper &&
2405          m_rThis.IsTextFrame() && !IsFollow() )
2406     {
2407         sal_uInt32 nToPageNum( 0 );
2408         const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
2409                                                 *(pOldPage->GetFormat()->GetDoc()),
2410                                                 static_cast<SwTextFrame&>(m_rThis),
2411                                                 nToPageNum );
2412         if ( bMoveFwdByObjPos &&
2413              pNewUpper->FindPageFrame()->GetPhyPageNum() < nToPageNum )
2414         {
2415             pNewUpper = nullptr;
2416         }
2417         // i#44049 - check, if one of its anchored objects
2418         // has restarted the layout process.
2419         else if ( m_rThis.GetDrawObjs() )
2420         {
2421             for (SwAnchoredObject* pAnchoredObj : *m_rThis.GetDrawObjs())
2422             {
2423                 // i#47697 - refine condition - see above
2424                 if ( pAnchoredObj->RestartLayoutProcess() &&
2425                      !pAnchoredObj->IsTmpConsiderWrapInfluence() )
2426                 {
2427                     pNewUpper = nullptr;
2428                     break;
2429                 }
2430             }
2431         }
2432     }
2433 
2434     // With Follows, it's only allowed to flow back if there's no neighbor
2435     // in the new environment (because that would be the Master).
2436     // (6677) If however we skipped empty pages, we still have to move.
2437     if ( pNewUpper && IsFollow() && pNewUpper->Lower() )
2438     {
2439         // i#79774
2440         // neglect empty sections in proposed new upper frame
2441         bool bProposedNewUpperContainsOnlyEmptySections( true );
2442         {
2443             const SwFrame* pLower( pNewUpper->Lower() );
2444             while ( pLower )
2445             {
2446                 if ( pLower->IsSctFrame() &&
2447                      !dynamic_cast<const SwSectionFrame&>(*pLower).GetSection() )
2448                 {
2449                     pLower = pLower->GetNext();
2450                     continue;
2451                 }
2452                 else
2453                 {
2454                     bProposedNewUpperContainsOnlyEmptySections = false;
2455                     break;
2456                 }
2457             }
2458         }
2459         if ( !bProposedNewUpperContainsOnlyEmptySections )
2460         {
2461             if ( SwFlowFrame::IsMoveBwdJump() )
2462             {
2463                 // Don't move after the Master, but into the next empty page.
2464                 SwFrame *pFrame = pNewUpper->Lower();
2465                 while ( pFrame->GetNext() )
2466                     pFrame = pFrame->GetNext();
2467                 pNewUpper = pFrame->GetLeaf( MAKEPAGE_INSERT, true );
2468                 if( pNewUpper == m_rThis.GetUpper() ) // Did we end up in the same place?
2469                     pNewUpper = nullptr;               // If so, moving is not needed.
2470             }
2471             else
2472                 pNewUpper = nullptr;
2473         }
2474     }
2475     if ( pNewUpper && !ShouldBwdMoved( pNewUpper, rbReformat ) )
2476     {
2477         if( !pNewUpper->Lower() )
2478         {
2479             if( pNewUpper->IsFootnoteContFrame() )
2480             {
2481                 pNewUpper->Cut();
2482                 SwFrame::DestroyFrame(pNewUpper);
2483             }
2484             else
2485             {
2486                 SwSectionFrame* pSectFrame = pNewUpper->FindSctFrame();
2487 
2488                 if ( pSectFrame && !pSectFrame->IsColLocked() &&
2489                      !pSectFrame->ContainsContent() && !pSectFrame->ContainsAny( true ) )
2490                 {
2491                     pSectFrame->DelEmpty( true );
2492                     SwFrame::DestroyFrame(pSectFrame);
2493                     m_rThis.setFrameAreaPositionValid(true);
2494                 }
2495             }
2496         }
2497         pNewUpper = nullptr;
2498     }
2499 
2500     // i#21478 - don't move backward, if flow frame wants to
2501     // keep with next frame and next frame is locked.
2502     // i#38232 - If next frame is a table, do *not* check,
2503     // if it's locked.
2504     if ( pNewUpper && !IsFollow() &&
2505          m_rThis.GetAttrSet()->GetKeep().GetValue() && m_rThis.GetIndNext() )
2506     {
2507         SwFrame* pIndNext = m_rThis.GetIndNext();
2508         // i#38232
2509         if ( !pIndNext->IsTabFrame() )
2510         {
2511             // get first content of section, while empty sections are skipped
2512             while ( pIndNext && pIndNext->IsSctFrame() )
2513             {
2514                 if( static_cast<SwSectionFrame*>(pIndNext)->GetSection() )
2515                 {
2516                     SwFrame* pTmp = static_cast<SwSectionFrame*>(pIndNext)->ContainsAny();
2517                     if ( pTmp )
2518                     {
2519                         pIndNext = pTmp;
2520                         break;
2521                     }
2522                 }
2523                 pIndNext = pIndNext->GetIndNext();
2524             }
2525             OSL_ENSURE( !pIndNext || dynamic_cast<const SwTextFrame*>( pIndNext) !=  nullptr,
2526                     "<SwFlowFrame::MovedBwd(..)> - incorrect next found." );
2527             if ( pIndNext && pIndNext->IsFlowFrame() &&
2528                  SwFlowFrame::CastFlowFrame(pIndNext)->IsJoinLocked() )
2529             {
2530                 pNewUpper = nullptr;
2531             }
2532         }
2533     }
2534 
2535     // i#65250
2536     // layout loop control for flowing content again and again moving
2537     // backward under the same layout condition.
2538     if ( pNewUpper && !IsFollow() &&
2539          pNewUpper != m_rThis.GetUpper() &&
2540          SwLayouter::MoveBwdSuppressed( *(pOldPage->GetFormat()->GetDoc()),
2541                                         *this, *pNewUpper ) )
2542     {
2543         SwLayoutFrame* pNextNewUpper = pNewUpper->GetLeaf(
2544                                     ( !m_rThis.IsSctFrame() && m_rThis.IsInSct() )
2545                                     ? MAKEPAGE_NOSECTION
2546                                     : MAKEPAGE_NONE,
2547                                     true );
2548         // i#73194 - make code robust
2549         OSL_ENSURE( pNextNewUpper, "<SwFlowFrame::MoveBwd(..)> - missing next new upper" );
2550         if ( pNextNewUpper &&
2551              ( pNextNewUpper == m_rThis.GetUpper() ||
2552                pNextNewUpper->GetType() != m_rThis.GetUpper()->GetType() ) )
2553         {
2554             // tdf#107398 do not leave empty footnote container around
2555             if (!pNewUpper->Lower() && pNewUpper->IsFootnoteContFrame())
2556             {
2557                 pNewUpper->Cut();
2558                 SwFrame::DestroyFrame(pNewUpper);
2559             }
2560             pNewUpper = nullptr;
2561             OSL_FAIL( "<SwFlowFrame::MoveBwd(..)> - layout loop control for layout action <Move Backward> applied!" );
2562         }
2563     }
2564 
2565     OSL_ENSURE( pNewUpper != m_rThis.GetUpper(),
2566             "<SwFlowFrame::MoveBwd(..)> - moving backward to the current upper frame!?" );
2567     if ( pNewUpper )
2568     {
2569         PROTOCOL_ENTER( &m_rThis, PROT::MoveBack, DbgAction::NONE, nullptr );
2570         if ( pNewUpper->IsFootnoteContFrame() )
2571         {
2572             // I may have gotten a Container
2573             SwFootnoteFrame *pNew = SwFootnoteContFrame::PrependChained(&m_rThis, false);
2574             pNew->Paste( pNewUpper );
2575             pNewUpper = pNew;
2576         }
2577         if( pNewUpper->IsFootnoteFrame() && m_rThis.IsInSct() )
2578         {
2579             SwSectionFrame* pSct = m_rThis.FindSctFrame();
2580             // If we're in a section of a footnote, we may need to create
2581             // a SwSectionFrame in the new upper
2582             if( pSct->IsInFootnote() )
2583             {
2584                 SwFrame* pTmp = pNewUpper->Lower();
2585                 if( pTmp )
2586                 {
2587                     while( pTmp->GetNext() )
2588                         pTmp = pTmp->GetNext();
2589                     if( !pTmp->IsSctFrame() ||
2590                         static_cast<SwSectionFrame*>(pTmp)->GetFollow() != pSct )
2591                         pTmp = nullptr;
2592                 }
2593                 if( pTmp )
2594                     pNewUpper = static_cast<SwSectionFrame*>(pTmp);
2595                 else
2596                 {
2597                     pSct = new SwSectionFrame( *pSct, true );
2598                     pSct->Paste( pNewUpper );
2599                     pSct->Init();
2600                     pNewUpper = pSct;
2601                     pSct->SimpleFormat();
2602                 }
2603             }
2604         }
2605         bool bUnlock = false;
2606         bool bFollow = false;
2607         // Lock section. Otherwise, it could get destroyed if the only Content
2608         // moves e.g. from the second into the first column.
2609         SwSectionFrame* pSect = pNewUpper->FindSctFrame();
2610         if( pSect )
2611         {
2612             bUnlock = !pSect->IsColLocked();
2613             pSect->ColLock();
2614             bFollow = pSect->HasFollow();
2615         }
2616 
2617         {
2618             auto const pOld = m_rThis.GetUpper();
2619 #if BOOST_VERSION < 105600
2620             std::list<SwFrameDeleteGuard> g;
2621 #else
2622             ::std::optional<SwFrameDeleteGuard> g;
2623 #endif
2624             if (m_rThis.GetUpper()->IsCellFrame())
2625             {
2626                 // note: IsFollowFlowRow() is never set for new-style tables
2627                 SwTabFrame const*const pTabFrame(m_rThis.FindTabFrame());
2628                 if (   pTabFrame->IsFollow()
2629                     && static_cast<SwTabFrame const*>(pTabFrame->GetPrecede())->HasFollowFlowLine()
2630                     && pTabFrame->GetFirstNonHeadlineRow() == m_rThis.GetUpper()->GetUpper())
2631                 {
2632                     // lock follow-flow-row (similar to sections above)
2633 #if BOOST_VERSION < 105600
2634                     g.emplace_back(m_rThis.GetUpper()->GetUpper());
2635 #else
2636                     g.emplace(m_rThis.GetUpper()->GetUpper());
2637 #endif
2638                     assert(m_rThis.GetUpper()->GetUpper()->IsDeleteForbidden());
2639                 }
2640             }
2641             pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2642             SAL_WARN_IF(pOld != m_rThis.GetUpper(), "sw.core",
2643                     "MoveBwd(): pNewUpper->Calc() moved this frame?");
2644         }
2645 
2646         m_rThis.Cut();
2647 
2648         // optimization: format section, if its size is invalidated and if it's
2649         // the new parent of moved backward frame.
2650         bool bFormatSect( false );
2651         if( bUnlock )
2652         {
2653             pSect->ColUnlock();
2654             if( pSect->HasFollow() != bFollow )
2655             {
2656                 pSect->InvalidateSize();
2657                 // - optimization
2658                 if ( pSect == pNewUpper )
2659                     bFormatSect = true;
2660             }
2661         }
2662 
2663         m_rThis.Paste( pNewUpper );
2664         // - optimization
2665         if ( bFormatSect )
2666             pSect->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
2667 
2668         SwPageFrame *pNewPage = m_rThis.FindPageFrame();
2669         if( pNewPage != pOldPage )
2670         {
2671             m_rThis.Prepare( PrepareHint::BossChanged, static_cast<const void*>(pOldPage), false );
2672             SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
2673             if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
2674                 pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true);  // Will be done by CalcLayout() later on
2675 
2676             pNewPage->InvalidateSpelling();
2677             pNewPage->InvalidateSmartTags();
2678             pNewPage->InvalidateAutoCompleteWords();
2679             pNewPage->InvalidateWordCount();
2680 
2681             // No <CheckPageDesc(..)> in online layout
2682             if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
2683             {
2684                 if ( bCheckPageDescs && pNewPage->GetNext() )
2685                 {
2686                     SwPageFrame* pStartPage = bCheckPageDescOfNextPage ?
2687                                             pNewPage :
2688                                             static_cast<SwPageFrame*>(pNewPage->GetNext());
2689                     SwFrame::CheckPageDescs( pStartPage, false);
2690                 }
2691                 else if (m_rThis.GetPageDescItem().GetPageDesc())
2692                 {
2693                     // First page could get empty for example by disabling
2694                     // a section
2695                     SwFrame::CheckPageDescs( pNewPage, false);
2696                 }
2697             }
2698         }
2699     }
2700     return pNewUpper != nullptr;
2701 }
2702 
CastFlowFrame(SwFrame * pFrame)2703 SwFlowFrame *SwFlowFrame::CastFlowFrame( SwFrame *pFrame )
2704 {
2705     if ( pFrame->IsContentFrame() )
2706         return static_cast<SwContentFrame*>(pFrame);
2707     if ( pFrame->IsTabFrame() )
2708         return static_cast<SwTabFrame*>(pFrame);
2709     if ( pFrame->IsSctFrame() )
2710         return static_cast<SwSectionFrame*>(pFrame);
2711     return nullptr;
2712 }
2713 
CastFlowFrame(const SwFrame * pFrame)2714 const SwFlowFrame *SwFlowFrame::CastFlowFrame( const SwFrame *pFrame )
2715 {
2716     if ( pFrame->IsContentFrame() )
2717         return static_cast<const SwContentFrame*>(pFrame);
2718     if ( pFrame->IsTabFrame() )
2719         return static_cast<const SwTabFrame*>(pFrame);
2720     if ( pFrame->IsSctFrame() )
2721         return static_cast<const SwSectionFrame*>(pFrame);
2722     return nullptr;
2723 }
2724 
2725 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2726