/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/// Return value tells whether the Frame should be moved. bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
{ if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
{ // Floating back a frm uses a bit of time unfortunately. // The most common case is the following: The Frame wants to float to // somewhere where the FixSize is the same that the Frame itself has already. // In that case it's pretty easy to check if the Frame has enough space // for its VarSize. If this is NOT the case, we already know that // we don't need to move. // The Frame checks itself whether it has enough space - respecting the fact // that it could possibly split itself if needed. // If, however, the FixSize differs from the Frame or Flys are involved // (either in the old or the new position), checking is pointless, // and we have to move the Frame just to see what happens - if there's // some space available to do it, that is.
// The FixSize of the containers of Contents is always the width.
// If we moved more than one sheet back (for example jumping over empty // pages), we have to move either way. Otherwise, if the Frame doesn't fit // into the page, empty pages wouldn't be respected anymore.
sal_uInt8 nMoveAnyway = 0;
SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
SwPageFrame *pOldPage = FindPageFrame();
if ( SwFlowFrame::IsMoveBwdJump() ) returntrue;
if( IsInFootnote() && IsInSct() )
{
SwFootnoteFrame* pFootnote = FindFootnoteFrame();
SwSectionFrame* pMySect = pFootnote->FindSctFrame(); if( pMySect && pMySect->IsFootnoteLock() )
{
SwSectionFrame *pSect = pNewUpper->FindSctFrame(); while( pSect && pSect->IsInFootnote() )
pSect = pSect->GetUpper()->FindSctFrame();
OSL_ENSURE( pSect, "Escaping footnote" ); if( pSect != pMySect ) returnfalse;
}
}
SwRectFnSet aRectFnSet(this);
SwRectFnSet fnRectX(pNewUpper); if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) { // In this case, only a WouldFit_ with test move is possible
nMoveAnyway = 2;
}
// Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
{ const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
SwTwips nSpace = 0;
SwRect aRect( pNewUpper->getFramePrintArea() );
aRect.Pos() += pNewUpper->getFrameArea().Pos(); const SwFrame *pPrevFrame = pNewUpper->Lower(); while ( pPrevFrame )
{
SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea()); // Consider lower spacing of last frame in a table cell
{ // Check if last frame is inside table and if it includes its lower spacing. if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
{ const SwFrame* pLastFrame = pPrevFrame; // if last frame is a section, take its last content if ( pPrevFrame->IsSctFrame() )
{
pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent(); if ( pLastFrame &&
pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
{
pLastFrame = pLastFrame->FindTabFrame();
}
}
//determine space left in new upper frame
nSpace = fnRectX.GetHeight(aRect); const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell(); if ( IsInFootnote() ||
(pSh && pSh->GetViewOptions()->getBrowseMode()) ||
pNewUpper->IsCellFrame() ||
( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
( pNewUpper->IsColBodyFrame() &&
!pNewUpper->GetUpper()->GetPrev() &&
!pNewUpper->GetUpper()->GetNext() ) ) ) )
nSpace += pNewUpper->Grow( LONG_MAX, true );
auto pTextFrame = DynCastTextFrame(); if (pTextFrame)
{ // This is a text frame. Check if it's an anchor for a non-last element in a split // fly chain. If so, we can only move back in case not only the text frame itself, // but also its fly fits nSpace.
SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj(); if (pFly && pFly->getFrameArea().Height() > nSpace)
{ returnfalse;
}
}
if ( nMoveAnyway < 3 )
{ if (nSpace || IsHiddenNow())
{ // Do not notify footnotes which are stuck to the paragraph: // This would require extremely confusing code, taking into // account the widths // and Flys, that in turn influence the footnotes, ...
// WouldFit_ can only be used if the width is the same and // ONLY self-anchored Flys are present.
// WouldFit_ can also be used if ONLY Flys anchored // somewhere else are present. // In this case, the width doesn't even matter, // because we're running a TestFormat in the new upper. const sal_uInt8 nBwdMoveNecessaryResult =
BwdMoveNecessary( pNewPage, aRect); constbool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
nBwdMoveNecessaryResult == 3 );
return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
bObjsInNewUpper );
} // It's impossible for WouldFit_ to return a usable result if // we have a fresh multi-column section - so we really have to // float back unless there is no space. return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
!fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
( pNewUpper->GetUpper()->GetPrev() ||
pNewUpper->GetUpper()->GetNext() );
}
// Check for space left in new upper return nSpace != 0 || IsHiddenNow();
}
} returnfalse;
}
// Calc methods
// Two little friendships form a secret society inlinevoid PrepareLock( SwFlowFrame *pTab )
{
pTab->LockJoin();
} inlinevoid PrepareUnlock( SwFlowFrame *pTab )
{
pTab->UnlockJoin();
}
// hopefully, one day this function simply will return 'false' staticbool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
{ return !rFrame.GetUpper()->IsSctFrame() &&
!rFrame.GetUpper()->IsFooterFrame() && // No format of upper Writer fly frame
!rFrame.GetUpper()->IsFlyFrame() &&
!( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
!( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
}
/** Prepares the Frame for "formatting" (MakeAll()). * * This method serves to save stack space: To calculate the position of the Frame * we have to make sure that the positions of Upper and Prev respectively are * valid. This may require a recursive call (a loop would be quite expensive, * as it's not required very often). * * Every call of MakeAll requires around 500 bytes on the stack - you easily * see where this leads to. This method requires only a little bit of stack * space, so the recursive call should not be a problem here. * * Another advantage is that one nice day, this method and with it the * formatting of predecessors could be avoided. Then it could probably be * possible to jump "quickly" to the document's end. * * @see MakeAll()
*/ void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
{
StackHack aHack; if ( GetUpper() )
{
SwFrameDeleteGuard aDeleteGuard(this); if ( lcl_IsCalcUpperAllowed( *this ) )
GetUpper()->Calc(pRenderContext);
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." ); if ( !GetUpper() ) return;
if ( bTab )
{
pThis = static_cast<SwTabFrame*>(this);
bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
::PrepareLock( static_cast<SwTabFrame*>(this) );
bFoll = pThis->IsFollow();
} elseif( IsSctFrame() )
{
pThis = static_cast<SwSectionFrame*>(this);
bFoll = pThis->IsFollow();
bNoSect = false;
} elseif ( bCnt )
{
bFoll = pThis->IsFollow(); if ( bFoll && GetPrev() )
{ // Do not follow the chain when we need only one instance const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster(); if ( pMaster && pMaster->IsLocked() )
{
MakeAll(pRenderContext); return;
}
}
}
// There is no format of previous frame, if current frame is a table // frame and its previous frame wants to keep with it. bool bFormatPrev{!bTab}; if (!bFormatPrev)
{
SwFrame const* pPrev{this}; do
{
pPrev = pPrev->GetPrev();
} while (pPrev && pPrev->IsHiddenNow());
bFormatPrev = pPrev && !pPrev->GetAttrSet()->GetKeep().GetValue();
} if ( bFormatPrev )
{
SwFrame *pFrame = GetUpper()->Lower(); while ( pFrame != this )
{
OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." ); if ( !pFrame ) return; //Oioioioi ...
if ( !pFrame->isFrameAreaDefinitionValid() )
{ // A small interference that hopefully improves on the stability: // If I'm Follow AND neighbor of a Frame before me, it would delete // me when formatting. This as you can see could easily become a // confusing situation that we want to avoid. if ( bFoll && pFrame->IsFlowFrame() &&
SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) ) break;
boolconst isLast(pFrame->GetNext() == this); // note: this seems obvious but does *not* hold, a MakeAll() // could move more than 1 frame backwards! // that's why FindNext() is used below // assert(pFrame->GetUpper() == GetUpper());
pFrame->MakeAll(pRenderContext); if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() ) break; if (isLast && pFrame->GetUpper() != GetUpper())
{
assert(GetUpper()->Lower() == this // empty section frames are created all the time...
|| GetUpper()->Lower()->IsSctFrame() // tab frame/section frame may split multiple times
|| ( SwFlowFrame::CastFlowFrame(pFrame)
&& SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
&& SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
&& (GetUpper()->Lower()->GetNext() == this // if it's more than 10 pages long...
|| (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
== SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
&& GetUpper()->Lower()->GetNext()->GetNext() == this) // pre-existing empty section frames may end up between them...
|| GetUpper()->Lower()->GetNext()->IsSctFrame()))); break; // tdf#119109 frame was moved backward, prevent // FindNext() returning a frame inside this if
} // this is a table!
} // With ContentFrames, the chain may be broken while walking through // it. Therefore we have to figure out the next frame in a bit more // complicated way. However, I'll HAVE to get back to myself // sometime again.
pFrame = pFrame->FindNext();
// If we started out in a SectionFrame, it might have happened that // we landed in a Section Follow via the MakeAll calls. // FindNext only gives us the SectionFrame, not it's content - we // won't find ourselves anymore! if( bNoSect && pFrame && pFrame->IsSctFrame() )
{
SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny(); if( pCnt )
pFrame = pCnt;
}
}
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." ); if ( !GetUpper() ) return;
if ( lcl_IsCalcUpperAllowed( *this ) )
GetUpper()->Calc(pRenderContext);
SwFrame *pFrame = GetUpper()->Lower(); while ( pFrame != this )
{
OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." ); if ( !pFrame ) return; //Oioioioi ...
if ( !pFrame->isFrameAreaDefinitionValid() )
{ // A small interference that hopefully improves on the stability: // If I'm Follow AND neighbor of a Frame before me, it would delete // me when formatting. This as you can see could easily become a // confusing situation that we want to avoid. if ( bFoll && pFrame->IsFlowFrame() &&
SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) ) break;
boolconst isLast(pFrame->GetNext() == this);
pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut()); if (isLast && pFrame->GetUpper() != GetUpper())
{
assert(GetUpper()->Lower() == this // empty section frames are created all the time...
|| GetUpper()->Lower()->IsSctFrame() // tab frame/section frame may split multiple times
|| ( SwFlowFrame::CastFlowFrame(pFrame)
&& SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
&& SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
&& (GetUpper()->Lower()->GetNext() == this // if it's more than 10 pages long...
|| (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
== SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
&& GetUpper()->Lower()->GetNext()->GetNext() == this) // pre-existing empty section frames may end up between them...
|| GetUpper()->Lower()->GetNext()->IsSctFrame()))); break; // tdf#119109 frame was moved backward, prevent // FindNext() returning a frame inside this if
} // this is a table!
} // With ContentFrames, the chain may be broken while walking through // it. Therefore we have to figure out the next frame in a bit more // complicated way. However, I'll HAVE to get back to myself // sometime again.
pFrame = pFrame->FindNext(); if( bNoSect && pFrame && pFrame->IsSctFrame() )
{
SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny(); if( pCnt )
pFrame = pCnt;
}
}
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." ); if ( !GetUpper() ) return;
// Here we return GetPrev(); however we will ignore empty SectionFrames static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
{
SwFrame* pRet = pFrame->GetPrev(); if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
bSectPrv && !pFrame->IsColumnFrame() )
pRet = pFrame->GetUpper()->GetPrev(); while( pRet && pRet->IsSctFrame() &&
!static_cast<SwSectionFrame*>(pRet)->GetSection() )
pRet = pRet->GetPrev(); return pRet;
}
static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
{
SwFrame *pRet = pFrame; do
{
pRet = lcl_Prev( pRet );
} while ( pRet && pRet->IsHiddenNow() ); return pRet;
}
void SwFrame::MakePos()
{ if ( isFrameAreaPositionValid() ) return;
setFrameAreaPositionValid(true); bool bUseUpper = false;
SwFrame* pPrv = lcl_Prev( this ); if ( pPrv &&
( !pPrv->IsContentFrame() ||
( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
)
{ if ( !StackHack::IsLocked() &&
( !IsInSct() || IsSctFrame() ) &&
!pPrv->IsSctFrame() &&
!pPrv->GetAttrSet()->GetKeep().GetValue()
)
{ // tdf#151866 pPrv may MoveBwd and if this is a newly created // section frame then CheckPageDescs() may delete the whole page!
SwFrameDeleteGuard g(this); // Prevent it.
pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
} elseif ( pPrv->getFrameArea().Top() == 0 )
{
bUseUpper = true;
}
}
// cells may now leave their uppers if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
}
} elseif( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
{ if ( aRectFnSet.IsVertL2R() )
{
aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
} else
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
}
} else
{
aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
}
} elseif ( GetUpper() )
{ // If parent frame is a footer frame and its <ColLocked()>, then // do *not* calculate it. // NOTE: Footer frame is <ColLocked()> during its // <FormatSize(..)>, which is called from <Format(..)>, which // is called from <MakeAll()>, which is called from <Calc()>. // #i56850# // - no format of upper Writer fly frame, which is anchored // at-paragraph or at-character. if ( !GetUpper()->IsTabFrame() &&
!( IsTabFrame() && GetUpper()->IsInTab() ) &&
!GetUpper()->IsSctFrame() &&
!dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
!( GetUpper()->IsFooterFrame() &&
GetUpper()->IsColLocked() )
)
{
GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
}
pPrv = lcl_Prev( this, false ); if ( !bUseUpper && pPrv )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos( pPrv->getFrameArea().Pos() );
const SwRect aOldRect( getFrameArea() ); // Adjust root size const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
std::optional<SwBorderAttrAccess> oAccess; const SwBorderAttrs*pAttrs = nullptr;
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{ if ( !isFrameAreaPositionValid() )
{
setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
}
if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{ if ( IsEmptyPage() )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Width( 0 );
aFrm.Height( 0 );
SwLayoutFrame *pBody = FindBodyCont(); if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
{ // Columns have a fixed height
aFrm.Height( pAttrs->GetSize().Height() );
} else
{ // In pages without columns, the content defines the size.
tools::Long nBot = GetContentHeight(nTop, nBottom);
// #i35143# - If second page frame // exists, the first page doesn't have to fulfill the // visible area. if ( !GetPrev() && !GetNext() )
{
nBot = std::max( nBot, pSh->VisArea().Height() );
} // #i35143# - Assure, that the page // doesn't exceed the defined browse height.
aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
}
}
setFrameAreaSizeValid(true);
setFramePrintAreaValid(true); continue;
} elseif (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
{
tools::Long height = 0;
SwLayoutFrame *pBody = FindBodyCont();
SwTwips nFullBodyHeight = pAttrs->GetSize().Height() - pAttrs->CalcTop() - pAttrs->CalcBottom(); if (pRootFrame->GetLastPage() == this)
{ // Last page is only reduced by the top/bottom margin, the body frame height // is not reduced.
height = nFullBodyHeight;
} elseif ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
{ // Columns have a fixed height
height = pAttrs->GetSize().Height();
} else
{ // No need for borders.
height = GetContentHeight(0, 0); if (height > nFullBodyHeight)
{ // Content height would be larger than the show-whitespace body height, // limit it.
height = nFullBodyHeight;
}
}
// Fallback to default formatting. Especially relevant // when loading a doc when Hide Whitespace is enabled. // Heights are zero initially.
}
// Set FixSize. For pages, this is not done from Upper, but from // the attribute. //FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.SSize( pAttrs->GetSize() );
}
Format( pRenderContext, pAttrs );
}
}
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(), "Upper (Root) must be wide enough to contain the widest page");
}
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{ if ( !isFrameAreaPositionValid() )
{
MakePos();
}
if (IsHiddenNow())
{
MakeValidZeroHeight();
}
if ( GetUpper() )
{ // NEW TABLES if ( IsLeaveUpperAllowed() )
{ if ( !isFrameAreaSizeValid() )
{
setFramePrintAreaValid(false);
}
} else
{ if ( !isFrameAreaSizeValid() )
{ // Set FixSize; VarSize is set by Format() after calculating the PrtArea
setFramePrintAreaValid(false);
bool SwFrame::IsCollapse() const
{ if (!IsTextFrame()) returnfalse;
const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this); const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps(); // TODO this SwTextNode function is pointless and should be merged in here return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
}
const SwDoc& rDoc = pTextFrame->GetDoc(); const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess(); if (!rIDSA.get(DocumentSettingId::TAB_OVER_SPACING) || rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN))
{ // Writer or Word Word <= 2010 style: upper margin is never ignored. returnfalse;
}
if (IsInFly())
{ // Not in a page's body. returnfalse;
}
// Word >= 2013 style: when we're at the top of the page's body, but not on the first page, then // ignore the upper margin for paragraphs. if (GetPrev() || !GetUpper() || !GetUpper()->IsBodyFrame())
{ returnfalse;
}
// Avoid the ignore after applying a new page style (but do it after page breaks). const SwTextNode* pTextNode = pTextFrame->GetTextNodeForParaProps(); if (pTextNode)
{ if (pTextNode->HasSwAttrSet() && pTextNode->GetSwAttrSet().HasItem(RES_PAGEDESC))
{ returnfalse;
}
}
nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
} else
{ // Simplification: ContentFrames are always variable in height!
// At the FixSize, the surrounding Frame enforces the size; // the borders are simply subtracted. const tools::Long nLeft = rAttrs.CalcLeft( this ); const tools::Long nRight = rAttrs.CalcRight( this );
aRectFnSet.SetXMargins( *this, nLeft, nRight );
SwViewShell *pSh = getRootFrame()->GetCurrShell();
SwTwips nWidthArea; if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
pSh->GetViewOptions()->getBrowseMode() )
{ // Do not protrude the edge of the visible area. The page may be // wider, because there may be objects with excess width // (RootFrame::ImplCalcBrowseWidth())
tools::Long nMinWidth = 0;
for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
{ // #i28701# - consider changed type of // <SwSortedObjs> entries
SwAnchoredObject* pObj = (*GetDrawObjs())[i]; const SwFrameFormat* pFormat = pObj->GetFrameFormat(); constbool bFly = pObj->DynCastFlyFrame() != nullptr; if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
|| pFormat->GetFrameSize().GetWidthPercent())
{ continue;
}
if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
{ // The PrtArea should already be at least MINLAY wide, matching the // minimal values of the UI
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
// The following rules apply for VarSize: // 1. The first entry of a chain has no top border // 2. There is never a bottom border // 3. The top border is the maximum of the distance // of Prev downwards and our own distance upwards // Those three rules apply when calculating spacings // that are given by UL- and LRSpace. There might be a spacing // in all directions however; this may be caused by borders // and / or shadows. // 4. The spacing for TextFrames corresponds to the interline lead, // at a minimum.
nUpper += nLower;
nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
} // If there's a difference between old and new size, call Grow() or // Shrink() respectively. if ( nUpper )
{ if ( nUpper > 0 )
GrowFrame( nUpper ); else
ShrinkFrame( -nUpper );
}
}
// takes care of the notification in the dtor
std::optional<SwContentNotify> oNotify( std::in_place, this );
// as long as bMakePage is true, a new page can be created (exactly once) bool bMakePage = true; // bMovedBwd gets set to true when the frame flows backwards bool bMovedBwd = false; // as long as bMovedFwd is false, the Frame may flow backwards (until // it has been moved forward once) bool bMovedFwd = false;
sal_Bool bFormatted = false; // For the widow/orphan rules, we encourage the // last ContentFrame of a chain to format. This only // needs to happen once. Every time the Frame is // moved, the flag will have to be reset. bool bMustFit = false; // Once the emergency brake is pulled, // no other prepares will be triggered bool bFitPromise = false; // If a paragraph didn't fit, but promises // with WouldFit that it would adjust accordingly, // this flag is set. If it turns out that it // didn't keep it's promise, we can act in a // controlled fashion. constbool bFly = IsInFly(); constbool bTab = IsInTab(); constbool bFootnote = IsInFootnote(); constbool bSct = IsInSct();
Point aOldFramePos; // This is so we can compare with the last pos
Point aOldPrtPos; // and determine whether it makes sense to Prepare
SwBorderAttrAccess aAccess( SwFrame::GetCache(), this ); const SwBorderAttrs &rAttrs = *aAccess.Get();
// #i28701# - move master forward, if it has to move, // because of its object positioning. if (!IsFollow())
{
sal_uInt32 nToPageNum = 0; constbool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
GetAttrSet()->GetDoc(),
*static_cast<SwTextFrame*>(this),
nToPageNum ); // #i58182# // Also move a paragraph forward, which is the first one inside a table cell. if ( bMoveFwdByObjPos &&
FindPageFrame()->GetPhyPageNum() < nToPageNum &&
( lcl_Prev( this ) ||
GetUpper()->IsCellFrame() ||
( GetUpper()->IsSctFrame() &&
GetUpper()->GetUpper()->IsCellFrame() ) ) &&
IsMoveable() )
{
bMovedFwd = true;
MoveFwd( bMakePage, false );
}
} elseif (auto* prev = lcl_Prev(this); prev && IsMoveable())
{ // If a Follow sits next to its Master and doesn't fit, we know it can be moved right now.
bMovedFwd = true; // If follow frame is in table, its master will be the last in the // current table cell. Thus, invalidate the printing area of the master. if ( IsInTab() )
{
prev->InvalidatePrt();
}
MoveFwd( bMakePage, false );
}
// Check footnote content for forward move. // If a content of a footnote is on a prior page/column as its invalid // reference, it can be moved forward. if ( bFootnote && !isFrameAreaPositionValid() )
{
SwFootnoteFrame* pFootnote = FindFootnoteFrame();
SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;
if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
{
SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame(); //<loop of movefwd until condition held or no move> if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
{
bMovedFwd = true;
MoveFwd( bMakePage, false );
}
}
}
bool bMoveable = IsMoveable(); if (bMoveable)
{
SwFrame *pPre = GetIndPrev(); if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
{
aRectFnSet.Refresh(this);
bMovedFwd = true; if ( bMovedBwd )
{ // While flowing back, the Upper was encouraged to // completely re-paint itself. We can skip this now after // flowing back and forth.
GetUpper()->ResetCompletePaint(); // The predecessor was invalidated, so this is obsolete as well now.
assert(pPre); if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
::ValidateSz( pPre );
}
bMoveable = IsMoveable();
}
}
if (isHiddenNow)
{ // call this after MakePos() otherwise Shrink may not work
MakeValidZeroHeight();
}
//Set FixSize. VarSize is being adjusted by Format(). if ( !isFrameAreaSizeValid() )
{
assert(!isHiddenNow); // hidden frame must not be formatted // invalidate printing area flag, if the following conditions are hold: // - current frame width is 0. // - current printing area width is 0. // - frame width is adjusted to a value greater than 0. // - printing area flag is true. // Thus, it's assured that the printing area is adjusted, if the // frame area width changes its width from 0 to something greater // than 0. // Note: A text frame can be in such a situation, if the format is // triggered by method call <SwCursorShell::SetCursor()> after // loading the document. const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
// When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos. if( GetUpper() && GetUpper()->IsFlyFrame() &&
GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
{ static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
GetUpper()->SetCompletePaint();
}
} if ( !isFramePrintAreaValid() )
{
assert(!isHiddenNow); // hidden frame must not be formatted const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea()); // #i34730# - keep current frame height const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
MakePrtArea( rAttrs ); if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
Prepare( PrepareHint::FixSizeChanged ); // #i34730# - check, if frame height has changed. // If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to // force a format. The format will check in its method // <SwTextFrame::CalcPreps()>, if the already formatted lines still // fit and if not, performs necessary actions. // #i40150# - no check, if frame is undersized. if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
{ // #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size // invalidation, if height decreases only by the additional // lower space as last content of a table cell and an existing // follow containing one line exists. const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea()); constbool bNoPrepAdjustFrame =
nHDiff > 0 && IsInTab() && GetFollow() &&
(1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
|| aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff; if ( !bNoPrepAdjustFrame )
{
Prepare( PrepareHint::AdjustSizeWithoutFormatting );
setFrameAreaSizeValid(false);
}
}
}
// To make the widow and orphan rules work, we need to notify the ContentFrame. // Criteria: // - It needs to be movable (otherwise, splitting doesn't make sense) // - It needs to overlap with the lower edge of the PrtArea of the Upper if (!bMustFit && !isHiddenNow)
{ bool bWidow = true; const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper()); if( bMoveable && !bFormatted &&
( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
{
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
setFrameAreaSizeValid(false);
bWidow = false;
} if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
{ // In this Prepare, an InvalidateSize_() might happen. // isFrameAreaSizeValid() becomes false and Format() gets called.
Prepare( PrepareHint::FramePositionChanged, static_cast<constvoid*>(&bFormatted), false ); if ( bWidow && GetFollow() )
{
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
setFrameAreaSizeValid(false);
}
}
} if ( !isFrameAreaSizeValid() )
{
assert(!isHiddenNow); // hidden frame must not be formatted
setFrameAreaSizeValid(true);
bFormatted = true;
++nFormatCount; if( nFormatCount > STOP_FLY_FORMAT )
SetFlyLock( true ); // - loop prevention // No format any longer, if <cnStopFormat> consecutive formats // without change occur. if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
{
Format(getRootFrame()->GetCurrShell()->GetOut());
} #if OSL_DEBUG_LEVEL > 0 else
{
OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
} #endif
}
// If this is the first one in a chain, check if this can flow // backwards (if this is movable at all). // To prevent oscillations/loops, check that this has not just // flowed forwards. bool bDummy; autoconst pTemp(GetIndPrev()); autoconst bTemp(pTemp && pTemp->isFrameAreaSizeValid()
&& pTemp->isFramePrintAreaValid()); if ( !lcl_Prev( this ) &&
!bMovedFwd &&
( bMoveable || ( bFly && !bTab ) ) &&
( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
&& MoveBwd( bDummy ) )
{
aRectFnSet.Refresh(this);
pMoveBwdPre = pTemp;
isMoveBwdPreValid = bTemp;
bMovedBwd = true; if (isHiddenNow)
{ // MoveBwd invalidated the size! Validate to prevent Format!
MakeValidZeroHeight();
} else
{
bFormatted = false;
} if (bKeep && bMoveable && !isHiddenNow)
{ if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
{
bMovedFwd = true;
bMoveable = IsMoveable();
aRectFnSet.Refresh(this);
}
Point aOldPos = aRectFnSet.GetPos(getFrameArea());
MakePos(); if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
{
Prepare( PrepareHint::FramePositionChanged, static_cast<constvoid*>(&bFormatted), false ); if ( !isFrameAreaSizeValid() )
{
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
}
// In footnotes, the TextFrame may validate itself, which can lead to the // situation that it's position is wrong despite being "valid". if ( isFrameAreaPositionValid() )
{ // #i59341# // Workaround for inadequate layout algorithm: // suppress invalidation and calculation of position, if paragraph // has formatted itself at least STOP_FLY_FORMAT times and // has anchored objects. // Thus, the anchored objects get the possibility to format itself // and this probably solve the layout loop. if ( bFootnote &&
nFormatCount <= STOP_FLY_FORMAT &&
!GetDrawObjs() )
{
setFrameAreaPositionValid(false);
MakePos();
aOldFramePos = aRectFnSet.GetPos(getFrameArea());
aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
}
}
// Yet again an invalid value? Repeat from the start... if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() ) continue;
// Done? // Attention: because height == 0, it's better to use Top()+Height() instead of // Bottom(). This might happen with undersized TextFrames on the lower edge of a // multi-column section const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
// Hide whitespace may require not to insert a new page.
SwPageFrame* pPageFrame = FindPageFrame(); constbool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist); if (!bHeightValid)
{
pPageFrame->InvalidateSize();
nBottomDist = 0;
}
if( nBottomDist >= 0 )
{ if (bKeep && bMoveable && !isHiddenNow)
{ // We make sure the successor will be formatted the same. // This way, we keep control until (almost) everything is stable, // allowing us to avoid endless loops caused by ever repeating // retries.
// bMoveFwdInvalid is required for #38407#. This was originally solved // in flowfrm.cxx rev 1.38, but broke the above schema and // preferred to play towers of hanoi (#43669#).
SwFrame *pNxt = HasFollow() ? nullptr : FindNext(); // For sections we prefer the content, because it can change // the page if required. while( pNxt && pNxt->IsSctFrame() )
{ if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
{
pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny(); break;
}
pNxt = pNxt->FindNext();
} if ( pNxt )
{ constbool bMoveFwdInvalid = nullptr != GetIndNext(); constbool bNxtNew =
( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
!pNxt->IsHiddenNow();
// I don't fit into my parents, so it's time to make changes // as constructively as possible.
//If I'm NOT allowed to leave the parent Frame, I've got a problem. // Following Arthur Dent, we do the only thing that you can do with // an unsolvable problem: We ignore it with all our power. if ( !bMoveable || IsUndersized() )
{ break;
}
// If there's no way I can make myself fit into my Upper, the situation // could still probably be mitigated by splitting up. // This situation arises with freshly created Follows that had been moved // to the next page but is still too big for it - ie. needs to be split // as well.
// If I'm unable to split (WouldFit()) and can't be fitted, I'm going // to tell my TextFrame part that, if possible, we still need to split despite // the "don't split" attribute. bool bMoveOrFit = false; bool bDontMoveMe = !GetIndPrev(); if( bDontMoveMe && IsInSct() )
{
SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
bDontMoveMe = !pBoss->IsInSct() ||
( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
}
// Finally, we are able to split table rows. Therefore, bDontMoveMe // can be set to false: if( bDontMoveMe && IsInTab() &&
nullptr != GetNextCellLeaf() )
bDontMoveMe = false;
assert(bMoveable);
if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
{ if ( !bFitPromise )
{
SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
aRectFnSet.GetTop(getFramePrintArea()); bool bSplit = !IsFwdMoveAllowed(); if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
{
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
setFrameAreaSizeValid(false);
bFitPromise = true; continue;
} /* * In earlier days, we never tried to fit TextFrames in * frames and sections using bMoveOrFit by ignoring * its attributes (Widows, Keep). * This should have been done at least for column frames; * as it must be tried anyway with linked frames and sections. * Exception: If we sit in FormatWidthCols, we must not ignore * the attributes.
*/ elseif ( !bFootnote &&
( !bFly || !FindFlyFrame()->IsColLocked() ) &&
( !bSct || !FindSctFrame()->IsColLocked() ) )
bMoveOrFit = true;
} #if OSL_DEBUG_LEVEL > 0 else
{
OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
} #endif
}
// Let's see if I can find some space somewhere... // footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
SwFrame *pPre = GetIndPrev();
SwFrame *pOldUp = GetUpper();
/* MA 13. Oct. 98: What is this supposed to be!? * AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be * moved not only to the next column, but probably even to the next page, creating * a section-follow there.
*/ if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
( pPre || pOldUp->GetUpper()->GetPrev() ) && static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
{
bMovedFwd = false;
}
if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
bMakePage = false;
aRectFnSet.Refresh(this); if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
{ // SwFlowFrame::CutTree() could have formatted and deleted pPre autoconst pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
FindFootnoteFrame())->GetMaster()); bool bReset = true; if (pPrevFootnoteFrame)
{ // use GetIndNext() in case there are sections for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
{ if (p == pPre)
{
bReset = false; break;
}
}
} if (bReset)
{
pPre = nullptr;
}
}
// If MoveFwd moves the paragraph to the next page, a following // paragraph, which contains footnotes can cause the old upper // frame to grow. In this case we explicitly allow a new check // for MoveBwd. Robust: We also check the bMovedBwd flag again. // If pOldUp was a footnote frame, it has been deleted inside MoveFwd. // Therefore we only check for growing body frames.
bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;
bFormatted = false; if ( bMoveOrFit && GetUpper() == pOldUp )
{ // FME 2007-08-30 #i81146# new loop control if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
{
Prepare( PrepareHint::MustFit, nullptr, false );
setFrameAreaSizeValid(false);
bMustFit = true; continue;
}
#if OSL_DEBUG_LEVEL > 0
OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" ); #endif
} if ( bMovedBwd && GetUpper() )
{ // Retire invalidations that have become useless.
GetUpper()->ResetCompletePaint(); if( pPre && !pPre->IsSctFrame() )
::ValidateSz( pPre );
}
// NEW: Looping Louie (Light). Should not be applied in balanced sections. // Should only be applied if there is no better solution!
LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
(
void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
{ // fix(25455): Validate, otherwise this leads to a recursion. // The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as // the Keep may not be considered properly anymore (27417). constbool bOldPos = pFrame->isFrameAreaPositionValid(); constbool bOldSz = pFrame->isFrameAreaSizeValid(); constbool bOldPrt = pFrame->isFramePrintAreaValid();
pFrame->setFrameAreaPositionValid(true);
pFrame->setFrameAreaSizeValid(true);
pFrame->setFramePrintAreaValid(true);
// fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and // we recursively end up in here again. if ( pNxt->IsContentFrame() )
{
SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt ); const SwBorderAttrs &rAttrs = *aAccess.Get(); if ( !pNxt->isFrameAreaSizeValid() )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
/// This routine checks whether there are no other FootnoteBosses /// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss. staticbool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
{
assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
pFrame = pFrame->FindFootnoteBossFrame();
pNxt = pNxt->FindFootnoteBossFrame(); // If pFrame is a last column, we use the page instead. while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
pFrame = pFrame->GetUpper()->FindFootnoteBossFrame(); // If pNxt is a first column, we use the page instead. while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
pNxt = pNxt->GetUpper()->FindFootnoteBossFrame(); // So... now pFrame and pNxt are either two adjacent pages or columns. return pFrame && pNxt && pFrame->GetNext() == pNxt;
}
bool SwContentFrame::WouldFit_( SwTwips nSpace,
SwLayoutFrame *pNewUpper, bool bTstMove, constbool bObjsInNewUpper )
{ // To have the footnote select its place carefully, it needs // to be moved in any case if there is at least one page/column // between the footnote and the new Upper.
SwFootnoteFrame* pFootnoteFrame = nullptr; if ( IsInFootnote() )
{ if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) ) returntrue;
pFootnoteFrame = FindFootnoteFrame();
}
// tdf#156727 if the previous one has keep-with-next, ignore it on this one! boolconst isIgnoreKeep(pTmpPrev && pTmpPrev->IsFlowFrame()
&& SwFlowFrame::CastFlowFrame(pTmpPrev)->IsKeep(
pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()));
do
{ if (pFrame->IsHiddenNow())
{ // shortcut
assert(pFrame == this);
bRet = true;
pFrame = nullptr; break;
}
if ( bTstMove || IsInFly() || ( IsInSct() &&
( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
{ // This is going to get a bit insidious now. If you're faint of heart, // you'd better look away here. If a Fly contains columns, then the Contents // are movable, except ones in the last column (see SwFrame::IsMoveable()). // Of course they're allowed to float back. WouldFit() only returns a usable // value if the Frame is movable. To fool WouldFit() into believing there's // a movable Frame, I'm just going to hang it somewhere else for the time. // The same procedure applies for column sections to make SwSectionFrame::Growable() // return the proper value. // Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if // there's no SwFootnoteFrame there.
SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ? static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
SwLayoutFrame *pUp = pTmpFrame->GetUpper();
SwFrame *pOldNext = pTmpFrame->GetNext();
pTmpFrame->RemoveFromLayout();
pTmpFrame->InsertBefore( pNewUpper, nullptr ); // tdf#107126 for a section in a footnote, we have only inserted // the SwTextFrame but no SwSectionFrame - reset mbInfSct flag // to avoid crashing (but perhaps we should create a temp // SwSectionFrame here because WidowsAndOrphans checks for that?)
pTmpFrame->InvalidateInfFlags(); if ( pFrame->IsTextFrame() &&
( bTstMove || static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
!static_cast<SwTextFrame*>(pFrame)->IsEmpty()
)
)
)
{
bTstMove = true;
bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
} else
bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
// Sad but true: We need to consider the spacing in our calculation. // This already happened in TestFormat. if ( bRet && !bTstMove )
{
SwTwips nUpper;
// in balanced columned section frames we do not want the // common border bool bCommonBorder = true; if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
{ const SwSectionFrame* pSct = pFrame->FindSctFrame();
bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
}
// #i46181# if ( nSecondCheck > 0 )
{ // The following code is intended to solve a (rare) problem // causing some frames not to move backward: // SwTextFrame::WouldFit() claims that the whole paragraph // fits into the given space and subtracts the height of // all lines from nSpace. nSpace - nUpper is not a valid // indicator if the frame should be allowed to move backward. // We do a second check with the original remaining space // reduced by the required upper space:
nOldSpace -= nSecondCheck; constbool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true); if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
{
bRet = true;
bSplit = true;
}
}
}
}
// Also consider lower spacing in table cells
IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess()); if ( bRet && IsInTab() &&
rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
{
nSpace -= rAttrs.GetULSpace().GetLower();
if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
{
nSpace -= rAttrs.CalcLineSpacing();
} if ( nSpace < 0 )
{
bRet = false;
}
}
bool bKeep = false; if (bRet && !bSplit && !isIgnoreKeep
&& pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
{ // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't // change that decision here.
bKeep = pFrame->IgnoringSplitFlyAnchor(true);
}
if (bKeep)
{ if( bTstMove )
{ while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
{
pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
} // If last follow frame of <this> text frame isn't valid, // a formatting of the next content frame doesn't makes sense. // Thus, return true. if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
{
OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" ); returntrue;
}
}
SwFrame *const pNxt{pFrame->FindNextIgnoreHidden()}; if (nullptr != pNxt && pNxt->IsContentFrame() &&
( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
{ // TestFormat(?) does not like paragraph- or character anchored objects.
// current solution for the test formatting doesn't work, if // objects are present in the remaining area of the new upper if ( bTstMove &&
( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
{ returntrue;
}
if ( !pNxt->isFrameAreaDefinitionValid() )
{
MakeNxt( pFrame, pNxt );
}
// Little trick: if the next has a predecessor, then the paragraph // spacing has been calculated already, and we don't need to re-calculate // it in an expensive way. if( lcl_NotHiddenPrev( pNxt ) )
pTmpPrev = nullptr; else
{
pTmpPrev = pFrame;
}
pFrame = static_cast<SwContentFrame*>(pNxt);
} else
pFrame = nullptr;
} else
pFrame = nullptr;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.91Bemerkung:
(vorverarbeitet am 2026-05-05)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.