1590 lines
55 KiB
C++
1590 lines
55 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 <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 ); // 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 )
|
||
{
|
||
bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
|
||
!pTmp->Lower()->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*>(pCont))->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 *const pDoc = &GetDoc();
|
||
if( SwLayouter::Collecting( pDoc, pSect, pFootnoteFrame ) )
|
||
{
|
||
if( !pSrcFrame )
|
||
{
|
||
SwFootnoteFrame *pNew = new SwFootnoteFrame(pDoc->GetDfltFrameFormat(),this,this,pFootnote);
|
||
SwNodeIndex aIdx( *pFootnote->GetStartNode(), 1 );
|
||
::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
|
||
pDoc->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 );
|
||
}
|
||
|
||
SwPosSize 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: */
|