/* -*- 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 <pagefrm.hxx>
#include <fmtcntnt.hxx>
#include <fmthdft.hxx>
#include <fmtfsize.hxx>
#include <viewopt.hxx>
#include <hffrm.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <flyfrm.hxx>
#include <frmtool.hxx>
#include <hfspacingitem.hxx>
#include <sortedobjs.hxx>
#include <objectformatter.hxx>
#include <ndindex.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
static SwTwips lcl_GetFrameMinHeight(const SwLayoutFrame & rFrame)
{
const SwFormatFrameSize &rSz = rFrame.GetFormat()->GetFrameSize();
SwTwips nMinHeight;
switch (rSz.GetHeightSizeType())
{
case SwFrameSize::Minimum:
nMinHeight = rSz.GetHeight();
break;
default:
nMinHeight = 0;
}
return nMinHeight;
}
static SwTwips lcl_CalcContentHeight(SwLayoutFrame & frm)
{
SwTwips nRemaining = 0;
SwFrame* pFrame = frm.Lower();
while ( pFrame )
{
SwTwips nTmp;
nTmp = pFrame->getFrameArea().Height();
nRemaining += nTmp;
if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
{
nTmp = static_cast<SwTextFrame*>(pFrame)->GetParHeight()
- pFrame->getFramePrintArea().Height();
// This TextFrame would like to be a bit bigger
nRemaining += nTmp;
}
else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
{
nTmp = static_cast<SwSectionFrame*>(pFrame)->Undersize();
nRemaining += nTmp;
}
pFrame = pFrame->GetNext();
}
return nRemaining;
}
static void lcl_LayoutFrameEnsureMinHeight(SwLayoutFrame & rFrame)
{
SwTwips nMinHeight = lcl_GetFrameMinHeight(rFrame);
if (rFrame.getFrameArea().Height() < nMinHeight)
{
rFrame.Grow(nMinHeight - rFrame.getFrameArea().Height());
}
}
SwHeadFootFrame::SwHeadFootFrame( SwFrameFormat * pFormat, SwFrame* pSib, SwFrameType nTypeIn)
: SwLayoutFrame( pFormat, pSib )
{
mnFrameType = nTypeIn;
SetDerivedVert( false );
const SwFormatContent &rCnt = pFormat->GetContent();
OSL_ENSURE( rCnt.GetContentIdx(), "No content for Header." );
// Have the objects created right now for header and footer
bool bOld = bObjsDirect;
bObjsDirect = true;
SwNodeOffset nIndex = rCnt.GetContentIdx()->GetIndex();
::InsertCnt_( this, pFormat->GetDoc(), ++nIndex );
bObjsDirect = bOld;
}
void SwHeadFootFrame::FormatPrt(SwTwips & nUL, const SwBorderAttrs * pAttrs)
{
if (GetEatSpacing())
{
/* The minimal height of the print area is the minimal height of the
frame without the height needed for borders and shadow. */
SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
nMinHeight -= pAttrs->CalcTop();
nMinHeight -= pAttrs->CalcBottom();
/* If the minimal height of the print area is negative, try to
compensate by overlapping */
SwTwips nOverlap = 0;
if (nMinHeight < 0)
{
nOverlap = -nMinHeight;
nMinHeight = 0;
}
/* Calculate desired height of content. The minimal height has to be
adhered. */
SwTwips nHeight;
if ( ! HasFixSize() )
nHeight = lcl_CalcContentHeight(*this);
else
nHeight = nMinHeight;
if (nHeight < nMinHeight)
nHeight = nMinHeight;
/* calculate initial spacing/line space */
SwTwips nSpace, nLine;
if (IsHeaderFrame())
{
nSpace = pAttrs->CalcBottom();
nLine = pAttrs->CalcBottomLine();
}
else
{
nSpace = pAttrs->CalcTop();
nLine = pAttrs->CalcTopLine();
}
/* calculate overlap and correct spacing */
nOverlap += nHeight - nMinHeight;
if (nOverlap < nSpace - nLine)
nSpace -= nOverlap;
else
nSpace = nLine;
/* calculate real vertical space between frame and print area */
if (IsHeaderFrame())
nUL = pAttrs->CalcTop() + nSpace;
else
nUL = pAttrs->CalcBottom() + nSpace;
/* set print area */
SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Left(pAttrs->CalcLeft(this));
if (IsHeaderFrame())
{
aPrt.Top(pAttrs->CalcTop());
}
else
{
aPrt.Top(nSpace);
}
aPrt.Width(getFrameArea().Width() - nLR);
SwTwips nNewHeight;
if (nUL < getFrameArea().Height())
{
nNewHeight = getFrameArea().Height() - nUL;
}
else
{
nNewHeight = 0;
}
aPrt.Height(nNewHeight);
}
else
{
// Set position
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Left( pAttrs->CalcLeft( this ) );
aPrt.Top ( pAttrs->CalcTop() );
// Set sizes - the sizes are given by the surrounding Frame, just
// subtract the borders.
SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
aPrt.Width ( getFrameArea().Width() - nLR );
aPrt.Height( getFrameArea().Height()- nUL );
}
setFramePrintAreaValid(true);
}
void SwHeadFootFrame::FormatSize(SwTwips nUL, const SwBorderAttrs * pAttrs)
{
if ( !HasFixSize() )
{
if( !IsColLocked() )
{
setFramePrintAreaValid(true);
setFrameAreaSizeValid(true);
const SwTwips nBorder = nUL;
SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
nMinHeight -= pAttrs->CalcTop();
nMinHeight -= pAttrs->CalcBottom();
if (nMinHeight < 0)
nMinHeight = 0;
ColLock();
SwTwips nMaxHeight = LONG_MAX;
SwTwips nRemaining, nOldHeight;
// #i64301#
// use the position of the footer printing area to control invalidation
// of the first footer content.
Point aOldFooterPrtPos;
do
{
nOldHeight = getFramePrintArea().Height();
SwFrame* pFrame = Lower();
// #i64301#
if ( pFrame &&
aOldFooterPrtPos != ( getFrameArea().Pos() + getFramePrintArea().Pos() ) )
{
pFrame->InvalidatePos_();
aOldFooterPrtPos = getFrameArea().Pos() + getFramePrintArea().Pos();
}
int nLoopControl = 0;
while( pFrame )
{
pFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i43771# - format also object anchored
// at the frame
// #i46941# - frame has to be valid.
// Note: frame could be invalid after calling its format,
// if it's locked
OSL_ENSURE( StackHack::IsLocked() || !pFrame->IsTextFrame() ||
pFrame->isFrameAreaDefinitionValid() ||
static_cast<SwTextFrame*>(pFrame)->IsJoinLocked(),
"<SwHeadFootFrame::FormatSize(..)> - text frame invalid and not locked." );
if ( pFrame->IsTextFrame() && pFrame->isFrameAreaDefinitionValid() )
{
if ( !SwObjectFormatter::FormatObjsAtFrame( *pFrame,
*(pFrame->FindPageFrame()) ) )
{
if (nLoopControl++ < 20)
{
// restart format with first content
pFrame = Lower();
continue;
}
else
SAL_WARN("sw", "SwHeadFootFrame::FormatSize: loop detection triggered");
}
}
pFrame = pFrame->GetNext();
}
nRemaining = 0;
pFrame = Lower();
while ( pFrame )
{
nRemaining += pFrame->getFrameArea().Height();
if( pFrame->IsTextFrame() &&
static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
// This TextFrame would like to be a bit bigger
nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
- pFrame->getFramePrintArea().Height();
else if( pFrame->IsSctFrame() &&
static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
pFrame = pFrame->GetNext();
}
if ( nRemaining < nMinHeight )
nRemaining = nMinHeight;
SwTwips nDiff = nRemaining - nOldHeight;
if( !nDiff )
break;
if( nDiff < 0 )
{
nMaxHeight = nOldHeight;
if( nRemaining <= nMinHeight )
nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
}
else
{
if (nOldHeight > nMinHeight)
nMinHeight = nOldHeight;
if( nRemaining >= nMaxHeight )
nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
}
nDiff = nRemaining - nOldHeight;
if ( nDiff )
{
ColUnlock();
if ( nDiff > 0 )
{
if ( Grow( nDiff ) )
{
pFrame = Lower();
while ( pFrame )
{
if( pFrame->IsTextFrame())
{
SwTextFrame * pTmpFrame = static_cast<SwTextFrame*>(pFrame);
if (pTmpFrame->IsUndersized() )
{
pTmpFrame->InvalidateSize();
pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
}
}
/* #i3568# Undersized sections need to be
invalidated too. */
else if (pFrame->IsSctFrame())
{
SwSectionFrame * pTmpFrame =
static_cast<SwSectionFrame*>(pFrame);
if (pTmpFrame->IsUndersized() )
{
pTmpFrame->InvalidateSize();
pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
}
}
pFrame = pFrame->GetNext();
}
}
}
else
Shrink( -nDiff );
// Quickly update the position
MakePos();
ColLock();
}
else
break;
// Don't overwrite the lower edge of the upper
if ( GetUpper() && getFrameArea().Height() )
{
const SwTwips nDeadLine = GetUpper()->getFrameArea().Top() + GetUpper()->getFramePrintArea().Bottom();
const SwTwips nBot = getFrameArea().Bottom();
if ( nBot > nDeadLine )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Bottom( nDeadLine );
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Height( getFrameArea().Height() - nBorder );
}
}
setFramePrintAreaValid(true);
setFrameAreaSizeValid(true);
} while( nRemaining<=nMaxHeight && nOldHeight!=getFramePrintArea().Height() );
ColUnlock();
}
setFramePrintAreaValid(true);
setFrameAreaSizeValid(true);
}
else //if (GetType() & FRM_HEADFOOT)
{
do
{
if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
{
ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
}
setFrameAreaSizeValid(true);
MakePos();
} while ( !isFrameAreaSizeValid() );
}
}
void SwHeadFootFrame::Format(vcl::RenderContext* pRenderContext, const SwBorderAttrs * pAttrs)
{
assert(pAttrs && "SwFooterFrame::Format, pAttrs is 0.");
if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
return;
if ( ! GetEatSpacing() && IsHeaderFrame())
{
SwLayoutFrame::Format(pRenderContext, pAttrs);
}
else
{
lcl_LayoutFrameEnsureMinHeight(*this);
SwTwips nUL = pAttrs->CalcTop() + pAttrs->CalcBottom();
if ( !isFramePrintAreaValid() )
FormatPrt(nUL, pAttrs);
if ( !isFrameAreaSizeValid() )
FormatSize(nUL, pAttrs);
}
}
SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool bInfo )
{
SwTwips nResult;
if ( IsColLocked() )
{
nResult = 0;
reason = SwResizeLimitReason::Unspecified;
}
else if (!GetEatSpacing())
{
nResult = SwLayoutFrame::GrowFrame(nDist, reason, bTst, bInfo);
}
else
{
nResult = 0;
std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
OSL_ENSURE(oAccess, "no border attributes");
SwBorderAttrs * pAttrs = oAccess->Get();
/* First assume the whole amount to grow can be provided by eating
spacing. */
SwTwips nEat = nDist;
SwTwips nMaxEat;
/* calculate maximum eatable spacing */
if (IsHeaderFrame())
nMaxEat = getFrameArea().Height() - getFramePrintArea().Top() - getFramePrintArea().Height() - pAttrs->CalcBottomLine();
else
nMaxEat = getFramePrintArea().Top() - pAttrs->CalcTopLine();
if (nMaxEat < 0)
nMaxEat = 0;
/* If the frame is too small, eat less spacing thus letting the frame
grow more. */
SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
SwTwips nFrameTooSmall = nMinHeight - getFrameArea().Height();
if (nFrameTooSmall > 0)
nEat -= nFrameTooSmall;
/* No negative eating, not eating more than allowed. */
if (nEat < 0)
nEat = 0;
else if (nEat > nMaxEat)
nEat = nMaxEat;
// Notify fly frame, if header frame
// grows. Consider, that 'normal' grow of layout frame already notifies
// the fly frames.
bool bNotifyFlys = false;
if (nEat > 0)
{
if ( ! bTst)
{
if (! IsHeaderFrame())
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Top(aPrt.Top() - nEat);
aPrt.Height(aPrt.Height() - nEat);
}
InvalidateAll();
}
nResult += nEat;
// trigger fly frame notify.
if ( IsHeaderFrame() )
{
bNotifyFlys = true;
}
}
if (nDist - nEat > 0)
{
const SwTwips nFrameGrow = SwLayoutFrame::GrowFrame(nDist - nEat, reason, bTst, bInfo);
nResult += nFrameGrow;
if (nFrameGrow > 0)
{
bNotifyFlys = false;
}
}
else
reason = SwResizeLimitReason::Unspecified;
// notify fly frames, if necessary and triggered.
if ( ( nResult > 0 ) && bNotifyFlys )
{
NotifyLowerObjs();
}
}
if ( nResult && !bTst )
SetCompletePaint();
return nResult;
}
SwTwips SwHeadFootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
{
SwTwips nResult;
if ( IsColLocked() )
{
nResult = 0;
}
else if (! GetEatSpacing())
{
nResult = SwLayoutFrame::ShrinkFrame(nDist, bTst, bInfo);
}
else
{
nResult = 0;
SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
SwTwips nOldHeight = getFrameArea().Height();
SwTwips nRest = 0; // Amount to shrink by spitting out spacing
if ( nOldHeight >= nMinHeight )
{
/* If the frame's height is bigger than its minimum height, shrink
the frame towards its minimum height. If this is not sufficient
to provide the shrinking requested provide the rest by spitting
out spacing. */
SwTwips nBiggerThanMin = nOldHeight - nMinHeight;
if (nBiggerThanMin < nDist)
{
nRest = nDist - nBiggerThanMin;
}
/* info: declaration of nRest -> else nRest = 0 */
}
else
/* The frame cannot shrink. Provide shrinking by spitting out
spacing. */
nRest = nDist;
// Notify fly frame, if header/footer frame shrinks.
// Consider, that 'normal' shrink of layout frame already notifies the fly frames.
bool bNotifyFlys = false;
if (nRest > 0)
{
std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
OSL_ENSURE(oAccess, "no border attributes");
SwBorderAttrs * pAttrs = oAccess->Get();
/* minimal height of print area */
SwTwips nMinPrtHeight = nMinHeight
- pAttrs->CalcTop()
- pAttrs->CalcBottom();
if (nMinPrtHeight < 0)
nMinPrtHeight = 0;
/* assume all shrinking can be provided */
SwTwips nShrink = nRest;
/* calculate maximum shrinking */
SwTwips nMaxShrink = getFramePrintArea().Height() - nMinPrtHeight;
/* shrink no more than maximum shrinking */
if (nShrink > nMaxShrink)
{
//nRest -= nShrink - nMaxShrink;
nShrink = nMaxShrink;
}
if (!bTst)
{
if (! IsHeaderFrame() )
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Top(aPrt.Top() + nShrink);
aPrt.Height(aPrt.Height() - nShrink);
}
InvalidateAll();
}
nResult += nShrink;
// Trigger fly frame notify.
if ( IsHeaderFrame() )
{
bNotifyFlys = true;
}
}
/* The shrinking not providable by spitting out spacing has to be done
by the frame. */
if (nDist - nRest > 0)
{
SwTwips nShrinkAmount = SwLayoutFrame::ShrinkFrame( nDist - nRest, bTst, bInfo );
nResult += nShrinkAmount;
if ( nShrinkAmount > 0 )
{
bNotifyFlys = false;
}
}
// Notify fly frames, if necessary.
if ( ( nResult > 0 ) && bNotifyFlys )
{
NotifyLowerObjs();
}
}
return nResult;
}
bool SwHeadFootFrame::GetEatSpacing() const
{
const SwFrameFormat * pFormat = GetFormat();
assert(pFormat && "SwHeadFootFrame: no format?");
return pFormat->GetHeaderAndFooterEatSpacing().GetValue();
}
static void DelFlys( const SwLayoutFrame& rFrame, SwPageFrame &rPage)
{
size_t i = 0;
while ( rPage.GetSortedObjs() &&
rPage.GetSortedObjs()->size() &&
i < rPage.GetSortedObjs()->size() )
{
SwAnchoredObject* pObj = (*rPage.GetSortedObjs())[i];
if (SwFlyFrame* pFlyFrame = pObj->DynCastFlyFrame())
{
if (rFrame.IsAnLower(pFlyFrame))
{
SwFrame::DestroyFrame(pFlyFrame);
// Do not increment index, in this case
continue;
}
}
++i;
}
}
/// Creates or removes headers
void SwPageFrame::PrepareHeader()
{
SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
if ( !pLay )
return;
const SwFormatHeader &rH = static_cast<SwFrameFormat*>(GetDep())->GetHeader();
const SwViewShell *pSh = getRootFrame()->GetCurrShell();
const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
pSh->GetViewOptions()->IsWhitespaceHidden()));
if ( bOn && rH.IsActive() )
{ //Implant header, but remove first, if already present
OSL_ENSURE( rH.GetHeaderFormat(), "FrameFormat for Header not found." );
if ( pLay->GetFormat() == rH.GetHeaderFormat() )
return; // Header is already the correct one.
if ( pLay->IsHeaderFrame() )
{ SwLayoutFrame *pDel = pLay;
pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
::DelFlys(*pDel, *this);
pDel->Cut();
SwFrame::DestroyFrame(pDel);
}
OSL_ENSURE( pLay, "Where to with the Header?" );
SwHeaderFrame *pH = new SwHeaderFrame( const_cast<SwFrameFormat*>(rH.GetHeaderFormat()), this );
pH->Paste( this, pLay );
if ( GetUpper() )
::RegistFlys( this, pH );
}
else if (pLay->IsHeaderFrame())
{ // Remove header if present.
::DelFlys(*pLay, *this);
pLay->Cut();
SwFrame::DestroyFrame(pLay);
}
}
/// Creates or removes footer
void SwPageFrame::PrepareFooter()
{
SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
if ( !pLay )
return;
const SwFormatFooter &rF = static_cast<SwFrameFormat*>(GetDep())->GetFooter();
while ( pLay->GetNext() )
pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
const SwViewShell *pSh = getRootFrame()->GetCurrShell();
const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
pSh->GetViewOptions()->IsWhitespaceHidden()));
if ( bOn && rF.IsActive() )
{ //Implant footer, but remove first, if already present
OSL_ENSURE( rF.GetFooterFormat(), "FrameFormat for Footer not found." );
if ( pLay->GetFormat() == rF.GetFooterFormat() )
return; // Footer is already the correct one.
if ( pLay->IsFooterFrame() )
{
::DelFlys(*pLay, *this);
pLay->Cut();
SwFrame::DestroyFrame(pLay);
}
SwFooterFrame *pF = new SwFooterFrame( const_cast<SwFrameFormat*>(rF.GetFooterFormat()), this );
pF->Paste( this );
if ( GetUpper() )
::RegistFlys( this, pF );
}
else if ( pLay->IsFooterFrame() )
{
// Remove footer if already present
::DelFlys(*pLay, *this);
SwViewShell *pShell;
if ( pLay->GetPrev() && nullptr != (pShell = getRootFrame()->GetCurrShell()) &&
pShell->VisArea().HasArea() )
pShell->InvalidateWindows( pShell->VisArea() );
pLay->Cut();
SwFrame::DestroyFrame(pLay);
}
}
void SwHeaderFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
(void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("header"));
dumpAsXmlAttributes(writer);
SwHeadFootFrame::dumpAsXml(writer);
(void)xmlTextWriterEndElement(writer);
}
void SwFooterFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
(void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("footer"));
dumpAsXmlAttributes(writer);
SwHeadFootFrame::dumpAsXml(writer);
(void)xmlTextWriterEndElement(writer);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1007 The value from the potentially uninitialized optional 'oAccess' is used. Probably it is a mistake.
↑ V1007 The value from the potentially uninitialized optional 'oAccess' is used. Probably it is a mistake.