/* -*- 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 <libxml/xmlwriter.h>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <ftnidx.hxx>
#include <pagefrm.hxx>
#include <colfrm.hxx>
#include <rootfrm.hxx>
#include <frmtool.hxx>
#include <ftnfrm.hxx>
#include <txtfrm.hxx>
#include <tabfrm.hxx>
#include <pagedesc.hxx>
#include <ftninfo.hxx>
#include <sectfrm.hxx>
#include <objectformatter.hxx>
#include <viewopt.hxx>
#include <calbck.hxx>
#include <ndindex.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <IDocumentSettingAccess.hxx>
#include <flyfrm.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <docsh.hxx>
#include <poolfmt.hxx>
#include <swfntcch.hxx>
#include <wrtsh.hxx>
#define ENDNOTE 0x80000000
namespace
{
/// Calculates the height of the line that hosts the separator line (the top margin of the
/// container), based on the default paragraph style in rDoc.
bool FootnoteSeparatorHeightFromParagraph(SwDoc& rDoc, SwTwips& rHeight)
{
const SwTextFormatColl* pDefaultParaFormat
= rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
if (!pDefaultParaFormat)
{
return false;
}
SwViewShell* pSh = rDoc.GetDocShell()->GetWrtShell();
if (!pSh)
{
return false;
}
SwFontAccess aFontAccess(pDefaultParaFormat, pSh);
SwFont aFont(aFontAccess.Get()->GetFont());
OutputDevice& rOut = pSh->GetRefDev();
rHeight = aFont.GetHeight(pSh, rOut);
return true;
}
}
/// Search the position of an attribute in the FootnoteArray at the document,
/// because all footnotes are located there, ordered by their index.
static sal_uInt32 lcl_FindFootnotePos( const SwDoc *pDoc, const SwTextFootnote *pAttr )
{
const SwFootnoteIdxs &rFootnoteIdxs = pDoc->GetFootnoteIdxs();
SwTextFootnote* pBla = const_cast<SwTextFootnote*>(pAttr);
SwFootnoteIdxs::const_iterator it = rFootnoteIdxs.find( pBla );
if ( it != rFootnoteIdxs.end() )
{
sal_uInt32 nRet = it - rFootnoteIdxs.begin();
if( pAttr->GetFootnote().IsEndNote() )
return nRet + ENDNOTE;
return nRet;
}
OSL_ENSURE( !pDoc, "FootnotePos not found." );
return 0;
}
bool SwFootnoteFrame::operator<( const SwTextFootnote* pTextFootnote ) const
{
const SwDoc* pDoc = GetFormat()->GetDoc();
OSL_ENSURE( pDoc, "SwFootnoteFrame: Missing doc!" );
return lcl_FindFootnotePos( pDoc, GetAttr() ) <
lcl_FindFootnotePos( pDoc, pTextFootnote );
}
/*
|*
|* bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* pBoss, SwPageFrame* pPage)
|* sets pBoss on the next SwFootnoteBossFrame, which can either be a column
|* or a page (without columns). If the page changes meanwhile,
|* pPage contains the new page and this function returns true.
|*
|*/
static bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* &rpBoss, SwPageFrame* &rpPage,
bool bDontLeave )
{
if( rpBoss->IsColumnFrame() )
{
if( rpBoss->GetNext() )
{
rpBoss = static_cast<SwFootnoteBossFrame*>(rpBoss->GetNext()); //next column
return false;
}
if( rpBoss->IsInSct() )
{
SwSectionFrame* pSct = rpBoss->FindSctFrame()->GetFollow();
if( pSct )
{
OSL_ENSURE( pSct->Lower() && pSct->Lower()->IsColumnFrame(),
"Where's the column?" );
rpBoss = static_cast<SwColumnFrame*>(pSct->Lower());
SwPageFrame* pOld = rpPage;
rpPage = pSct->FindPageFrame();
return pOld != rpPage;
}
else if( bDontLeave )
{
rpPage = nullptr;
rpBoss = nullptr;
return false;
}
}
}
rpPage = static_cast<SwPageFrame*>(rpPage->GetNext()); // next page
rpBoss = rpPage;
if( rpPage )
{
SwLayoutFrame* pBody = rpPage->FindBodyCont();
if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
rpBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // first column
}
return true;
}
/// @returns column number if pBoss is a column, otherwise 0.
static sal_uInt16 lcl_ColumnNum( const SwFrame* pBoss )
{
sal_uInt16 nRet = 0;
if( !pBoss->IsColumnFrame() )
return 0;
const SwFrame* pCol;
if( pBoss->IsInSct() )
{
pCol = pBoss->GetUpper()->FindColFrame();
if( pBoss->GetNext() || pBoss->GetPrev() )
{
while( pBoss )
{
++nRet; // Section columns
pBoss = pBoss->GetPrev();
}
}
}
else
pCol = pBoss;
while( pCol )
{
nRet += 256; // Page columns
pCol = pCol->GetPrev();
}
return nRet;
}
SwFootnoteContFrame::SwFootnoteContFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
SwLayoutFrame( pFormat, pSib )
{
mnFrameType = SwFrameType::FtnCont;
}
SwFootnoteFrame* SwFootnoteContFrame::AddChained(bool bAppend, SwFrame* pThis, bool bDefaultFormat)
{
SwFootnoteFrame *pOld = pThis->FindFootnoteFrame();
SwFrameFormat *pFormat = pOld->GetFormat();
if (bDefaultFormat)
pFormat = pFormat->GetDoc()->GetDfltFrameFormat();
SwFootnoteFrame *pNew = new SwFootnoteFrame(pFormat, pOld, pOld->GetRef(), pOld->GetAttr());
if (bAppend)
{
if (pOld->GetFollow())
{
pNew->SetFollow(pOld->GetFollow());
pOld->GetFollow()->SetMaster(pNew);
}
pOld->SetFollow(pNew);
pNew->SetMaster(pOld);
}
else
{
if (pOld->GetMaster())
{
pNew->SetMaster(pOld->GetMaster());
pOld->GetMaster()->SetFollow(pNew);
}
pNew->SetFollow(pOld);
pOld->SetMaster(pNew);
}
return pNew;
}
// lcl_Undersize(..) walks over a SwFrame and its contents
// and returns the sum of all requested TextFrame magnifications.
static tools::Long lcl_Undersize( const SwFrame* pFrame )
{
tools::Long nRet = 0;
SwRectFnSet aRectFnSet(pFrame);
if( pFrame->IsTextFrame() )
{
if( static_cast<const SwTextFrame*>(pFrame)->IsUndersized() )
{
// Does this TextFrame would like to be a little bit bigger?
nRet = static_cast<const SwTextFrame*>(pFrame)->GetParHeight() -
aRectFnSet.GetHeight(pFrame->getFramePrintArea());
if( nRet < 0 )
nRet = 0;
}
}
else if( pFrame->IsLayoutFrame() )
{
const SwFrame* pNxt = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
while( pNxt )
{
nRet += lcl_Undersize( pNxt );
pNxt = pNxt->GetNext();
}
}
return nRet;
}
namespace sw {
SwTwips FootnoteSeparatorHeight(SwDoc& rDoc, SwPageFootnoteInfo const& rInf)
{
const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
if (rIDSA.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
{
// Word style: try to calculate the height from the default para format.
SwTwips nHeight{};
if (FootnoteSeparatorHeightFromParagraph(rDoc, nHeight))
{
return nHeight;
}
}
// Writer style: calculate from the page style.
return rInf.GetTopDist() + rInf.GetBottomDist() + rInf.GetLineWidth();
}
} // namespace sw
/// "format" the frame (Fixsize is not set here).
void SwFootnoteContFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
{
// calculate total border, only one distance to the top
const SwPageFrame* pPage = FindPageFrame();
const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
SwDoc* pDoc = getRootFrame()->GetCurrShell()->GetDoc();
const SwTwips nBorder = sw::FootnoteSeparatorHeight(*pDoc, rInf);
SwRectFnSet aRectFnSet(this);
if ( !isFramePrintAreaValid() )
{
setFramePrintAreaValid(true);
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aRectFnSet.SetTop( aPrt, nBorder );
aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
aRectFnSet.SetHeight(aPrt, aRectFnSet.GetHeight(getFrameArea()) - nBorder );
if( aRectFnSet.GetHeight(aPrt) < 0 && !pPage->IsFootnotePage() )
{
setFrameAreaSizeValid(false);
}
}
if ( isFrameAreaSizeValid() )
return;
bool bGrow = pPage->IsFootnotePage();
if( bGrow )
{
const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
if( pSh && pSh->GetViewOptions()->getBrowseMode() )
bGrow = false;
}
if( bGrow )
Grow( LONG_MAX );
else
{
// VarSize is determined based on the content plus the borders
SwTwips nRemaining = 0;
SwFrame *pFrame = m_pLower;
while ( pFrame )
{ // lcl_Undersize(..) respects (recursively) TextFrames, which
// would like to be bigger. They are created especially in
// columnized borders, if these do not have their maximum
// size yet.
nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea()) + lcl_Undersize( pFrame );
pFrame = pFrame->GetNext();
}
// add the own border
nRemaining += nBorder;
SwTwips nDiff;
if( IsInSct() )
{
nDiff = -aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
if( nDiff > 0 )
{
if( nDiff > aRectFnSet.GetHeight(getFrameArea()) )
{
nDiff = aRectFnSet.GetHeight(getFrameArea());
}
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.AddBottom( aFrm, -nDiff );
aRectFnSet.AddHeight( aFrm, -nDiff );
}
}
nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
if ( nDiff > 0 )
Shrink( nDiff );
else if ( nDiff < 0 )
{
Grow( -nDiff );
// It may happen that there is less space available,
// than what the border needs - the size of the PrtArea
// will then be negative.
SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
if( nPrtHeight < 0 )
{
const SwTwips nTmpDiff = std::max( SwTwips(aRectFnSet.GetTop(getFramePrintArea())), -nPrtHeight );
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aRectFnSet.SubTop( aPrt, nTmpDiff );
}
}
}
setFrameAreaSizeValid(true);
}
SwTwips SwFootnoteContFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool)
{
// No check if FixSize since FootnoteContainer are variable up to their max. height.
// If the max. height is LONG_MAX, take as much space as needed.
// If the page is a special footnote page, take also as much as possible.
assert(GetUpper() && GetUpper()->IsFootnoteBossFrame());
const auto nOrigDist = std::max(nDist, SwTwips(0));
reason = SwResizeLimitReason::Unspecified;
SwRectFnSet aRectFnSet(this);
if( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
nDist > ( LONG_MAX - aRectFnSet.GetHeight(getFrameArea()) ) )
nDist = LONG_MAX - aRectFnSet.GetHeight(getFrameArea());
SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
if( IsInSct() )
{
SwSectionFrame* pSect = FindSctFrame();
OSL_ENSURE( pSect, "GrowFrame: Missing SectFrame" );
// In a section, which has to maximize, a footnotecontainer is allowed
// to grow, when the section can't grow anymore.
if( !bTst && !pSect->IsColLocked() &&
pSect->ToMaximize( false ) && pSect->Growable() )
{
pSect->InvalidateSize();
if (nOrigDist)
reason = SwResizeLimitReason::FlowToFollow;
return 0;
}
}
const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
SwPageFrame *pPage = pBoss->FindPageFrame();
if ( bBrowseMode || !pPage->IsFootnotePage() )
{
if ( pBoss->GetMaxFootnoteHeight() != LONG_MAX )
{
nDist = std::min( nDist,
SwTwips(pBoss->GetMaxFootnoteHeight() - aRectFnSet.GetHeight(getFrameArea())) );
if ( nDist <= 0 )
{
if (nOrigDist)
reason = SwResizeLimitReason::FlowToFollow;
return 0;
}
}
// FootnoteBoss also influences the max value
if( !IsInSct() )
{
const SwTwips nMax = pBoss->GetVarSpace();
if ( nDist > nMax )
{
nDist = nMax;
if (nOrigDist)
reason = SwResizeLimitReason::FlowToFollow;
}
if ( nDist <= 0 )
return 0;
}
}
else if( nDist > aRectFnSet.GetHeight(GetPrev()->getFrameArea()) )
{
// do not use more space than the body has
nDist = aRectFnSet.GetHeight(GetPrev()->getFrameArea());
if (nOrigDist)
reason = SwResizeLimitReason::FlowToFollow;
}
tools::Long nAvail = 0;
if ( bBrowseMode )
{
nAvail = GetUpper()->getFramePrintArea().Height();
const SwFrame *pAvail = GetUpper()->Lower();
do
{ nAvail -= pAvail->getFrameArea().Height();
pAvail = pAvail->GetNext();
} while ( pAvail );
if ( nAvail > nDist )
nAvail = nDist;
}
if ( !bTst )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nDist );
if( IsVertical() && !IsVertLR() )
{
aFrm.Pos().AdjustX( -nDist );
}
}
tools::Long nGrow = nDist - nAvail,
nReal = 0;
if ( nGrow > 0 )
{
SwNeighbourAdjust nAdjust = pBoss->NeighbourhoodAdjustment();
if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
nReal = AdjustNeighbourhood( nGrow, bTst );
else
{
if( SwNeighbourAdjust::GrowAdjust == nAdjust )
{
SwFrame* pFootnote = Lower();
if( pFootnote )
{
while( pFootnote->GetNext() )
pFootnote = pFootnote->GetNext();
if( static_cast<SwFootnoteFrame*>(pFootnote)->GetAttr()->GetFootnote().IsEndNote() )
{
nReal = AdjustNeighbourhood( nGrow, bTst );
nAdjust = SwNeighbourAdjust::GrowShrink; // no more AdjustNeighbourhood
}
}
}
nReal += pBoss->Grow(nGrow - nReal, reason, bTst, false);
if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
&& nReal < nGrow )
nReal += AdjustNeighbourhood( nGrow - nReal, bTst );
}
}
nReal += nAvail;
if ( !bTst )
{
if ( nReal != nDist )
{
nDist -= nReal;
// We can only respect the boundless wish so much
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.AddHeight( -nDist );
if( IsVertical() && !IsVertLR() )
{
aFrm.Pos().AdjustX(nDist );
}
}
// growing happens upwards, so successors to not need to be invalidated
if( nReal )
{
InvalidateSize_();
InvalidatePos_();
InvalidatePage( pPage );
}
}
if (nOrigDist > nReal && reason == SwResizeLimitReason::Unspecified)
reason = SwResizeLimitReason::FlowToFollow;
return nReal;
}
SwTwips SwFootnoteContFrame::ShrinkFrame( SwTwips nDiff, bool bTst, bool bInfo )
{
SwPageFrame *pPage = FindPageFrame();
bool bShrink = false;
if ( pPage )
{
if( !pPage->IsFootnotePage() )
bShrink = true;
else
{
const SwViewShell *pSh = getRootFrame()->GetCurrShell();
if( pSh && pSh->GetViewOptions()->getBrowseMode() )
bShrink = true;
}
}
if( bShrink )
{
SwTwips nRet = SwLayoutFrame::ShrinkFrame( nDiff, bTst, bInfo );
if( IsInSct() && !bTst )
FindSctFrame()->InvalidateNextPos();
if ( !bTst && nRet )
{
InvalidatePos_();
InvalidatePage( pPage );
}
return nRet;
}
return 0;
}
SwFootnoteFrame::SwFootnoteFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwContentFrame *pCnt, SwTextFootnote *pAt ):
SwLayoutFrame( pFormat, pSib ),
mpFollow( nullptr ),
mpMaster( nullptr ),
mpReference( pCnt ),
mpAttribute( pAt ),
mbBackMoveLocked( false ),
// #i49383#
mbUnlockPosOfLowerObjs( true )
{
mnFrameType = SwFrameType::Ftn;
}
void SwFootnoteFrame::InvalidateNxtFootnoteCnts( SwPageFrame const *pPage )
{
if ( !GetNext() )
return;
SwFrame *pCnt = static_cast<SwLayoutFrame*>(GetNext())->ContainsAny();
if( !pCnt )
return;
pCnt->InvalidatePage( pPage );
pCnt->InvalidatePrt_();
do
{ pCnt->InvalidatePos_();
if( pCnt->IsSctFrame() )
{
SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
if( pTmp )
pTmp->InvalidatePos_();
}
pCnt->GetUpper()->InvalidateSize_();
pCnt = pCnt->FindNext();
} while ( pCnt && GetUpper()->IsAnLower( pCnt ) );
}
bool SwFootnoteFrame::IsDeleteForbidden() const
{
if (SwLayoutFrame::IsDeleteForbidden())
return true;
// needs to be in sync with the ::Cut logic
const SwLayoutFrame *pUp = GetUpper();
if (pUp)
{
if (GetPrev())
return false;
// The last footnote takes its container along if it
// is deleted. Cut would put pUp->Lower() to the value
// of GetNext(), so if there is no GetNext then
// Cut would delete pUp. If that condition is true
// here then check if the container is delete-forbidden
return !GetNext() && pUp->IsDeleteForbidden();
}
return false;
}
void SwFootnoteFrame::Cut()
{
if ( GetNext() )
GetNext()->InvalidatePos();
else if ( GetPrev() )
GetPrev()->SetRetouche();
// first move then shrink Upper
SwLayoutFrame *pUp = GetUpper();
// correct chaining
SwFootnoteFrame *pFootnote = this;
if ( pFootnote->GetFollow() )
pFootnote->GetFollow()->SetMaster( pFootnote->GetMaster() );
if ( pFootnote->GetMaster() )
pFootnote->GetMaster()->SetFollow( pFootnote->GetFollow() );
pFootnote->SetFollow( nullptr );
pFootnote->SetMaster( nullptr );
// cut all connections
RemoveFromLayout();
if ( !pUp )
return;
// The last footnote takes its container along
if (!pUp->Lower())
{
SwPageFrame *pPage = pUp->FindPageFrame();
if ( pPage )
{
SwLayoutFrame *pBody = pPage->FindBodyCont();
if( pBody && !pBody->ContainsContent() )
pPage->getRootFrame()->SetSuperfluous();
}
SwSectionFrame* pSect = pUp->FindSctFrame();
pUp->Cut();
SwFrame::DestroyFrame(pUp);
// If the last footnote container was removed from a column
// section without a Follow, then this section can be shrunk.
if( pSect && !pSect->ToMaximize( false ) && !pSect->IsColLocked() )
pSect->InvalidateSize_();
}
else
{ if ( getFrameArea().Height() )
pUp->Shrink( getFrameArea().Height() );
pUp->SetCompletePaint();
pUp->InvalidatePage();
}
}
void SwFootnoteFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
{
OSL_ENSURE( pParent, "no parent in Paste." );
OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
OSL_ENSURE( pParent != this, "I am my own parent." );
OSL_ENSURE( pSibling != this, "I am my own sibling." );
OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
"I am still somewhere registered." );
// insert into tree structure
InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
SwRectFnSet aRectFnSet(this);
if( aRectFnSet.GetWidth(getFrameArea())!=aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
InvalidateSize_();
InvalidatePos_();
if (SwFrame *const pContent = ContainsContent())
{ // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
pContent->InvalidatePrt_();
}
SwPageFrame *pPage = FindPageFrame();
InvalidatePage( pPage );
if (SwFootnoteFrame *const pNext = static_cast<SwFootnoteFrame *>(GetNext()))
{
pNext->InvalidatePos_();
if (SwFrame *const pContent = pNext->ContainsContent())
{ // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
pContent->InvalidatePrt_();
}
}
if( aRectFnSet.GetHeight(getFrameArea()) )
pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
// If the predecessor is the master and/or the successor is the Follow,
// then take their content and destroy them.
if ( GetPrev() && GetPrev() == GetMaster() )
{
OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetPrev()->GetLower() ),
"Footnote without content?" );
SwFlowFrame::CastFlowFrame( GetPrev()->GetLower())->
MoveSubTree( this, GetLower() );
SwFrame *pDel = GetPrev();
assert(pDel != this);
pDel->Cut();
SwFrame::DestroyFrame(pDel);
}
if ( GetNext() && GetNext() == GetFollow() )
{
OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetNext()->GetLower() ),
"Footnote without content?" );
SwFlowFrame::CastFlowFrame( GetNext()->GetLower() )->MoveSubTree( this );
SwFrame *pDel = GetNext();
assert(pDel != this);
pDel->Cut();
SwFrame::DestroyFrame(pDel);
}
#if OSL_DEBUG_LEVEL > 0
SwDoc *pDoc = GetFormat()->GetDoc();
if ( GetPrev() )
{
OSL_ENSURE( lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetPrev())->GetAttr() ) <=
lcl_FindFootnotePos( pDoc, GetAttr() ), "Prev is not FootnotePrev" );
}
if ( GetNext() )
{
OSL_ENSURE( lcl_FindFootnotePos( pDoc, GetAttr() ) <=
lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetNext())->GetAttr() ),
"Next is not FootnoteNext" );
}
#endif
InvalidateNxtFootnoteCnts( pPage );
}
/// Return the next layout leaf in that the frame can be moved.
/// New pages will only be created if specified by the parameter.
SwLayoutFrame *SwFrame::GetNextFootnoteLeaf( MakePageType eMakePage )
{
SwFootnoteBossFrame *pOldBoss = FindFootnoteBossFrame();
SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
SwPageFrame* pPage;
SwFootnoteBossFrame *pBoss = pOldBoss->IsColumnFrame() ?
static_cast<SwFootnoteBossFrame*>(pOldBoss->GetNext()) : nullptr; // next column, if existing
if( pBoss )
pPage = nullptr;
else
{
if( pOldBoss->GetUpper()->IsSctFrame() )
{ // this can only be in a column area
SwLayoutFrame* pNxt = pOldBoss->GetNextSctLeaf( eMakePage );
if( pNxt )
{
OSL_ENSURE( pNxt->IsColBodyFrame(), "GetNextFootnoteLeaf: Funny Leaf" );
pBoss = static_cast<SwFootnoteBossFrame*>(pNxt->GetUpper());
pPage = pBoss->FindPageFrame();
}
else
return nullptr;
}
else
{
// next page
pPage = static_cast<SwPageFrame*>(pOldPage->GetNext());
// skip empty pages
if( pPage && pPage->IsEmptyPage() )
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
pBoss = pPage;
}
}
// What do we have until here?
// pBoss != NULL, pPage==NULL => pBoss is the next column on the same page
// pBoss != NULL, pPage!=NULL => pBoss and pPage are the following page (empty pages skipped)
// pBoss == NULL => pPage == NULL, so there are no following pages
// If the footnote has already a Follow we do not need to search.
// However, if there are unwanted empty columns/pages between Footnote and Follow,
// create another Follow on the next best column/page and the rest will sort itself out.
SwFootnoteFrame *pFootnote = FindFootnoteFrame();
if ( pFootnote && pFootnote->GetFollow() )
{
SwFootnoteBossFrame* pTmpBoss = pFootnote->GetFollow()->FindFootnoteBossFrame();
// Following cases will be handled:
// 1. both "FootnoteBoss"es are neighboring columns/pages
// 2. the new one is the first column of a neighboring page
// 3. the new one is the first column in a section of the next page
while( pTmpBoss != pBoss && pTmpBoss && !pTmpBoss->GetPrev() )
pTmpBoss = pTmpBoss->GetUpper()->FindFootnoteBossFrame();
if( pTmpBoss == pBoss )
return pFootnote->GetFollow();
}
// If no pBoss could be found or it is a "wrong" page, we need a new page.
if ( !pBoss || ( pPage && pPage->IsEndNotePage() && !pOldPage->IsEndNotePage() ) )
{
if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
{
pBoss = InsertPage( pOldPage, pOldPage->IsFootnotePage() );
static_cast<SwPageFrame*>(pBoss)->SetEndNotePage( pOldPage->IsEndNotePage() );
}
else
return nullptr;
}
if( pBoss->IsPageFrame() )
{
// If this page has columns, then go to the first one
SwLayoutFrame* pLay = pBoss->FindBodyCont();
if( pLay && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
pBoss = static_cast<SwFootnoteBossFrame*>(pLay->Lower());
}
// found column/page - add myself
SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
if ( !pCont && pBoss->GetMaxFootnoteHeight() &&
( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
pCont = pBoss->MakeFootnoteCont();
return pCont;
}
/// Get the preceding layout leaf in that the frame can be moved.
SwLayoutFrame *SwFrame::GetPrevFootnoteLeaf( MakePageType eMakeFootnote )
{
// The predecessor of a footnote is (if possible)
// the master of the chain of the footnote.
SwFootnoteFrame *pFootnote = FindFootnoteFrame();
SwLayoutFrame *pRet = pFootnote->GetMaster();
SwFootnoteBossFrame* pOldBoss = FindFootnoteBossFrame();
SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
if ( !pOldBoss->GetPrev() && !pOldPage->GetPrev() )
return pRet; // there is neither a predecessor column nor page
if ( !pRet )
{
bool bEndn = pFootnote->GetAttr()->GetFootnote().IsEndNote();
SwFrame* pTmpRef = nullptr;
const IDocumentSettingAccess& rSettings
= pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
bool bContEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
if( bEndn && pFootnote->IsInSct() && !bContEndnotes)
{
SwSectionFrame* pSect = pFootnote->FindSctFrame();
if( pSect->IsEndnAtEnd() )
// Endnotes at the end of the section.
pTmpRef = pSect->FindLastContent( SwFindMode::LastCnt );
}
else if (bEndn && bContEndnotes)
{
// Endnotes at the end of the document.
pTmpRef = pFootnote->FindFootnoteBossFrame();
}
if( !pTmpRef )
// Endnotes on a separate page.
pTmpRef = pFootnote->GetRef();
SwFootnoteBossFrame* pStop = pTmpRef->FindFootnoteBossFrame( !bEndn );
const sal_uInt16 nNum = pStop->GetPhyPageNum();
// Do not leave the corresponding page if the footnote should
// be shown at the document ending or the footnote is an endnote.
const bool bEndNote = pOldPage->IsEndNotePage();
const bool bFootnoteEndDoc = pOldPage->IsFootnotePage();
SwFootnoteBossFrame* pNxtBoss = pOldBoss;
SwSectionFrame *pSect = pNxtBoss->GetUpper()->IsSctFrame() ?
static_cast<SwSectionFrame*>(pNxtBoss->GetUpper()) : nullptr;
do
{
if( pNxtBoss->IsColumnFrame() && pNxtBoss->GetPrev() )
pNxtBoss = static_cast<SwFootnoteBossFrame*>(pNxtBoss->GetPrev()); // one column backwards
else // one page backwards
{
SwLayoutFrame* pBody = nullptr;
if( pSect )
{
if( pSect->IsFootnoteLock() )
{
if( pNxtBoss == pOldBoss )
return nullptr;
pStop = pNxtBoss;
}
else
{
pSect = pSect->FindMaster();
if( !pSect || !pSect->Lower() )
return nullptr;
OSL_ENSURE( pSect->Lower()->IsColumnFrame(),
"GetPrevFootnoteLeaf: Where's the column?" );
pNxtBoss = static_cast<SwFootnoteBossFrame*>(pSect->Lower());
pBody = pSect;
}
}
else
{
SwPageFrame* pPage = static_cast<SwPageFrame*>(pNxtBoss->FindPageFrame()->GetPrev());
if( !pPage || pPage->GetPhyPageNum() < nNum ||
bEndNote != pPage->IsEndNotePage() || bFootnoteEndDoc != pPage->IsFootnotePage() )
return nullptr; // no further pages found
pNxtBoss = pPage;
pBody = pPage->FindBodyCont();
}
// We have the previous page, we might need to find the last column of it
if( pBody )
{
if ( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
{
pNxtBoss = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
}
}
}
SwFootnoteContFrame *pCont = pNxtBoss->FindFootnoteCont();
if ( pCont )
{
pRet = pCont;
break;
}
if ( pStop == pNxtBoss )
{
// Reached the column/page of the reference.
// Try to add a container and paste our content.
if ( eMakeFootnote == MAKEPAGE_FTN && pNxtBoss->GetMaxFootnoteHeight() )
pRet = pNxtBoss->MakeFootnoteCont();
break;
}
} while( !pRet );
}
if ( pRet )
{
const SwFootnoteBossFrame* pNewBoss = pRet->FindFootnoteBossFrame();
bool bJump = false;
if( pOldBoss->IsColumnFrame() && pOldBoss->GetPrev() ) // a previous column exists
bJump = pOldBoss->GetPrev() != static_cast<SwFrame const *>(pNewBoss); // did we chose it?
else if( pNewBoss->IsColumnFrame() && pNewBoss->GetNext() )
bJump = true; // there is another column after the boss (not the old boss)
else
{
// Will be reached only if old and new boss are both either pages or the last (new)
// or first (old) column of a page. In this case, check if pages were skipped.
const sal_uInt16 nDiff = pOldPage->GetPhyPageNum() - pRet->FindPageFrame()->GetPhyPageNum();
if ( nDiff > 2 ||
(nDiff > 1 && !static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage()) )
bJump = true;
}
if( bJump )
SwFlowFrame::SetMoveBwdJump( true );
}
return pRet;
}
bool SwFrame::IsFootnoteAllowed() const
{
bool bSplitFly = false;
const SwFlyFrame* pFlyFrame = FindFlyFrame();
if (pFlyFrame)
{
// This is a fly. Check if it's a split fly, which is OK to host a footnote.
bSplitFly = pFlyFrame->IsFlySplitAllowed();
}
if (!IsInDocBody() && !bSplitFly)
return false;
if ( IsInTab() )
{
// no footnotes in repeated headlines
const SwTabFrame *pTab = const_cast<SwFrame*>(this)->ImplFindTabFrame();
assert(pTab);
if ( pTab->IsFollow() )
return !pTab->IsInHeadline( *this );
}
return true;
}
void SwRootFrame::UpdateFootnoteNums()
{
// page numbering only if set at the document
if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum == FTNNUM_PAGE )
{
SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
while ( pPage && !pPage->IsFootnotePage() )
{
pPage->UpdateFootnoteNum();
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
}
}
}
/// remove all footnotes (not the references) and all footnote pages
void sw_RemoveFootnotes( SwFootnoteBossFrame* pBoss, bool bPageOnly, bool bEndNotes )
{
do
{
SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
if ( pCont )
{
SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
assert(pFootnote);
if ( bPageOnly )
while ( pFootnote->GetMaster() )
pFootnote = pFootnote->GetMaster();
do
{
SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
if ( !pFootnote->GetAttr()->GetFootnote().IsEndNote() ||
bEndNotes )
{
SwContentFrame* pCF = pFootnote->GetRef();
// it's possible that the contentframe is empty when closing Writer
if (!pCF)
return;
if (!pCF->IsInDtor())
// NOTE: I REPRO'D A CRASH HERE BUT THE DEBUGGER DIDN'T INDICATE
// WHAT THE PROBLEM WAS -- the objects are valid. Happens when
// undoing/redoing rapidly for some time then saving and the crash
// happens on close of LO
pCF->Prepare(PrepareHint::FootnoteInvalidation,
static_cast<void*>(pFootnote->GetAttr()));
if ( bPageOnly && !pNxt )
pNxt = pFootnote->GetFollow();
pFootnote->Cut();
SwFrame::DestroyFrame(pFootnote);
}
pFootnote = pNxt;
} while ( pFootnote );
}
if( !pBoss->IsInSct() )
{
// A sectionframe with the Footnote/EndnAtEnd-flags may contain
// foot/endnotes. If the last lower frame of the bodyframe is
// a multicolumned sectionframe, it may contain footnotes, too.
SwLayoutFrame* pBody = pBoss->FindBodyCont();
if( pBody && pBody->Lower() )
{
SwFrame* pLow = pBody->Lower();
while (pLow)
{
if( pLow->IsSctFrame() && ( !pLow->GetNext() ||
static_cast<SwSectionFrame*>(pLow)->IsAnyNoteAtEnd() ) &&
static_cast<SwSectionFrame*>(pLow)->Lower() &&
static_cast<SwSectionFrame*>(pLow)->Lower()->IsColumnFrame() )
sw_RemoveFootnotes( static_cast<SwColumnFrame*>(static_cast<SwSectionFrame*>(pLow)->Lower()),
bPageOnly, bEndNotes );
pLow = pLow->GetNext();
}
}
}
// is there another column?
pBoss = pBoss->IsColumnFrame() ? static_cast<SwColumnFrame*>(pBoss->GetNext()) : nullptr;
} while( pBoss );
}
void SwRootFrame::RemoveFootnotes( SwPageFrame *pPage, bool bPageOnly, bool bEndNotes )
{
if ( !pPage )
pPage = static_cast<SwPageFrame*>(Lower());
do
{ // On columned pages we have to clean up in all columns
SwFootnoteBossFrame* pBoss;
SwLayoutFrame* pBody = pPage->FindBodyCont();
if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // the first column
else
pBoss = pPage; // no columns
sw_RemoveFootnotes( pBoss, bPageOnly, bEndNotes );
if ( !bPageOnly )
{
if ( pPage->IsFootnotePage() &&
(!pPage->IsEndNotePage() || bEndNotes) )
{
SwFrame *pDel = pPage;
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
pDel->Cut();
SwFrame::DestroyFrame(pDel);
}
else
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
}
else
break;
} while ( pPage );
}
/// Change the page template of the footnote pages
void SwRootFrame::CheckFootnotePageDescs( bool bEndNote )
{
SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
while ( pPage && !pPage->IsFootnotePage() )
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
while ( pPage && pPage->IsEndNotePage() != bEndNote )
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
if ( pPage )
SwFrame::CheckPageDescs( pPage, false );
}
/** Insert a footnote container
*
* A footnote container is always placed directly behind the body text.
*
* The frame format (FrameFormat) is always the default frame format.
*
* @return footnote container frame
*/
SwFootnoteContFrame *SwFootnoteBossFrame::MakeFootnoteCont()
{
SAL_WARN_IF(FindFootnoteCont(), "sw.core", "footnote container exists already");
SwFootnoteContFrame *pNew = new SwFootnoteContFrame( GetFormat()->GetDoc()->GetDfltFrameFormat(), this );
SwLayoutFrame *pLay = FindBodyCont();
pNew->Paste( this, pLay->GetNext() );
return pNew;
}
SwFootnoteContFrame *SwFootnoteBossFrame::FindFootnoteCont()
{
SwFrame *pFrame = Lower();
while( pFrame && !pFrame->IsFootnoteContFrame() )
pFrame = pFrame->GetNext();
#if OSL_DEBUG_LEVEL > 0
if ( pFrame )
{
SwFrame *pFootnote = pFrame->GetLower();
assert(pFootnote);
while ( pFootnote )
{
assert(pFootnote->IsFootnoteFrame() && "Neighbor of footnote must be a footnote");
pFootnote = pFootnote->GetNext();
}
}
#endif
return static_cast<SwFootnoteContFrame*>(pFrame);
}
/// Search the next available footnote container.
SwFootnoteContFrame *SwFootnoteBossFrame::FindNearestFootnoteCont( bool bDontLeave )
{
SwFootnoteContFrame *pCont = nullptr;
if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
{
pCont = FindFootnoteCont();
if ( !pCont )
{
SwPageFrame *pPage = FindPageFrame();
SwFootnoteBossFrame* pBoss = this;
bool bEndNote = pPage->IsEndNotePage();
do
{
bool bChgPage = lcl_NextFootnoteBoss( pBoss, pPage, bDontLeave );
// Found another boss? When changing pages, also the endnote flag must match.
if( pBoss && ( !bChgPage || pPage->IsEndNotePage() == bEndNote ) )
pCont = pBoss->FindFootnoteCont();
} while ( !pCont && pPage );
}
}
return pCont;
}
SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote()
{
// search for the nearest footnote container
SwFootnoteContFrame *pCont = FindNearestFootnoteCont();
if ( !pCont )
return nullptr;
// Starting from the first footnote, search the first
// footnote that is referenced by the current column/page
SwFootnoteFrame *pRet = static_cast<SwFootnoteFrame*>(pCont->Lower());
const sal_uInt16 nRefNum = FindPageFrame()->GetPhyPageNum();
const sal_uInt16 nRefCol = lcl_ColumnNum( this );
sal_uInt16 nPgNum, nColNum; // page number, column number
SwFootnoteBossFrame* pBoss;
SwPageFrame* pPage;
if( pRet )
{
pBoss = pRet->GetRef()->FindFootnoteBossFrame();
OSL_ENSURE( pBoss, "FindFirstFootnote: No boss found" );
if( !pBoss )
return nullptr; // ?There must be a bug, but no GPF
pPage = pBoss->FindPageFrame();
// it's possible that there is no page frame when performing an undo operation
if (!pPage)
return nullptr;
nPgNum = pPage->GetPhyPageNum();
if ( nPgNum == nRefNum )
{
nColNum = lcl_ColumnNum( pBoss );
if( nColNum == nRefCol )
return pRet; // found
else if( nColNum > nRefCol )
return nullptr; // at least one column too far
}
else if ( nPgNum > nRefNum )
return nullptr; // at least one column too far
}
else
return nullptr;
// Done if Ref is on a subsequent page or on the same page in a subsequent column
do
{
while ( pRet->GetFollow() )
pRet = pRet->GetFollow();
SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pRet->GetNext());
if ( !pNxt )
{
pBoss = pRet->FindFootnoteBossFrame();
// it's possible that there is no boss frame when performing an undo operation
if (!pBoss)
return nullptr;
// it's possible that there is no page frame when performing an undo operation
pPage = pBoss->FindPageFrame();
if (!pPage)
return nullptr;
lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
if ( pCont )
pNxt = static_cast<SwFootnoteFrame*>(pCont->Lower());
}
if ( pNxt )
{
pRet = pNxt;
pBoss = pRet->GetRef()->FindFootnoteBossFrame();
// it's possible that there is no boss frame when performing an undo operation
if (!pBoss)
return nullptr;
// it's possible that there is no page frame when performing an undo operation
pPage = pBoss->FindPageFrame();
if (!pPage)
return nullptr;
nPgNum = pPage->GetPhyPageNum();
if ( nPgNum == nRefNum )
{
nColNum = lcl_ColumnNum( pBoss );
if( nColNum == nRefCol )
break; // found
else if( nColNum > nRefCol )
pRet = nullptr; // at least one column too far
}
else if ( nPgNum > nRefNum )
pRet = nullptr; // at least a page too far
}
else
pRet = nullptr; // there is none
} while( pRet );
return pRet;
}
/// Get the first footnote of a given content
const SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote( SwContentFrame const *pCnt ) const
{
const SwFootnoteFrame *pRet = const_cast<SwFootnoteBossFrame*>(this)->FindFirstFootnote();
if ( pRet )
{
const sal_uInt16 nColNum = lcl_ColumnNum( this );
const sal_uInt16 nPageNum = GetPhyPageNum();
while ( pRet && (pRet->GetRef() != pCnt) )
{
while ( pRet->GetFollow() )
pRet = pRet->GetFollow();
if ( pRet->GetNext() )
pRet = static_cast<const SwFootnoteFrame*>(pRet->GetNext());
else
{ SwFootnoteBossFrame *pBoss = const_cast<SwFootnoteBossFrame*>(pRet->FindFootnoteBossFrame());
SwPageFrame *pPage = pBoss->FindPageFrame();
lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
SwFootnoteContFrame *pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
pRet = pCont ? static_cast<SwFootnoteFrame*>(pCont->Lower()) : nullptr;
}
if ( pRet )
{
const SwFootnoteBossFrame* pBoss = pRet->GetRef()->FindFootnoteBossFrame();
if( !pBoss || pBoss->GetPhyPageNum() != nPageNum ||
nColNum != lcl_ColumnNum( pBoss ) )
pRet = nullptr;
}
}
}
return pRet;
}
void SwFootnoteBossFrame::ResetFootnote( const SwFootnoteFrame *pCheck )
{
// Destroy the incarnations of footnotes to an attribute, if they don't
// belong to pAssumed
OSL_ENSURE( !pCheck->GetMaster(), "given master is not a Master." );
SwNodeIndex aIdx( *pCheck->GetAttr()->GetStartNode(), 1 );
SwContentNode *pNd = aIdx.GetNode().GetContentNode();
if ( !pNd )
pNd = SwNodes::GoNextSection(&aIdx, true, false);
SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
SwFrame* pFrame = aIter.First();
while( pFrame )
{
if( pFrame->getRootFrame() == pCheck->getRootFrame() )
{
SwFrame *pTmp = pFrame->GetUpper();
while ( pTmp && !pTmp->IsFootnoteFrame() )
pTmp = pTmp->GetUpper();
SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pTmp);
while ( pFootnote && pFootnote->GetMaster() )
pFootnote = pFootnote->GetMaster();
if ( pFootnote != pCheck )
{
while (pFootnote && !pFootnote->IsDeleteForbidden())
{
SwFootnoteFrame *pNxt = pFootnote->GetFollow();
pFootnote->Cut();
SwFrame::DestroyFrame(pFootnote);
pFootnote = pNxt;
}
}
}
pFrame = aIter.Next();
}
}
void SwFootnoteBossFrame::InsertFootnote( SwFootnoteFrame* pNew )
{
// Place the footnote in front of the footnote whose attribute
// is in front of the new one (get position via the Doc).
// If there is no footnote in this footnote-boss yet, create a new container.
// If there is a container but no footnote for this footnote-boss yet, place
// the footnote behind the last footnote of the closest previous column/page.
ResetFootnote( pNew );
SwFootnoteFrame *pSibling = FindFirstFootnote();
bool bDontLeave = false;
// Ok, a sibling has been found, but is the sibling in an acceptable
// environment?
if( IsInSct() )
{
SwSectionFrame* pMySect = ImplFindSctFrame();
bool bEndnt = pNew->GetAttr()->GetFootnote().IsEndNote();
if( bEndnt )
{
const SwSectionFormat* pEndFormat = pMySect->GetEndSectFormat();
bDontLeave = nullptr != pEndFormat;
if( pSibling )
{
if( pEndFormat )
{
if( !pSibling->IsInSct() ||
!pSibling->ImplFindSctFrame()->IsDescendantFrom( pEndFormat ) )
pSibling = nullptr;
}
else if( pSibling->IsInSct() )
pSibling = nullptr;
}
}
else
{
bDontLeave = pMySect->IsFootnoteAtEnd();
if( pSibling )
{
if( pMySect->IsFootnoteAtEnd() )
{
if( !pSibling->IsInSct() ||
!pMySect->IsAnFollow( pSibling->ImplFindSctFrame() ) )
pSibling = nullptr;
}
else if( pSibling->IsInSct() )
pSibling = nullptr;
}
}
}
if( pSibling && pSibling->FindPageFrame()->IsEndNotePage() !=
FindPageFrame()->IsEndNotePage() )
pSibling = nullptr;
// use the Doc to find out the position
SwDoc *pDoc = GetFormat()->GetDoc();
const sal_uInt32 nStPos = ::lcl_FindFootnotePos( pDoc, pNew->GetAttr() );
sal_uInt32 nCmpPos = 0;
sal_uInt32 nLastPos = 0;
SwFootnoteContFrame *pParent = nullptr;
if( pSibling )
{
nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
if( nCmpPos > nStPos )
pSibling = nullptr;
}
if ( !pSibling )
{ pParent = FindFootnoteCont();
if ( !pParent )
{
// There is no footnote container yet. Before creating one, keep in mind that
// there might exist another following footnote that must be placed before the
// new inserted one e.g. because it was divided over multiple pages etc.
pParent = FindNearestFootnoteCont( bDontLeave );
if ( pParent )
{
SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pParent->Lower());
if ( pFootnote )
{
nCmpPos = ::lcl_FindFootnotePos( pDoc, pFootnote->GetAttr() );
if ( nCmpPos > nStPos )
pParent = nullptr;
}
else
pParent = nullptr;
}
}
if ( !pParent )
// here, we are sure that we can create a footnote container
pParent = MakeFootnoteCont();
else
{
// Based on the first footnote below the Parent, search for the first footnote whose
// index is after the index of the newly inserted, to place the new one correctly
pSibling = static_cast<SwFootnoteFrame*>(pParent->Lower());
if ( !pSibling )
{
OSL_ENSURE( false, "Could not find space for footnote.");
return;
}
nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
SwFootnoteBossFrame *pNxtB; // remember the last one to not
SwFootnoteFrame *pLastSib = nullptr; // go too far.
while ( pSibling && nCmpPos <= nStPos )
{
pLastSib = pSibling; // potential candidate
nLastPos = nCmpPos;
while ( pSibling->GetFollow() )
pSibling = pSibling->GetFollow();
if ( pSibling->GetNext() )
{
pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
OSL_ENSURE( !pSibling->GetMaster() || ( ENDNOTE > nStPos &&
pSibling->GetAttr()->GetFootnote().IsEndNote() ),
"InsertFootnote: Master expected I" );
}
else
{
pNxtB = pSibling->FindFootnoteBossFrame();
SwPageFrame *pSibPage = pNxtB->FindPageFrame();
bool bEndNote = pSibPage->IsEndNotePage();
bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
// When changing pages, also the endnote flag must match.
SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
pSibPage->IsEndNotePage() == bEndNote )
? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
if( pCont )
pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
else // no further FootnoteContainer, insert after pSibling
break;
}
if ( pSibling )
{
nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
OSL_ENSURE( nCmpPos > nLastPos, "InsertFootnote: Order of FootnoteFrame's buggy" );
}
}
// pLastSib is the last footnote before the new one and
// pSibling is empty or the first one after the new one
if ( pSibling && pLastSib && (pSibling != pLastSib) )
{
// too far?
if ( nCmpPos > nStPos )
pSibling = pLastSib;
}
else if ( !pSibling )
{
// Last chance: Take the last footnote of the parent.
// Special case that happens e.g. when moving paragraphs with multiple footnotes.
// To keep the order, use the parent of the last inspected footnote.
pSibling = pLastSib;
while( pSibling->GetFollow() )
pSibling = pSibling->GetFollow();
OSL_ENSURE( !pSibling->GetNext(), "InsertFootnote: Who's that guy?" );
}
}
}
else
{
// First footnote of the column/page found. Now search from there for the first one on the
// same column/page whose index is after the given one. The last one found is the predecessor.
SwFootnoteBossFrame* pBoss = pNew->GetRef()->FindFootnoteBossFrame(
!pNew->GetAttr()->GetFootnote().IsEndNote() );
sal_uInt16 nRefNum = pBoss->GetPhyPageNum(); // page number of the new footnote
sal_uInt16 nRefCol = lcl_ColumnNum( pBoss ); // column number of the new footnote
bool bEnd = false;
SwFootnoteFrame *pLastSib = nullptr;
while ( pSibling && !bEnd && (nCmpPos <= nStPos) )
{
pLastSib = pSibling;
nLastPos = nCmpPos;
while ( pSibling->GetFollow() )
pSibling = pSibling->GetFollow();
SwFootnoteFrame *pFoll = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
if ( pFoll )
{
pBoss = pSibling->GetRef()->FindFootnoteBossFrame( !pSibling->
GetAttr()->GetFootnote().IsEndNote() );
sal_uInt16 nTmpRef;
// it's possible pBoss is empty here on an undo/redo operation
if (pBoss && (nStPos >= ENDNOTE ||
(nTmpRef = pBoss->GetPhyPageNum()) < nRefNum ||
( nTmpRef == nRefNum && lcl_ColumnNum( pBoss ) <= nRefCol )))
pSibling = pFoll;
else
bEnd = true;
}
else
{
SwFootnoteBossFrame* pNxtB = pSibling->FindFootnoteBossFrame();
SwPageFrame *pSibPage = pNxtB->FindPageFrame();
bool bEndNote = pSibPage->IsEndNotePage();
bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
// When changing pages, also the endnote flag must match.
SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
pSibPage->IsEndNotePage() == bEndNote )
? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
if ( pCont )
pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
else
bEnd = true;
}
if ( !bEnd && pSibling )
nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
if (pSibling && (pSibling != pLastSib))
{
// too far?
if ( (nLastPos < nCmpPos) && (nCmpPos > nStPos) )
{
pSibling = pLastSib;
bEnd = true;
}
}
}
}
if ( pSibling )
{
nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
if ( nCmpPos < nStPos )
{
while ( pSibling->GetFollow() )
pSibling = pSibling->GetFollow();
pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
}
else
{
if( pSibling->GetMaster() )
{
if( ENDNOTE > nCmpPos || nStPos >= ENDNOTE )
{
OSL_FAIL( "InsertFootnote: Master expected II" );
do
pSibling = pSibling->GetMaster();
while ( pSibling->GetMaster() );
}
}
pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
}
}
OSL_ENSURE( pParent, "paste in space?" );
pNew->Paste( pParent, pSibling );
}
static SwPageFrame* lcl_GetApproximateFootnotePage(const bool bEnd, const SwPageFrame* pPage,
const SwDoc *pDoc, const SwTextFootnote *pAttr)
{
// We can at least search the approximately correct page
// to ensure that we will finish in finite time even if
// hundreds of footnotes exist.
const SwPageFrame *pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
const sal_uInt32 nStPos = ::lcl_FindFootnotePos(pDoc, pAttr);
while (pNxt && (bEnd ? pNxt->IsEndNotePage() : pNxt->IsFootnotePage() && !pNxt->IsEndNotePage()))
{
const SwFootnoteContFrame *pCont = pNxt->FindFootnoteCont();
if (pCont && pCont->Lower())
{
OSL_ENSURE( pCont->Lower()->IsFootnoteFrame(), "no footnote in the container" );
if (nStPos > ::lcl_FindFootnotePos(pDoc,
static_cast<const SwFootnoteFrame*>(pCont->Lower())->GetAttr()))
{
pPage = pNxt;
pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
continue;
}
}
break;
}
return const_cast<SwPageFrame*>(pPage);
}
void SwFootnoteBossFrame::AppendFootnote( SwContentFrame *pRef, SwTextFootnote *pAttr )
{
// If the footnote already exists, do nothing.
if ( FindFootnote( pRef, pAttr ) )
return;
// If footnotes are inserted at the end of the document,
// we only need to search from the relevant page on.
// If there is none yet, we need to create one.
// If it is an Endnote, we need to search for or create an
// Endnote page.
SwDoc *pDoc = GetFormat()->GetDoc();
SwFootnoteBossFrame *pBoss = this;
SwPageFrame *pPage = FindPageFrame();
SwPageFrame *pMyPage = pPage;
bool bChgPage = false;
const bool bEnd = pAttr->GetFootnote().IsEndNote();
if (bEnd)
{
const IDocumentSettingAccess& rSettings = *pAttr->GetTextNode().getIDocumentSettingAccess();
if( GetUpper()->IsSctFrame() &&
static_cast<SwSectionFrame*>(GetUpper())->IsEndnAtEnd() )
{
// Endnotes at the end of the section.
SwFrame* pLast =
static_cast<SwSectionFrame*>(GetUpper())->FindLastContent( SwFindMode::EndNote );
if( pLast )
{
pBoss = pLast->FindFootnoteBossFrame();
pPage = pBoss->FindPageFrame();
}
}
else if (rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
{
// Endnotes at the end of the document.
// Find the first page that hosts an endnote section.
SwSectionFrame* pEndnoteSection = pPage->GetEndNoteSection();
while (pPage->GetNext() && !pEndnoteSection)
{
pPage = pPage->GetNext()->DynCastPageFrame();
pEndnoteSection = pPage->GetEndNoteSection();
}
// If there are no endnotes sections yet, create one at the end of the document.
// Ignore sections which are already marked for deletion, they don't have an SwSection
// anymore, so not usable for us.
if (!pEndnoteSection || !pEndnoteSection->GetSection())
{
SwSection* pSwSection = pDoc->GetEndNoteInfo().GetSwSection(*pDoc);
pEndnoteSection = new SwSectionFrame(*pSwSection, pPage);
SwLayoutFrame* pParent = pPage->FindBodyCont();
SwFrame* pBefore = pPage->FindLastBodyContent();
while (pBefore)
{
// Check if the last content frame is directly under the body frame or there is
// something in-between, e.g. a section frame.
if (pBefore->GetUpper() == pParent)
{
break;
}
// If so, insert behind the parent of the content frame, not inside the parent.
pBefore = pBefore->GetUpper();
}
pEndnoteSection->InsertBehind(pParent, pBefore);
pEndnoteSection->Init();
pEndnoteSection->SetEndNoteSection(true);
}
SwFrame* pColumnFrame = pEndnoteSection->GetLower();
if (pColumnFrame->IsColumnFrame())
{
pBoss = static_cast<SwColumnFrame*>(pColumnFrame);
}
}
else
{
// Endnotes on a separate page.
while ( pPage->GetNext() && !pPage->IsEndNotePage() )
{
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
bChgPage = true;
}
if ( !pPage->IsEndNotePage() )
{
SwPageDesc *pDesc = pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
!pPage->OnRightPage(), false, false, true, nullptr );
pPage->SetEndNotePage( true );
bChgPage = true;
}
else
pPage = lcl_GetApproximateFootnotePage(true, pPage, pDoc, pAttr);
}
}
else if( FTNPOS_CHAPTER == pDoc->GetFootnoteInfo().m_ePos && ( !GetUpper()->
IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd() ) )
{
while ( pPage->GetNext() && !pPage->IsFootnotePage() &&
!static_cast<SwPageFrame*>(pPage->GetNext())->IsEndNotePage() )
{
pPage = static_cast<SwPageFrame*>(pPage->GetNext());
bChgPage = true;
}
if ( !pPage->IsFootnotePage() )
{
SwPageDesc *pDesc = pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
!pPage->OnRightPage(), false, false, true, pPage->GetNext() );
bChgPage = true;
}
else
pPage = lcl_GetApproximateFootnotePage(false, pPage, pDoc, pAttr);
}
// For now, create a footnote and the corresponding content frames
if ( !pAttr->GetStartNode() )
{
OSL_ENSURE( false, "no footnote content." );
return;
}
// If there is already a footnote content on the column/page,
// another one cannot be created in a column area.
if( pBoss->IsInSct() && pBoss->IsColumnFrame() && !pPage->IsFootnotePage() )
{
SwSectionFrame* pSct = pBoss->FindSctFrame();
if( bEnd ? !pSct->IsEndnAtEnd() : !pSct->IsFootnoteAtEnd() )
{
SwFootnoteContFrame* pFootnoteCont = pSct->FindFootnoteBossFrame(!bEnd)->FindFootnoteCont();
if( pFootnoteCont )
{
SwFootnoteFrame* pTmp = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
if( bEnd )
while( pTmp && !pTmp->GetAttr()->GetFootnote().IsEndNote() )
pTmp = static_cast<SwFootnoteFrame*>(pTmp->GetNext());
if( pTmp && *pTmp < pAttr )
return;
}
}
}
SwFootnoteFrame *pNew = new SwFootnoteFrame( pDoc->GetDfltFrameFormat(), this, pRef, pAttr );
{
SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
}
// If the page was changed or newly created,
// we need to place ourselves in the first column
if( bChgPage )
{
SwLayoutFrame* pBody = pPage->FindBodyCont();
OSL_ENSURE( pBody, "AppendFootnote: NoPageBody?" );
if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower());
else
pBoss = pPage; // page if no columns exist
}
pBoss->InsertFootnote( pNew );
if ( pNew->GetUpper() ) // inserted or not?
{
::RegistFlys( pNew->FindPageFrame(), pNew );
SwSectionFrame* pSect = FindSctFrame();
// The content of a FootnoteContainer in a (column) section only need to be calculated
// if the section stretches already to the bottom edge of the Upper.
if( pSect && !pSect->IsJoinLocked() && ( bEnd ? !pSect->IsEndnAtEnd() :
!pSect->IsFootnoteAtEnd() ) && pSect->Growable() )
pSect->InvalidateSize();
else
{
// #i49383# - disable unlock of position of
// lower objects during format of footnote content.
const bool bOldFootnoteFrameLocked( pNew->IsColLocked() );
pNew->ColLock();
pNew->KeepLockPosOfLowerObjs();
// #i57914# - adjust fix #i49383#
SwContentFrame *pCnt = pNew->ContainsContent();
while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
{
pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
{
if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
*(pCnt->FindPageFrame()) ) )
{
// restart format with first content
pCnt = pNew->ContainsContent();
continue;
}
}
pCnt = pCnt->FindNextCnt();
}
// #i49383#
if ( !bOldFootnoteFrameLocked )
{
pNew->ColUnlock();
}
// #i57914# - adjust fix #i49383#
// enable lock of lower object position before format of footnote frame.
pNew->UnlockPosOfLowerObjs();
pNew->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i57914# - adjust fix #i49383#
if ( !bOldFootnoteFrameLocked && !pNew->GetLower() &&
!pNew->IsColLocked() && !pNew->IsBackMoveLocked() &&
!pNew->IsDeleteForbidden() )
{
pNew->Cut();
SwFrame::DestroyFrame(pNew);
}
}
pMyPage->UpdateFootnoteNum();
}
else
SwFrame::DestroyFrame(pNew);
}
SwFootnoteFrame *SwFootnoteBossFrame::FindFootnote( const SwContentFrame *pRef, const SwTextFootnote *pAttr )
{
// the easiest and savest way goes via the attribute
OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
SwContentNode *pNd = aIdx.GetNode().GetContentNode();
if ( !pNd )
pNd = SwNodes::GoNextSection(&aIdx, true, false);
if ( !pNd )
return nullptr;
SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
SwFrame* pFrame = aIter.First();
if( pFrame )
do
{
pFrame = pFrame->GetUpper();
// #i28500#, #i27243# Due to the endnode collector, there are
// SwFootnoteFrames, which are not in the layout. Therefore the
// bInfFootnote flags are not set correctly, and a cell of FindFootnoteFrame
// would return 0. Therefore we better call ImplFindFootnoteFrame().
SwFootnoteFrame *pFootnote = pFrame->ImplFindFootnoteFrame();
if ( pFootnote && pFootnote->GetRef() == pRef )
{
// The following condition becomes true, if the whole
// footnotecontent is a section. While no frames exist,
// the HiddenFlag of the section is set, this causes
// the GoNextSection-function leaves the footnote.
if( pFootnote->GetAttr() != pAttr )
return nullptr;
while ( pFootnote && pFootnote->GetMaster() )
pFootnote = pFootnote->GetMaster();
return pFootnote;
}
} while ( nullptr != (pFrame = aIter.Next()) );
return nullptr;
}
bool SwFootnoteBossFrame::RemoveFootnote(
const SwContentFrame *const pRef, const SwTextFootnote *const pAttr,
bool bPrep )
{
bool ret(false);
SwFootnoteFrame *pFootnote = FindFootnote( pRef, pAttr );
if( pFootnote )
{
ret = true;
do
{
SwFootnoteFrame *pFoll = pFootnote->GetFollow();
pFootnote->Cut();
SwFrame::DestroyFrame(pFootnote);
pFootnote = pFoll;
} while ( pFootnote );
if( bPrep && pRef->IsFollow() )
{
OSL_ENSURE( pRef->IsTextFrame(), "NoTextFrame has Footnote?" );
SwTextFrame* pMaster = pRef->FindMaster();
if( !pMaster->IsLocked() )
pMaster->Prepare( PrepareHint::FootnoteInvalidationGone );
}
}
FindPageFrame()->UpdateFootnoteNum();
return ret;
}
void SwFootnoteBossFrame::ChangeFootnoteRef( const SwContentFrame *pOld, const SwTextFootnote *pAttr,
SwContentFrame *pNew )
{
SwFootnoteFrame *pFootnote = FindFootnote( pOld, pAttr );
while ( pFootnote )
{
pFootnote->SetRef( pNew );
pFootnote = pFootnote->GetFollow();
}
}
/// OD 03.04.2003 #108446# - add parameter <_bCollectOnlyPreviousFootnotes> in
/// order to control, if only footnotes, which are positioned before the
/// footnote boss frame <this> have to be collected.
void SwFootnoteBossFrame::CollectFootnotes( const SwContentFrame* _pRef,
SwFootnoteBossFrame* _pOld,
SwFootnoteFrames& _rFootnoteArr,
const bool _bCollectOnlyPreviousFootnotes )
{
SwFootnoteFrame *pFootnote = _pOld->FindFirstFootnote();
while( !pFootnote )
{
if( _pOld->IsColumnFrame() )
{
// visit columns
while ( !pFootnote && _pOld->GetPrev() )
{
// Still no problem if no footnote was found yet. The loop is needed to pick up
// following rows in tables. In all other cases it might correct bad contexts.
_pOld = static_cast<SwFootnoteBossFrame*>(_pOld->GetPrev());
pFootnote = _pOld->FindFirstFootnote();
}
}
if( !pFootnote )
{
// previous page
SwPageFrame* pPg;
for ( SwFrame* pTmp = _pOld;
nullptr != ( pPg = static_cast<SwPageFrame*>(pTmp->FindPageFrame()->GetPrev()))
&& pPg->IsEmptyPage() ;
)
{
pTmp = pPg;
}
if( !pPg )
return;
SwLayoutFrame* pBody = pPg->FindBodyCont();
if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
{
// multiple columns on one page => search last column
_pOld = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
}
else
_pOld = pPg; // single column page
pFootnote = _pOld->FindFirstFootnote();
}
}
CollectFootnotes_(_pRef, pFootnote, _rFootnoteArr, _bCollectOnlyPreviousFootnotes ? this : nullptr);
}
static void FootnoteInArr( SwFootnoteFrames& rFootnoteArr, SwFootnoteFrame* pFootnote )
{
if ( rFootnoteArr.end() == std::find( rFootnoteArr.begin(), rFootnoteArr.end(), pFootnote ) )
rFootnoteArr.push_back( pFootnote );
}
void SwFootnoteBossFrame::CollectFootnotes_( const SwContentFrame* _pRef,
SwFootnoteFrame* _pFootnote,
SwFootnoteFrames& _rFootnoteArr,
const SwFootnoteBossFrame* _pRefFootnoteBossFrame)
{
// Collect all footnotes referenced by pRef (attribute by attribute), combine them
// (the content might be divided over multiple pages) and cut them.
// For robustness, we do not log the corresponding footnotes here. If a footnote
// is touched twice, there might be a crash. This allows this function here to
// also handle corrupt layouts in some degrees (without loops or even crashes).
SwFootnoteFrames aNotFootnoteArr;
// here we have a footnote placed in front of the first one of the reference
OSL_ENSURE( !_pFootnote->GetMaster() || _pFootnote->GetRef() != _pRef, "move FollowFootnote?" );
while ( _pFootnote->GetMaster() )
_pFootnote = _pFootnote->GetMaster();
bool bFound = false;
do
{
// Search for the next footnote in this column/page so that
// we do not start from zero again after cutting one footnote.
SwFootnoteFrame *pNxtFootnote = _pFootnote;
while ( pNxtFootnote->GetFollow() )
pNxtFootnote = pNxtFootnote->GetFollow();
pNxtFootnote = static_cast<SwFootnoteFrame*>(pNxtFootnote->GetNext());
if ( !pNxtFootnote )
{
SwFootnoteBossFrame* pBoss = _pFootnote->FindFootnoteBossFrame();
SwPageFrame* pPage = pBoss->FindPageFrame();
do
{
lcl_NextFootnoteBoss( pBoss, pPage, false );
if( pBoss )
{
SwLayoutFrame* pCont = pBoss->FindFootnoteCont();
if( pCont )
{
pNxtFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
if( pNxtFootnote )
{
while( pNxtFootnote->GetMaster() )
pNxtFootnote = pNxtFootnote->GetMaster();
if( pNxtFootnote == _pFootnote )
pNxtFootnote = nullptr;
}
}
}
} while( !pNxtFootnote && pBoss );
}
else if( !pNxtFootnote->GetAttr()->GetFootnote().IsEndNote() )
{
OSL_ENSURE( !pNxtFootnote->GetMaster(), "_CollectFootnote: Master expected" );
while ( pNxtFootnote->GetMaster() )
pNxtFootnote = pNxtFootnote->GetMaster();
}
if ( pNxtFootnote == _pFootnote )
{
OSL_FAIL( "_CollectFootnote: Vicious circle" );
pNxtFootnote = nullptr;
}
// OD 03.04.2003 #108446# - determine, if found footnote has to be collected.
bool bCollectFoundFootnote = false;
// Ignore endnotes which are on a separate endnote page.
bool bEndNote = _pFootnote->GetAttr()->GetFootnote().IsEndNote();
if (_pFootnote->GetRef() == _pRef && !bEndNote)
{
if (_pRefFootnoteBossFrame)
{
SwFootnoteBossFrame* pBossOfFoundFootnote = _pFootnote->FindFootnoteBossFrame( true );
OSL_ENSURE( pBossOfFoundFootnote,
"<SwFootnoteBossFrame::CollectFootnotes_(..)> - footnote boss frame of found footnote frame missing.\nWrong layout!" );
if ( !pBossOfFoundFootnote || // don't crash, if no footnote boss is found.
pBossOfFoundFootnote->IsBefore( _pRefFootnoteBossFrame )
)
{
bCollectFoundFootnote = true;
}
}
else
{
bCollectFoundFootnote = true;
}
}
if ( bCollectFoundFootnote )
{
OSL_ENSURE( !_pFootnote->GetMaster(), "move FollowFootnote?" );
SwFootnoteFrame *pNxt = _pFootnote->GetFollow();
while ( pNxt )
{
SwFrame *pCnt = pNxt->ContainsAny();
if ( pCnt )
{
// destroy the follow on the way as it is empty
do
{ SwFrame *pNxtCnt = pCnt->GetNext();
pCnt->Cut();
pCnt->Paste( _pFootnote );
pCnt = pNxtCnt;
} while ( pCnt );
}
else
{
OSL_ENSURE( !pNxt, "footnote without content?" );
pNxt->Cut();
SwFrame::DestroyFrame(pNxt);
}
pNxt = _pFootnote->GetFollow();
}
_pFootnote->Cut();
FootnoteInArr( _rFootnoteArr, _pFootnote );
bFound = true;
}
else
{
FootnoteInArr( aNotFootnoteArr, _pFootnote );
if( bFound )
break;
}
if ( pNxtFootnote &&
_rFootnoteArr.end() == std::find( _rFootnoteArr.begin(), _rFootnoteArr.end(), pNxtFootnote ) &&
aNotFootnoteArr.end() == std::find( aNotFootnoteArr.begin(), aNotFootnoteArr.end(), pNxtFootnote ) )
_pFootnote = pNxtFootnote;
else
break;
}
while ( _pFootnote );
}
void SwFootnoteBossFrame::MoveFootnotes_( SwFootnoteFrames &rFootnoteArr, bool bCalc )
{
// All footnotes referenced by pRef need to be moved
// to a new position (based on the new column/page)
const sal_uInt16 nMyNum = FindPageFrame()->GetPhyPageNum();
const sal_uInt16 nMyCol = lcl_ColumnNum( this );
SwRectFnSet aRectFnSet(this);
// #i21478# - keep last inserted footnote in order to
// format the content of the following one.
SwFootnoteFrame* pLastInsertedFootnote = nullptr;
for (SwFootnoteFrame* pFootnote : rFootnoteArr)
{
SwFootnoteBossFrame* pRefBoss(pFootnote->GetRef()->FindFootnoteBossFrame(
!pFootnote->GetAttr()->GetFootnote().IsEndNote()));
if( pRefBoss != this )
{
const sal_uInt16 nRefNum = pRefBoss->FindPageFrame()->GetPhyPageNum();
const sal_uInt16 nRefCol = lcl_ColumnNum( this );
if( nRefNum < nMyNum || ( nRefNum == nMyNum && nRefCol <= nMyCol ) )
pRefBoss = this;
}
pRefBoss->InsertFootnote( pFootnote );
if ( pFootnote->GetUpper() ) // robust, e.g. with duplicates
{
// First condense the content so that footnote frames that do not fit on the page
// do not do too much harm (Loop 66312). So, the footnote content first grows as
// soon as the content gets formatted and it is sure that it fits on the page.
SwFrame *pCnt = pFootnote->ContainsAny();
while( pCnt )
{
if( pCnt->IsLayoutFrame() )
{
SwFrame* pTmp = static_cast<SwLayoutFrame*>(pCnt)->ContainsAny();
while( pTmp && static_cast<SwLayoutFrame*>(pCnt)->IsAnLower( pTmp ) )
{
pTmp->Prepare( PrepareHint::FootnoteMove );
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
aRectFnSet.SetHeight(aFrm, 0);
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
aRectFnSet.SetHeight(aPrt, 0);
pTmp = pTmp->FindNext();
}
}
else
{
pCnt->Prepare( PrepareHint::FootnoteMove );
}
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCnt);
aRectFnSet.SetHeight(aFrm, 0);
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCnt);
aRectFnSet.SetHeight(aPrt, 0);
pCnt = pCnt->GetNext();
}
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFootnote);
aRectFnSet.SetHeight(aFrm, 0);
}
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFootnote);
aRectFnSet.SetHeight(aPrt, 0);
}
pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
pFootnote->GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
if( bCalc )
{
SwTextFootnote *pAttr = pFootnote->GetAttr();
pCnt = pFootnote->ContainsAny();
bool bUnlock = !pFootnote->IsBackMoveLocked();
pFootnote->LockBackMove();
// #i49383# - disable unlock of position of
// lower objects during format of footnote content.
pFootnote->KeepLockPosOfLowerObjs();
// #i57914# - adjust fix #i49383#
while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
{
pCnt->InvalidatePos_();
pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
{
if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
*(pCnt->FindPageFrame()) ) )
{
// restart format with first content
pCnt = pFootnote->ContainsAny();
continue;
}
}
if( pCnt->IsSctFrame() )
{
// If the area is not empty, iterate also over the content
SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
if( pTmp )
pCnt = pTmp;
else
pCnt = pCnt->FindNext();
}
else
pCnt = pCnt->FindNext();
}
if( bUnlock )
{
pFootnote->UnlockBackMove();
if( !pFootnote->ContainsAny() && !pFootnote->IsColLocked() )
{
pFootnote->Cut();
SwFrame::DestroyFrame(pFootnote);
// #i21478#
pFootnote = nullptr;
}
}
// #i49383#
if ( pFootnote )
{
// #i57914# - adjust fix #i49383#
// enable lock of lower object position before format of footnote frame.
pFootnote->UnlockPosOfLowerObjs();
pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
}
}
}
else
{
OSL_ENSURE( !pFootnote->GetMaster() && !pFootnote->GetFollow(),
"DelFootnote and Master/Follow?" );
SwFrame::DestroyFrame(pFootnote);
// #i21478#
pFootnote = nullptr;
}
// #i21478#
if ( pFootnote )
{
pLastInsertedFootnote = pFootnote;
}
}
// #i21478# - format content of footnote following
// the new inserted ones.
if ( !(bCalc && pLastInsertedFootnote) )
return;
if ( !pLastInsertedFootnote->GetNext() )
return;
SwFootnoteFrame* pNextFootnote = static_cast<SwFootnoteFrame*>(pLastInsertedFootnote->GetNext());
SwTextFootnote* pAttr = pNextFootnote->GetAttr();
SwFrame* pCnt = pNextFootnote->ContainsAny();
bool bUnlock = !pNextFootnote->IsBackMoveLocked();
pNextFootnote->LockBackMove();
// #i49383# - disable unlock of position of
// lower objects during format of footnote content.
pNextFootnote->KeepLockPosOfLowerObjs();
// #i57914# - adjust fix #i49383#
while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
{
pCnt->InvalidatePos_();
pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
{
if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
*(pCnt->FindPageFrame()) ) )
{
// restart format with first content
pCnt = pNextFootnote->ContainsAny();
continue;
}
}
if( pCnt->IsSctFrame() )
{
// If the area is not empty, iterate also over the content
SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
if( pTmp )
pCnt = pTmp;
else
pCnt = pCnt->FindNext();
}
else
pCnt = pCnt->FindNext();
}
if( bUnlock )
{
pNextFootnote->UnlockBackMove();
}
// #i49383#
// #i57914# - adjust fix #i49383#
// enable lock of lower object position before format of footnote frame.
pNextFootnote->UnlockPosOfLowerObjs();
pNextFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
}
void SwFootnoteBossFrame::MoveFootnotes( const SwContentFrame *pSrc, SwContentFrame *pDest,
SwTextFootnote const *pAttr )
{
if( ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
(!GetUpper()->IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd()))
|| pAttr->GetFootnote().IsEndNote() )
return;
OSL_ENSURE( this == pSrc->FindFootnoteBossFrame( true ),
"SwPageFrame::MoveFootnotes: source frame isn't on that FootnoteBoss" );
SwFootnoteFrame *pFootnote = FindFirstFootnote();
if( !pFootnote )
return;
ChangeFootnoteRef( pSrc, pAttr, pDest );
SwFootnoteBossFrame *pDestBoss = pDest->FindFootnoteBossFrame( true );
OSL_ENSURE( pDestBoss, "+SwPageFrame::MoveFootnotes: no destination boss" );
if( !pDestBoss ) // robust
return;
SwFootnoteFrames aFootnoteArr;
SwFootnoteBossFrame::CollectFootnotes_(pDest, pFootnote, aFootnoteArr, nullptr);
if ( aFootnoteArr.empty() )
return;
pDestBoss->MoveFootnotes_( aFootnoteArr, true );
SwPageFrame* pSrcPage = FindPageFrame();
SwPageFrame* pDestPage = pDestBoss->FindPageFrame();
// update FootnoteNum only at page change
if( pSrcPage != pDestPage )
{
if( pSrcPage->GetPhyPageNum() > pDestPage->GetPhyPageNum() )
pSrcPage->UpdateFootnoteNum();
pDestPage->UpdateFootnoteNum();
}
}
void SwFootnoteBossFrame::RearrangeFootnotes( const SwTwips nDeadLine, const bool bLock,
const SwTextFootnote *pAttr )
{
// Format all footnotes of a column/page so that they might change the column/page.
SwSaveFootnoteHeight aSave( this, nDeadLine );
SwFootnoteFrame *pFootnote = FindFirstFootnote();
if( pFootnote && pFootnote->GetPrev() && bLock )
{
SwFootnoteFrame* pFirst = static_cast<SwFootnoteFrame*>(pFootnote->GetUpper()->Lower());
SwFrame* pContent = pFirst->ContainsAny();
if( pContent )
{
bool bUnlock = !pFirst->IsBackMoveLocked();
pFirst->LockBackMove();
pFirst->Calc(getRootFrame()->GetCurrShell()->GetOut());
pContent->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pContent->IsTextFrame() && pContent->isFrameAreaDefinitionValid() )
{
SwObjectFormatter::FormatObjsAtFrame( *pContent,
*(pContent->FindPageFrame()) );
}
if( bUnlock )
pFirst->UnlockBackMove();
}
pFootnote = FindFirstFootnote();
}
SwDoc *pDoc = GetFormat()->GetDoc();
const sal_uInt32 nFootnotePos = pAttr ? ::lcl_FindFootnotePos( pDoc, pAttr ) : 0;
SwFrame *pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
if ( !pCnt )
return;
bool bMore = true;
bool bStart = pAttr == nullptr; // If no attribute is given, process all
// #i49383# - disable unlock of position of
// lower objects during format of footnote and footnote content.
SwFootnoteFrame* pLastFootnoteFrame( nullptr );
// footnote frame needs to be locked, if <bLock> isn't set.
bool bUnlockLastFootnoteFrame( false );
do
{
if( !bStart )
bStart = ::lcl_FindFootnotePos( pDoc, pCnt->FindFootnoteFrame()->GetAttr() )
== nFootnotePos;
if( bStart )
{
pCnt->InvalidatePos_();
pCnt->InvalidateSize_();
pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
assert(pFootnoteFrame);
// #i49383#
if ( pFootnoteFrame != pLastFootnoteFrame )
{
if ( pLastFootnoteFrame )
{
if ( !bLock && bUnlockLastFootnoteFrame )
{
pLastFootnoteFrame->ColUnlock();
}
// #i57914# - adjust fix #i49383#
// enable lock of lower object position before format of footnote frame.
pLastFootnoteFrame->UnlockPosOfLowerObjs();
pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
if ( !bLock && bUnlockLastFootnoteFrame &&
!pLastFootnoteFrame->GetLower() &&
!pLastFootnoteFrame->IsColLocked() &&
!pLastFootnoteFrame->IsBackMoveLocked() &&
!pLastFootnoteFrame->IsDeleteForbidden() )
{
pLastFootnoteFrame->Cut();
SwFrame::DestroyFrame(pLastFootnoteFrame);
// pLastFootnoteFrame overwritten at end of block
}
}
if ( !bLock )
{
bUnlockLastFootnoteFrame = !pFootnoteFrame->IsColLocked();
pFootnoteFrame->ColLock();
}
pFootnoteFrame->KeepLockPosOfLowerObjs();
pLastFootnoteFrame = pFootnoteFrame;
}
// OD 30.10.2002 #97265# - invalidate position of footnote
// frame, if it's below its footnote container, in order to
// assure its correct position, probably calculating its previous
// footnote frames.
{
SwRectFnSet aRectFnSet(this);
SwFrame* pFootnoteContFrame = pFootnoteFrame->GetUpper();
if ( aRectFnSet.TopDist(pFootnoteFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pFootnoteContFrame)) > 0 )
{
pFootnoteFrame->InvalidatePos_();
}
}
if ( bLock )
{
bool bUnlock = !pFootnoteFrame->IsBackMoveLocked();
pFootnoteFrame->LockBackMove();
pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
{
SwFrameDeleteGuard aDeleteGuard(pFootnote);
if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
*(pCnt->FindPageFrame()) ) )
{
// restart format with first content
pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
if (!pCnt)
bMore = false;
continue;
}
}
if( bUnlock )
{
pFootnoteFrame->UnlockBackMove();
if( !pFootnoteFrame->Lower() &&
!pFootnoteFrame->IsColLocked() )
{
// #i49383#
OSL_ENSURE( pLastFootnoteFrame == pFootnoteFrame,
"<SwFootnoteBossFrame::RearrangeFootnotes(..)> - <pLastFootnoteFrame> != <pFootnoteFrame>" );
pLastFootnoteFrame = nullptr;
pFootnoteFrame->Cut();
SwFrame::DestroyFrame(pFootnoteFrame);
if (pFootnote == pFootnoteFrame)
pFootnote = nullptr;
}
}
}
else
{
pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
// #i49383# - format anchored objects
if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
{
if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
*(pCnt->FindPageFrame()) ) )
{
// restart format with first content
pCnt = pFootnote->ContainsAny();
continue;
}
}
}
}
SwSectionFrame *pDel = nullptr;
if( pCnt->IsSctFrame() )
{
SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
if( pTmp )
{
pCnt = pTmp;
continue;
}
pDel = static_cast<SwSectionFrame*>(pCnt);
}
if ( pCnt->GetNext() )
pCnt = pCnt->GetNext();
else
{
pCnt = pCnt->FindNext();
if ( pCnt )
{
SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
if( pFootnoteFrame->GetRef()->FindFootnoteBossFrame(
pFootnoteFrame->GetAttr()->GetFootnote().IsEndNote() ) != this )
bMore = false;
}
else
bMore = false;
}
if( pDel )
{
bool bUnlockLastFootnoteFrameGuard = pLastFootnoteFrame && !pLastFootnoteFrame->IsColLocked();
if (bUnlockLastFootnoteFrameGuard)
pLastFootnoteFrame->ColLock();
pDel->Cut();
if (bUnlockLastFootnoteFrameGuard)
pLastFootnoteFrame->ColUnlock();
SwFrame::DestroyFrame(pDel);
}
if ( bMore )
{
// Go not further than to the provided footnote (if given)
if ( pAttr &&
(::lcl_FindFootnotePos( pDoc,
pCnt->FindFootnoteFrame()->GetAttr()) > nFootnotePos ) )
bMore = false;
}
} while ( bMore );
// #i49383#
if ( !pLastFootnoteFrame )
return;
if ( !bLock && bUnlockLastFootnoteFrame )
{
pLastFootnoteFrame->ColUnlock();
}
// #i57914# - adjust fix #i49383#
// enable lock of lower object position before format of footnote frame.
pLastFootnoteFrame->UnlockPosOfLowerObjs();
pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
if ( !bLock && bUnlockLastFootnoteFrame &&
!pLastFootnoteFrame->GetLower() &&
!pLastFootnoteFrame->IsColLocked() &&
!pLastFootnoteFrame->IsBackMoveLocked() &&
!pLastFootnoteFrame->IsDeleteForbidden() )
{
pLastFootnoteFrame->Cut();
SwFrame::DestroyFrame(pLastFootnoteFrame);
}
}
void SwPageFrame::UpdateFootnoteNum()
{
// page numbering only if set at the document
if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum != FTNNUM_PAGE )
return;
SwLayoutFrame* pBody = FindBodyCont();
if( !pBody || !pBody->Lower() )
return;
SwContentFrame* pContent = pBody->ContainsContent();
sal_uInt16 nNum = 0;
while( pContent && pContent->FindPageFrame() == this )
{
if( static_cast<SwTextFrame*>(pContent)->HasFootnote() )
{
SwFootnoteBossFrame* pBoss = pContent->FindFootnoteBossFrame( true );
if( pBoss->GetUpper()->IsSctFrame() &&
static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsOwnFootnoteNum() )
pContent = static_cast<SwSectionFrame*>(pBoss->GetUpper())->FindLastContent();
else
{
SwFootnoteFrame* pFootnote = const_cast<SwFootnoteFrame*>(pBoss->FindFirstFootnote( pContent ));
while( pFootnote )
{
SwTextFootnote* pTextFootnote = pFootnote->GetAttr();
if( !pTextFootnote->GetFootnote().IsEndNote() &&
pTextFootnote->GetFootnote().GetNumStr().isEmpty() &&
!pFootnote->GetMaster())
{
// sw_redlinehide: the layout can only keep one number
// up to date; depending on its setting, this is either
// the non-hidden or the hidden number; the other
// number will simply be preserved as-is (so in case
// there are 2 layouts, maybe both can be updated...)
++nNum;
sal_uInt16 const nOldNum(pTextFootnote->GetFootnote().GetNumber());
sal_uInt16 const nOldNumRLHidden(pTextFootnote->GetFootnote().GetNumberRLHidden());
if (getRootFrame()->IsHideRedlines())
{
if (nNum != nOldNumRLHidden)
{
pTextFootnote->SetNumber(nOldNum, nNum, OUString());
}
}
else
{
if (nNum != nOldNum)
{
pTextFootnote->SetNumber(nNum, nOldNumRLHidden, OUString());
}
}
}
if ( pFootnote->GetNext() )
pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
else
{
SwFootnoteBossFrame* pTmpBoss = pFootnote->FindFootnoteBossFrame( true );
if( pTmpBoss )
{
SwPageFrame* pPage = pTmpBoss->FindPageFrame();
pFootnote = nullptr;
lcl_NextFootnoteBoss( pTmpBoss, pPage, false );
SwFootnoteContFrame *pCont = pTmpBoss ? pTmpBoss->FindNearestFootnoteCont() : nullptr;
if ( pCont )
pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
}
}
if( pFootnote && pFootnote->GetRef() != pContent )
pFootnote = nullptr;
}
}
}
pContent = pContent->FindNextCnt();
}
}
void SwFootnoteBossFrame::SetFootnoteDeadLine( const SwTwips nDeadLine )
{
SwFrame *pBody = FindBodyCont();
pBody->Calc(getRootFrame()->GetCurrShell()->GetOut());
SwFrame *pCont = FindFootnoteCont();
const SwTwips nMax = m_nMaxFootnoteHeight;// current should exceed MaxHeight
SwRectFnSet aRectFnSet(this);
if ( pCont )
{
pCont->Calc(getRootFrame()->GetCurrShell()->GetOut());
m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pCont->getFrameArea(), nDeadLine );
}
else
m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pBody->getFrameArea(), nDeadLine );
const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
if( pSh && pSh->GetViewOptions()->getBrowseMode() )
m_nMaxFootnoteHeight += pBody->Grow( LONG_MAX, true );
if ( IsInSct() )
m_nMaxFootnoteHeight += FindSctFrame()->Grow( LONG_MAX, true );
if ( m_nMaxFootnoteHeight < 0 )
m_nMaxFootnoteHeight = 0;
if ( nMax != LONG_MAX && m_nMaxFootnoteHeight > nMax )
m_nMaxFootnoteHeight = nMax;
}
SwTwips SwFootnoteBossFrame::GetVarSpace() const
{
// To not fall below 20% of the page height
// (in contrast to MSOffice where footnotes can fill a whole column/page)
const SwPageFrame* pPg = FindPageFrame();
OSL_ENSURE( pPg || IsInSct(), "Footnote lost page" );
const SwFrame *pBody = FindBodyCont();
SwTwips nRet;
if( pBody )
{
SwRectFnSet aRectFnSet(this);
nRet = aRectFnSet.GetHeight(pBody->getFrameArea());
if( IsInSct() )
{
SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pBody),
aRectFnSet.GetTop(getFrameArea()) );
const SwSectionFrame* pSect = FindSctFrame();
// Endnotes in a ftncontainer causes a deadline:
// the bottom of the last contentfrm
if( pSect->IsEndnAtEnd() ) // endnotes allowed?
{
OSL_ENSURE( !Lower() || !Lower()->GetNext() || Lower()->GetNext()->
IsFootnoteContFrame(), "FootnoteContainer expected" );
const SwFootnoteContFrame* pCont = Lower() ?
static_cast<const SwFootnoteContFrame*>(Lower()->GetNext()) : nullptr;
if( pCont )
{
const SwFootnoteFrame* pFootnote = static_cast<const SwFootnoteFrame*>(pCont->Lower());
while( pFootnote)
{
if( pFootnote->GetAttr()->GetFootnote().IsEndNote() )
{ // endnote found
const SwFrame* pFrame = static_cast<const SwLayoutFrame*>(Lower())->Lower();
if( pFrame )
{
while( pFrame->GetNext() )
pFrame = pFrame->GetNext(); // last cntntfrm
nTmp += aRectFnSet.YDiff(
aRectFnSet.GetTop(getFrameArea()),
aRectFnSet.GetBottom(pFrame->getFrameArea()) );
}
break;
}
pFootnote = static_cast<const SwFootnoteFrame*>(pFootnote->GetNext());
}
}
}
if( nTmp < 0 )
nRet += nTmp;
}
else
{
assert(pPg);
nRet -= aRectFnSet.GetHeight(pPg->getFramePrintArea())/5;
}
if( nRet < 0 )
nRet = 0;
}
else
nRet = 0;
if ( IsPageFrame() )
{
const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
if( pSh && pSh->GetViewOptions()->getBrowseMode() )
nRet += BROWSE_HEIGHT - getFrameArea().Height();
}
return nRet;
}
/** Obtain if pFrame's size adjustment should be processed
*
* For a page frame of columns directly below the page AdjustNeighbourhood() needs
* to be called, or Grow()/ Shrink() for frame columns respectively.
*
* A column section is special, since if there is a footnote container in a column
* and those footnotes are not collected, it is handled like a page frame.
*
* @see AdjustNeighbourhood()
* @see Grow()
* @see Shrink()
*/
SwNeighbourAdjust SwFootnoteBossFrame::NeighbourhoodAdjustment_() const
{
SwNeighbourAdjust nRet = SwNeighbourAdjust::OnlyAdjust;
if( GetUpper() && !GetUpper()->IsPageBodyFrame() )
{
// column sections need grow/shrink
if( GetUpper()->IsFlyFrame() )
nRet = SwNeighbourAdjust::GrowShrink;
else
{
OSL_ENSURE( GetUpper()->IsSctFrame(), "NeighbourhoodAdjustment: Unexpected Upper" );
if( !GetNext() && !GetPrev() )
nRet = SwNeighbourAdjust::GrowAdjust; // section with a single column (FootnoteAtEnd)
else
{
const SwFrame* pTmp = Lower();
assert(pTmp && "NeighbourhoodAdjustment: Missing Lower()");
if( !pTmp->GetNext() )
nRet = SwNeighbourAdjust::GrowShrink;
else if( !GetUpper()->IsColLocked() )
nRet = SwNeighbourAdjust::AdjustGrow;
OSL_ENSURE( !pTmp->GetNext() || pTmp->GetNext()->IsFootnoteContFrame(),
"NeighbourhoodAdjustment: Who's that guy?" );
}
}
}
return nRet;
}
void SwPageFrame::SetColMaxFootnoteHeight()
{
SwLayoutFrame *pBody = FindBodyCont();
if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
{
SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pBody->Lower());
do
{
pCol->SetMaxFootnoteHeight( GetMaxFootnoteHeight() );
pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
} while ( pCol );
}
}
bool SwLayoutFrame::MoveLowerFootnotes( SwContentFrame *pStart, SwFootnoteBossFrame *pOldBoss,
SwFootnoteBossFrame *pNewBoss, const bool bFootnoteNums )
{
SwDoc *pDoc = GetFormat()->GetDoc();
if ( pDoc->GetFootnoteIdxs().empty() )
return false;
if( pDoc->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
( !IsInSct() || !FindSctFrame()->IsFootnoteAtEnd() ) )
return true;
if ( !pNewBoss )
pNewBoss = FindFootnoteBossFrame( true );
if ( pNewBoss == pOldBoss )
return false;
bool bMoved = false;
if( !pStart )
pStart = ContainsContent();
SwFootnoteFrames aFootnoteArr;
while ( IsAnLower( pStart ) )
{
if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
{
// OD 03.04.2003 #108446# - To avoid unnecessary moves of footnotes
// use new parameter <_bCollectOnlyPreviousFootnote> (4th parameter of
// method <SwFootnoteBossFrame::CollectFootnote(..)>) to control, that only
// footnotes have to be collected, that are positioned before the
// new dedicated footnote boss frame.
pNewBoss->CollectFootnotes( pStart, pOldBoss, aFootnoteArr, true );
}
pStart = pStart->GetNextContentFrame();
}
OSL_ENSURE( pOldBoss->IsInSct() == pNewBoss->IsInSct(),
"MoveLowerFootnotes: Section confusion" );
std::unique_ptr<SwFootnoteFrames> pFootnoteArr;
SwLayoutFrame* pNewChief = nullptr;
SwLayoutFrame* pOldChief = nullptr;
bool bFoundCandidate = false;
if (pStart && pOldBoss->IsInSct())
{
pOldChief = pOldBoss->FindSctFrame();
pNewChief = pNewBoss->FindSctFrame();
bFoundCandidate = pOldChief != pNewChief;
}
if (bFoundCandidate)
{
pFootnoteArr.reset(new SwFootnoteFrames);
pOldChief = pOldBoss->FindFootnoteBossFrame( true );
pNewChief = pNewBoss->FindFootnoteBossFrame( true );
while( pOldChief->IsAnLower( pStart ) )
{
if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
static_cast<SwFootnoteBossFrame*>(pNewChief)->CollectFootnotes( pStart,
pOldBoss, *pFootnoteArr );
pStart = pStart->GetNextContentFrame();
}
if( pFootnoteArr->empty() )
{
pFootnoteArr.reset();
}
}
else
pFootnoteArr = nullptr;
if ( !aFootnoteArr.empty() || pFootnoteArr )
{
if( !aFootnoteArr.empty() )
pNewBoss->MoveFootnotes_( aFootnoteArr, true );
if( pFootnoteArr )
{
assert(pNewChief);
static_cast<SwFootnoteBossFrame*>(pNewChief)->MoveFootnotes_( *pFootnoteArr, true );
pFootnoteArr.reset();
}
bMoved = true;
// update FootnoteNum only at page change
if ( bFootnoteNums )
{
SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
SwPageFrame* pNewPage =pNewBoss->FindPageFrame();
if( pOldPage != pNewPage )
{
pOldPage->UpdateFootnoteNum();
pNewPage->UpdateFootnoteNum();
}
}
}
return bMoved;
}
/// Return value guarantees that a new page was not created. See SwFlowFrame::MoveFwd.
bool SwContentFrame::MoveFootnoteCntFwd( bool bMakePage, SwFootnoteBossFrame *pOldBoss )
{
OSL_ENSURE( IsInFootnote(), "no footnote." );
SwLayoutFrame *pFootnote = FindFootnoteFrame();
// The first paragraph in the first footnote in the first column in the
// sectionfrm at the top of the page has not to move forward, if the
// columnbody is empty.
if( pOldBoss->IsInSct() && !pOldBoss->GetIndPrev() && !GetIndPrev() &&
!pFootnote->GetPrev() )
{
SwLayoutFrame* pBody = pOldBoss->FindBodyCont();
if( !pBody || !pBody->Lower() )
return true;
}
//fix(9538): if the footnote has neighbors behind itself, remove them temporarily
SwLayoutFrame *pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
SwLayoutFrame *pLst = nullptr;
while ( pNxt )
{
while ( pNxt->GetNext() )
pNxt = static_cast<SwLayoutFrame*>(pNxt->GetNext());
if ( pNxt == pLst )
pNxt = nullptr;
else
{ pLst = pNxt;
SwContentFrame *pCnt = pNxt->ContainsContent();
if( pCnt )
pCnt->MoveFootnoteCntFwd( true, pOldBoss );
pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
}
}
bool bSamePage = true;
SwLayoutFrame *pNewUpper =
GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
if ( pNewUpper )
{
SwFootnoteBossFrame * const pNewBoss = pNewUpper->FindFootnoteBossFrame();
// Are we changing the column/page?
bool bSameBoss = pNewBoss == pOldBoss;
if ( !bSameBoss )
{
bSamePage = pOldBoss->FindPageFrame() == pNewBoss->FindPageFrame(); // page change?
pNewUpper->Calc(getRootFrame()->GetCurrShell()->GetOut());
}
// The layout leaf of the footnote is either a footnote container or a footnote.
// If it is a footnote and it has the same footnote reference like the old Upper,
// then move the content inside of it.
// If it is a container or the reference differs, create a new footnote and add
// it into the container.
// Create also a SectionFrame if currently in an area inside a footnote.
SwFootnoteFrame* pTmpFootnote = pNewUpper->IsFootnoteFrame() ? static_cast<SwFootnoteFrame*>(pNewUpper) : nullptr;
if (!pTmpFootnote)
{
assert(pNewUpper->IsFootnoteContFrame() && "New Upper not a FootnoteCont");
SwFootnoteContFrame *pCont = static_cast<SwFootnoteContFrame*>(pNewUpper);
pTmpFootnote = SwFootnoteContFrame::AppendChained(this, true);
SwFrame* pNx = pCont->Lower();
if( pNx && pTmpFootnote->GetAttr()->GetFootnote().IsEndNote() )
while(pNx && !static_cast<SwFootnoteFrame*>(pNx)->GetAttr()->GetFootnote().IsEndNote())
pNx = pNx->GetNext();
pTmpFootnote->Paste( pCont, pNx );
pTmpFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
}
OSL_ENSURE( pTmpFootnote->GetAttr() == FindFootnoteFrame()->GetAttr(), "Wrong Footnote!" );
// areas inside of footnotes get a special treatment
SwLayoutFrame *pNewUp = pTmpFootnote;
if( IsInSct() )
{
SwSectionFrame* pSect = FindSctFrame();
// area inside of a footnote (or only footnote in an area)?
if( pSect->IsInFootnote() )
{
if( pTmpFootnote->Lower() && pTmpFootnote->Lower()->IsSctFrame() &&
pSect->GetFollow() == static_cast<SwSectionFrame*>(pTmpFootnote->Lower()) )
pNewUp = static_cast<SwSectionFrame*>(pTmpFootnote->Lower());
else
{
pNewUp = new SwSectionFrame( *pSect, false );
pNewUp->InsertBefore( pTmpFootnote, pTmpFootnote->Lower() );
static_cast<SwSectionFrame*>(pNewUp)->Init();
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNewUp);
aFrm.Pos() = pTmpFootnote->getFrameArea().Pos();
aFrm.Pos().AdjustY(1 ); // for notifications
}
// If the section frame has a successor then the latter needs
// to be moved behind the new Follow of the section frame.
SwFrame* pTmp = pSect->GetNext();
if( pTmp )
{
SwFlowFrame* pTmpNxt;
if( pTmp->IsContentFrame() )
pTmpNxt = static_cast<SwContentFrame*>(pTmp);
else if( pTmp->IsSctFrame() )
pTmpNxt = static_cast<SwSectionFrame*>(pTmp);
else
{
OSL_ENSURE( pTmp->IsTabFrame(), "GetNextSctLeaf: Wrong Type" );
pTmpNxt = static_cast<SwTabFrame*>(pTmp);
}
// we will dereference pNewUp in the following MoveSubTree call
// so it certainly should not be deleted before that
SwFrameDeleteGuard aDeleteGuard(pNewUp);
pTmpNxt->MoveSubTree( pTmpFootnote, pNewUp->GetNext() );
}
}
}
}
MoveSubTree( pNewUp, pNewUp->Lower() );
if( !bSameBoss )
Prepare( PrepareHint::BossChanged );
}
return bSamePage;
}
SwSaveFootnoteHeight::SwSaveFootnoteHeight( SwFootnoteBossFrame *pBs, const SwTwips nDeadLine ) :
aGuard(pBs),
pBoss( pBs ),
nOldHeight( pBs->GetMaxFootnoteHeight() )
{
pBoss->SetFootnoteDeadLine( nDeadLine );
nNewHeight = pBoss->GetMaxFootnoteHeight();
}
SwSaveFootnoteHeight::~SwSaveFootnoteHeight()
{
// If somebody tweaked the deadline meanwhile, we let it happen
if ( nNewHeight == pBoss->GetMaxFootnoteHeight() )
pBoss->m_nMaxFootnoteHeight = nOldHeight;
}
#ifdef DBG_UTIL
//JP 15.10.2001: in a non pro version test if the attribute has the same
// meaning which his reference is
// Normally, the pRef member and the GetRefFromAttr() result has to be
// identically. Sometimes footnote will be moved from a master to its follow,
// but the GetRef() is called first, so we have to ignore a master/follow
// mismatch.
const SwContentFrame* SwFootnoteFrame::GetRef() const
{
(void) GetRefFromAttr();
return mpReference;
}
SwContentFrame* SwFootnoteFrame::GetRef()
{
(void) GetRefFromAttr();
return mpReference;
}
#endif
const SwContentFrame* SwFootnoteFrame::GetRefFromAttr() const
{
SwFootnoteFrame* pThis = const_cast<SwFootnoteFrame*>(this);
return pThis->GetRefFromAttr();
}
SwContentFrame* SwFootnoteFrame::GetRefFromAttr()
{
assert(mpAttribute && "invalid Attribute");
SwTextNode& rTNd = const_cast<SwTextNode&>(mpAttribute->GetTextNode());
SwPosition aPos( rTNd, mpAttribute->GetStart() );
SwContentFrame* pCFrame = rTNd.getLayoutFrame(getRootFrame(), &aPos);
return pCFrame;
}
/** search for last content in the current footnote frame
OD 2005-12-02 #i27138#
*/
SwContentFrame* SwFootnoteFrame::FindLastContent()
{
SwContentFrame* pLastContentFrame( nullptr );
// find last lower, which is a content frame or contains content.
// hidden text frames, empty sections and empty tables have to be skipped.
SwFrame* pLastLowerOfFootnote( GetLower() );
SwFrame* pTmpLastLower( pLastLowerOfFootnote );
while ( pTmpLastLower && pTmpLastLower->GetNext() )
{
pTmpLastLower = pTmpLastLower->GetNext();
if (!pTmpLastLower->IsHiddenNow()
&& (!pTmpLastLower->IsLayoutFrame()
|| static_cast<SwLayoutFrame*>(pTmpLastLower)->ContainsContent()))
{
pLastLowerOfFootnote = pTmpLastLower;
}
}
// determine last content frame depending on type of found last lower.
if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsTabFrame() )
{
pLastContentFrame = static_cast<SwTabFrame*>(pLastLowerOfFootnote)->FindLastContent();
}
else if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsSctFrame() )
{
pLastContentFrame = static_cast<SwSectionFrame*>(pLastLowerOfFootnote)->FindLastContent();
}
else
{
pLastContentFrame = dynamic_cast<SwContentFrame*>(pLastLowerOfFootnote);
}
return pLastContentFrame;
}
void SwFootnoteFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
(void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("ftn"));
dumpAsXmlAttributes(writer);
(void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
dumpInfosAsXml(writer);
(void)xmlTextWriterEndElement(writer);
dumpChildrenAsXml(writer);
(void)xmlTextWriterEndElement(writer);
}
void SwFootnoteFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
{
SwLayoutFrame::dumpAsXmlAttributes(writer);
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("ref"), "%" SAL_PRIuUINT32, GetRef()->GetFrameId() );
if (GetMaster())
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("master"), "%" SAL_PRIuUINT32, GetMaster()->GetFrameId() );
if (GetFollow())
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("follow"), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V654 The condition '!pRet' of loop is always true.
↑ V654 The condition '_pFootnote' of loop is always true.
↑ V547 Expression '!pNxt' is always false.
↑ V1048 The 'pFootnoteArr' variable was assigned the same value.
↑ V1051 Consider checking for misprints. It's possible that the 'nDist' should be checked here.