/* -*- 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 <sal/config.h>
#include <string_view>
#include <utility>
#include <viewsh.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <ndtxt.hxx>
#include <SwPortionHandler.hxx>
#include <txtftn.hxx>
#include <flyfrm.hxx>
#include <fmtftn.hxx>
#include <fmtsrnd.hxx>
#include <ftninfo.hxx>
#include <charfmt.hxx>
#include <rowfrm.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/charrotateitem.hxx>
#include <tabfrm.hxx>
#include <sortedobjs.hxx>
#include <swfont.hxx>
#include "porftn.hxx"
#include "porfly.hxx"
#include "porlay.hxx"
#include <txtfrm.hxx>
#include "itrform2.hxx"
#include <ftnfrm.hxx>
#include <pagedesc.hxx>
#include "redlnitr.hxx"
#include <sectfrm.hxx>
#include <layouter.hxx>
#include <frmtool.hxx>
#include <ndindex.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <swmodule.hxx>
#include <unotextrange.hxx>
#include <redline.hxx>
#include <editeng/colritem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/CharSet.hpp>
#include <com/sun/star/text/XTextRange.hpp>
using namespace ::com::sun::star;
bool SwTextFrame::IsFootnoteNumFrame_()
const
{
if (IsInTab())
return false;
// tdf#102073 first frame in cell doesn't have mpPrev set
const SwFootnoteFrame* pFootnote = FindFootnoteFrame()->GetMaster();
while( pFootnote && !pFootnote->ContainsContent() )
pFootnote = pFootnote->GetMaster();
return !pFootnote;
}
/**
* Looks for the TextFrame matching the SwTextFootnote within a master-follow chain
*/
SwTextFrame *SwTextFrame::FindFootnoteRef(
const SwTextFootnote *pFootnote )
{
SwTextFrame *pFrame =
this;
const bool bFwd = MapModelToView(&pFootnote->GetTextNode(), pFootnote->GetStart()) >= GetOffset();
while( pFrame )
{
if( SwFootnoteBossFrame::FindFootnote( pFrame, pFootnote ) )
return pFrame;
pFrame = bFwd ? pFrame->GetFollow() :
pFrame->IsFollow() ? pFrame->FindMaster() : nullptr;
}
return pFrame;
}
void SwTextFrame::SetHasRotatedPortions(
bool bHasRotatedPortions)
{
mbHasRotatedPortions = bHasRotatedPortions;
}
#ifdef DBG_UTIL
void SwTextFrame::CalcFootnoteFlag(TextFrameIndex nStop)
// For testing the SplitFrame
#else
void SwTextFrame::CalcFootnoteFlag()
#endif
{
mbFootnote =
false;
#ifdef DBG_UTIL
const TextFrameIndex nEnd = nStop != TextFrameIndex(COMPLETE_STRING)
? nStop
: GetFollow() ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING);
#else
const TextFrameIndex nEnd = GetFollow()
? GetFollow()->GetOffset()
: TextFrameIndex(COMPLETE_STRING);
#endif
SwTextNode
const* pNode(nullptr);
sw::MergedAttrIter iter(*
this);
for (SwTextAttr
const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
{
if ( pHt->Which() == RES_TXTATR_FTN )
{
TextFrameIndex
const nIdx(MapModelToView(pNode, pHt->GetStart()));
if ( nEnd < nIdx )
break;
if( GetOffset() <= nIdx )
{
mbFootnote =
true;
break;
}
}
}
}
bool SwTextFrame::CalcPrepFootnoteAdjust()
{
OSL_ENSURE( HasFootnote(),
"Who´s calling me?" );
SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame(
true );
const SwFootnoteFrame *pFootnote = pBoss->FindFirstFootnote(
this );
if (pFootnote && FTNPOS_CHAPTER != GetDoc().GetFootnoteInfo().m_ePos &&
( !pBoss->GetUpper()->IsSctFrame() ||
!
static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsFootnoteAtEnd() ) )
{
const SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
bool bReArrange =
true;
SwRectFnSet aRectFnSet(
this);
if ( pCont && aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
{
pBoss->RearrangeFootnotes( aRectFnSet.GetBottom(getFrameArea()),
false,
pFootnote->GetAttr() );
ValidateBodyFrame();
ValidateFrame();
pFootnote = pBoss->FindFirstFootnote(
this );
}
else
bReArrange =
false;
if( !pCont || !pFootnote || bReArrange != (pFootnote->FindFootnoteBossFrame() == pBoss) )
{
SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(),
this );
SwTextFormatter aLine(
this, &aInf );
aLine.TruncLines();
SetPara(nullptr,
true);
// May be deleted!
ResetPreps();
return false;
}
}
return true;
}
/**
* Local helper function. Checks if nLower should be taken as the boundary
* for the footnote.
*/
static SwTwips lcl_GetFootnoteLower(
const SwTextFrame* pFrame, SwTwips nLower )
{
// nLower is an absolute value. It denotes the bottom of the line
// containing the footnote.
SwRectFnSet aRectFnSet(pFrame);
OSL_ENSURE( !pFrame->IsVertical() || !pFrame->IsSwapped(),
"lcl_GetFootnoteLower with swapped frame" );
SwTwips nAdd;
SwTwips nRet = nLower;
// Check if text is inside a table.
if ( pFrame->IsInTab() )
{
// If pFrame is inside a table, we have to check if
// a) The table is not allowed to split or
// b) The table row is not allowed to split
// Inside a table, there are no footnotes,
// see SwFrame::FindFootnoteBossFrame. So we don't have to check
// the case that pFrame is inside a (footnote collecting) section
// within the table.
const SwFrame* pRow = pFrame;
while( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() )
pRow = pRow->GetUpper();
const SwTabFrame* pTabFrame =
static_cast<
const SwTabFrame*>(pRow->GetUpper());
OSL_ENSURE( pTabFrame && pRow &&
pRow->GetUpper()->IsTabFrame(),
"Upper of row should be tab" );
const bool bDontSplit = !pTabFrame->IsFollow() &&
!pTabFrame->IsLayoutSplitAllowed();
SwTwips nMin = 0;
if ( bDontSplit )
nMin = aRectFnSet.GetBottom(pTabFrame->getFrameArea());
else if ( !
static_cast<
const SwRowFrame*>(pRow)->IsRowSplitAllowed() )
nMin = aRectFnSet.GetBottom(pRow->getFrameArea());
if ( nMin && aRectFnSet.YDiff( nMin, nLower ) > 0 )
nRet = nMin;
nAdd = aRectFnSet.GetBottomMargin(*pRow->GetUpper());
}
else
nAdd = aRectFnSet.GetBottomMargin(*pFrame);
if( nAdd > 0 )
{
if ( aRectFnSet.IsVert() )
nRet -= nAdd;
else
nRet += nAdd;
}
// #i10770#: If there are fly frames anchored at previous paragraphs,
// the deadline should consider their lower borders.
const SwFrame* pStartFrame = pFrame->GetUpper()->GetLower();
OSL_ENSURE( pStartFrame,
"Upper has no lower" );
SwTwips nFlyLower = aRectFnSet.IsVert() ? LONG_MAX : 0;
while ( pStartFrame != pFrame )
{
assert(pStartFrame &&
"Frame chain is broken");
if ( pStartFrame->GetDrawObjs() )
{
const SwSortedObjs &rObjs = *pStartFrame->GetDrawObjs();
for (SwAnchoredObject* pAnchoredObj : rObjs)
{
if (pAnchoredObj->GetFrameFormat()->GetSurround().GetSurround()
== text::WrapTextMode_THROUGH)
{
continue;
// tdf#161718 no effect on text flow, skip
}
SwRect aRect( pAnchoredObj->GetObjRect() );
auto pFlyFrame = pAnchoredObj->DynCastFlyFrame();
if ( !pFlyFrame ||
pFlyFrame->isFrameAreaDefinitionValid() )
{
const SwTwips nBottom = aRectFnSet.GetBottom(aRect);
if ( aRectFnSet.YDiff( nBottom, nFlyLower ) > 0 )
nFlyLower = nBottom;
}
}
}
pStartFrame = pStartFrame->GetNext();
}
if ( aRectFnSet.IsVert() )
nRet = std::min( nRet, nFlyLower );
else
nRet = std::max( nRet, nFlyLower );
return nRet;
}
SwTwips SwTextFrame::GetFootnoteLine(
const SwTextFootnote *pFootnote )
const
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
"SwTextFrame::GetFootnoteLine with swapped frame" );
SwTextFrame *pThis =
const_cast<SwTextFrame*>(
this);
if( !HasPara() )
{
// #109071# GetFormatted() does not work here, because most probably
// the frame is currently locked. We return the previous value.
return pThis->mnFootnoteLine > 0 ?
pThis->mnFootnoteLine :
IsVertical() ? getFrameArea().Left() : getFrameArea().Bottom();
}
SwTwips nRet;
{
SwSwapIfNotSwapped swap(
const_cast<SwTextFrame *>(
this));
SwTextInfo aInf( pThis );
SwTextIter aLine( pThis, &aInf );
TextFrameIndex
const nPos(MapModelToView(
&pFootnote->GetTextNode(), pFootnote->GetStart()));
aLine.CharToLine( nPos );
nRet = aLine.Y() + aLine.GetLineHeight();
if( IsVertical() )
nRet = SwitchHorizontalToVertical( nRet );
}
nRet = lcl_GetFootnoteLower( pThis, nRet );
pThis->mnFootnoteLine = nRet;
return nRet;
}
/**
* Calculates the maximum reachable height for the TextFrame in the Footnote Area.
* The cell's bottom margin with the Footnote Reference limit's this height.
*/
SwTwips SwTextFrame::GetFootnoteFrameHeight_()
const
{
OSL_ENSURE( !IsFollow() && IsInFootnote(),
"SwTextFrame::SetFootnoteLine: moon walk" );
const SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame();
const SwTextFrame *pRef =
static_cast<
const SwTextFrame *>(pFootnoteFrame->GetRef());
const SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame();
if( pBoss != pRef->FindFootnoteBossFrame( !pFootnoteFrame->GetAttr()->
GetFootnote().IsEndNote() ) )
return 0;
SwSwapIfSwapped swap(
const_cast<SwTextFrame *>(
this));
SwTwips nHeight = pRef->IsInFootnoteConnect() ?
1 : pRef->GetFootnoteLine( pFootnoteFrame->GetAttr() );
if( nHeight )
{
// As odd as it may seem: the first Footnote on the page may not touch the
// Footnote Reference, when entering text in the Footnote Area.
const SwFrame *pCont = pFootnoteFrame->GetUpper();
// Height within the Container which we're allowed to consume anyways
SwRectFnSet aRectFnSet(pCont);
SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pCont),
aRectFnSet.GetTop(getFrameArea()) );
#if OSL_DEBUG_LEVEL > 0
if( nTmp < 0 )
{
bool bInvalidPos =
false;
const SwLayoutFrame* pTmp = GetUpper();
while( !bInvalidPos && pTmp )
{
const SwFrame* pLower = pTmp->Lower();
bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
!pLower || !pLower->isFrameAreaPositionValid();
if( pTmp == pCont )
break;
pTmp = pTmp->GetUpper();
}
OSL_ENSURE( bInvalidPos,
"Hanging below FootnoteCont" );
}
#endif
if ( aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight) > 0 )
{
// Growth potential of the container
if ( !pRef->IsInFootnoteConnect() )
{
SwSaveFootnoteHeight aSave(
const_cast<SwFootnoteBossFrame*>(pBoss), nHeight );
nHeight =
const_cast<SwFootnoteContFrame*>(
static_cast<
const SwFootnoteContFrame*>(pCon
t))->Grow( LONG_MAX, true );
}
else
nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
nHeight += nTmp;
if( nHeight < 0 )
nHeight = 0;
}
else
{ // The container has to shrink
nTmp += aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight);
if( nTmp > 0 )
nHeight = nTmp;
else
nHeight = 0;
}
}
return nHeight;
}
SwTextFrame *SwTextFrame::FindQuoVadisFrame()
{
// Check whether we're in a FootnoteFrame
if( GetIndPrev() || !IsInFootnote() )
return nullptr;
// To the preceding FootnoteFrame
SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame()->GetMaster();
if( !pFootnoteFrame )
return nullptr;
// Now the last Content
SwContentFrame *pCnt = pFootnoteFrame->ContainsContent();
if( !pCnt )
return nullptr;
SwContentFrame *pLast;
do
{ pLast = pCnt;
pCnt = pCnt->GetNextContentFrame();
} while( pCnt && pFootnoteFrame->IsAnLower( pCnt ) );
return static_cast<SwTextFrame*>(pLast);
}
void SwTextFrame::RemoveFootnote(TextFrameIndex const nStart, TextFrameIndex const nLen)
{
if ( !IsFootnoteAllowed() )
return;
bool bRollBack = nLen != TextFrameIndex(COMPLETE_STRING);
TextFrameIndex nEnd;
SwTextFrame* pSource;
if( bRollBack )
{
nEnd = nStart + nLen;
pSource = GetFollow();
if( !pSource )
return;
}
else
{
nEnd = TextFrameIndex(COMPLETE_STRING);
pSource = this;
}
SwPageFrame* pUpdate = nullptr;
bool bRemove = false;
SwFootnoteBossFrame *pFootnoteBoss = nullptr;
SwFootnoteBossFrame *pEndBoss = nullptr;
bool bFootnoteEndDoc = FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos;
SwTextNode const* pNode(nullptr);
sw::MergedAttrIterReverse iter(*this);
for (SwTextAttr const* pHt = iter.PrevAttr(&pNode); pHt; pHt = iter.PrevAttr(&pNode))
{
if (RES_TXTATR_FTN != pHt->Which())
continue;
TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
if (nStart > nIdx)
break;
if (nEnd >= nIdx)
{
SwTextFootnote const*const pFootnote(static_cast<SwTextFootnote const*>(pHt));
const bool bEndn = pFootnote->GetFootnote().IsEndNote();
if (bEndn)
{
if (!pEndBoss)
pEndBoss = pSource->FindFootnoteBossFrame();
}
else
{
if (!pFootnoteBoss)
{
pFootnoteBoss = pSource->FindFootnoteBossFrame( true );
if( pFootnoteBoss->GetUpper()->IsSctFrame() )
{
SwSectionFrame* pSect = static_cast<SwSectionFrame*>(
pFootnoteBoss->GetUpper());
if (pSect->IsFootnoteAtEnd())
bFootnoteEndDoc = false;
}
}
}
// We don't delete, but move instead.
// Three cases are to be considered:
// 1) There's neither Follow nor PrevFollow:
// -> RemoveFootnote() (maybe even a OSL_ENSURE(value))
//
// 2) nStart > GetOffset, I have a Follow
// -> Footnote moves into Follow
//
// 3) nStart < GetOffset, I am a Follow
// -> Footnote moves into the PrevFollow
//
// Both need to be on one Page/in one Column
SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote(pSource, pFootnote);
if (pFootnoteFrame)
{
const bool bEndDoc = bEndn || bFootnoteEndDoc;
if( bRollBack )
{
while (pFootnoteFrame)
{
pFootnoteFrame->SetRef( this );
pFootnoteFrame = pFootnoteFrame->GetFollow();
SetFootnote( true );
}
}
else if (GetFollow())
{
SwContentFrame *pDest = GetFollow();
while (pDest->GetFollow() && static_cast<SwTextFrame*>(pDest->
GetFollow())->GetOffset() <= nIdx)
pDest = pDest->GetFollow();
OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote(
pDest,pFootnote),"SwTextFrame::RemoveFootnote: footnote exists");
// Never deregister; always move
if (bEndDoc ||
!pFootnoteFrame->FindFootnoteBossFrame()->IsBefore(pDest->FindFootnoteBossFrame(!bEndn))
)
{
SwPageFrame* pTmp = pFootnoteFrame->FindPageFrame();
if( pUpdate && pUpdate != pTmp )
pUpdate->UpdateFootnoteNum();
pUpdate = pTmp;
while ( pFootnoteFrame )
{
pFootnoteFrame->SetRef( pDest );
pFootnoteFrame = pFootnoteFrame->GetFollow();
}
}
else
{
pFootnoteBoss->MoveFootnotes( this, pDest, pFootnote );
bRemove = true;
}
static_cast<SwTextFrame*>(pDest)->SetFootnote( true );
OSL_ENSURE( SwFootnoteBossFrame::FindFootnote( pDest,
pFootnote),"SwTextFrame::RemoveFootnote: footnote ChgRef failed");
}
else
{
if (!bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
!SwLayouter::Collecting( GetDoc(),
pEndBoss->FindSctFrame(), nullptr ) ))
{
if( bEndn )
pEndBoss->RemoveFootnote( this, pFootnote );
else
pFootnoteBoss->RemoveFootnote( this, pFootnote );
bRemove = bRemove || !bEndDoc;
OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote( this, pFootnote ),
"SwTextFrame::RemoveFootnote: can't get off that footnote" );
}
}
}
}
}
if (pUpdate)
pUpdate->UpdateFootnoteNum();
// We break the oscillation
if (bRemove && !bFootnoteEndDoc && HasPara())
{
ValidateBodyFrame();
ValidateFrame();
}
// We call the RemoveFootnote from within the FindBreak, because the last line is
// to be passed to the Follow. The Offset of the Follow is, however, outdated;
// it'll be set soon. CalcFntFlag depends on a correctly set Follow Offset.
// Therefore we temporarily calculate the Follow Offset here
TextFrameIndex nOldOfst(COMPLETE_STRING);
if( HasFollow() && nStart > GetOffset() )
{
nOldOfst = GetFollow()->GetOffset();
GetFollow()->ManipOfst(nStart + (bRollBack ? nLen : TextFrameIndex(0)));
}
pSource->CalcFootnoteFlag();
if (nOldOfst < TextFrameIndex(COMPLETE_STRING))
GetFollow()->ManipOfst( nOldOfst );
}
/**
* We basically only have two possibilities:
*
* a) The Footnote is already present
* => we move it, if another pSrcFrame has been found
*
* b) The Footnote is not present
* => we have it created for us
*
* Whether the Footnote ends up on our Page/Column, doesn't matter in this
* context.
*
* Optimization for Endnotes.
*
* Another problem: if the Deadline falls within the Footnote Area, we need
* to move the Footnote.
*
* @returns false on any type of error
*/
void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDeadLine )
{
OSL_ENSURE( !IsVertical() || !IsSwapped(),
"SwTextFrame::ConnectFootnote with swapped frame" );
mbFootnote = true;
mbInFootnoteConnect = true; // Just reset!
// See if pFootnote is an endnote on a separate endnote page.
const bool bEnd = pFootnote->GetFootnote().IsEndNote();
// We want to store this value, because it is needed as a fallback
// in GetFootnoteLine(), if there is no paragraph information available
mnFootnoteLine = nDeadLine;
// We always need a parent (Page/Column)
SwSectionFrame *pSect;
SwContentFrame *pContent = this;
if( bEnd && IsInSct() )
{
pSect = FindSctFrame();
if( pSect->IsEndnAtEnd() )
pContent = pSect->FindLastContent( SwFindMode::EndNote );
if( !pContent )
pContent = this;
}
SwFootnoteBossFrame *pBoss = pContent->FindFootnoteBossFrame( !bEnd );
pSect = pBoss->FindSctFrame();
bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) :
( !( pSect && pSect->IsFootnoteAtEnd() ) &&
FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos);
// Footnote can be registered with the Follow
SwContentFrame *pSrcFrame = FindFootnoteRef( pFootnote );
if( bDocEnd )
{
if (pSect && pSrcFrame)
{
SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
if (pFootnoteFrame && pFootnoteFrame->IsInSct())
{
pBoss->RemoveFootnote( pSrcFrame, pFootnote );
pSrcFrame = nullptr;
}
}
}
else if( bEnd && pSect )
{
SwFootnoteFrame *pFootnoteFrame = pSrcFrame ? SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote ) : nullptr;
if( pFootnoteFrame && !pFootnoteFrame->GetUpper() )
pFootnoteFrame = nullptr;
SwDoc& rDoc = GetDoc();
if( SwLayouter::Collecting( rDoc, pSect, pFootnoteFrame ) )
{
if( !pSrcFrame )
{
SwFootnoteFrame *pNew = new SwFootnoteFrame(rDoc.GetDfltFrameFormat(),this,this,pFootnote);
SwNodeIndex aIdx( *pFootnote->GetStartNode(), 1 );
::InsertCnt_( pNew, rDoc, aIdx.GetIndex() );
rDoc.getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew );
}
else if( pSrcFrame != this )
SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
mbInFootnoteConnect = false;
return;
}
else if (pSrcFrame && pFootnoteFrame)
{
SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
if( !pFootnoteBoss->IsInSct() ||
pFootnoteBoss->ImplFindSctFrame()->GetSection()!=pSect->GetSection() )
{
pBoss->RemoveFootnote( pSrcFrame, pFootnote );
pSrcFrame = nullptr;
}
}
}
if( bDocEnd || bEnd )
{
if( !pSrcFrame )
pBoss->AppendFootnote( this, pFootnote );
else if( pSrcFrame != this )
SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
mbInFootnoteConnect = false;
return;
}
SwSaveFootnoteHeight aHeight( pBoss, nDeadLine );
if( !pSrcFrame ) // No Footnote was found at all
pBoss->AppendFootnote( this, pFootnote );
else
{
SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
bool bBrutal = false;
if( pFootnoteBoss == pBoss ) // Ref and Footnote are on the same Page/Column
{
SwFrame *pCont = pFootnoteFrame->GetUpper();
SwRectFnSet aRectFnSet(pCont);
tools::Long nDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
nDeadLine );
if( nDiff >= 0 )
{
// If the Footnote has been registered to a Follow, we need to
// rewire it now too
if ( pSrcFrame != this )
SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
// We have some room left, so the Footnote can grow
if ( pFootnoteFrame->GetFollow() && nDiff > 0 )
{
SwFrameDeleteGuard aDeleteGuard(pCont);
SwTwips nHeight = aRectFnSet.GetHeight(pCont->getFrameArea());
pBoss->RearrangeFootnotes( nDeadLine, false, pFootnote );
ValidateBodyFrame();
ValidateFrame();
SwViewShell *pSh = getRootFrame()->GetCurrShell();
if ( pSh && nHeight == aRectFnSet.GetHeight(pCont->getFrameArea()) )
// So that we don't miss anything
pSh->InvalidateWindows( pCont->getFrameArea() );
}
mbInFootnoteConnect = false;
return;
}
else
bBrutal = true;
}
else
{
// Ref and Footnote are not on one Page; attempt to move is necessary
SwFrame* pTmp = this;
while( pTmp->GetNext() && pSrcFrame != pTmp )
pTmp = pTmp->GetNext();
if( pSrcFrame == pTmp )
bBrutal = true;
else
{ // If our Parent is in a column Area, but the Page already has a
// FootnoteContainer, we can only brute force it
if( pSect && pSect->FindFootnoteBossFrame( !bEnd )->FindFootnoteCont() )
bBrutal = true;
else if ( !pFootnoteFrame->GetPrev() ||
pFootnoteBoss->IsBefore( pBoss )
)
{
SwFootnoteBossFrame *pSrcBoss = pSrcFrame->FindFootnoteBossFrame( !bEnd );
pSrcBoss->MoveFootnotes( pSrcFrame, this, pFootnote );
}
else
SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
}
}
// The brute force method: Remove Footnote and append.
// We need to call SetFootnoteDeadLine(), as we can more easily adapt the
// nMaxFootnoteHeight after RemoveFootnote
if( bBrutal )
{
pBoss->RemoveFootnote( pSrcFrame, pFootnote, false );
std::unique_ptr<SwSaveFootnoteHeight> pHeight(bEnd ? nullptr : new SwSaveFootnoteHeight( pBoss, nDeadLine ));
pBoss->AppendFootnote( this, pFootnote );
}
}
// In column Areas, that not yet reach the Page's border a RearrangeFootnotes is not
// useful yet, as the Footnote container has not yet been calculated
if( !pSect || !pSect->Growable() )
{
// Validate environment, to avoid oscillation
SwSaveFootnoteHeight aNochmal( pBoss, nDeadLine );
ValidateBodyFrame();
pBoss->RearrangeFootnotes( nDeadLine, true );
ValidateFrame();
}
else if( pSect->IsFootnoteAtEnd() )
{
ValidateBodyFrame();
ValidateFrame();
}
mbInFootnoteConnect = false;
}
/**
* The portion for the Footnote Reference in the Text
*/
SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
SwTextAttr *pHint )
{
OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
"NewFootnotePortion with unswapped frame" );
if (!m_pFrame->IsFootnoteAllowed())
return new SwFootnotePortion(u""_ustr, nullptr);
SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint);
const SwFormatFootnote& rFootnote = pFootnote->GetFootnote();
SwDoc *const pDoc = &m_pFrame->GetDoc();
if( rInf.IsTest() )
return new SwFootnotePortion(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()), pFootnote);
SwSwapIfSwapped swap(m_pFrame);
SwTwips nReal;
{
SwTwips nOldReal = m_pCurr->GetRealHeight();
SwTwips nOldAscent = m_pCurr->GetAscent();
SwTwips nOldHeight = m_pCurr->Height();
CalcRealHeight();
nReal = m_pCurr->GetRealHeight();
if( nReal < nOldReal )
nReal = nOldReal;
m_pCurr->SetRealHeight( nOldReal );
m_pCurr->Height( nOldHeight );
m_pCurr->SetAscent( nOldAscent );
}
SwTwips nLower = Y() + nReal;
const bool bVertical = m_pFrame->IsVertical();
if( bVertical )
nLower = m_pFrame->SwitchHorizontalToVertical( nLower );
nLower = lcl_GetFootnoteLower( m_pFrame, nLower );
// We just refresh.
// The Connect does not do anything useful in this case, but will
// mostly throw away the Footnote and create it anew.
if( !rInf.IsQuick() )
m_pFrame->ConnectFootnote( pFootnote, nLower );
SwTextFrame *pScrFrame = m_pFrame->FindFootnoteRef( pFootnote );
SwFootnoteBossFrame *pBoss = m_pFrame->FindFootnoteBossFrame( !rFootnote.IsEndNote() );
SwFootnoteFrame *pFootnoteFrame = nullptr;
if( pScrFrame )
pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pScrFrame, pFootnote );
// We see whether our Append has caused some Footnote to
// still be on the Page/Column. If not, our line disappears too,
// which will lead to the following undesired behaviour:
// Footnote1 still fits onto the Page/Column, but Footnote2 doesn't.
// The Footnote2 Reference remains on the Page/Column. The Footnote itself
// is on the next Page/Column.
//
// Exception: If the Page/Column cannot accommodate another line,
// the Footnote Reference should be moved to the next one.
if( !rFootnote.IsEndNote() )
{
SwSectionFrame *pSct = pBoss->FindSctFrame();
bool bAtSctEnd = pSct && pSct->IsFootnoteAtEnd();
if( FTNPOS_CHAPTER != pDoc->GetFootnoteInfo().m_ePos || bAtSctEnd )
{
SwFrame* pFootnoteCont = pBoss->FindFootnoteCont();
// If the Parent is within an Area, it can only be a Column of this
// Area. If this one is not the first Column, we can avoid it.
if( !m_pFrame->IsInTab() && ( GetLineNr() > 1 || m_pFrame->GetPrev() ||
( !bAtSctEnd && m_pFrame->GetIndPrev() ) ||
( pSct && pBoss->GetPrev() ) ) )
{
if( !pFootnoteCont )
{
rInf.SetStop( true );
return nullptr;
}
else
{
// There must not be any Footnote Containers in column Areas and at the same time on the
// Page/Page column
if( pSct && !bAtSctEnd ) // Is the Container in a (column) Area?
{
SwFootnoteBossFrame* pTmp = pBoss->FindSctFrame()->FindFootnoteBossFrame( true );
SwFootnoteContFrame* pFootnoteC = pTmp->FindFootnoteCont();
if( pFootnoteC )
{
SwFootnoteFrame* pTmpFrame = static_cast<SwFootnoteFrame*>(pFootnoteC->Lower());
if( pTmpFrame && *pTmpFrame < pFootnote )
{
rInf.SetStop( true );
return nullptr;
}
}
}
// Is this the last Line that fits?
SwTwips nTmpBot = Y() + nReal * 2;
if( bVertical )
nTmpBot = m_pFrame->SwitchHorizontalToVertical( nTmpBot );
SwRectFnSet aRectFnSet(pFootnoteCont);
const tools::Long nDiff = aRectFnSet.YDiff(
aRectFnSet.GetTop(pFootnoteCont->getFrameArea()),
nTmpBot );
if( pScrFrame && nDiff < 0 )
{
if( pFootnoteFrame )
{
SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
if( pFootnoteBoss != pBoss )
{
// We're in the last Line and the Footnote has moved
// to another Page. We also want to be on that Page!
rInf.SetStop( true );
return nullptr;
}
}
}
}
}
}
}
// Finally: Create FootnotePortion and exit ...
SwFootnotePortion *pRet = new SwFootnotePortion(
rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()),
pFootnote, nReal );
rInf.SetFootnoteInside( true );
return pRet;
}
/**
* The portion for the Footnote Numbering in the Footnote Area
*/
SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const &rInf ) const
{
OSL_ENSURE( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() && !rInf.IsFootnoteDone(),
"This is the wrong place for a ftnnumber" );
if( rInf.GetTextStart() != m_nStart ||
rInf.GetTextStart() != rInf.GetIdx() )
return nullptr;
const SwFootnoteFrame* pFootnoteFrame = m_pFrame->FindFootnoteFrame();
const SwTextFootnote* pFootnote = pFootnoteFrame->GetAttr();
// Aha! So we're in the Footnote Area!
SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
SwDoc *const pDoc = &m_pFrame->GetDoc();
OUString aFootnoteText(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame(), true));
const SwEndNoteInfo* pInfo;
if( rFootnote.IsEndNote() )
pInfo = &pDoc->GetEndNoteInfo();
else
pInfo = &pDoc->GetFootnoteInfo();
const SwAttrSet* pParSet = &rInf.GetCharAttr();
const IDocumentSettingAccess* pIDSA = &pDoc->getIDocumentSettingAccess();
std::unique_ptr<SwFont> pNumFnt(new SwFont( pParSet, pIDSA ));
// #i37142#
// Underline style of paragraph font should not be considered
// Overline style of paragraph font should not be considered
// Weight style of paragraph font should not be considered
// Posture style of paragraph font should not be considered
// See also #i18463# and SwTextFormatter::NewNumberPortion()
pNumFnt->SetUnderline( LINESTYLE_NONE );
pNumFnt->SetOverline( LINESTYLE_NONE );
pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin );
pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK );
pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL );
pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin );
pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK );
pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL );
const rtl::Reference<SwXTextRange> xAnchor = rFootnote.getAnchor(*pDoc);
if (xAnchor.is())
{
auto aAny = xAnchor->getPropertyValue(u"CharFontCharSet"_ustr);
sal_Int16 eCharSet;
if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
{
OUString aFontName;
aAny = xAnchor->getPropertyValue(u"CharFontName"_ustr);
if (aAny >>= aFontName)
{
pNumFnt->SetName(aFontName, SwFontScript::Latin);
pNumFnt->SetName(aFontName, SwFontScript::CJK);
pNumFnt->SetName(aFontName, SwFontScript::CTL);
pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin);
pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CJK);
pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CTL);
}
}
}
const SwAttrSet& rSet = pInfo->GetCharFormat(*pDoc)->GetAttrSet();
pNumFnt->SetDiffFnt(&rSet, pIDSA );
pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
// tdf#85610 apply redline coloring to the footnote numbering in the footnote area
SwUnoInternalPaM aPam(*pDoc);
if ( ::sw::XTextRangeToSwPaM(aPam, xAnchor) )
{
SwRedlineTable::size_type nRedlinePos = 0;
const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
const SwRangeRedline* pRedline = rTable.FindAtPosition( *aPam.Start(), nRedlinePos );
if (pRedline)
{
SwAttrPool& rPool = pDoc->GetAttrPool();
SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
std::size_t aAuthor = (1 < pRedline->GetStackCount())
? pRedline->GetAuthor( 1 )
: pRedline->GetAuthor();
if ( RedlineType::Delete == pRedline->GetType() )
SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet);
else
SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet);
if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
pNumFnt->SetColor(pItem->GetValue());
if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
pNumFnt->SetUnderline(pItem->GetLineStyle());
if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
pNumFnt->SetStrikeout( pItem->GetStrikeout() );
}
}
SwFootnoteNumPortion* pNewPor = new SwFootnoteNumPortion( aFootnoteText, std::move(pNumFnt) );
pNewPor->SetLeft( !m_pFrame->IsRightToLeft() );
return pNewPor;
}
static OUString lcl_GetPageNumber( const SwPageFrame* pPage )
{
assert(pPage && "GetPageNumber: Homeless TextFrame");
const sal_uInt16 nVirtNum = pPage->GetVirtPageNum();
const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
return rNum.GetNumStr( nVirtNum );
}
SwErgoSumPortion *SwTextFormatter::NewErgoSumPortion( SwTextFormatInfo const &rInf ) const
{
// We cannot assume we're a Follow
if( !m_pFrame->IsInFootnote() || m_pFrame->GetPrev() ||
rInf.IsErgoDone() || rInf.GetIdx() != m_pFrame->GetOffset() ||
m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
return nullptr;
// we are in the footnote container
const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
SwTextFrame *pQuoFrame = m_pFrame->FindQuoVadisFrame();
if( !pQuoFrame )
return nullptr;
const SwPageFrame* pPage = m_pFrame->FindPageFrame();
const SwPageFrame* pQuoPage = pQuoFrame->FindPageFrame();
if( pPage == pQuoFrame->FindPageFrame() )
return nullptr; // If the QuoVadis is on the same Column/Page
const OUString aPage = lcl_GetPageNumber( pPage );
SwParaPortion *pPara = pQuoFrame->GetPara();
if( pPara )
pPara->SetErgoSumNum( aPage );
if( rFootnoteInfo.m_aErgoSum.isEmpty() )
return nullptr;
SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFootnoteInfo.m_aErgoSum,
lcl_GetPageNumber( pQuoPage ) );
return pErgo;
}
TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
{
OSL_ENSURE( ! m_pFrame->IsVertical() || ! m_pFrame->IsSwapped(),
"SwTextFormatter::FormatQuoVadis with swapped frame" );
if( !m_pFrame->IsInFootnote() || m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
return nOffset;
const SwFrame* pErgoFrame = m_pFrame->FindFootnoteFrame()->GetFollow();
if( !pErgoFrame && m_pFrame->HasFollow() )
pErgoFrame = m_pFrame->GetFollow();
if( !pErgoFrame )
return nOffset;
if( pErgoFrame == m_pFrame->GetNext() )
{
SwFrame *pCol = m_pFrame->FindColFrame();
while( pCol && !pCol->GetNext() )
pCol = pCol->GetUpper()->FindColFrame();
if( pCol )
return nOffset;
}
else
{
const SwPageFrame* pPage = m_pFrame->FindPageFrame();
const SwPageFrame* pErgoPage = pErgoFrame->FindPageFrame();
if( pPage == pErgoPage )
return nOffset; // If the ErgoSum is on the same Page
}
SwTextFormatInfo &rInf = GetInfo();
const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
if( rFootnoteInfo.m_aQuoVadis.isEmpty() )
return nOffset;
// A remark on QuoVadis/ErgoSum:
// We use the Font set for the Paragraph for these texts.
// Thus, we initialize:
// TODO: ResetFont();
FeedInf( rInf );
SeekStartAndChg( rInf, true );
if( GetRedln() && m_pCurr->HasRedline() )
{
std::pair<SwTextNode const*, sal_Int32> const pos(
GetTextFrame()->MapViewToModel(nOffset));
GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
}
// A tricky special case: Flyfrms extend into the Line and are at the
// position we want to insert the Quovadis text
// Let's see if it is that bad indeed:
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
SwTwips nLastLeft = 0;
while( pPor )
{
if ( pPor->IsFlyPortion() )
nLastLeft = static_cast<SwFlyPortion*>(pPor)->GetFix() +
static_cast<SwFlyPortion*>(pPor)->Width();
pPor = pPor->GetNextPortion();
}
// The old game all over again: we want the Line to wrap around
// at a certain point, so we adjust the width.
// nLastLeft is now basically the right margin
const SwTwips nOldRealWidth = rInf.RealWidth();
rInf.RealWidth( nOldRealWidth - nLastLeft );
OUString aErgo = lcl_GetPageNumber( pErgoFrame->FindPageFrame() );
SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFootnoteInfo.m_aQuoVadis, aErgo );
pQuo->SetAscent( rInf.GetAscent() );
pQuo->Height( rInf.GetTextHeight() );
pQuo->Format( rInf );
SwTwips nQuoWidth = pQuo->Width();
SwLinePortion* pCurrPor = pQuo;
while ( rInf.GetRest() )
{
SwLinePortion* pFollow = rInf.GetRest();
rInf.SetRest( nullptr );
pCurrPor->Move( rInf );
OSL_ENSURE( pFollow->IsQuoVadisPortion(),
"Quo Vadis, rest of QuoVadisPortion" );
// format the rest and append it to the other QuoVadis parts
pFollow->Format( rInf );
nQuoWidth = nQuoWidth + pFollow->Width();
pCurrPor->Append( pFollow );
pCurrPor = pFollow;
}
Right( Right() - nQuoWidth );
TextFrameIndex nRet;
{
SwSwapIfNotSwapped swap(m_pFrame);
nRet = FormatLine( m_nStart );
}
Right( rInf.Left() + nOldRealWidth - 1 );
nLastLeft = nOldRealWidth - m_pCurr->Width();
FeedInf( rInf );
// It's possible that there's a Margin Portion at the end, which would
// just cause a lot of trouble, when respanning
pPor = m_pCurr->FindLastPortion();
SwGluePortion *pGlue = pPor->IsMarginPortion() ? static_cast<SwMarginPortion*>(pPor) : nullptr;
if( pGlue )
{
pGlue->Height( 0 );
pGlue->Width( 0 );
pGlue->SetLen(TextFrameIndex(0));
pGlue->SetAscent( 0 );
pGlue->SetNextPortion( nullptr );
pGlue->SetFixWidth(0);
}
// Luxury: We make sure the QuoVadis text appears on the right, by
// using Glues.
nLastLeft = nLastLeft - nQuoWidth;
if( nLastLeft )
{
if( nLastLeft > pQuo->GetAscent() ) // Minimum distance
{
switch( GetAdjust() )
{
case SvxAdjust::Block:
{
if( !m_pCurr->GetLen() ||
CH_BREAK != GetInfo().GetChar(m_nStart + m_pCurr->GetLen() - TextFrameIndex(1)))
nLastLeft = pQuo->GetAscent();
nQuoWidth = nQuoWidth + nLastLeft;
break;
}
case SvxAdjust::Right:
{
nLastLeft = pQuo->GetAscent();
nQuoWidth = nQuoWidth + nLastLeft;
break;
}
case SvxAdjust::Center:
{
nQuoWidth = nQuoWidth + pQuo->GetAscent();
tools::Long nDiff = nLastLeft - nQuoWidth;
if( nDiff < 0 )
{
nLastLeft = pQuo->GetAscent();
nQuoWidth = -nDiff + nLastLeft;
}
else
{
nQuoWidth = 0;
nLastLeft = (pQuo->GetAscent() + nDiff) / 2;
}
break;
}
default:
nQuoWidth = nQuoWidth + nLastLeft;
}
}
else
nQuoWidth = nQuoWidth + nLastLeft;
if( nLastLeft )
{
pGlue = new SwGluePortion(0);
pGlue->Width( nLastLeft );
pPor->Append( pGlue );
pPor = pPor->GetNextPortion();
}
}
// Finally: we insert the QuoVadis Portion
pCurrPor = pQuo;
while ( pCurrPor )
{
// pPor->Append deletes the pPortion pointer of pPor.
// Therefore we have to keep a pointer to the next portion
pQuo = static_cast<SwQuoVadisPortion*>(pCurrPor->GetNextPortion());
pPor->Append( pCurrPor );
pPor = pPor->GetNextPortion();
pCurrPor = pQuo;
}
m_pCurr->Width( m_pCurr->Width() + nQuoWidth );
// And adjust again, due to the adjustment and due to the following special
// case:
// The DummyUser has set a smaller Font in the Line than the one used
// by the QuoVadis text ...
CalcAdjustLine( m_pCurr );
return nRet;
}
/**
* This function creates a Line that reaches to the other Page Margin.
* DummyLines or DummyPortions make sure, that oscillations stop, because
* there's no way to flow back.
* They are used for Footnotes in paragraph-bound Frames and for Footnote
* oscillations
*/
void SwTextFormatter::MakeDummyLine()
{
SwTwips nRstHeight = GetFrameRstHeight();
if( m_pCurr && nRstHeight > m_pCurr->Height() )
{
SwLineLayout *pLay = new SwLineLayout;
nRstHeight = nRstHeight - m_pCurr->Height();
pLay->Height( nRstHeight );
pLay->SetAscent( nRstHeight );
Insert( pLay );
Next();
}
}
namespace {
class SwFootnoteSave
{
SwTextSizeInfo* m_pInf;
SwFont* m_pFnt;
std::unique_ptr<SwFont> m_pOld;
SwFootnoteSave(const SwFootnoteSave&) = delete;
SwFootnoteSave& operator=(const SwFootnoteSave&) = delete;
public:
SwFootnoteSave( const SwTextSizeInfo &rInf,
const SwTextFootnote *pTextFootnote,
const bool bApplyGivenScriptType,
const SwFontScript nGivenScriptType );
~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE;
};
}
SwFootnoteSave::SwFootnoteSave(const SwTextSizeInfo& rInf, const SwTextFootnote* pTextFootnote,
const bool bApplyGivenScriptType,
const SwFontScript nGivenScriptType)
: m_pInf(&const_cast<SwTextSizeInfo&>(rInf))
, m_pFnt(nullptr)
{
if( pTextFootnote && rInf.GetTextFrame() )
{
m_pFnt = const_cast<SwTextSizeInfo&>(rInf).GetFont();
m_pOld.reset(new SwFont(*m_pFnt));
m_pOld->GetTox() = m_pFnt->GetTox();
m_pFnt->GetTox() = 0;
SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pTextFootnote->GetFootnote());
const SwDoc *const pDoc = &rInf.GetTextFrame()->GetDoc();
// #i98418#
if ( bApplyGivenScriptType )
{
m_pFnt->SetActual(nGivenScriptType);
}
else
{
// examine text and set script
OUString aTmpStr(rFootnote.GetViewNumStr(*pDoc, rInf.GetTextFrame()->getRootFrame()));
m_pFnt->SetActual(SwScriptInfo::WhichFont(0, aTmpStr));
}
const SwEndNoteInfo* pInfo;
if( rFootnote.IsEndNote() )
pInfo = &pDoc->GetEndNoteInfo();
else
pInfo = &pDoc->GetFootnoteInfo();
const SwAttrSet& rSet = pInfo->GetAnchorCharFormat(const_cast<SwDoc&>(*pDoc))->GetAttrSet();
m_pFnt->SetDiffFnt(&rSet, &pDoc->getIDocumentSettingAccess());
// we reduce footnote size, if we are inside a double line portion
if (!m_pOld->GetEscapement() && 50 == m_pOld->GetPropr())
{
Size aSize = m_pFnt->GetSize(m_pFnt->GetActual());
m_pFnt->SetSize(Size(aSize.Width() / 2, aSize.Height() / 2), m_pFnt->GetActual());
}
// set the correct rotation at the footnote font
if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
m_pFnt->SetVertical(pItem->GetValue(),
rInf.GetTextFrame()->IsVertical());
m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
if( const SvxBrushItem* pItem = rSet.GetItemIfSet( RES_CHRATR_BACKGROUND ) )
m_pFnt->SetBackColor(pItem->GetColor());
}
else
m_pFnt = nullptr;
}
SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
{
if (m_pFnt)
{
// Put back SwFont
*m_pFnt = *m_pOld;
m_pFnt->GetTox() = m_pOld->GetTox();
m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
m_pOld.reset();
}
}
SwFootnotePortion::SwFootnotePortion(const OUString& rExpand, SwTextFootnote* pFootn, SwTwips nReal)
: SwFieldPortion( rExpand, nullptr )
, m_pFootnote(pFootn)
, m_nOrigHeight( nReal )
// #i98418#
, mbPreferredScriptTypeSet( false )
, mnPreferredScriptType( SwFontScript::Latin )
{
SetLen(TextFrameIndex(1));
SetWhichPor( PortionType::Footnote );
}
bool SwFootnotePortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
{
rText = m_aExpand;
return true;
}
bool SwFootnotePortion::Format( SwTextFormatInfo &rInf )
{
// #i98418#
// SwFootnoteSave aFootnoteSave( rInf, pFootnote );
SwFootnoteSave aFootnoteSave( rInf, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
// the idx is manipulated in SwExpandPortion::Format
// this flag indicates, that a footnote is allowed to trigger
// an underflow during SwTextGuess::Guess
rInf.SetFakeLineStart( rInf.GetIdx() > rInf.GetLineStart() );
const bool bFull = SwFieldPortion::Format( rInf );
rInf.SetFakeLineStart( false );
SetAscent( rInf.GetAscent() );
Height( rInf.GetTextHeight() );
rInf.SetFootnoteDone( !bFull );
if (!bFull && m_pFootnote)
rInf.SetParaFootnote();
return bFull;
}
void SwFootnotePortion::Paint( const SwTextPaintInfo &rInf ) const
{
// #i98418#
// SwFootnoteSave aFootnoteSave( rInf, pFootnote );
SwFootnoteSave aFootnoteSave( rInf, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
rInf.DrawViewOpt( *this, PortionType::Footnote );
SwExpandPortion::Paint( rInf );
}
SwPositiveSize SwFootnotePortion::GetTextSize( const SwTextSizeInfo &rInfo ) const
{
// #i98418#
// SwFootnoteSave aFootnoteSave( rInfo, pFootnote );
SwFootnoteSave aFootnoteSave( rInfo, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
return SwExpandPortion::GetTextSize( rInfo );
}
// #i98418#
void SwFootnotePortion::SetPreferredScriptType( SwFontScript nPreferredScriptType )
{
mbPreferredScriptTypeSet = true;
mnPreferredScriptType = nPreferredScriptType;
}
SwFieldPortion *SwQuoVadisPortion::Clone( const OUString &rExpand ) const
{
return new SwQuoVadisPortion( rExpand, m_aErgo );
}
SwQuoVadisPortion::SwQuoVadisPortion( const OUString &rExp, OUString aStr )
: SwFieldPortion( rExp ), m_aErgo(std::move(aStr))
{
SetLen(TextFrameIndex(0));
SetWhichPor( PortionType::QuoVadis );
}
bool SwQuoVadisPortion::Format( SwTextFormatInfo &rInf )
{
// First try; maybe the Text fits
CheckScript( rInf );
bool bFull = SwFieldPortion::Format( rInf );
SetLen(TextFrameIndex(0));
if( bFull )
{
// Second try; we make the String shorter
m_aExpand = "...";
bFull = SwFieldPortion::Format( rInf );
SetLen(TextFrameIndex(0));
if( bFull )
// Third try; we're done: we crush
Width(rInf.Width() - rInf.X());
// No multiline Fields for QuoVadis and ErgoSum
if( rInf.GetRest() )
{
delete rInf.GetRest();
rInf.SetRest( nullptr );
}
}
return bFull;
}
bool SwQuoVadisPortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
{
rText = m_aExpand;
// if this QuoVadisPortion has a follow, the follow is responsible for
// the ergo text.
if ( ! HasFollow() )
rText += m_aErgo;
return true;
}
void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Special( GetLen(), m_aExpand + m_aErgo, GetWhichPor() );
}
void SwQuoVadisPortion::Paint( const SwTextPaintInfo &rInf ) const
{
// We _always_ want to output per DrawStretchText, because nErgo
// can quickly switch
if( PrtWidth() )
{
rInf.DrawViewOpt( *this, PortionType::QuoVadis );
SwTextSlot aDiffText( &rInf, this, true, false );
SwFontSave aSave( rInf, m_pFont.get() );
rInf.DrawText( *this, rInf.GetLen(), true );
}
}
SwFieldPortion *SwErgoSumPortion::Clone( const OUString &rExpand ) const
{
return new SwErgoSumPortion( rExpand, std::u16string_view() );
}
SwErgoSumPortion::SwErgoSumPortion(const OUString &rExp, std::u16string_view rStr)
: SwFieldPortion( rExp )
{
SetLen(TextFrameIndex(0));
m_aExpand += rStr;
// One blank distance to the text
m_aExpand += " ";
SetWhichPor( PortionType::ErgoSum );
}
TextFrameIndex SwErgoSumPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
bool SwErgoSumPortion::Format( SwTextFormatInfo &rInf )
{
const bool bFull = SwFieldPortion::Format( rInf );
SetLen(TextFrameIndex(0));
rInf.SetErgoDone( true );
// No multiline Fields for QuoVadis and ErgoSum
if( bFull && rInf.GetRest() )
{
delete rInf.GetRest();
rInf.SetRest( nullptr );
}
// We return false in order to get some text into the current line,
// even if it's full (better than looping)
return false;
}
void SwParaPortion::SetErgoSumNum( const OUString& rErgo )
{
SwLineLayout *pLay = this;
while( pLay->GetNext() )
{
pLay = pLay->GetNext();
}
SwLinePortion *pPor = pLay;
SwQuoVadisPortion *pQuo = nullptr;
while( pPor && !pQuo )
{
if ( pPor->IsQuoVadisPortion() )
pQuo = static_cast<SwQuoVadisPortion*>(pPor);
pPor = pPor->GetNextPortion();
}
if( pQuo )
pQuo->SetNumber( rErgo );
}
/**
* Is called in SwTextFrame::Prepare()
*/
bool SwParaPortion::UpdateQuoVadis( std::u16string_view rQuo )
{
SwLineLayout *pLay = this;
while( pLay->GetNext() )
{
pLay = pLay->GetNext();
}
SwLinePortion *pPor = pLay;
SwQuoVadisPortion *pQuo = nullptr;
while( pPor && !pQuo )
{
if ( pPor->IsQuoVadisPortion() )
pQuo = static_cast<SwQuoVadisPortion*>(pPor);
pPor = pPor->GetNextPortion();
}
if( !pQuo )
return false;
return pQuo->GetQuoText() == rQuo;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */