Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / calcmove.cxx
blob4d4b2de2899414a40eb84d700798932e646cebc8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <rootfrm.hxx>
22 #include <pagefrm.hxx>
23 #include <viewopt.hxx>
24 #include <frmatr.hxx>
25 #include <frmtool.hxx>
26 #include <txtftn.hxx>
27 #include <fmtftn.hxx>
28 #include <ndtxt.hxx>
29 #include <editeng/ulspitem.hxx>
30 #include <editeng/keepitem.hxx>
31 #include <osl/diagnose.h>
32 #include <svx/sdtaitm.hxx>
34 #include <fmtfsize.hxx>
35 #include <fmtanchr.hxx>
36 #include <fmtclbl.hxx>
38 #include <tabfrm.hxx>
39 #include <ftnfrm.hxx>
40 #include <txtfrm.hxx>
41 #include <sectfrm.hxx>
42 #include <dbg_lay.hxx>
44 #include <sortedobjs.hxx>
45 #include <layouter.hxx>
46 #include <flyfrms.hxx>
48 #include <DocumentSettingManager.hxx>
49 #include <IDocumentLayoutAccess.hxx>
51 // Move methods
53 /// Return value tells whether the Frame should be moved.
54 bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
56 if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
58 // Floating back a frm uses a bit of time unfortunately.
59 // The most common case is the following: The Frame wants to float to
60 // somewhere where the FixSize is the same that the Frame itself has already.
61 // In that case it's pretty easy to check if the Frame has enough space
62 // for its VarSize. If this is NOT the case, we already know that
63 // we don't need to move.
64 // The Frame checks itself whether it has enough space - respecting the fact
65 // that it could possibly split itself if needed.
66 // If, however, the FixSize differs from the Frame or Flys are involved
67 // (either in the old or the new position), checking is pointless,
68 // and we have to move the Frame just to see what happens - if there's
69 // some space available to do it, that is.
71 // The FixSize of the containers of Contents is always the width.
73 // If we moved more than one sheet back (for example jumping over empty
74 // pages), we have to move either way. Otherwise, if the Frame doesn't fit
75 // into the page, empty pages wouldn't be respected anymore.
76 sal_uInt8 nMoveAnyway = 0;
77 SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
78 SwPageFrame *pOldPage = FindPageFrame();
80 if ( SwFlowFrame::IsMoveBwdJump() )
81 return true;
83 if( IsInFootnote() && IsInSct() )
85 SwFootnoteFrame* pFootnote = FindFootnoteFrame();
86 SwSectionFrame* pMySect = pFootnote->FindSctFrame();
87 if( pMySect && pMySect->IsFootnoteLock() )
89 SwSectionFrame *pSect = pNewUpper->FindSctFrame();
90 while( pSect && pSect->IsInFootnote() )
91 pSect = pSect->GetUpper()->FindSctFrame();
92 OSL_ENSURE( pSect, "Escaping footnote" );
93 if( pSect != pMySect )
94 return false;
97 SwRectFnSet aRectFnSet(this);
98 SwRectFnSet fnRectX(pNewUpper);
99 if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
100 aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) {
101 // In this case, only a WouldFit_ with test move is possible
102 nMoveAnyway = 2;
105 // Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
106 nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
108 const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
109 SwTwips nSpace = 0;
110 SwRect aRect( pNewUpper->getFramePrintArea() );
111 aRect.Pos() += pNewUpper->getFrameArea().Pos();
112 const SwFrame *pPrevFrame = pNewUpper->Lower();
113 while ( pPrevFrame )
115 SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea());
116 // Consider lower spacing of last frame in a table cell
118 // Check if last frame is inside table and if it includes its lower spacing.
119 if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
120 rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
122 const SwFrame* pLastFrame = pPrevFrame;
123 // if last frame is a section, take its last content
124 if ( pPrevFrame->IsSctFrame() )
126 pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent();
127 if ( pLastFrame &&
128 pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
130 pLastFrame = pLastFrame->FindTabFrame();
134 if ( pLastFrame )
136 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
137 const SwBorderAttrs& rAttrs = *aAccess.Get();
138 nNewTop -= rAttrs.GetULSpace().GetLower();
139 if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
141 nNewTop -= rAttrs.CalcLineSpacing();
146 fnRectX.SetTop( aRect, nNewTop );
148 pPrevFrame = pPrevFrame->GetNext();
151 nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);
153 //determine space left in new upper frame
154 nSpace = fnRectX.GetHeight(aRect);
155 const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell();
156 if ( IsInFootnote() ||
157 (pSh && pSh->GetViewOptions()->getBrowseMode()) ||
158 pNewUpper->IsCellFrame() ||
159 ( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
160 ( pNewUpper->IsColBodyFrame() &&
161 !pNewUpper->GetUpper()->GetPrev() &&
162 !pNewUpper->GetUpper()->GetNext() ) ) ) )
163 nSpace += pNewUpper->Grow( LONG_MAX, true );
165 auto pTextFrame = DynCastTextFrame();
166 if (pTextFrame)
168 // This is a text frame. Check if it's an anchor for a non-last element in a split
169 // fly chain. If so, we can only move back in case not only the text frame itself,
170 // but also its fly fits nSpace.
171 SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj();
172 if (pFly && pFly->getFrameArea().Height() > nSpace)
174 return false;
178 if ( nMoveAnyway < 3 )
180 if ( nSpace )
182 // Do not notify footnotes which are stuck to the paragraph:
183 // This would require extremely confusing code, taking into
184 // account the widths
185 // and Flys, that in turn influence the footnotes, ...
187 // WouldFit_ can only be used if the width is the same and
188 // ONLY self-anchored Flys are present.
190 // WouldFit_ can also be used if ONLY Flys anchored
191 // somewhere else are present.
192 // In this case, the width doesn't even matter,
193 // because we're running a TestFormat in the new upper.
194 const sal_uInt8 nBwdMoveNecessaryResult =
195 BwdMoveNecessary( pNewPage, aRect);
196 const bool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
197 nBwdMoveNecessaryResult == 3 );
199 return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
200 bObjsInNewUpper );
202 // It's impossible for WouldFit_ to return a usable result if
203 // we have a fresh multi-column section - so we really have to
204 // float back unless there is no space.
205 return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
206 !fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
207 ( pNewUpper->GetUpper()->GetPrev() ||
208 pNewUpper->GetUpper()->GetNext() );
211 // Check for space left in new upper
212 return nSpace != 0;
215 return false;
218 // Calc methods
220 // Two little friendships form a secret society
221 inline void PrepareLock( SwFlowFrame *pTab )
223 pTab->LockJoin();
225 inline void PrepareUnlock( SwFlowFrame *pTab )
227 pTab->UnlockJoin();
231 // hopefully, one day this function simply will return 'false'
232 static bool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
234 return !rFrame.GetUpper()->IsSctFrame() &&
235 !rFrame.GetUpper()->IsFooterFrame() &&
236 // No format of upper Writer fly frame
237 !rFrame.GetUpper()->IsFlyFrame() &&
238 !( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
239 !( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
242 /** Prepares the Frame for "formatting" (MakeAll()).
244 * This method serves to save stack space: To calculate the position of the Frame
245 * we have to make sure that the positions of Upper and Prev respectively are
246 * valid. This may require a recursive call (a loop would be quite expensive,
247 * as it's not required very often).
249 * Every call of MakeAll requires around 500 bytes on the stack - you easily
250 * see where this leads to. This method requires only a little bit of stack
251 * space, so the recursive call should not be a problem here.
253 * Another advantage is that one nice day, this method and with it the
254 * formatting of predecessors could be avoided. Then it could probably be
255 * possible to jump "quickly" to the document's end.
257 * @see MakeAll()
259 void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
261 StackHack aHack;
262 if ( GetUpper() )
264 SwFrameDeleteGuard aDeleteGuard(this);
265 if ( lcl_IsCalcUpperAllowed( *this ) )
266 GetUpper()->Calc(pRenderContext);
267 OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
268 if ( !GetUpper() )
269 return;
271 const bool bCnt = IsContentFrame();
272 const bool bTab = IsTabFrame();
273 bool bNoSect = IsInSct();
274 bool bOldTabLock = false, bFoll = false;
275 SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
277 if ( bTab )
279 pThis = static_cast<SwTabFrame*>(this);
280 bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
281 ::PrepareLock( static_cast<SwTabFrame*>(this) );
282 bFoll = pThis->IsFollow();
284 else if( IsSctFrame() )
286 pThis = static_cast<SwSectionFrame*>(this);
287 bFoll = pThis->IsFollow();
288 bNoSect = false;
290 else if ( bCnt )
292 bFoll = pThis->IsFollow();
293 if ( bFoll && GetPrev() )
295 // Do not follow the chain when we need only one instance
296 const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster();
297 if ( pMaster && pMaster->IsLocked() )
299 MakeAll(pRenderContext);
300 return;
305 // There is no format of previous frame, if current frame is a table
306 // frame and its previous frame wants to keep with it.
307 const bool bFormatPrev = !bTab ||
308 !GetPrev() ||
309 !GetPrev()->GetAttrSet()->GetKeep().GetValue();
310 if ( bFormatPrev )
312 SwFrame *pFrame = GetUpper()->Lower();
313 while ( pFrame != this )
315 OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
316 if ( !pFrame )
317 return; //Oioioioi ...
319 if ( !pFrame->isFrameAreaDefinitionValid() )
321 // A small interference that hopefully improves on the stability:
322 // If I'm Follow AND neighbor of a Frame before me, it would delete
323 // me when formatting. This as you can see could easily become a
324 // confusing situation that we want to avoid.
325 if ( bFoll && pFrame->IsFlowFrame() &&
326 SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
327 break;
329 bool const isLast(pFrame->GetNext() == this);
330 // note: this seems obvious but does *not* hold, a MakeAll()
331 // could move more than 1 frame backwards!
332 // that's why FindNext() is used below
333 // assert(pFrame->GetUpper() == GetUpper());
334 pFrame->MakeAll(pRenderContext);
335 if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
336 break;
337 if (isLast && pFrame->GetUpper() != GetUpper())
339 assert(GetUpper()->Lower() == this
340 // empty section frames are created all the time...
341 || GetUpper()->Lower()->IsSctFrame()
342 // tab frame/section frame may split multiple times
343 || ( SwFlowFrame::CastFlowFrame(pFrame)
344 && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
345 && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
346 SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
347 && (GetUpper()->Lower()->GetNext() == this
348 // if it's more than 10 pages long...
349 || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
350 == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
351 && GetUpper()->Lower()->GetNext()->GetNext() == this)
352 // pre-existing empty section frames may end up between them...
353 || GetUpper()->Lower()->GetNext()->IsSctFrame())));
354 break; // tdf#119109 frame was moved backward, prevent
355 // FindNext() returning a frame inside this if
356 } // this is a table!
358 // With ContentFrames, the chain may be broken while walking through
359 // it. Therefore we have to figure out the next frame in a bit more
360 // complicated way. However, I'll HAVE to get back to myself
361 // sometime again.
362 pFrame = pFrame->FindNext();
364 // If we started out in a SectionFrame, it might have happened that
365 // we landed in a Section Follow via the MakeAll calls.
366 // FindNext only gives us the SectionFrame, not it's content - we
367 // won't find ourselves anymore!
368 if( bNoSect && pFrame && pFrame->IsSctFrame() )
370 SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
371 if( pCnt )
372 pFrame = pCnt;
375 OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
376 if ( !GetUpper() )
377 return;
379 if ( lcl_IsCalcUpperAllowed( *this ) )
380 GetUpper()->Calc(pRenderContext);
382 OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
385 if ( bTab && !bOldTabLock )
386 ::PrepareUnlock( static_cast<SwTabFrame*>(this) );
388 MakeAll(pRenderContext);
391 void SwFrame::OptPrepareMake()
393 // #i23129#, #i36347# - no format of upper Writer fly frame
394 if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
395 !GetUpper()->IsFlyFrame() )
398 SwFrameDeleteGuard aDeleteGuard(this);
399 GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
401 OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
402 if ( !GetUpper() )
403 return;
405 if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
407 PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
409 else
411 StackHack aHack;
412 MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
416 void SwFrame::PrepareCursor()
418 StackHack aHack;
419 if( GetUpper() && !GetUpper()->IsSctFrame() )
421 const bool bCnt = IsContentFrame();
422 const bool bTab = IsTabFrame();
423 bool bNoSect = IsInSct();
425 std::optional<FlowFrameJoinLockGuard> tabGuard;
426 std::optional<SwFrameDeleteGuard> rowGuard;
427 SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
429 if ( bTab )
431 tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741
432 pThis = static_cast<SwTabFrame*>(this);
434 else if (IsRowFrame())
436 rowGuard.emplace(this); // tdf#125741 keep this alive
438 else if( IsSctFrame() )
440 pThis = static_cast<SwSectionFrame*>(this);
441 bNoSect = false;
444 GetUpper()->PrepareCursor();
445 GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
447 OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
448 if ( !GetUpper() )
449 return;
451 bool const bFoll = pThis && pThis->IsFollow();
453 SwFrame *pFrame = GetUpper()->Lower();
454 while ( pFrame != this )
456 OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
457 if ( !pFrame )
458 return; //Oioioioi ...
460 if ( !pFrame->isFrameAreaDefinitionValid() )
462 // A small interference that hopefully improves on the stability:
463 // If I'm Follow AND neighbor of a Frame before me, it would delete
464 // me when formatting. This as you can see could easily become a
465 // confusing situation that we want to avoid.
466 if ( bFoll && pFrame->IsFlowFrame() &&
467 SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
468 break;
470 bool const isLast(pFrame->GetNext() == this);
471 pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
472 if (isLast && pFrame->GetUpper() != GetUpper())
474 assert(GetUpper()->Lower() == this
475 // empty section frames are created all the time...
476 || GetUpper()->Lower()->IsSctFrame()
477 // tab frame/section frame may split multiple times
478 || ( SwFlowFrame::CastFlowFrame(pFrame)
479 && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
480 && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
481 SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
482 && (GetUpper()->Lower()->GetNext() == this
483 // if it's more than 10 pages long...
484 || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
485 == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
486 && GetUpper()->Lower()->GetNext()->GetNext() == this)
487 // pre-existing empty section frames may end up between them...
488 || GetUpper()->Lower()->GetNext()->IsSctFrame())));
489 break; // tdf#119109 frame was moved backward, prevent
490 // FindNext() returning a frame inside this if
491 } // this is a table!
493 // With ContentFrames, the chain may be broken while walking through
494 // it. Therefore we have to figure out the next frame in a bit more
495 // complicated way. However, I'll HAVE to get back to myself
496 // sometime again.
497 pFrame = pFrame->FindNext();
498 if( bNoSect && pFrame && pFrame->IsSctFrame() )
500 SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
501 if( pCnt )
502 pFrame = pCnt;
505 OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
506 if ( !GetUpper() )
507 return;
509 GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
511 OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
513 Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
516 // Here we return GetPrev(); however we will ignore empty SectionFrames
517 static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
519 SwFrame* pRet = pFrame->GetPrev();
520 if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
521 bSectPrv && !pFrame->IsColumnFrame() )
522 pRet = pFrame->GetUpper()->GetPrev();
523 while( pRet && pRet->IsSctFrame() &&
524 !static_cast<SwSectionFrame*>(pRet)->GetSection() )
525 pRet = pRet->GetPrev();
526 return pRet;
529 static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
531 SwFrame *pRet = pFrame;
534 pRet = lcl_Prev( pRet );
535 } while ( pRet && pRet->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() );
536 return pRet;
539 void SwFrame::MakePos()
541 if ( isFrameAreaPositionValid() )
542 return;
544 setFrameAreaPositionValid(true);
545 bool bUseUpper = false;
546 SwFrame* pPrv = lcl_Prev( this );
547 if ( pPrv &&
548 ( !pPrv->IsContentFrame() ||
549 ( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
552 if ( !StackHack::IsLocked() &&
553 ( !IsInSct() || IsSctFrame() ) &&
554 !pPrv->IsSctFrame() &&
555 !pPrv->GetAttrSet()->GetKeep().GetValue()
558 // tdf#151866 pPrv may MoveBwd and if this is a newly created
559 // section frame then CheckPageDescs() may delete the whole page!
560 SwFrameDeleteGuard g(this); // Prevent it.
561 pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
563 else if ( pPrv->getFrameArea().Top() == 0 )
565 bUseUpper = true;
569 pPrv = lcl_Prev( this, false );
570 const SwFrameType nMyType = GetType();
571 SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? GetUpper() : this));
572 if ( !bUseUpper && pPrv )
574 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
575 aFrm.Pos( pPrv->getFrameArea().Pos() );
577 if( FRM_NEIGHBOUR & nMyType )
579 const bool bR2L = IsRightToLeft();
581 if( bR2L )
583 aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
585 else
587 aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
590 // cells may now leave their uppers
591 if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
593 aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
596 else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
598 if ( aRectFnSet.IsVertL2R() )
600 aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
602 else
604 aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
607 else
609 aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
612 else if ( GetUpper() )
614 // If parent frame is a footer frame and its <ColLocked()>, then
615 // do *not* calculate it.
616 // NOTE: Footer frame is <ColLocked()> during its
617 // <FormatSize(..)>, which is called from <Format(..)>, which
618 // is called from <MakeAll()>, which is called from <Calc()>.
619 // #i56850#
620 // - no format of upper Writer fly frame, which is anchored
621 // at-paragraph or at-character.
622 if ( !GetUpper()->IsTabFrame() &&
623 !( IsTabFrame() && GetUpper()->IsInTab() ) &&
624 !GetUpper()->IsSctFrame() &&
625 !dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
626 !( GetUpper()->IsFooterFrame() &&
627 GetUpper()->IsColLocked() )
630 GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
632 pPrv = lcl_Prev( this, false );
633 if ( !bUseUpper && pPrv )
635 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
636 aFrm.Pos( pPrv->getFrameArea().Pos() );
638 if( FRM_NEIGHBOUR & nMyType )
640 const bool bR2L = IsRightToLeft();
642 if( bR2L )
644 aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
646 else
648 aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
651 // cells may now leave their uppers
652 if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
654 aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
657 else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
659 aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
661 else
663 aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
666 else
668 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
669 aFrm.Pos( GetUpper()->getFrameArea().Pos() );
671 if( GetUpper()->IsFlyFrame() )
673 aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
675 else
677 aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
680 if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
682 if( aRectFnSet.IsVert() )
684 aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
686 else
688 aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
691 else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
693 aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
697 else
699 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
700 aFrm.Pos().setX(0);
701 aFrm.Pos().setY(0);
704 if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
706 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
707 aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
710 setFrameAreaPositionValid(true);
713 // #i28701# - new type <SwSortedObjs>
714 static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, tools::Long& rBot)
716 // And then there can be paragraph anchored frames that sit below their paragraph.
717 tools::Long nMax = 0;
718 for (SwAnchoredObject* pObj : rSortedObjs)
720 // #i28701# - consider changed type of <SwSortedObjs>
721 // entries.
722 tools::Long nTmp = 0;
723 if ( auto pFly = pObj->DynCastFlyFrame() )
725 if( pFly->getFrameArea().Top() != FAR_AWAY &&
726 ( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
727 ( pFly->IsFlyAtContentFrame() &&
728 ( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
729 pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
731 nTmp = pFly->getFrameArea().Bottom();
734 else
735 nTmp = pObj->GetObjRect().Bottom();
736 nMax = std::max( nTmp, nMax );
738 ++nMax; // Lower edge vs. height!
739 rBot = std::max( rBot, nMax );
742 size_t SwPageFrame::GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const
744 OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
745 "SwPageFrame::GetContentHeight(): No support for columns.");
747 // In pages without columns, the content defines the size.
748 tools::Long nBot = getFrameArea().Top() + nTop;
749 const SwFrame *pFrame = Lower();
750 while (pFrame)
752 tools::Long nTmp = 0;
753 const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
754 while (pCnt && (pCnt->GetUpper() == pFrame ||
755 static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
757 nTmp += pCnt->getFrameArea().Height();
758 if (pCnt->IsTextFrame() &&
759 static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
761 // This TextFrame would like to be a bit bigger.
762 nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
763 - pCnt->getFramePrintArea().Height();
765 else if (pCnt->IsSctFrame())
767 // Grow if undersized, but don't shrink if oversized.
768 const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
769 if (delta > 0)
770 nTmp += delta;
773 pCnt = pCnt->FindNext();
775 // Consider invalid body frame properties
776 if (pFrame->IsBodyFrame() &&
777 (!pFrame->isFrameAreaSizeValid() ||
778 !pFrame->isFramePrintAreaValid()) &&
779 (pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
782 nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
784 else
786 // Assert invalid lower property
787 OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
788 "SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
789 nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
791 if (!pFrame->IsBodyFrame())
792 nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
793 nBot += nTmp;
794 // Here we check whether paragraph anchored objects
795 // protrude outside the Body/FootnoteCont.
796 if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
797 !pFrame->IsFooterFrame())
798 lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
799 pFrame = pFrame->GetNext();
801 nBot += nBottom;
802 // And the page anchored ones
803 if (m_pSortedObjs)
804 lcl_CheckObjects(*m_pSortedObjs, this, nBot);
805 nBot -= getFrameArea().Top();
807 return nBot;
810 void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
812 PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
814 const SwRect aOldRect( getFrameArea() ); // Adjust root size
815 const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
816 std::optional<SwBorderAttrAccess> oAccess;
817 const SwBorderAttrs*pAttrs = nullptr;
819 while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
821 if ( !isFrameAreaPositionValid() )
823 setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
826 if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
828 if ( IsEmptyPage() )
830 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
831 aFrm.Width( 0 );
832 aFrm.Height( 0 );
834 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
835 aPrt.Width( 0 );
836 aPrt.Height( 0 );
837 aPrt.Left( 0 );
838 aPrt.Top( 0 );
840 setFrameAreaSizeValid(true);
841 setFramePrintAreaValid(true);
843 else
845 if (!oAccess)
847 oAccess.emplace(SwFrame::GetCache(), this);
848 pAttrs = oAccess->Get();
850 assert(pAttrs);
852 SwRootFrame* pRootFrame = getRootFrame();
853 SwViewShell* pSh = pRootFrame->GetCurrShell();
854 if (pSh && pSh->GetViewOptions()->getBrowseMode())
856 // In BrowseView, we use fixed settings
857 const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
858 const tools::Long nTop = pAttrs->CalcTopLine() + aBorder.Height();
859 const tools::Long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();
861 tools::Long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
862 const auto nDefWidth = pSh->GetBrowseWidth();
863 if (nWidth < nDefWidth)
864 nWidth = nDefWidth;
865 nWidth += + 2 * aBorder.Width();
867 constexpr tools::Long constTwips_2cm = o3tl::toTwips(2, o3tl::Length::cm);
868 nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);
871 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
872 aFrm.Width( nWidth );
874 SwLayoutFrame *pBody = FindBodyCont();
875 if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
877 // Columns have a fixed height
878 aFrm.Height( pAttrs->GetSize().Height() );
880 else
882 // In pages without columns, the content defines the size.
883 tools::Long nBot = GetContentHeight(nTop, nBottom);
885 // #i35143# - If second page frame
886 // exists, the first page doesn't have to fulfill the
887 // visible area.
888 if ( !GetPrev() && !GetNext() )
890 nBot = std::max( nBot, pSh->VisArea().Height() );
892 // #i35143# - Assure, that the page
893 // doesn't exceed the defined browse height.
894 aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
899 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
900 aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
901 aPrt.Top ( nTop );
902 aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
903 aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
906 setFrameAreaSizeValid(true);
907 setFramePrintAreaValid(true);
908 continue;
910 else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden() && pRootFrame->GetLastPage() != this)
912 tools::Long height = 0;
913 SwLayoutFrame *pBody = FindBodyCont();
914 if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
916 // Columns have a fixed height
917 height = pAttrs->GetSize().Height();
919 else
921 // No need for borders.
922 height = GetContentHeight(0, 0);
925 if (height > 0)
927 ChgSize(Size(getFrameArea().Width(), height));
928 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
929 aPrt.Top(0);
930 aPrt.Height(height);
931 setFrameAreaSizeValid(true);
932 setFramePrintAreaValid(true);
933 continue;
936 // Fallback to default formatting. Especially relevant
937 // when loading a doc when Hide Whitespace is enabled.
938 // Heights are zero initially.
941 // Set FixSize. For pages, this is not done from Upper, but from
942 // the attribute.
943 //FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
945 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
946 aFrm.SSize( pAttrs->GetSize() );
948 Format( pRenderContext, pAttrs );
951 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
953 if ( getFrameArea() != aOldRect && GetUpper() )
954 static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
956 OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
957 "Upper (Root) must be wide enough to contain the widest page");
960 void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
962 PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
964 // takes care of the notification in the dtor
965 const SwLayNotify aNotify( this );
966 bool bVert = IsVertical();
968 SwRectFn fnRect = ( IsNeighbourFrame() == bVert )? fnRectHori : ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
970 std::optional<SwBorderAttrAccess> oAccess;
971 const SwBorderAttrs*pAttrs = nullptr;
973 while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
975 if ( !isFrameAreaPositionValid() )
976 MakePos();
978 if ( GetUpper() )
980 // NEW TABLES
981 if ( IsLeaveUpperAllowed() )
983 if ( !isFrameAreaSizeValid() )
985 setFramePrintAreaValid(false);
988 else
990 if ( !isFrameAreaSizeValid() )
992 // Set FixSize; VarSize is set by Format() after calculating the PrtArea
993 setFramePrintAreaValid(false);
995 SwTwips nPrtWidth = (GetUpper()->getFramePrintArea().*fnRect->fnGetWidth)();
996 if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
998 SwFrame* pNxt = GetPrev();
999 while( pNxt && !pNxt->IsHeaderFrame() )
1000 pNxt = pNxt->GetPrev();
1001 if( pNxt )
1002 nPrtWidth -= pNxt->getFrameArea().Height();
1003 pNxt = GetNext();
1004 while( pNxt && !pNxt->IsFooterFrame() )
1005 pNxt = pNxt->GetNext();
1006 if( pNxt )
1007 nPrtWidth -= pNxt->getFrameArea().Height();
1010 const tools::Long nDiff = nPrtWidth - (getFrameArea().*fnRect->fnGetWidth)();
1011 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1012 // SwRectFn switched between horizontal and vertical when bVert == IsNeighbourFrame().
1013 // We pick fnSubLeft or fnAddRight that is correspondent to SwRectFn->fnAddBottom
1014 if( ( IsCellFrame() && IsRightToLeft() ) || ( IsColumnFrame() && bVert && !IsVertLR() ) )
1016 (aFrm.*fnRect->fnSubLeft)( nDiff );
1018 else
1020 (aFrm.*fnRect->fnAddRight)( nDiff );
1023 else
1025 // Don't leave your upper
1026 const SwTwips nDeadLine = (GetUpper()->*fnRect->fnGetPrtBottom)();
1027 if( (getFrameArea().*fnRect->fnOverStep)( nDeadLine ) )
1029 setFrameAreaSizeValid(false);
1035 if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
1037 if ( !oAccess )
1039 oAccess.emplace(SwFrame::GetCache(), this);
1040 pAttrs = oAccess->Get();
1042 Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
1044 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
1047 bool SwTextNode::IsCollapse() const
1049 if (GetDoc().GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
1050 && GetText().isEmpty())
1052 SwNodeOffset nIdx=GetIndex();
1053 const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
1054 const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();
1056 // The paragraph is collapsed only if the NdAfter is the end of a cell
1057 bool bInTable = FindTableNode( ) != nullptr;
1059 SwSortedObjs* pObjs = getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
1060 const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;
1062 return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
1065 return false;
1068 bool SwFrame::IsCollapse() const
1070 if (!IsTextFrame())
1071 return false;
1073 const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this);
1074 const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps();
1075 // TODO this SwTextNode function is pointless and should be merged in here
1076 return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
1079 void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
1081 if ( isFramePrintAreaValid() )
1082 return;
1084 setFramePrintAreaValid(true);
1085 SwRectFnSet aRectFnSet(this);
1086 const bool bTextFrame = IsTextFrame();
1087 SwTwips nUpper = 0;
1088 if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() )
1090 if ( static_cast<SwTextFrame*>(this)->HasFollow() )
1091 static_cast<SwTextFrame*>(this)->JoinFrame();
1093 if( aRectFnSet.GetHeight(getFramePrintArea()) )
1095 static_cast<SwTextFrame*>(this)->HideHidden();
1099 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1100 aPrt.Pos().setX(0);
1101 aPrt.Pos().setY(0);
1102 aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
1103 aRectFnSet.SetHeight( aPrt, 0 );
1106 nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
1108 else
1110 // Simplification: ContentFrames are always variable in height!
1112 // At the FixSize, the surrounding Frame enforces the size;
1113 // the borders are simply subtracted.
1114 const tools::Long nLeft = rAttrs.CalcLeft( this );
1115 const tools::Long nRight = rAttrs.CalcRight( this );
1116 aRectFnSet.SetXMargins( *this, nLeft, nRight );
1118 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1119 SwTwips nWidthArea;
1120 if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
1121 GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
1122 pSh->GetViewOptions()->getBrowseMode() )
1124 // Do not protrude the edge of the visible area. The page may be
1125 // wider, because there may be objects with excess width
1126 // (RootFrame::ImplCalcBrowseWidth())
1127 tools::Long nMinWidth = 0;
1129 for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
1131 // #i28701# - consider changed type of
1132 // <SwSortedObjs> entries
1133 SwAnchoredObject* pObj = (*GetDrawObjs())[i];
1134 const SwFrameFormat& rFormat = pObj->GetFrameFormat();
1135 const bool bFly = pObj->DynCastFlyFrame() != nullptr;
1136 if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
1137 || rFormat.GetFrameSize().GetWidthPercent())
1139 continue;
1142 if ( RndStdIds::FLY_AS_CHAR == rFormat.GetAnchor().GetAnchorId() )
1144 nMinWidth = std::max( nMinWidth,
1145 bFly ? rFormat.GetFrameSize().GetWidth()
1146 : pObj->GetObjRect().Width() );
1150 const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
1151 tools::Long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
1152 nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
1153 nWidth -= rAttrs.CalcRightLine();
1154 nWidth = std::max( nMinWidth, nWidth );
1156 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1157 aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
1160 if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
1162 // The PrtArea should already be at least MINLAY wide, matching the
1163 // minimal values of the UI
1164 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1165 aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
1166 SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
1168 if( aRectFnSet.GetLeft(aPrt) > nTmp )
1170 aRectFnSet.SetLeft( aPrt, nTmp );
1174 // The following rules apply for VarSize:
1175 // 1. The first entry of a chain has no top border
1176 // 2. There is never a bottom border
1177 // 3. The top border is the maximum of the distance
1178 // of Prev downwards and our own distance upwards
1179 // Those three rules apply when calculating spacings
1180 // that are given by UL- and LRSpace. There might be a spacing
1181 // in all directions however; this may be caused by borders
1182 // and / or shadows.
1183 // 4. The spacing for TextFrames corresponds to the interline lead,
1184 // at a minimum.
1186 nUpper = CalcUpperSpace( &rAttrs );
1188 SwTwips nLower = CalcLowerSpace( &rAttrs );
1189 if (IsCollapse()) {
1190 nUpper=0;
1191 nLower=0;
1195 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1196 aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
1199 nUpper += nLower;
1200 nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
1202 // If there's a difference between old and new size, call Grow() or
1203 // Shrink() respectively.
1204 if ( nUpper )
1206 if ( nUpper > 0 )
1207 GrowFrame( nUpper );
1208 else
1209 ShrinkFrame( -nUpper );
1213 #define STOP_FLY_FORMAT 10
1214 // - loop prevention
1215 const int cnStopFormat = 15;
1217 inline void ValidateSz( SwFrame *pFrame )
1219 if ( pFrame )
1221 pFrame->setFrameAreaSizeValid(true);
1222 pFrame->setFramePrintAreaValid(true);
1226 void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
1228 OSL_ENSURE( GetUpper(), "no Upper?" );
1229 OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );
1231 if ( !IsFollow() && StackHack::IsLocked() )
1232 return;
1234 if ( IsJoinLocked() )
1235 return;
1237 OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );
1239 StackHack aHack;
1241 if ( static_cast<SwTextFrame*>(this)->IsLocked() )
1243 OSL_FAIL( "Format for locked TextFrame." );
1244 return;
1247 std::optional<SwFrameDeleteGuard> oDeleteGuard(std::in_place, this);
1248 LockJoin();
1249 tools::Long nFormatCount = 0;
1250 // - loop prevention
1251 int nConsecutiveFormatsWithoutChange = 0;
1252 PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
1254 // takes care of the notification in the dtor
1255 std::optional<SwContentNotify> oNotify( std::in_place, this );
1257 // as long as bMakePage is true, a new page can be created (exactly once)
1258 bool bMakePage = true;
1259 // bMovedBwd gets set to true when the frame flows backwards
1260 bool bMovedBwd = false;
1261 // as long as bMovedFwd is false, the Frame may flow backwards (until
1262 // it has been moved forward once)
1263 bool bMovedFwd = false;
1264 sal_Bool bFormatted = false; // For the widow/orphan rules, we encourage the
1265 // last ContentFrame of a chain to format. This only
1266 // needs to happen once. Every time the Frame is
1267 // moved, the flag will have to be reset.
1268 bool bMustFit = false; // Once the emergency brake is pulled,
1269 // no other prepares will be triggered
1270 bool bFitPromise = false; // If a paragraph didn't fit, but promises
1271 // with WouldFit that it would adjust accordingly,
1272 // this flag is set. If it turns out that it
1273 // didn't keep it's promise, we can act in a
1274 // controlled fashion.
1275 const bool bFly = IsInFly();
1276 const bool bTab = IsInTab();
1277 const bool bFootnote = IsInFootnote();
1278 const bool bSct = IsInSct();
1279 Point aOldFramePos; // This is so we can compare with the last pos
1280 Point aOldPrtPos; // and determine whether it makes sense to Prepare
1282 SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
1283 const SwBorderAttrs &rAttrs = *aAccess.Get();
1285 if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
1287 oNotify->SetBordersJoinedWithPrev();
1290 const bool bKeep = IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem());
1292 std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
1293 if ( bFootnote )
1295 SwFootnoteFrame *pFootnote = FindFootnoteFrame();
1296 SwSectionFrame* pSct = pFootnote->FindSctFrame();
1297 if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
1299 SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
1300 pFootnote->GetAttr()->GetFootnote().IsEndNote() );
1301 if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
1302 pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
1303 static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
1307 if ( GetUpper()->IsSctFrame() &&
1308 HasFollow() && !GetFollow()->IsDeleteForbidden() &&
1309 &GetFollow()->GetFrame() == GetNext() )
1311 static_cast<SwTextFrame&>(*this).JoinFrame();
1314 // #i28701# - move master forward, if it has to move,
1315 // because of its object positioning.
1316 if ( !static_cast<SwTextFrame*>(this)->IsFollow() )
1318 sal_uInt32 nToPageNum = 0;
1319 const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
1320 *(GetAttrSet()->GetDoc()),
1321 *static_cast<SwTextFrame*>(this),
1322 nToPageNum );
1323 // #i58182#
1324 // Also move a paragraph forward, which is the first one inside a table cell.
1325 if ( bMoveFwdByObjPos &&
1326 FindPageFrame()->GetPhyPageNum() < nToPageNum &&
1327 ( lcl_Prev( this ) ||
1328 GetUpper()->IsCellFrame() ||
1329 ( GetUpper()->IsSctFrame() &&
1330 GetUpper()->GetUpper()->IsCellFrame() ) ) &&
1331 IsMoveable() )
1333 bMovedFwd = true;
1334 MoveFwd( bMakePage, false );
1338 // If a Follow sits next to its Master and doesn't fit, we know it can
1339 // be moved right now.
1340 if ( lcl_Prev( this ) && static_cast<SwTextFrame*>(this)->IsFollow() && IsMoveable() )
1342 bMovedFwd = true;
1343 // If follow frame is in table, its master will be the last in the
1344 // current table cell. Thus, invalidate the printing area of the master.
1345 if ( IsInTab() )
1347 lcl_Prev( this )->InvalidatePrt();
1349 MoveFwd( bMakePage, false );
1352 // Check footnote content for forward move.
1353 // If a content of a footnote is on a prior page/column as its invalid
1354 // reference, it can be moved forward.
1355 if ( bFootnote && !isFrameAreaPositionValid() )
1357 SwFootnoteFrame* pFootnote = FindFootnoteFrame();
1358 SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;
1360 if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
1362 SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
1363 SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame();
1364 //<loop of movefwd until condition held or no move>
1365 if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
1366 pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
1367 pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
1369 bMovedFwd = true;
1370 MoveFwd( bMakePage, false );
1375 SwRectFnSet aRectFnSet(this);
1377 SwFrame const* pMoveBwdPre(nullptr);
1378 bool isMoveBwdPreValid(false);
1380 SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
1381 SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;
1383 while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
1385 // - loop prevention
1386 aOldFrame_StopFormat2 = aOldFrame_StopFormat;
1387 aOldPrt_StopFormat2 = aOldPrt_StopFormat;
1388 aOldFrame_StopFormat = getFrameArea();
1389 aOldPrt_StopFormat = getFramePrintArea();
1391 bool bMoveable = IsMoveable();
1392 if (bMoveable)
1394 SwFrame *pPre = GetIndPrev();
1395 if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
1397 aRectFnSet.Refresh(this);
1398 bMovedFwd = true;
1399 if ( bMovedBwd )
1401 // While flowing back, the Upper was encouraged to
1402 // completely re-paint itself. We can skip this now after
1403 // flowing back and forth.
1404 GetUpper()->ResetCompletePaint();
1405 // The predecessor was invalidated, so this is obsolete as well now.
1406 assert(pPre);
1407 if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
1408 ::ValidateSz( pPre );
1410 bMoveable = IsMoveable();
1414 aOldFramePos = aRectFnSet.GetPos(getFrameArea());
1415 aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
1417 if ( !isFrameAreaPositionValid() )
1418 MakePos();
1420 //Set FixSize. VarSize is being adjusted by Format().
1421 if ( !isFrameAreaSizeValid() )
1423 // invalidate printing area flag, if the following conditions are hold:
1424 // - current frame width is 0.
1425 // - current printing area width is 0.
1426 // - frame width is adjusted to a value greater than 0.
1427 // - printing area flag is true.
1428 // Thus, it's assured that the printing area is adjusted, if the
1429 // frame area width changes its width from 0 to something greater
1430 // than 0.
1431 // Note: A text frame can be in such a situation, if the format is
1432 // triggered by method call <SwCursorShell::SetCursor()> after
1433 // loading the document.
1434 const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
1436 if ( isFramePrintAreaValid() &&
1437 nNewFrameWidth > 0 &&
1438 aRectFnSet.GetWidth(getFrameArea()) == 0 &&
1439 aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
1441 setFramePrintAreaValid(false);
1445 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1446 aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
1449 // When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos.
1450 if( GetUpper() && GetUpper()->IsFlyFrame() &&
1451 GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
1453 static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
1454 GetUpper()->SetCompletePaint();
1457 if ( !isFramePrintAreaValid() )
1459 const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
1460 // #i34730# - keep current frame height
1461 const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
1462 MakePrtArea( rAttrs );
1463 if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
1464 Prepare( PrepareHint::FixSizeChanged );
1465 // #i34730# - check, if frame height has changed.
1466 // If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to
1467 // force a format. The format will check in its method
1468 // <SwTextFrame::CalcPreps()>, if the already formatted lines still
1469 // fit and if not, performs necessary actions.
1470 // #i40150# - no check, if frame is undersized.
1471 if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
1473 // #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size
1474 // invalidation, if height decreases only by the additional
1475 // lower space as last content of a table cell and an existing
1476 // follow containing one line exists.
1477 const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea());
1478 const bool bNoPrepAdjustFrame =
1479 nHDiff > 0 && IsInTab() && GetFollow() &&
1480 (1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
1481 || aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
1482 GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff;
1483 if ( !bNoPrepAdjustFrame )
1485 Prepare( PrepareHint::AdjustSizeWithoutFormatting );
1486 setFrameAreaSizeValid(false);
1491 // To make the widow and orphan rules work, we need to notify the ContentFrame.
1492 // Criteria:
1493 // - It needs to be movable (otherwise, splitting doesn't make sense)
1494 // - It needs to overlap with the lower edge of the PrtArea of the Upper
1495 if ( !bMustFit )
1497 bool bWidow = true;
1498 const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
1499 if( bMoveable && !bFormatted &&
1500 ( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
1502 Prepare( PrepareHint::WidowsOrphans, nullptr, false );
1503 setFrameAreaSizeValid(false);
1504 bWidow = false;
1506 if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
1507 aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
1509 // In this Prepare, an InvalidateSize_() might happen.
1510 // isFrameAreaSizeValid() becomes false and Format() gets called.
1511 Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
1512 if ( bWidow && GetFollow() )
1514 Prepare( PrepareHint::WidowsOrphans, nullptr, false );
1515 setFrameAreaSizeValid(false);
1519 if ( !isFrameAreaSizeValid() )
1521 setFrameAreaSizeValid(true);
1522 bFormatted = true;
1523 ++nFormatCount;
1524 if( nFormatCount > STOP_FLY_FORMAT )
1525 SetFlyLock( true );
1526 // - loop prevention
1527 // No format any longer, if <cnStopFormat> consecutive formats
1528 // without change occur.
1529 if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
1531 Format(getRootFrame()->GetCurrShell()->GetOut());
1533 #if OSL_DEBUG_LEVEL > 0
1534 else
1536 OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
1538 #endif
1541 // If this is the first one in a chain, check if this can flow
1542 // backwards (if this is movable at all).
1543 // To prevent oscillations/loops, check that this has not just
1544 // flowed forwards.
1545 bool bDummy;
1546 auto const pTemp(GetIndPrev());
1547 auto const bTemp(pTemp && pTemp->isFrameAreaSizeValid()
1548 && pTemp->isFramePrintAreaValid());
1549 if ( !lcl_Prev( this ) &&
1550 !bMovedFwd &&
1551 ( bMoveable || ( bFly && !bTab ) ) &&
1552 ( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
1553 && MoveBwd( bDummy ) )
1555 aRectFnSet.Refresh(this);
1556 pMoveBwdPre = pTemp;
1557 isMoveBwdPreValid = bTemp;
1558 bMovedBwd = true;
1559 bFormatted = false;
1560 if ( bKeep && bMoveable )
1562 if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
1564 bMovedFwd = true;
1565 bMoveable = IsMoveable();
1566 aRectFnSet.Refresh(this);
1568 Point aOldPos = aRectFnSet.GetPos(getFrameArea());
1569 MakePos();
1570 if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
1572 Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
1573 if ( !isFrameAreaSizeValid() )
1576 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1577 aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
1580 if ( !isFramePrintAreaValid() )
1582 const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
1583 MakePrtArea( rAttrs );
1584 if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
1585 Prepare( PrepareHint::FixSizeChanged, nullptr, false );
1587 if( GetFollow() )
1589 Prepare( PrepareHint::WidowsOrphans, nullptr, false );
1592 setFrameAreaSizeValid(true);
1593 bFormatted = true;
1594 Format(getRootFrame()->GetCurrShell()->GetOut());
1597 SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
1598 while( pNxt && pNxt->IsSctFrame() )
1599 { // Leave empty sections out, go into the other ones.
1600 if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
1602 SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
1603 if( pTmp )
1605 pNxt = pTmp;
1606 break;
1609 pNxt = pNxt->FindNext();
1611 if ( pNxt )
1613 pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
1614 if( isFrameAreaPositionValid() && !GetIndNext() )
1616 SwSectionFrame *pSct = FindSctFrame();
1617 if( pSct && !pSct->isFrameAreaSizeValid() )
1619 SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
1620 if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
1622 setFrameAreaPositionValid(false);
1625 else
1627 setFrameAreaPositionValid(false);
1634 // In footnotes, the TextFrame may validate itself, which can lead to the
1635 // situation that it's position is wrong despite being "valid".
1636 if ( isFrameAreaPositionValid() )
1638 // #i59341#
1639 // Workaround for inadequate layout algorithm:
1640 // suppress invalidation and calculation of position, if paragraph
1641 // has formatted itself at least STOP_FLY_FORMAT times and
1642 // has anchored objects.
1643 // Thus, the anchored objects get the possibility to format itself
1644 // and this probably solve the layout loop.
1645 if ( bFootnote &&
1646 nFormatCount <= STOP_FLY_FORMAT &&
1647 !GetDrawObjs() )
1649 setFrameAreaPositionValid(false);
1650 MakePos();
1651 aOldFramePos = aRectFnSet.GetPos(getFrameArea());
1652 aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
1656 // - loop prevention
1658 if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
1659 (aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
1661 ++nConsecutiveFormatsWithoutChange;
1663 else
1665 nConsecutiveFormatsWithoutChange = 0;
1669 // Yet again an invalid value? Repeat from the start...
1670 if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
1671 continue;
1673 // Done?
1674 // Attention: because height == 0, it's better to use Top()+Height() instead of
1675 // Bottom(). This might happen with undersized TextFrames on the lower edge of a
1676 // multi-column section
1677 const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
1678 tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
1680 // Hide whitespace may require not to insert a new page.
1681 SwPageFrame* pPageFrame = FindPageFrame();
1682 const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
1683 if (!bHeightValid)
1685 pPageFrame->InvalidateSize();
1686 nBottomDist = 0;
1689 if( nBottomDist >= 0 )
1691 if ( bKeep && bMoveable )
1693 // We make sure the successor will be formatted the same.
1694 // This way, we keep control until (almost) everything is stable,
1695 // allowing us to avoid endless loops caused by ever repeating
1696 // retries.
1698 // bMoveFwdInvalid is required for #38407#. This was originally solved
1699 // in flowfrm.cxx rev 1.38, but broke the above schema and
1700 // preferred to play towers of hanoi (#43669#).
1701 SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
1702 // For sections we prefer the content, because it can change
1703 // the page if required.
1704 while( pNxt && pNxt->IsSctFrame() )
1706 if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
1708 pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
1709 break;
1711 pNxt = pNxt->FindNext();
1713 if ( pNxt )
1715 const bool bMoveFwdInvalid = nullptr != GetIndNext();
1716 const bool bNxtNew =
1717 ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
1718 (!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow());
1720 pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
1722 if ( !bMovedBwd &&
1723 ((bMoveFwdInvalid && !GetIndNext()) ||
1724 bNxtNew) )
1726 if( bMovedFwd )
1727 oNotify->SetInvaKeep();
1728 bMovedFwd = false;
1732 continue;
1735 // I don't fit into my parents, so it's time to make changes
1736 // as constructively as possible.
1738 //If I'm NOT allowed to leave the parent Frame, I've got a problem.
1739 // Following Arthur Dent, we do the only thing that you can do with
1740 // an unsolvable problem: We ignore it with all our power.
1741 if ( !bMoveable || IsUndersized() )
1743 break;
1746 // If there's no way I can make myself fit into my Upper, the situation
1747 // could still probably be mitigated by splitting up.
1748 // This situation arises with freshly created Follows that had been moved
1749 // to the next page but is still too big for it - ie. needs to be split
1750 // as well.
1752 // If I'm unable to split (WouldFit()) and can't be fitted, I'm going
1753 // to tell my TextFrame part that, if possible, we still need to split despite
1754 // the "don't split" attribute.
1755 bool bMoveOrFit = false;
1756 bool bDontMoveMe = !GetIndPrev();
1757 if( bDontMoveMe && IsInSct() )
1759 SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
1760 bDontMoveMe = !pBoss->IsInSct() ||
1761 ( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
1764 // Finally, we are able to split table rows. Therefore, bDontMoveMe
1765 // can be set to false:
1766 if( bDontMoveMe && IsInTab() &&
1767 nullptr != GetNextCellLeaf() )
1768 bDontMoveMe = false;
1770 assert(bMoveable);
1772 if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
1773 aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
1775 if ( !bFitPromise )
1777 SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
1778 aRectFnSet.GetTop(getFramePrintArea());
1779 bool bSplit = !IsFwdMoveAllowed();
1780 if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
1782 Prepare( PrepareHint::WidowsOrphans, nullptr, false );
1783 setFrameAreaSizeValid(false);
1784 bFitPromise = true;
1785 continue;
1788 * In earlier days, we never tried to fit TextFrames in
1789 * frames and sections using bMoveOrFit by ignoring
1790 * its attributes (Widows, Keep).
1791 * This should have been done at least for column frames;
1792 * as it must be tried anyway with linked frames and sections.
1793 * Exception: If we sit in FormatWidthCols, we must not ignore
1794 * the attributes.
1796 else if ( !bFootnote &&
1797 ( !bFly || !FindFlyFrame()->IsColLocked() ) &&
1798 ( !bSct || !FindSctFrame()->IsColLocked() ) )
1799 bMoveOrFit = true;
1801 #if OSL_DEBUG_LEVEL > 0
1802 else
1804 OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
1806 #endif
1809 // Let's see if I can find some space somewhere...
1810 // footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
1811 SwFrame *pPre = GetIndPrev();
1812 SwFrame *pOldUp = GetUpper();
1814 /* MA 13. Oct. 98: What is this supposed to be!?
1815 * AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be
1816 * moved not only to the next column, but probably even to the next page, creating
1817 * a section-follow there.
1819 if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
1820 pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
1821 ( pPre || pOldUp->GetUpper()->GetPrev() ) &&
1822 static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
1824 bMovedFwd = false;
1827 const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
1828 const tools::Long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());
1830 if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
1831 bMakePage = false;
1832 aRectFnSet.Refresh(this);
1833 if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
1834 { // SwFlowFrame::CutTree() could have formatted and deleted pPre
1835 auto const pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
1836 FindFootnoteFrame())->GetMaster());
1837 bool bReset = true;
1838 if (pPrevFootnoteFrame)
1839 { // use GetIndNext() in case there are sections
1840 for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
1842 if (p == pPre)
1844 bReset = false;
1845 break;
1849 if (bReset)
1851 pPre = nullptr;
1855 // If MoveFwd moves the paragraph to the next page, a following
1856 // paragraph, which contains footnotes can cause the old upper
1857 // frame to grow. In this case we explicitly allow a new check
1858 // for MoveBwd. Robust: We also check the bMovedBwd flag again.
1859 // If pOldUp was a footnote frame, it has been deleted inside MoveFwd.
1860 // Therefore we only check for growing body frames.
1861 bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
1862 aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;
1864 bFormatted = false;
1865 if ( bMoveOrFit && GetUpper() == pOldUp )
1867 // FME 2007-08-30 #i81146# new loop control
1868 if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
1870 Prepare( PrepareHint::MustFit, nullptr, false );
1871 setFrameAreaSizeValid(false);
1872 bMustFit = true;
1873 continue;
1876 #if OSL_DEBUG_LEVEL > 0
1877 OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" );
1878 #endif
1880 if ( bMovedBwd && GetUpper() )
1881 { // Retire invalidations that have become useless.
1882 GetUpper()->ResetCompletePaint();
1883 if( pPre && !pPre->IsSctFrame() )
1884 ::ValidateSz( pPre );
1887 } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
1889 // NEW: Looping Louie (Light). Should not be applied in balanced sections.
1890 // Should only be applied if there is no better solution!
1891 LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
1894 ( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||
1896 // #i33887#
1897 ( IsInSct() && bKeep )
1899 // ... add your conditions here ...
1902 static_cast<SwTextFrame&>(*this) );
1904 pSaveFootnote.reset();
1906 UnlockJoin();
1907 oDeleteGuard.reset();
1908 if ( bMovedFwd || bMovedBwd )
1909 oNotify->SetInvaKeep();
1910 if ( bMovedFwd )
1912 oNotify->SetInvalidatePrevPrtArea();
1914 oNotify.reset();
1915 SetFlyLock( false );
1918 void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
1920 // fix(25455): Validate, otherwise this leads to a recursion.
1921 // The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as
1922 // the Keep may not be considered properly anymore (27417).
1923 const bool bOldPos = pFrame->isFrameAreaPositionValid();
1924 const bool bOldSz = pFrame->isFrameAreaSizeValid();
1925 const bool bOldPrt = pFrame->isFramePrintAreaValid();
1926 pFrame->setFrameAreaPositionValid(true);
1927 pFrame->setFrameAreaSizeValid(true);
1928 pFrame->setFramePrintAreaValid(true);
1930 // fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and
1931 // we recursively end up in here again.
1932 if ( pNxt->IsContentFrame() )
1934 SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
1935 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
1936 const SwBorderAttrs &rAttrs = *aAccess.Get();
1937 if ( !pNxt->isFrameAreaSizeValid() )
1939 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
1941 if( pNxt->IsVertical() )
1943 aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
1945 else
1947 aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
1950 static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
1951 pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
1953 else
1955 SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
1956 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
1957 const SwBorderAttrs &rAttrs = *aAccess.Get();
1958 if ( !pNxt->isFrameAreaSizeValid() )
1960 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
1962 if( pNxt->IsVertical() )
1964 aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
1966 else
1968 aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
1971 pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
1974 pFrame->setFrameAreaPositionValid(bOldPos);
1975 pFrame->setFrameAreaSizeValid(bOldSz);
1976 pFrame->setFramePrintAreaValid(bOldPrt);
1979 /// This routine checks whether there are no other FootnoteBosses
1980 /// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
1981 static bool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
1983 assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
1984 pFrame = pFrame->FindFootnoteBossFrame();
1985 pNxt = pNxt->FindFootnoteBossFrame();
1986 // If pFrame is a last column, we use the page instead.
1987 while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
1988 pFrame = pFrame->GetUpper()->FindFootnoteBossFrame();
1989 // If pNxt is a first column, we use the page instead.
1990 while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
1991 pNxt = pNxt->GetUpper()->FindFootnoteBossFrame();
1992 // So... now pFrame and pNxt are either two adjacent pages or columns.
1993 return pFrame && pNxt && pFrame->GetNext() == pNxt;
1996 bool SwContentFrame::WouldFit_( SwTwips nSpace,
1997 SwLayoutFrame *pNewUpper,
1998 bool bTstMove,
1999 const bool bObjsInNewUpper )
2001 // To have the footnote select its place carefully, it needs
2002 // to be moved in any case if there is at least one page/column
2003 // between the footnote and the new Upper.
2004 SwFootnoteFrame* pFootnoteFrame = nullptr;
2005 if ( IsInFootnote() )
2007 if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) )
2008 return true;
2009 pFootnoteFrame = FindFootnoteFrame();
2012 bool bRet;
2013 bool bSplit = !pNewUpper->Lower();
2014 SwContentFrame *pFrame = this;
2015 const SwFrame *pTmpPrev = pNewUpper->Lower();
2016 if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
2017 pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
2018 while ( pTmpPrev && pTmpPrev->GetNext() )
2019 pTmpPrev = pTmpPrev->GetNext();
2021 // tdf#156727 if the previous one has keep-with-next, ignore it on this one!
2022 bool const isIgnoreKeep(pTmpPrev && pTmpPrev->IsFlowFrame()
2023 && SwFlowFrame::CastFlowFrame(pTmpPrev)->IsKeep(
2024 pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()));
2028 // #i46181#
2029 SwTwips nSecondCheck = 0;
2030 SwTwips nOldSpace = nSpace;
2031 bool bOldSplit = bSplit;
2033 if ( bTstMove || IsInFly() || ( IsInSct() &&
2034 ( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
2035 pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
2037 // This is going to get a bit insidious now. If you're faint of heart,
2038 // you'd better look away here. If a Fly contains columns, then the Contents
2039 // are movable, except ones in the last column (see SwFrame::IsMoveable()).
2040 // Of course they're allowed to float back. WouldFit() only returns a usable
2041 // value if the Frame is movable. To fool WouldFit() into believing there's
2042 // a movable Frame, I'm just going to hang it somewhere else for the time.
2043 // The same procedure applies for column sections to make SwSectionFrame::Growable()
2044 // return the proper value.
2045 // Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if
2046 // there's no SwFootnoteFrame there.
2047 SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ?
2048 static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
2049 SwLayoutFrame *pUp = pTmpFrame->GetUpper();
2050 SwFrame *pOldNext = pTmpFrame->GetNext();
2051 pTmpFrame->RemoveFromLayout();
2052 pTmpFrame->InsertBefore( pNewUpper, nullptr );
2053 // tdf#107126 for a section in a footnote, we have only inserted
2054 // the SwTextFrame but no SwSectionFrame - reset mbInfSct flag
2055 // to avoid crashing (but perhaps we should create a temp
2056 // SwSectionFrame here because WidowsAndOrphans checks for that?)
2057 pTmpFrame->InvalidateInfFlags();
2058 if ( pFrame->IsTextFrame() &&
2059 ( bTstMove ||
2060 static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
2061 ( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
2062 !static_cast<SwTextFrame*>(pFrame)->IsEmpty()
2067 bTstMove = true;
2068 bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
2070 else
2071 bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
2073 pTmpFrame->RemoveFromLayout();
2074 pTmpFrame->InsertBefore( pUp, pOldNext );
2075 pTmpFrame->InvalidateInfFlags(); // restore flags
2077 else
2079 bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
2080 nSecondCheck = !bSplit ? 1 : 0;
2083 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
2084 const SwBorderAttrs &rAttrs = *aAccess.Get();
2086 // Sad but true: We need to consider the spacing in our calculation.
2087 // This already happened in TestFormat.
2088 if ( bRet && !bTstMove )
2090 SwTwips nUpper;
2092 if ( pTmpPrev )
2094 nUpper = CalcUpperSpace( nullptr, pTmpPrev );
2096 // in balanced columned section frames we do not want the
2097 // common border
2098 bool bCommonBorder = true;
2099 if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
2101 const SwSectionFrame* pSct = pFrame->FindSctFrame();
2102 bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
2105 // #i46181#
2106 nSecondCheck = ( 1 == nSecondCheck &&
2107 pFrame == this &&
2108 IsTextFrame() &&
2109 bCommonBorder &&
2110 !static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
2111 nUpper :
2114 nUpper += bCommonBorder ?
2115 rAttrs.GetBottomLine( *pFrame ) :
2116 rAttrs.CalcBottomLine();
2119 else
2121 // #i46181#
2122 nSecondCheck = 0;
2124 if( pFrame->IsVertical() )
2125 nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
2126 else
2127 nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
2130 nSpace -= nUpper;
2132 if ( nSpace < 0 )
2134 bRet = false;
2136 // #i46181#
2137 if ( nSecondCheck > 0 )
2139 // The following code is intended to solve a (rare) problem
2140 // causing some frames not to move backward:
2141 // SwTextFrame::WouldFit() claims that the whole paragraph
2142 // fits into the given space and subtracts the height of
2143 // all lines from nSpace. nSpace - nUpper is not a valid
2144 // indicator if the frame should be allowed to move backward.
2145 // We do a second check with the original remaining space
2146 // reduced by the required upper space:
2147 nOldSpace -= nSecondCheck;
2148 const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true);
2149 if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
2151 bRet = true;
2152 bSplit = true;
2158 // Also consider lower spacing in table cells
2159 IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess());
2160 if ( bRet && IsInTab() &&
2161 rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
2163 nSpace -= rAttrs.GetULSpace().GetLower();
2165 if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
2167 nSpace -= rAttrs.CalcLineSpacing();
2169 if ( nSpace < 0 )
2171 bRet = false;
2175 if (bRet && !bSplit && !isIgnoreKeep
2176 && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
2178 if( bTstMove )
2180 while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
2182 pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
2184 // If last follow frame of <this> text frame isn't valid,
2185 // a formatting of the next content frame doesn't makes sense.
2186 // Thus, return true.
2187 if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
2189 OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" );
2190 return true;
2193 SwFrame *pNxt;
2194 if( nullptr != (pNxt = pFrame->FindNext()) && pNxt->IsContentFrame() &&
2195 ( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
2196 pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
2198 // TestFormat(?) does not like paragraph- or character anchored objects.
2200 // current solution for the test formatting doesn't work, if
2201 // objects are present in the remaining area of the new upper
2202 if ( bTstMove &&
2203 ( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
2205 return true;
2208 if ( !pNxt->isFrameAreaDefinitionValid() )
2210 MakeNxt( pFrame, pNxt );
2213 // Little trick: if the next has a predecessor, then the paragraph
2214 // spacing has been calculated already, and we don't need to re-calculate
2215 // it in an expensive way.
2216 if( lcl_NotHiddenPrev( pNxt ) )
2217 pTmpPrev = nullptr;
2218 else
2220 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() )
2221 pTmpPrev = lcl_NotHiddenPrev( pFrame );
2222 else
2223 pTmpPrev = pFrame;
2225 pFrame = static_cast<SwContentFrame*>(pNxt);
2227 else
2228 pFrame = nullptr;
2230 else
2231 pFrame = nullptr;
2233 } while ( bRet && pFrame );
2235 return bRet;
2238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */