3126 lines
117 KiB
C++
3126 lines
117 KiB
C++
/* -*- 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: */
|