/* -*- 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 .
*/
#include <memory>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <viewopt.hxx>
#include <frmtool.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/keepitem.hxx>
#include <svx/sdtaitm.hxx>
#include <fmtfsize.hxx>
#include <fmtanchr.hxx>
#include <fmtclbl.hxx>
#include <tabfrm.hxx>
#include <ftnfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <dbg_lay.hxx>
#include <sortedobjs.hxx>
#include <layouter.hxx>
#include <flyfrms.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <o3tl/make_unique.hxx>
// Move methods
/// Return value tells whether the Frame should be moved.
bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool, 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() )
return true;
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 )
return false;
}
}
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();
}
}
if ( pLastFrame )
{
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
const SwBorderAttrs& rAttrs = *aAccess.Get();
nNewTop -= rAttrs.GetULSpace().GetLower();
}
}
}
fnRectX.SetTop( aRect, nNewTop );
pPrevFrame = pPrevFrame->GetNext();
}
nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);
//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 );
if ( nMoveAnyway < 3 )
{
if ( nSpace )
{
// 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);
const bool 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;
}
}
return false;
}
// Calc methods
// Two little friendships form a secret society
inline void PrepareLock( SwFlowFrame *pTab )
{
pTab->LockJoin();
}
inline void PrepareUnlock( SwFlowFrame *pTab )
{
pTab->UnlockJoin();
}
// hopefully, one day this function simply will return 'false'
static bool 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() )
{
if ( lcl_IsCalcUpperAllowed( *this ) )
GetUpper()->Calc(pRenderContext);
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
if ( !GetUpper() )
return;
const bool bCnt = IsContentFrame();
const bool bTab = IsTabFrame();
bool bNoSect = IsInSct();
bool bOldTabLock = false, bFoll = false;
SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
if ( bTab )
{
pThis = static_cast<SwTabFrame*>(this);
bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
::PrepareLock( static_cast<SwTabFrame*>(this) );
bFoll = pThis->IsFollow();
}
else if( IsSctFrame() )
{
pThis = static_cast<SwSectionFrame*>(this);
bFoll = pThis->IsFollow();
bNoSect = false;
}
else if ( 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.
const bool bFormatPrev = !bTab ||
!GetPrev() ||
!GetPrev()->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;
pFrame->MakeAll(pRenderContext);
if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
break;
}
// With ContentFrames, the chain may be broken while walking through
// it. Therefore we have to figure out the follower 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);
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
}
if ( bTab && !bOldTabLock )
::PrepareUnlock( static_cast<SwTabFrame*>(this) );
}
MakeAll(pRenderContext);
}
void SwFrame::OptPrepareMake()
{
// #i23129#, #i36347# - no format of upper Writer fly frame
if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
!GetUpper()->IsFlyFrame() )
{
{
SwFrameDeleteGuard aDeleteGuard(this);
GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
}
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
if ( !GetUpper() )
return;
}
if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
{
PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
}
else
{
StackHack aHack;
MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
}
}
void SwFrame::PrepareCursor()
{
StackHack aHack;
if( GetUpper() && !GetUpper()->IsSctFrame() )
{
GetUpper()->PrepareCursor();
GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
if ( !GetUpper() )
return;
const bool bCnt = IsContentFrame();
const bool bTab = IsTabFrame();
bool bNoSect = IsInSct();
bool bOldTabLock = false, bFoll;
SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
if ( bTab )
{
bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
::PrepareLock( static_cast<SwTabFrame*>(this) );
pThis = static_cast<SwTabFrame*>(this);
}
else if( IsSctFrame() )
{
pThis = static_cast<SwSectionFrame*>(this);
bNoSect = false;
}
bFoll = pThis && pThis->IsFollow();
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;
pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
}
// With ContentFrames, the chain may be broken while walking through
// it. Therefore we have to figure out the follower 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;
GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
if ( bTab && !bOldTabLock )
::PrepareUnlock( static_cast<SwTabFrame*>(this) );
}
Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
}
// 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->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() );
return pRet;
}
void SwFrame::MakePos()
{
if ( !isFrameAreaPositionValid() )
{
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()
)
{
pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
}
else if ( pPrv->getFrameArea().Top() == 0 )
{
bUseUpper = true;
}
}
pPrv = lcl_Prev( this, false );
const SwFrameType nMyType = GetType();
SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? GetUpper() : this));
if ( !bUseUpper && pPrv )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos( pPrv->getFrameArea().Pos() );
if( FRM_NEIGHBOUR & nMyType )
{
const bool bR2L = IsRightToLeft();
if( bR2L )
{
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
}
else
{
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
}
// cells may now leave their uppers
if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
}
}
else if( 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());
}
}
else if ( 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() );
if( FRM_NEIGHBOUR & nMyType )
{
const bool bR2L = IsRightToLeft();
if( bR2L )
{
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
}
else
{
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
}
// cells may now leave their uppers
if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
}
}
else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
}
else
{
aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
}
}
else
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos( GetUpper()->getFrameArea().Pos() );
if( GetUpper()->IsFlyFrame() )
{
aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
}
else
{
aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
}
if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
{
if( aRectFnSet.IsVert() )
{
aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
}
else
{
aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
}
}
else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
{
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
}
}
}
else
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos().setX(0);
aFrm.Pos().setY(0);
}
if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
}
setFrameAreaPositionValid(true);
}
}
// #i28701# - new type <SwSortedObjs>
static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, long& rBot)
{
// And then there can be paragraph anchored frames that sit below their paragraph.
long nMax = 0;
for (SwAnchoredObject* pObj : rSortedObjs)
{
// #i28701# - consider changed type of <SwSortedObjs>
// entries.
long nTmp = 0;
if ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr )
{
SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pObj);
if( pFly->getFrameArea().Top() != FAR_AWAY &&
( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
( pFly->IsFlyAtContentFrame() &&
( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
{
nTmp = pFly->getFrameArea().Bottom();
}
}
else
nTmp = pObj->GetObjRect().Bottom();
nMax = std::max( nTmp, nMax );
}
++nMax; // Lower edge vs. height!
rBot = std::max( rBot, nMax );
}
size_t SwPageFrame::GetContentHeight(const long nTop, const long nBottom) const
{
OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
"SwPageFrame::GetContentHeight(): No support for columns.");
// In pages without columns, the content defines the size.
long nBot = getFrameArea().Top() + nTop;
const SwFrame *pFrame = Lower();
while (pFrame)
{
long nTmp = 0;
const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
while (pCnt && (pCnt->GetUpper() == pFrame ||
static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
{
nTmp += pCnt->getFrameArea().Height();
if (pCnt->IsTextFrame() &&
static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
{
// This TextFrame would like to be a bit bigger.
nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
- pCnt->getFramePrintArea().Height();
}
else if (pCnt->IsSctFrame())
{
// Grow if undersized, but don't shrink if oversized.
const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
if (delta > 0)
nTmp += delta;
}
pCnt = pCnt->FindNext();
}
// Consider invalid body frame properties
if (pFrame->IsBodyFrame() &&
(!pFrame->isFrameAreaSizeValid() ||
!pFrame->isFramePrintAreaValid()) &&
(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
)
{
nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
}
else
{
// Assert invalid lower property
OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
"SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
}
if (!pFrame->IsBodyFrame())
nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
nBot += nTmp;
// Here we check whether paragraph anchored objects
// protrude outside the Body/FootnoteCont.
if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
!pFrame->IsFooterFrame())
lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
pFrame = pFrame->GetNext();
}
nBot += nBottom;
// And the page anchored ones
if (m_pSortedObjs)
lcl_CheckObjects(*m_pSortedObjs, this, nBot);
nBot -= getFrameArea().Top();
return nBot;
}
void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
{
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
const SwRect aOldRect( getFrameArea() ); // Adjust root size
const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
std::unique_ptr<SwBorderAttrAccess> pAccess;
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 );
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Width( 0 );
aPrt.Height( 0 );
aPrt.Left( 0 );
aPrt.Top( 0 );
setFrameAreaSizeValid(true);
setFramePrintAreaValid(true);
}
else
{
if (!pAccess)
{
pAccess = o3tl::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this);
pAttrs = pAccess->Get();
}
assert(pAttrs);
SwRootFrame* pRootFrame = getRootFrame();
SwViewShell* pSh = pRootFrame->GetCurrShell();
if (pSh && pSh->GetViewOptions()->getBrowseMode())
{
// In BrowseView, we use fixed settings
const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
const long nTop = pAttrs->CalcTopLine() + aBorder.Height();
const long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();
long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
const auto nDefWidth = pSh->GetBrowseWidth();
if (nWidth < nDefWidth)
nWidth = nDefWidth;
nWidth += + 2 * aBorder.Width();
nWidth = std::max( nWidth, 2L * aBorder.Width() + 4*MM50 );
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Width( nWidth );
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.
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 ) );
}
}
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
aPrt.Top ( nTop );
aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
}
setFrameAreaSizeValid(true);
setFramePrintAreaValid(true);
continue;
}
else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden() && pRootFrame->GetLastPage() != this)
{
long height = 0;
SwLayoutFrame *pBody = FindBodyCont();
if ( 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 > 0)
{
ChgSize(Size(getFrameArea().Width(), height));
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Top(0);
aPrt.Height(height);
setFrameAreaSizeValid(true);
setFramePrintAreaValid(true);
continue;
}
// 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() )
if ( getFrameArea() != aOldRect && GetUpper() )
static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
"Upper (Root) must be wide enough to contain the widest page");
}
void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
{
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
// takes care of the notification in the dtor
const SwLayNotify aNotify( this );
bool bVert = IsVertical();
SwRectFn fnRect = ( IsNeighbourFrame() == bVert )? fnRectHori : ( IsVertLR() ? fnRectVertL2R : fnRectVert );
std::unique_ptr<SwBorderAttrAccess> pAccess;
const SwBorderAttrs*pAttrs = nullptr;
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{
if ( !isFrameAreaPositionValid() )
MakePos();
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);
SwTwips nPrtWidth = (GetUpper()->getFramePrintArea().*fnRect->fnGetWidth)();
if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
{
SwFrame* pNxt = GetPrev();
while( pNxt && !pNxt->IsHeaderFrame() )
pNxt = pNxt->GetPrev();
if( pNxt )
nPrtWidth -= pNxt->getFrameArea().Height();
pNxt = GetNext();
while( pNxt && !pNxt->IsFooterFrame() )
pNxt = pNxt->GetNext();
if( pNxt )
nPrtWidth -= pNxt->getFrameArea().Height();
}
const long nDiff = nPrtWidth - (getFrameArea().*fnRect->fnGetWidth)();
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
if( IsNeighbourFrame() && IsRightToLeft() )
{
(aFrm.*fnRect->fnSubLeft)( nDiff );
}
else
{
(aFrm.*fnRect->fnAddRight)( nDiff );
}
}
else
{
// Don't leave your upper
const SwTwips nDeadLine = (GetUpper()->*fnRect->fnGetPrtBottom)();
if( (getFrameArea().*fnRect->fnOverStep)( nDeadLine ) )
{
setFrameAreaSizeValid(false);
}
}
}
}
if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{
if ( !pAccess )
{
pAccess = o3tl::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this);
pAttrs = pAccess->Get();
}
Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
}
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
}
bool SwTextNode::IsCollapse() const
{
if (GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
&& GetText().isEmpty())
{
sal_uLong nIdx=GetIndex();
const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();
// The paragraph is collapsed only if the NdAfter is the end of a cell
bool bInTable = FindTableNode( ) != nullptr;
SwSortedObjs* pObjs = getLayoutFrame( GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;
return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
}
return false;
}
bool SwFrame::IsCollapse() const
{
if (!IsTextFrame())
return false;
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();
}
void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
{
if ( !isFramePrintAreaValid() )
{
setFramePrintAreaValid(true);
SwRectFnSet aRectFnSet(this);
const bool bTextFrame = IsTextFrame();
SwTwips nUpper = 0;
if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() )
{
if ( static_cast<SwTextFrame*>(this)->HasFollow() )
static_cast<SwTextFrame*>(this)->JoinFrame();
if( aRectFnSet.GetHeight(getFramePrintArea()) )
{
static_cast<SwTextFrame*>(this)->HideHidden();
}
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Pos().setX(0);
aPrt.Pos().setY(0);
aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
aRectFnSet.SetHeight( aPrt, 0 );
}
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 long nLeft = rAttrs.CalcLeft( this );
const 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())
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& rFormat = pObj->GetFrameFormat();
const bool bFly = dynamic_cast<const SwFlyFrame*>( pObj) != nullptr;
if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
|| rFormat.GetFrameSize().GetWidthPercent())
{
continue;
}
if ( RndStdIds::FLY_AS_CHAR == rFormat.GetAnchor().GetAnchorId() )
{
nMinWidth = std::max( nMinWidth,
bFly ? rFormat.GetFrameSize().GetWidth()
: pObj->GetObjRect().Width() );
}
}
const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
nWidth -= rAttrs.CalcRightLine();
nWidth = std::max( nMinWidth, nWidth );
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
}
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( long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
if( aRectFnSet.GetLeft(aPrt) > nTmp )
{
aRectFnSet.SetLeft( aPrt, nTmp );
}
}
// 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 = CalcUpperSpace( &rAttrs );
SwTwips nLower = CalcLowerSpace( &rAttrs );
if (IsCollapse()) {
nUpper=0;
nLower=0;
}
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
}
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 );
}
}
}
#define STOP_FLY_FORMAT 10
// - loop prevention
const int cnStopFormat = 15;
inline void ValidateSz( SwFrame *pFrame )
{
if ( pFrame )
{
pFrame->setFrameAreaSizeValid(true);
pFrame->setFramePrintAreaValid(true);
}
}
void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
{
OSL_ENSURE( GetUpper(), "no Upper?" );
OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );
if ( !IsFollow() && StackHack::IsLocked() )
return;
if ( IsJoinLocked() )
return;
OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );
StackHack aHack;
if ( static_cast<SwTextFrame*>(this)->IsLocked() )
{
OSL_FAIL( "Format for locked TextFrame." );
return;
}
auto xDeleteGuard = o3tl::make_unique<SwFrameDeleteGuard>(this);
LockJoin();
long nFormatCount = 0;
// - loop prevention
int nConsecutiveFormatsWithoutChange = 0;
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
// takes care of the notification in the dtor
std::unique_ptr<SwContentNotify> pNotify(new SwContentNotify( 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.
const bool bFly = IsInFly();
const bool bTab = IsInTab();
const bool bFootnote = IsInFootnote();
const bool 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();
if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
{
pNotify->SetBordersJoinedWithPrev();
}
const bool bKeep = IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem());
std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
if ( bFootnote )
{
SwFootnoteFrame *pFootnote = FindFootnoteFrame();
SwSectionFrame* pSct = pFootnote->FindSctFrame();
if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
{
SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
pFootnote->GetAttr()->GetFootnote().IsEndNote() );
if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
}
}
if ( GetUpper()->IsSctFrame() &&
HasFollow() &&
&GetFollow()->GetFrame() == GetNext() )
{
dynamic_cast<SwTextFrame&>(*this).JoinFrame();
}
// #i28701# - move master forward, if it has to move,
// because of its object positioning.
if ( !static_cast<SwTextFrame*>(this)->IsFollow() )
{
sal_uInt32 nToPageNum = 0;
const bool 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 );
}
}
// If a Follow sits next to its Master and doesn't fit, we know it can
// be moved right now.
if ( lcl_Prev( this ) && static_cast<SwTextFrame*>(this)->IsFollow() && IsMoveable() )
{
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() )
{
lcl_Prev( this )->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 );
}
}
}
SwRectFnSet aRectFnSet(this);
SwFrame const* pMoveBwdPre(nullptr);
bool isMoveBwdPreValid(false);
SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
{
// - loop prevention
aOldFrame_StopFormat2 = aOldFrame_StopFormat;
aOldPrt_StopFormat2 = aOldPrt_StopFormat;
aOldFrame_StopFormat = getFrameArea();
aOldPrt_StopFormat = getFramePrintArea();
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();
}
}
aOldFramePos = aRectFnSet.GetPos(getFrameArea());
aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
if ( !isFrameAreaPositionValid() )
MakePos();
//Set FixSize. VarSize is being adjusted by Format().
if ( !isFrameAreaSizeValid() )
{
// 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());
if ( isFramePrintAreaValid() &&
nNewFrameWidth > 0 &&
aRectFnSet.GetWidth(getFrameArea()) == 0 &&
aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
{
setFramePrintAreaValid(false);
}
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
}
// 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() )
{
const long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
// #i34730# - keep current frame height
const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
MakePrtArea( rAttrs );
if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
Prepare( PREP_FIXSIZE_CHG );
// #i34730# - check, if frame height has changed.
// If yes, send a PREP_ADJUST_FRM 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 PREP_ADJUST_FRM 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());
const bool 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( PREP_ADJUST_FRM );
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 )
{
bool bWidow = true;
const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
if( bMoveable && !bFormatted &&
( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
{
Prepare( PREP_WIDOWS_ORPHANS, 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( PREP_POS_CHGD, static_cast<const void*>(&bFormatted), false );
if ( bWidow && GetFollow() )
{
Prepare( PREP_WIDOWS_ORPHANS, nullptr, false );
setFrameAreaSizeValid(false);
}
}
}
if ( !isFrameAreaSizeValid() )
{
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;
auto const pTemp(GetIndPrev());
auto const 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;
bFormatted = false;
if ( bKeep && bMoveable )
{
if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
{
bMovedFwd = true;
bMoveable = IsMoveable();
aRectFnSet.Refresh(this);
}
Point aOldPos = aRectFnSet.GetPos(getFrameArea());
MakePos();
if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
{
Prepare( PREP_POS_CHGD, static_cast<const void*>(&bFormatted), false );
if ( !isFrameAreaSizeValid() )
{
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
}
if ( !isFramePrintAreaValid() )
{
const long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
MakePrtArea( rAttrs );
if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
Prepare( PREP_FIXSIZE_CHG, nullptr, false );
}
if( GetFollow() )
{
Prepare( PREP_WIDOWS_ORPHANS, nullptr, false );
}
setFrameAreaSizeValid(true);
bFormatted = true;
Format(getRootFrame()->GetCurrShell()->GetOut());
}
}
SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
while( pNxt && pNxt->IsSctFrame() )
{ // Leave empty sections out, go into the other ones.
if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
{
SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
if( pTmp )
{
pNxt = pTmp;
break;
}
}
pNxt = pNxt->FindNext();
}
if ( pNxt )
{
pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
if( isFrameAreaPositionValid() && !GetIndNext() )
{
SwSectionFrame *pSct = FindSctFrame();
if( pSct && !pSct->isFrameAreaSizeValid() )
{
SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
{
setFrameAreaPositionValid(false);
}
}
else
{
setFrameAreaPositionValid(false);
}
}
}
}
}
// 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());
}
}
// - loop prevention
{
if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
(aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
{
++nConsecutiveFormatsWithoutChange;
}
else
{
nConsecutiveFormatsWithoutChange = 0;
}
}
// 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 long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
// Hide whitespace may require not to insert a new page.
SwPageFrame* pPageFrame = FindPageFrame();
const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
if (!bHeightValid)
{
pPageFrame->InvalidateSize();
nBottomDist = 0;
}
if( nBottomDist >= 0 )
{
if ( bKeep && bMoveable )
{
// 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 )
{
const bool bMoveFwdInvalid = nullptr != GetIndNext();
const bool bNxtNew =
( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
(!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow());
pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
if ( !bMovedBwd &&
((bMoveFwdInvalid && !GetIndNext()) ||
bNxtNew) )
{
if( bMovedFwd )
pNotify->SetInvaKeep();
bMovedFwd = false;
}
}
}
continue;
}
// 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() )
{
if( !bMoveable && IsInTab() )
{
long nDiff = -aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
long nReal = GetUpper()->Grow( nDiff );
if( nReal )
continue;
}
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;
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 ) )
{
Prepare( PREP_WIDOWS_ORPHANS, 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.
*/
else if ( !bFootnote && bMoveable &&
( !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;
}
const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
const long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());
if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
bMakePage = false;
aRectFnSet.Refresh(this);
if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
{ // SwFlowFrame::CutTree() could have formatted and deleted pPre
auto const 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( PREP_MUST_FIT, 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 );
}
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
// 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() &&
(
( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||
// #i33887#
( IsInSct() && bKeep )
// ... add your conditions here ...
),
static_cast<SwTextFrame&>(*this) );
pSaveFootnote.reset();
UnlockJoin();
xDeleteGuard.reset();
if ( bMovedFwd || bMovedBwd )
pNotify->SetInvaKeep();
if ( bMovedFwd )
{
pNotify->SetInvalidatePrevPrtArea();
}
pNotify.reset();
SetFlyLock( false );
}
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).
const bool bOldPos = pFrame->isFrameAreaPositionValid();
const bool bOldSz = pFrame->isFrameAreaSizeValid();
const bool 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);
if( pNxt->IsVertical() )
{
aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
}
else
{
aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
}
}
static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
}
else
{
SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
const SwBorderAttrs &rAttrs = *aAccess.Get();
if ( !pNxt->isFrameAreaSizeValid() )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
if( pNxt->IsVertical() )
{
aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
}
else
{
aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
}
}
pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
}
pFrame->setFrameAreaPositionValid(bOldPos);
pFrame->setFrameAreaSizeValid(bOldSz);
pFrame->setFramePrintAreaValid(bOldPrt);
}
/// This routine checks whether there are no other FootnoteBosses
/// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
static bool 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,
const bool 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 ) )
return true;
pFootnoteFrame = FindFootnoteFrame();
}
bool bRet;
bool bSplit = !pNewUpper->Lower();
SwContentFrame *pFrame = this;
const SwFrame *pTmpPrev = pNewUpper->Lower();
if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
while ( pTmpPrev && pTmpPrev->GetNext() )
pTmpPrev = pTmpPrev->GetNext();
do
{
// #i46181#
SwTwips nSecondCheck = 0;
SwTwips nOldSpace = nSpace;
bool bOldSplit = bSplit;
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 );
pTmpFrame->RemoveFromLayout();
pTmpFrame->InsertBefore( pUp, pOldNext );
pTmpFrame->InvalidateInfFlags(); // restore flags
}
else
{
bRet = pFrame->WouldFit( nSpace, bSplit, false );
nSecondCheck = !bSplit ? 1 : 0;
}
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
const SwBorderAttrs &rAttrs = *aAccess.Get();
// Sad but true: We need to consider the spacing in our calculation.
// This already happened in TestFormat.
if ( bRet && !bTstMove )
{
SwTwips nUpper;
if ( pTmpPrev )
{
nUpper = CalcUpperSpace( nullptr, pTmpPrev );
// 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#
nSecondCheck = ( 1 == nSecondCheck &&
pFrame == this &&
IsTextFrame() &&
bCommonBorder &&
!static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
nUpper :
0;
nUpper += bCommonBorder ?
rAttrs.GetBottomLine( *pFrame ) :
rAttrs.CalcBottomLine();
}
else
{
// #i46181#
nSecondCheck = 0;
if( pFrame->IsVertical() )
nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
else
nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
}
nSpace -= nUpper;
if ( nSpace < 0 )
{
bRet = false;
// #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;
const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit( nOldSpace, bOldSplit, false );
if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
{
bRet = true;
bSplit = true;
}
}
}
}
// Also consider lower spacing in table cells
if ( bRet && IsInTab() &&
pNewUpper->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
{
nSpace -= rAttrs.GetULSpace().GetLower();
if ( nSpace < 0 )
{
bRet = false;
}
}
if (bRet && !bSplit && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
{
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!" );
return true;
}
}
SwFrame *pNxt;
if( nullptr != (pNxt = pFrame->FindNext()) && 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 ) )
{
return true;
}
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
{
if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() )
pTmpPrev = lcl_NotHiddenPrev( pFrame );
else
pTmpPrev = pFrame;
}
pFrame = static_cast<SwContentFrame*>(pNxt);
}
else
pFrame = nullptr;
}
else
pFrame = nullptr;
} while ( bRet && pFrame );
return bRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: bMoveable.