1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
27 #include <IDocumentLayoutAccess.hxx>
28 #include <pagefrm.hxx>
29 #include <rootfrm.hxx>
31 #include <SwPortionHandler.hxx>
35 #include <ftninfo.hxx>
36 #include <charfmt.hxx>
38 #include <editeng/brushitem.hxx>
39 #include <editeng/charrotateitem.hxx>
41 #include <sortedobjs.hxx>
48 #include "itrform2.hxx"
50 #include <pagedesc.hxx>
51 #include "redlnitr.hxx"
52 #include <sectfrm.hxx>
53 #include <layouter.hxx>
54 #include <frmtool.hxx>
55 #include <ndindex.hxx>
56 #include <IDocumentSettingAccess.hxx>
57 #include <IDocumentRedlineAccess.hxx>
58 #include <swmodule.hxx>
59 #include <unotextrange.hxx>
60 #include <redline.hxx>
61 #include <editeng/colritem.hxx>
62 #include <editeng/udlnitem.hxx>
63 #include <editeng/crossedoutitem.hxx>
65 #include <com/sun/star/beans/XPropertySet.hpp>
66 #include <com/sun/star/awt/CharSet.hpp>
67 #include <com/sun/star/text/XTextRange.hpp>
69 using namespace ::com::sun::star
;
71 bool SwTextFrame::IsFootnoteNumFrame_() const
74 return false; // tdf#102073 first frame in cell doesn't have mpPrev set
75 const SwFootnoteFrame
* pFootnote
= FindFootnoteFrame()->GetMaster();
76 while( pFootnote
&& !pFootnote
->ContainsContent() )
77 pFootnote
= pFootnote
->GetMaster();
82 * Looks for the TextFrame matching the SwTextFootnote within a master-follow chain
84 SwTextFrame
*SwTextFrame::FindFootnoteRef( const SwTextFootnote
*pFootnote
)
86 SwTextFrame
*pFrame
= this;
87 const bool bFwd
= MapModelToView(&pFootnote
->GetTextNode(), pFootnote
->GetStart()) >= GetOffset();
90 if( SwFootnoteBossFrame::FindFootnote( pFrame
, pFootnote
) )
92 pFrame
= bFwd
? pFrame
->GetFollow() :
93 pFrame
->IsFollow() ? pFrame
->FindMaster() : nullptr;
98 void SwTextFrame::SetHasRotatedPortions(bool bHasRotatedPortions
)
100 mbHasRotatedPortions
= bHasRotatedPortions
;
104 void SwTextFrame::CalcFootnoteFlag(TextFrameIndex nStop
) // For testing the SplitFrame
106 void SwTextFrame::CalcFootnoteFlag()
112 const TextFrameIndex nEnd
= nStop
!= TextFrameIndex(COMPLETE_STRING
)
114 : GetFollow() ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING
);
116 const TextFrameIndex nEnd
= GetFollow()
117 ? GetFollow()->GetOffset()
118 : TextFrameIndex(COMPLETE_STRING
);
121 SwTextNode
const* pNode(nullptr);
122 sw::MergedAttrIter
iter(*this);
123 for (SwTextAttr
const* pHt
= iter
.NextAttr(&pNode
); pHt
; pHt
= iter
.NextAttr(&pNode
))
125 if ( pHt
->Which() == RES_TXTATR_FTN
)
127 TextFrameIndex
const nIdx(MapModelToView(pNode
, pHt
->GetStart()));
130 if( GetOffset() <= nIdx
)
139 bool SwTextFrame::CalcPrepFootnoteAdjust()
141 OSL_ENSURE( HasFootnote(), "Who´s calling me?" );
142 SwFootnoteBossFrame
*pBoss
= FindFootnoteBossFrame( true );
143 const SwFootnoteFrame
*pFootnote
= pBoss
->FindFirstFootnote( this );
144 if (pFootnote
&& FTNPOS_CHAPTER
!= GetDoc().GetFootnoteInfo().m_ePos
&&
145 ( !pBoss
->GetUpper()->IsSctFrame() ||
146 !static_cast<SwSectionFrame
*>(pBoss
->GetUpper())->IsFootnoteAtEnd() ) )
148 const SwFootnoteContFrame
*pCont
= pBoss
->FindFootnoteCont();
149 bool bReArrange
= true;
151 SwRectFnSet
aRectFnSet(this);
152 if ( pCont
&& aRectFnSet
.YDiff( aRectFnSet
.GetTop(pCont
->getFrameArea()),
153 aRectFnSet
.GetBottom(getFrameArea()) ) > 0 )
155 pBoss
->RearrangeFootnotes( aRectFnSet
.GetBottom(getFrameArea()), false,
156 pFootnote
->GetAttr() );
159 pFootnote
= pBoss
->FindFirstFootnote( this );
163 if( !pCont
|| !pFootnote
|| bReArrange
!= (pFootnote
->FindFootnoteBossFrame() == pBoss
) )
165 SwTextFormatInfo
aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
166 SwTextFormatter
aLine( this, &aInf
);
168 SetPara( nullptr ); // May be deleted!
177 * Local helper function. Checks if nLower should be taken as the boundary
180 static SwTwips
lcl_GetFootnoteLower( const SwTextFrame
* pFrame
, SwTwips nLower
)
182 // nLower is an absolute value. It denotes the bottom of the line
183 // containing the footnote.
184 SwRectFnSet
aRectFnSet(pFrame
);
186 OSL_ENSURE( !pFrame
->IsVertical() || !pFrame
->IsSwapped(),
187 "lcl_GetFootnoteLower with swapped frame" );
190 SwTwips nRet
= nLower
;
192 // Check if text is inside a table.
193 if ( pFrame
->IsInTab() )
195 // If pFrame is inside a table, we have to check if
196 // a) The table is not allowed to split or
197 // b) The table row is not allowed to split
199 // Inside a table, there are no footnotes,
200 // see SwFrame::FindFootnoteBossFrame. So we don't have to check
201 // the case that pFrame is inside a (footnote collecting) section
203 const SwFrame
* pRow
= pFrame
;
204 while( !pRow
->IsRowFrame() || !pRow
->GetUpper()->IsTabFrame() )
205 pRow
= pRow
->GetUpper();
206 const SwTabFrame
* pTabFrame
= static_cast<const SwTabFrame
*>(pRow
->GetUpper());
208 OSL_ENSURE( pTabFrame
&& pRow
&&
209 pRow
->GetUpper()->IsTabFrame(), "Upper of row should be tab" );
211 const bool bDontSplit
= !pTabFrame
->IsFollow() &&
212 !pTabFrame
->IsLayoutSplitAllowed();
216 nMin
= aRectFnSet
.GetBottom(pTabFrame
->getFrameArea());
217 else if ( !static_cast<const SwRowFrame
*>(pRow
)->IsRowSplitAllowed() )
218 nMin
= aRectFnSet
.GetBottom(pRow
->getFrameArea());
220 if ( nMin
&& aRectFnSet
.YDiff( nMin
, nLower
) > 0 )
223 nAdd
= aRectFnSet
.GetBottomMargin(*pRow
->GetUpper());
226 nAdd
= aRectFnSet
.GetBottomMargin(*pFrame
);
230 if ( aRectFnSet
.IsVert() )
236 // #i10770#: If there are fly frames anchored at previous paragraphs,
237 // the deadline should consider their lower borders.
238 const SwFrame
* pStartFrame
= pFrame
->GetUpper()->GetLower();
239 OSL_ENSURE( pStartFrame
, "Upper has no lower" );
240 SwTwips nFlyLower
= aRectFnSet
.IsVert() ? LONG_MAX
: 0;
241 while ( pStartFrame
!= pFrame
)
243 OSL_ENSURE( pStartFrame
, "Frame chain is broken" );
244 if ( pStartFrame
->GetDrawObjs() )
246 const SwSortedObjs
&rObjs
= *pStartFrame
->GetDrawObjs();
247 for (SwAnchoredObject
* pAnchoredObj
: rObjs
)
249 SwRect
aRect( pAnchoredObj
->GetObjRect() );
251 auto pFlyFrame
= pAnchoredObj
->DynCastFlyFrame();
253 pFlyFrame
->isFrameAreaDefinitionValid() )
255 const SwTwips nBottom
= aRectFnSet
.GetBottom(aRect
);
256 if ( aRectFnSet
.YDiff( nBottom
, nFlyLower
) > 0 )
262 pStartFrame
= pStartFrame
->GetNext();
265 if ( aRectFnSet
.IsVert() )
266 nRet
= std::min( nRet
, nFlyLower
);
268 nRet
= std::max( nRet
, nFlyLower
);
273 SwTwips
SwTextFrame::GetFootnoteLine( const SwTextFootnote
*pFootnote
) const
275 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
276 "SwTextFrame::GetFootnoteLine with swapped frame" );
278 SwTextFrame
*pThis
= const_cast<SwTextFrame
*>(this);
282 // #109071# GetFormatted() does not work here, because most probably
283 // the frame is currently locked. We return the previous value.
284 return pThis
->mnFootnoteLine
> 0 ?
285 pThis
->mnFootnoteLine
:
286 IsVertical() ? getFrameArea().Left() : getFrameArea().Bottom();
291 SwSwapIfNotSwapped
swap(const_cast<SwTextFrame
*>(this));
293 SwTextInfo
aInf( pThis
);
294 SwTextIter
aLine( pThis
, &aInf
);
295 TextFrameIndex
const nPos(MapModelToView(
296 &pFootnote
->GetTextNode(), pFootnote
->GetStart()));
297 aLine
.CharToLine( nPos
);
299 nRet
= aLine
.Y() + aLine
.GetLineHeight();
301 nRet
= SwitchHorizontalToVertical( nRet
);
304 nRet
= lcl_GetFootnoteLower( pThis
, nRet
);
306 pThis
->mnFootnoteLine
= nRet
;
311 * Calculates the maximum reachable height for the TextFrame in the Footnote Area.
312 * The cell's bottom margin with the Footnote Reference limit's this height.
314 SwTwips
SwTextFrame::GetFootnoteFrameHeight_() const
316 OSL_ENSURE( !IsFollow() && IsInFootnote(), "SwTextFrame::SetFootnoteLine: moon walk" );
318 const SwFootnoteFrame
*pFootnoteFrame
= FindFootnoteFrame();
319 const SwTextFrame
*pRef
= static_cast<const SwTextFrame
*>(pFootnoteFrame
->GetRef());
320 const SwFootnoteBossFrame
*pBoss
= FindFootnoteBossFrame();
321 if( pBoss
!= pRef
->FindFootnoteBossFrame( !pFootnoteFrame
->GetAttr()->
322 GetFootnote().IsEndNote() ) )
325 SwSwapIfSwapped
swap(const_cast<SwTextFrame
*>(this));
327 SwTwips nHeight
= pRef
->IsInFootnoteConnect() ?
328 1 : pRef
->GetFootnoteLine( pFootnoteFrame
->GetAttr() );
331 // As odd as it may seem: the first Footnote on the page may not touch the
332 // Footnote Reference, when entering text in the Footnote Area.
333 const SwFrame
*pCont
= pFootnoteFrame
->GetUpper();
335 // Height within the Container which we're allowed to consume anyways
336 SwRectFnSet
aRectFnSet(pCont
);
337 SwTwips nTmp
= aRectFnSet
.YDiff( aRectFnSet
.GetPrtBottom(*pCont
),
338 aRectFnSet
.GetTop(getFrameArea()) );
340 #if OSL_DEBUG_LEVEL > 0
343 bool bInvalidPos
= false;
344 const SwLayoutFrame
* pTmp
= GetUpper();
345 while( !bInvalidPos
&& pTmp
)
347 bInvalidPos
= !pTmp
->isFrameAreaPositionValid() ||
348 !pTmp
->Lower()->isFrameAreaPositionValid();
351 pTmp
= pTmp
->GetUpper();
353 OSL_ENSURE( bInvalidPos
, "Hanging below FootnoteCont" );
357 if ( aRectFnSet
.YDiff( aRectFnSet
.GetTop(pCont
->getFrameArea()), nHeight
) > 0 )
359 // Growth potential of the container
360 if ( !pRef
->IsInFootnoteConnect() )
362 SwSaveFootnoteHeight
aSave( const_cast<SwFootnoteBossFrame
*>(pBoss
), nHeight
);
363 nHeight
= const_cast<SwFootnoteContFrame
*>(static_cast<const SwFootnoteContFrame
*>(pCont
))->Grow( LONG_MAX
, true );
366 nHeight
= const_cast<SwFootnoteContFrame
*>(static_cast<const SwFootnoteContFrame
*>(pCont
))->Grow( LONG_MAX
, true );
373 { // The container has to shrink
374 nTmp
+= aRectFnSet
.YDiff( aRectFnSet
.GetTop(pCont
->getFrameArea()), nHeight
);
385 SwTextFrame
*SwTextFrame::FindQuoVadisFrame()
387 // Check whether we're in a FootnoteFrame
388 if( GetIndPrev() || !IsInFootnote() )
391 // To the preceding FootnoteFrame
392 SwFootnoteFrame
*pFootnoteFrame
= FindFootnoteFrame()->GetMaster();
393 if( !pFootnoteFrame
)
396 // Now the last Content
397 SwContentFrame
*pCnt
= pFootnoteFrame
->ContainsContent();
400 SwContentFrame
*pLast
;
403 pCnt
= pCnt
->GetNextContentFrame();
404 } while( pCnt
&& pFootnoteFrame
->IsAnLower( pCnt
) );
405 return static_cast<SwTextFrame
*>(pLast
);
408 void SwTextFrame::RemoveFootnote(TextFrameIndex
const nStart
, TextFrameIndex
const nLen
)
410 if ( !IsFootnoteAllowed() )
413 bool bRollBack
= nLen
!= TextFrameIndex(COMPLETE_STRING
);
415 SwTextFrame
* pSource
;
418 nEnd
= nStart
+ nLen
;
419 pSource
= GetFollow();
425 nEnd
= TextFrameIndex(COMPLETE_STRING
);
429 SwPageFrame
* pUpdate
= nullptr;
430 bool bRemove
= false;
431 SwFootnoteBossFrame
*pFootnoteBoss
= nullptr;
432 SwFootnoteBossFrame
*pEndBoss
= nullptr;
433 bool bFootnoteEndDoc
= FTNPOS_CHAPTER
== GetDoc().GetFootnoteInfo().m_ePos
;
434 SwTextNode
const* pNode(nullptr);
435 sw::MergedAttrIterReverse
iter(*this);
436 for (SwTextAttr
const* pHt
= iter
.PrevAttr(&pNode
); pHt
; pHt
= iter
.PrevAttr(&pNode
))
438 if (RES_TXTATR_FTN
!= pHt
->Which())
441 TextFrameIndex
const nIdx(MapModelToView(pNode
, pHt
->GetStart()));
447 SwTextFootnote
const*const pFootnote(static_cast<SwTextFootnote
const*>(pHt
));
448 const bool bEndn
= pFootnote
->GetFootnote().IsEndNote();
453 pEndBoss
= pSource
->FindFootnoteBossFrame();
459 pFootnoteBoss
= pSource
->FindFootnoteBossFrame( true );
460 if( pFootnoteBoss
->GetUpper()->IsSctFrame() )
462 SwSectionFrame
* pSect
= static_cast<SwSectionFrame
*>(
463 pFootnoteBoss
->GetUpper());
464 if (pSect
->IsFootnoteAtEnd())
465 bFootnoteEndDoc
= false;
470 // We don't delete, but move instead.
471 // Three cases are to be considered:
472 // 1) There's neither Follow nor PrevFollow:
473 // -> RemoveFootnote() (maybe even a OSL_ENSURE(value))
475 // 2) nStart > GetOffset, I have a Follow
476 // -> Footnote moves into Follow
478 // 3) nStart < GetOffset, I am a Follow
479 // -> Footnote moves into the PrevFollow
481 // Both need to be on one Page/in one Column
482 SwFootnoteFrame
*pFootnoteFrame
= SwFootnoteBossFrame::FindFootnote(pSource
, pFootnote
);
486 const bool bEndDoc
= bEndn
|| bFootnoteEndDoc
;
489 while (pFootnoteFrame
)
491 pFootnoteFrame
->SetRef( this );
492 pFootnoteFrame
= pFootnoteFrame
->GetFollow();
496 else if (GetFollow())
498 SwContentFrame
*pDest
= GetFollow();
499 while (pDest
->GetFollow() && static_cast<SwTextFrame
*>(pDest
->
500 GetFollow())->GetOffset() <= nIdx
)
501 pDest
= pDest
->GetFollow();
502 OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote(
503 pDest
,pFootnote
),"SwTextFrame::RemoveFootnote: footnote exists");
505 // Never deregister; always move
507 !pFootnoteFrame
->FindFootnoteBossFrame()->IsBefore(pDest
->FindFootnoteBossFrame(!bEndn
))
510 SwPageFrame
* pTmp
= pFootnoteFrame
->FindPageFrame();
511 if( pUpdate
&& pUpdate
!= pTmp
)
512 pUpdate
->UpdateFootnoteNum();
514 while ( pFootnoteFrame
)
516 pFootnoteFrame
->SetRef( pDest
);
517 pFootnoteFrame
= pFootnoteFrame
->GetFollow();
522 pFootnoteBoss
->MoveFootnotes( this, pDest
, pFootnote
);
525 static_cast<SwTextFrame
*>(pDest
)->SetFootnote( true );
527 OSL_ENSURE( SwFootnoteBossFrame::FindFootnote( pDest
,
528 pFootnote
),"SwTextFrame::RemoveFootnote: footnote ChgRef failed");
532 if (!bEndDoc
|| ( bEndn
&& pEndBoss
->IsInSct() &&
533 !SwLayouter::Collecting( &GetDoc(),
534 pEndBoss
->FindSctFrame(), nullptr ) ))
537 pEndBoss
->RemoveFootnote( this, pFootnote
);
539 pFootnoteBoss
->RemoveFootnote( this, pFootnote
);
540 bRemove
= bRemove
|| !bEndDoc
;
541 OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote( this, pFootnote
),
542 "SwTextFrame::RemoveFootnote: can't get off that footnote" );
549 pUpdate
->UpdateFootnoteNum();
551 // We break the oscillation
552 if (bRemove
&& !bFootnoteEndDoc
&& HasPara())
558 // We call the RemoveFootnote from within the FindBreak, because the last line is
559 // to be passed to the Follow. The Offset of the Follow is, however, outdated;
560 // it'll be set soon. CalcFntFlag depends on a correctly set Follow Offset.
561 // Therefore we temporarily calculate the Follow Offset here
562 TextFrameIndex
nOldOfst(COMPLETE_STRING
);
563 if( HasFollow() && nStart
> GetOffset() )
565 nOldOfst
= GetFollow()->GetOffset();
566 GetFollow()->ManipOfst(nStart
+ (bRollBack
? nLen
: TextFrameIndex(0)));
568 pSource
->CalcFootnoteFlag();
569 if (nOldOfst
< TextFrameIndex(COMPLETE_STRING
))
570 GetFollow()->ManipOfst( nOldOfst
);
575 * We basically only have two possibilities:
577 * a) The Footnote is already present
578 * => we move it, if another pSrcFrame has been found
580 * b) The Footnote is not present
581 * => we have it created for us
583 * Whether the Footnote ends up on our Page/Column, doesn't matter in this
586 * Optimization for Endnotes.
588 * Another problem: if the Deadline falls within the Footnote Area, we need
589 * to move the Footnote.
591 * @returns false on any type of error
593 void SwTextFrame::ConnectFootnote( SwTextFootnote
*pFootnote
, const SwTwips nDeadLine
)
595 OSL_ENSURE( !IsVertical() || !IsSwapped(),
596 "SwTextFrame::ConnectFootnote with swapped frame" );
599 mbInFootnoteConnect
= true; // Just reset!
600 // See if pFootnote is an endnote on a separate endnote page.
601 const IDocumentSettingAccess
& rSettings
= GetDoc().getIDocumentSettingAccess();
602 const bool bContinuousEndnotes
= rSettings
.get(DocumentSettingId::CONTINUOUS_ENDNOTES
);
603 const bool bEnd
= pFootnote
->GetFootnote().IsEndNote();
605 // We want to store this value, because it is needed as a fallback
606 // in GetFootnoteLine(), if there is no paragraph information available
607 mnFootnoteLine
= nDeadLine
;
609 // We always need a parent (Page/Column)
610 SwSectionFrame
*pSect
;
611 SwContentFrame
*pContent
= this;
612 if( bEnd
&& IsInSct() )
614 pSect
= FindSctFrame();
615 if( pSect
->IsEndnAtEnd() )
616 pContent
= pSect
->FindLastContent( SwFindMode::EndNote
);
621 SwFootnoteBossFrame
*pBoss
= pContent
->FindFootnoteBossFrame( !bEnd
);
623 pSect
= pBoss
->FindSctFrame();
624 bool bDocEnd
= bEnd
? !( pSect
&& pSect
->IsEndnAtEnd() ) :
625 ( !( pSect
&& pSect
->IsFootnoteAtEnd() ) &&
626 FTNPOS_CHAPTER
== GetDoc().GetFootnoteInfo().m_ePos
);
628 // Footnote can be registered with the Follow
629 SwContentFrame
*pSrcFrame
= FindFootnoteRef( pFootnote
);
633 if ((pSect
|| bContinuousEndnotes
) && pSrcFrame
)
635 SwFootnoteFrame
*pFootnoteFrame
= SwFootnoteBossFrame::FindFootnote( pSrcFrame
, pFootnote
);
636 if (pFootnoteFrame
&& (pFootnoteFrame
->IsInSct() || bContinuousEndnotes
))
638 // We either have a foot/endnote that goes to the end of the section or are in Word
639 // compatibility mode where endnotes go to the end of the document. Handle both
640 // cases by removing the footnote here, then later appending them to the correct
641 // last page of the document or section.
642 pBoss
->RemoveFootnote( pSrcFrame
, pFootnote
);
647 else if( bEnd
&& pSect
)
649 SwFootnoteFrame
*pFootnoteFrame
= pSrcFrame
? SwFootnoteBossFrame::FindFootnote( pSrcFrame
, pFootnote
) : nullptr;
650 if( pFootnoteFrame
&& !pFootnoteFrame
->GetUpper() )
651 pFootnoteFrame
= nullptr;
652 SwDoc
*const pDoc
= &GetDoc();
653 if( SwLayouter::Collecting( pDoc
, pSect
, pFootnoteFrame
) )
657 SwFootnoteFrame
*pNew
= new SwFootnoteFrame(pDoc
->GetDfltFrameFormat(),this,this,pFootnote
);
658 SwNodeIndex
aIdx( *pFootnote
->GetStartNode(), 1 );
659 ::InsertCnt_( pNew
, pDoc
, aIdx
.GetIndex() );
660 pDoc
->getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew
);
662 else if( pSrcFrame
!= this )
663 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame
, pFootnote
, this );
664 mbInFootnoteConnect
= false;
667 else if (pSrcFrame
&& pFootnoteFrame
)
669 SwFootnoteBossFrame
*pFootnoteBoss
= pFootnoteFrame
->FindFootnoteBossFrame();
670 if( !pFootnoteBoss
->IsInSct() ||
671 pFootnoteBoss
->ImplFindSctFrame()->GetSection()!=pSect
->GetSection() )
673 pBoss
->RemoveFootnote( pSrcFrame
, pFootnote
);
679 if( bDocEnd
|| bEnd
)
682 pBoss
->AppendFootnote( this, pFootnote
);
683 else if( pSrcFrame
!= this )
684 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame
, pFootnote
, this );
685 mbInFootnoteConnect
= false;
689 SwSaveFootnoteHeight
aHeight( pBoss
, nDeadLine
);
691 if( !pSrcFrame
) // No Footnote was found at all
692 pBoss
->AppendFootnote( this, pFootnote
);
695 SwFootnoteFrame
*pFootnoteFrame
= SwFootnoteBossFrame::FindFootnote( pSrcFrame
, pFootnote
);
696 SwFootnoteBossFrame
*pFootnoteBoss
= pFootnoteFrame
->FindFootnoteBossFrame();
698 bool bBrutal
= false;
700 if( pFootnoteBoss
== pBoss
) // Ref and Footnote are on the same Page/Column
702 SwFrame
*pCont
= pFootnoteFrame
->GetUpper();
704 SwRectFnSet
aRectFnSet(pCont
);
705 tools::Long nDiff
= aRectFnSet
.YDiff( aRectFnSet
.GetTop(pCont
->getFrameArea()),
710 // If the Footnote has been registered to a Follow, we need to
712 if ( pSrcFrame
!= this )
713 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame
, pFootnote
, this );
715 // We have some room left, so the Footnote can grow
716 if ( pFootnoteFrame
->GetFollow() && nDiff
> 0 )
718 SwFrameDeleteGuard
aDeleteGuard(pCont
);
719 SwTwips nHeight
= aRectFnSet
.GetHeight(pCont
->getFrameArea());
720 pBoss
->RearrangeFootnotes( nDeadLine
, false, pFootnote
);
723 SwViewShell
*pSh
= getRootFrame()->GetCurrShell();
724 if ( pSh
&& nHeight
== aRectFnSet
.GetHeight(pCont
->getFrameArea()) )
725 // So that we don't miss anything
726 pSh
->InvalidateWindows( pCont
->getFrameArea() );
728 mbInFootnoteConnect
= false;
736 // Ref and Footnote are not on one Page; attempt to move is necessary
737 SwFrame
* pTmp
= this;
738 while( pTmp
->GetNext() && pSrcFrame
!= pTmp
)
739 pTmp
= pTmp
->GetNext();
740 if( pSrcFrame
== pTmp
)
743 { // If our Parent is in a column Area, but the Page already has a
744 // FootnoteContainer, we can only brute force it
745 if( pSect
&& pSect
->FindFootnoteBossFrame( !bEnd
)->FindFootnoteCont() )
748 else if ( !pFootnoteFrame
->GetPrev() ||
749 pFootnoteBoss
->IsBefore( pBoss
)
752 SwFootnoteBossFrame
*pSrcBoss
= pSrcFrame
->FindFootnoteBossFrame( !bEnd
);
753 pSrcBoss
->MoveFootnotes( pSrcFrame
, this, pFootnote
);
756 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame
, pFootnote
, this );
760 // The brute force method: Remove Footnote and append.
761 // We need to call SetFootnoteDeadLine(), as we can more easily adapt the
762 // nMaxFootnoteHeight after RemoveFootnote
765 pBoss
->RemoveFootnote( pSrcFrame
, pFootnote
, false );
766 std::unique_ptr
<SwSaveFootnoteHeight
> pHeight(bEnd
? nullptr : new SwSaveFootnoteHeight( pBoss
, nDeadLine
));
767 pBoss
->AppendFootnote( this, pFootnote
);
771 // In column Areas, that not yet reach the Page's border a RearrangeFootnotes is not
772 // useful yet, as the Footnote container has not yet been calculated
773 if( !pSect
|| !pSect
->Growable() )
775 // Validate environment, to avoid oscillation
776 SwSaveFootnoteHeight
aNochmal( pBoss
, nDeadLine
);
778 pBoss
->RearrangeFootnotes( nDeadLine
, true );
781 else if( pSect
->IsFootnoteAtEnd() )
787 mbInFootnoteConnect
= false;
791 * The portion for the Footnote Reference in the Text
793 SwFootnotePortion
*SwTextFormatter::NewFootnotePortion( SwTextFormatInfo
&rInf
,
796 OSL_ENSURE( ! m_pFrame
->IsVertical() || m_pFrame
->IsSwapped(),
797 "NewFootnotePortion with unswapped frame" );
799 SwTextFootnote
*pFootnote
= static_cast<SwTextFootnote
*>(pHint
);
801 if( !m_pFrame
->IsFootnoteAllowed() )
802 return new SwFootnotePortion("", pFootnote
);
804 const SwFormatFootnote
& rFootnote
= pFootnote
->GetFootnote();
805 SwDoc
*const pDoc
= &m_pFrame
->GetDoc();
808 return new SwFootnotePortion(rFootnote
.GetViewNumStr(*pDoc
, m_pFrame
->getRootFrame()), pFootnote
);
810 SwSwapIfSwapped
swap(m_pFrame
);
814 sal_uInt16 nOldReal
= m_pCurr
->GetRealHeight();
815 sal_uInt16 nOldAscent
= m_pCurr
->GetAscent();
816 sal_uInt16 nOldHeight
= m_pCurr
->Height();
818 nReal
= m_pCurr
->GetRealHeight();
819 if( nReal
< nOldReal
)
821 m_pCurr
->SetRealHeight( nOldReal
);
822 m_pCurr
->Height( nOldHeight
);
823 m_pCurr
->SetAscent( nOldAscent
);
826 SwTwips nLower
= Y() + nReal
;
828 const bool bVertical
= m_pFrame
->IsVertical();
830 nLower
= m_pFrame
->SwitchHorizontalToVertical( nLower
);
832 nLower
= lcl_GetFootnoteLower( m_pFrame
, nLower
);
835 // The Connect does not do anything useful in this case, but will
836 // mostly throw away the Footnote and create it anew.
837 if( !rInf
.IsQuick() )
838 m_pFrame
->ConnectFootnote( pFootnote
, nLower
);
840 SwTextFrame
*pScrFrame
= m_pFrame
->FindFootnoteRef( pFootnote
);
841 SwFootnoteBossFrame
*pBoss
= m_pFrame
->FindFootnoteBossFrame( !rFootnote
.IsEndNote() );
842 SwFootnoteFrame
*pFootnoteFrame
= nullptr;
844 pFootnoteFrame
= SwFootnoteBossFrame::FindFootnote( pScrFrame
, pFootnote
);
846 // We see whether our Append has caused some Footnote to
847 // still be on the Page/Column. If not, our line disappears too,
848 // which will lead to the following undesired behaviour:
849 // Footnote1 still fits onto the Page/Column, but Footnote2 doesn't.
850 // The Footnote2 Reference remains on the Page/Column. The Footnote itself
851 // is on the next Page/Column.
853 // Exception: If the Page/Column cannot accommodate another line,
854 // the Footnote Reference should be moved to the next one.
855 if( !rFootnote
.IsEndNote() )
857 SwSectionFrame
*pSct
= pBoss
->FindSctFrame();
858 bool bAtSctEnd
= pSct
&& pSct
->IsFootnoteAtEnd();
859 if( FTNPOS_CHAPTER
!= pDoc
->GetFootnoteInfo().m_ePos
|| bAtSctEnd
)
861 SwFrame
* pFootnoteCont
= pBoss
->FindFootnoteCont();
862 // If the Parent is within an Area, it can only be a Column of this
863 // Area. If this one is not the first Column, we can avoid it.
864 if( !m_pFrame
->IsInTab() && ( GetLineNr() > 1 || m_pFrame
->GetPrev() ||
865 ( !bAtSctEnd
&& m_pFrame
->GetIndPrev() ) ||
866 ( pSct
&& pBoss
->GetPrev() ) ) )
870 rInf
.SetStop( true );
875 // There must not be any Footnote Containers in column Areas and at the same time on the
877 if( pSct
&& !bAtSctEnd
) // Is the Container in a (column) Area?
879 SwFootnoteBossFrame
* pTmp
= pBoss
->FindSctFrame()->FindFootnoteBossFrame( true );
880 SwFootnoteContFrame
* pFootnoteC
= pTmp
->FindFootnoteCont();
883 SwFootnoteFrame
* pTmpFrame
= static_cast<SwFootnoteFrame
*>(pFootnoteC
->Lower());
884 if( pTmpFrame
&& *pTmpFrame
< pFootnote
)
886 rInf
.SetStop( true );
891 // Is this the last Line that fits?
892 SwTwips nTmpBot
= Y() + nReal
* 2;
895 nTmpBot
= m_pFrame
->SwitchHorizontalToVertical( nTmpBot
);
897 SwRectFnSet
aRectFnSet(pFootnoteCont
);
899 const tools::Long nDiff
= aRectFnSet
.YDiff(
900 aRectFnSet
.GetTop(pFootnoteCont
->getFrameArea()),
903 if( pScrFrame
&& nDiff
< 0 )
907 SwFootnoteBossFrame
*pFootnoteBoss
= pFootnoteFrame
->FindFootnoteBossFrame();
908 if( pFootnoteBoss
!= pBoss
)
910 // We're in the last Line and the Footnote has moved
911 // to another Page. We also want to be on that Page!
912 rInf
.SetStop( true );
921 // Finally: Create FootnotePortion and exit ...
922 SwFootnotePortion
*pRet
= new SwFootnotePortion(
923 rFootnote
.GetViewNumStr(*pDoc
, m_pFrame
->getRootFrame()),
925 rInf
.SetFootnoteInside( true );
931 * The portion for the Footnote Numbering in the Footnote Area
933 SwNumberPortion
*SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo
const &rInf
) const
935 OSL_ENSURE( m_pFrame
->IsInFootnote() && !m_pFrame
->GetIndPrev() && !rInf
.IsFootnoteDone(),
936 "This is the wrong place for a ftnnumber" );
937 if( rInf
.GetTextStart() != m_nStart
||
938 rInf
.GetTextStart() != rInf
.GetIdx() )
941 const SwFootnoteFrame
* pFootnoteFrame
= m_pFrame
->FindFootnoteFrame();
942 const SwTextFootnote
* pFootnote
= pFootnoteFrame
->GetAttr();
944 // Aha! So we're in the Footnote Area!
945 SwFormatFootnote
& rFootnote
= const_cast<SwFormatFootnote
&>(pFootnote
->GetFootnote());
947 SwDoc
*const pDoc
= &m_pFrame
->GetDoc();
948 OUString
aFootnoteText(rFootnote
.GetViewNumStr(*pDoc
, m_pFrame
->getRootFrame(), true));
950 const SwEndNoteInfo
* pInfo
;
951 if( rFootnote
.IsEndNote() )
952 pInfo
= &pDoc
->GetEndNoteInfo();
954 pInfo
= &pDoc
->GetFootnoteInfo();
956 const SwAttrSet
* pParSet
= &rInf
.GetCharAttr();
957 const IDocumentSettingAccess
* pIDSA
= &pDoc
->getIDocumentSettingAccess();
958 std::unique_ptr
<SwFont
> pNumFnt(new SwFont( pParSet
, pIDSA
));
961 // Underline style of paragraph font should not be considered
962 // Overline style of paragraph font should not be considered
963 // Weight style of paragraph font should not be considered
964 // Posture style of paragraph font should not be considered
965 // See also #i18463# and SwTextFormatter::NewNumberPortion()
966 pNumFnt
->SetUnderline( LINESTYLE_NONE
);
967 pNumFnt
->SetOverline( LINESTYLE_NONE
);
968 pNumFnt
->SetItalic( ITALIC_NONE
, SwFontScript::Latin
);
969 pNumFnt
->SetItalic( ITALIC_NONE
, SwFontScript::CJK
);
970 pNumFnt
->SetItalic( ITALIC_NONE
, SwFontScript::CTL
);
971 pNumFnt
->SetWeight( WEIGHT_NORMAL
, SwFontScript::Latin
);
972 pNumFnt
->SetWeight( WEIGHT_NORMAL
, SwFontScript::CJK
);
973 pNumFnt
->SetWeight( WEIGHT_NORMAL
, SwFontScript::CTL
);
975 const auto xAnchor
= rFootnote
.getAnchor(*pDoc
);
976 uno::Reference
<beans::XPropertySet
> xAnchorProps(xAnchor
, uno::UNO_QUERY
);
977 if (xAnchorProps
.is())
979 auto aAny
= xAnchorProps
->getPropertyValue("CharFontCharSet");
981 if ((aAny
>>= eCharSet
) && eCharSet
== awt::CharSet::SYMBOL
)
984 aAny
= xAnchorProps
->getPropertyValue("CharFontName");
985 if (aAny
>>= aFontName
)
987 pNumFnt
->SetName(aFontName
, SwFontScript::Latin
);
988 pNumFnt
->SetName(aFontName
, SwFontScript::CJK
);
989 pNumFnt
->SetName(aFontName
, SwFontScript::CTL
);
990 pNumFnt
->SetCharSet(RTL_TEXTENCODING_SYMBOL
, SwFontScript::Latin
);
991 pNumFnt
->SetCharSet(RTL_TEXTENCODING_SYMBOL
, SwFontScript::CJK
);
992 pNumFnt
->SetCharSet(RTL_TEXTENCODING_SYMBOL
, SwFontScript::CTL
);
997 const SwAttrSet
& rSet
= pInfo
->GetCharFormat(*pDoc
)->GetAttrSet();
998 pNumFnt
->SetDiffFnt(&rSet
, pIDSA
);
999 pNumFnt
->SetVertical( pNumFnt
->GetOrientation(), m_pFrame
->IsVertical() );
1001 // tdf#85610 apply redline coloring to the footnote numbering in the footnote area
1002 SwUnoInternalPaM
aPam(*pDoc
);
1003 if ( ::sw::XTextRangeToSwPaM(aPam
, xAnchor
) )
1005 SwRedlineTable::size_type nRedlinePos
= 0;
1006 const SwRedlineTable
& rTable
= pDoc
->getIDocumentRedlineAccess().GetRedlineTable();
1007 const SwRangeRedline
* pRedline
= rTable
.FindAtPosition( *aPam
.Start(), nRedlinePos
);
1010 SwAttrPool
& rPool
= pDoc
->GetAttrPool();
1011 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aSet(rPool
);
1013 std::size_t aAuthor
= (1 < pRedline
->GetStackCount())
1014 ? pRedline
->GetAuthor( 1 )
1015 : pRedline
->GetAuthor();
1017 if ( RedlineType::Delete
== pRedline
->GetType() )
1018 SW_MOD()->GetDeletedAuthorAttr(aAuthor
, aSet
);
1020 SW_MOD()->GetInsertAuthorAttr(aAuthor
, aSet
);
1022 if (const SvxColorItem
* pItem
= aSet
.GetItemIfSet(RES_CHRATR_COLOR
))
1023 pNumFnt
->SetColor(pItem
->GetValue());
1024 if (const SvxUnderlineItem
* pItem
= aSet
.GetItemIfSet(RES_CHRATR_UNDERLINE
))
1025 pNumFnt
->SetUnderline(pItem
->GetLineStyle());
1026 if (const SvxCrossedOutItem
* pItem
= aSet
.GetItemIfSet(RES_CHRATR_CROSSEDOUT
))
1027 pNumFnt
->SetStrikeout( pItem
->GetStrikeout() );
1031 SwFootnoteNumPortion
* pNewPor
= new SwFootnoteNumPortion( aFootnoteText
, std::move(pNumFnt
) );
1032 pNewPor
->SetLeft( !m_pFrame
->IsRightToLeft() );
1036 static OUString
lcl_GetPageNumber( const SwPageFrame
* pPage
)
1038 OSL_ENSURE( pPage
, "GetPageNumber: Homeless TextFrame" );
1039 const sal_uInt16 nVirtNum
= pPage
->GetVirtPageNum();
1040 const SvxNumberType
& rNum
= pPage
->GetPageDesc()->GetNumType();
1041 return rNum
.GetNumStr( nVirtNum
);
1044 SwErgoSumPortion
*SwTextFormatter::NewErgoSumPortion( SwTextFormatInfo
const &rInf
) const
1046 // We cannot assume we're a Follow
1047 if( !m_pFrame
->IsInFootnote() || m_pFrame
->GetPrev() ||
1048 rInf
.IsErgoDone() || rInf
.GetIdx() != m_pFrame
->GetOffset() ||
1049 m_pFrame
->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
1052 // we are in the footnote container
1053 const SwFootnoteInfo
&rFootnoteInfo
= m_pFrame
->GetDoc().GetFootnoteInfo();
1054 SwTextFrame
*pQuoFrame
= m_pFrame
->FindQuoVadisFrame();
1057 const SwPageFrame
* pPage
= m_pFrame
->FindPageFrame();
1058 const SwPageFrame
* pQuoPage
= pQuoFrame
->FindPageFrame();
1059 if( pPage
== pQuoFrame
->FindPageFrame() )
1060 return nullptr; // If the QuoVadis is on the same Column/Page
1061 const OUString aPage
= lcl_GetPageNumber( pPage
);
1062 SwParaPortion
*pPara
= pQuoFrame
->GetPara();
1064 pPara
->SetErgoSumNum( aPage
);
1065 if( rFootnoteInfo
.m_aErgoSum
.isEmpty() )
1067 SwErgoSumPortion
*pErgo
= new SwErgoSumPortion( rFootnoteInfo
.m_aErgoSum
,
1068 lcl_GetPageNumber( pQuoPage
) );
1072 TextFrameIndex
SwTextFormatter::FormatQuoVadis(TextFrameIndex
const nOffset
)
1074 OSL_ENSURE( ! m_pFrame
->IsVertical() || ! m_pFrame
->IsSwapped(),
1075 "SwTextFormatter::FormatQuoVadis with swapped frame" );
1077 if( !m_pFrame
->IsInFootnote() || m_pFrame
->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
1080 const SwFrame
* pErgoFrame
= m_pFrame
->FindFootnoteFrame()->GetFollow();
1081 if( !pErgoFrame
&& m_pFrame
->HasFollow() )
1082 pErgoFrame
= m_pFrame
->GetFollow();
1086 if( pErgoFrame
== m_pFrame
->GetNext() )
1088 SwFrame
*pCol
= m_pFrame
->FindColFrame();
1089 while( pCol
&& !pCol
->GetNext() )
1090 pCol
= pCol
->GetUpper()->FindColFrame();
1096 const SwPageFrame
* pPage
= m_pFrame
->FindPageFrame();
1097 const SwPageFrame
* pErgoPage
= pErgoFrame
->FindPageFrame();
1098 if( pPage
== pErgoPage
)
1099 return nOffset
; // If the ErgoSum is on the same Page
1102 SwTextFormatInfo
&rInf
= GetInfo();
1103 const SwFootnoteInfo
&rFootnoteInfo
= m_pFrame
->GetDoc().GetFootnoteInfo();
1104 if( rFootnoteInfo
.m_aQuoVadis
.isEmpty() )
1107 // A remark on QuoVadis/ErgoSum:
1108 // We use the Font set for the Paragraph for these texts.
1109 // Thus, we initialize:
1110 // TODO: ResetFont();
1112 SeekStartAndChg( rInf
, true );
1113 if( GetRedln() && m_pCurr
->HasRedline() )
1115 std::pair
<SwTextNode
const*, sal_Int32
> const pos(
1116 GetTextFrame()->MapViewToModel(nOffset
));
1117 GetRedln()->Seek(*m_pFont
, pos
.first
->GetIndex(), pos
.second
, 0);
1120 // A tricky special case: Flyfrms extend into the Line and are at the
1121 // position we want to insert the Quovadis text
1122 // Let's see if it is that bad indeed:
1123 SwLinePortion
*pPor
= m_pCurr
->GetFirstPortion();
1124 sal_uInt16 nLastLeft
= 0;
1127 if ( pPor
->IsFlyPortion() )
1128 nLastLeft
= static_cast<SwFlyPortion
*>(pPor
)->GetFix() +
1129 static_cast<SwFlyPortion
*>(pPor
)->Width();
1130 pPor
= pPor
->GetNextPortion();
1133 // The old game all over again: we want the Line to wrap around
1134 // at a certain point, so we adjust the width.
1135 // nLastLeft is now basically the right margin
1136 const sal_uInt16 nOldRealWidth
= rInf
.RealWidth();
1137 rInf
.RealWidth( nOldRealWidth
- nLastLeft
);
1139 OUString aErgo
= lcl_GetPageNumber( pErgoFrame
->FindPageFrame() );
1140 SwQuoVadisPortion
*pQuo
= new SwQuoVadisPortion(rFootnoteInfo
.m_aQuoVadis
, aErgo
);
1141 pQuo
->SetAscent( rInf
.GetAscent() );
1142 pQuo
->Height( rInf
.GetTextHeight() );
1143 pQuo
->Format( rInf
);
1144 sal_uInt16 nQuoWidth
= pQuo
->Width();
1145 SwLinePortion
* pCurrPor
= pQuo
;
1147 while ( rInf
.GetRest() )
1149 SwLinePortion
* pFollow
= rInf
.GetRest();
1150 rInf
.SetRest( nullptr );
1151 pCurrPor
->Move( rInf
);
1153 OSL_ENSURE( pFollow
->IsQuoVadisPortion(),
1154 "Quo Vadis, rest of QuoVadisPortion" );
1156 // format the rest and append it to the other QuoVadis parts
1157 pFollow
->Format( rInf
);
1158 nQuoWidth
= nQuoWidth
+ pFollow
->Width();
1160 pCurrPor
->Append( pFollow
);
1164 Right( Right() - nQuoWidth
);
1166 TextFrameIndex nRet
;
1168 SwSwapIfNotSwapped
swap(m_pFrame
);
1170 nRet
= FormatLine( m_nStart
);
1173 Right( rInf
.Left() + nOldRealWidth
- 1 );
1175 nLastLeft
= nOldRealWidth
- m_pCurr
->Width();
1178 // It's possible that there's a Margin Portion at the end, which would
1179 // just cause a lot of trouble, when respanning
1180 pPor
= m_pCurr
->FindLastPortion();
1181 SwGluePortion
*pGlue
= pPor
->IsMarginPortion() ? static_cast<SwMarginPortion
*>(pPor
) : nullptr;
1186 pGlue
->SetLen(TextFrameIndex(0));
1187 pGlue
->SetAscent( 0 );
1188 pGlue
->SetNextPortion( nullptr );
1189 pGlue
->SetFixWidth(0);
1192 // Luxury: We make sure the QuoVadis text appears on the right, by
1194 nLastLeft
= nLastLeft
- nQuoWidth
;
1197 if( nLastLeft
> pQuo
->GetAscent() ) // Minimum distance
1199 switch( GetAdjust() )
1201 case SvxAdjust::Block
:
1203 if( !m_pCurr
->GetLen() ||
1204 CH_BREAK
!= GetInfo().GetChar(m_nStart
+ m_pCurr
->GetLen() - TextFrameIndex(1)))
1205 nLastLeft
= pQuo
->GetAscent();
1206 nQuoWidth
= nQuoWidth
+ nLastLeft
;
1209 case SvxAdjust::Right
:
1211 nLastLeft
= pQuo
->GetAscent();
1212 nQuoWidth
= nQuoWidth
+ nLastLeft
;
1215 case SvxAdjust::Center
:
1217 nQuoWidth
= nQuoWidth
+ pQuo
->GetAscent();
1218 tools::Long nDiff
= nLastLeft
- nQuoWidth
;
1221 nLastLeft
= pQuo
->GetAscent();
1222 nQuoWidth
= o3tl::narrowing
<sal_uInt16
>(-nDiff
+ nLastLeft
);
1227 nLastLeft
= sal_uInt16(( pQuo
->GetAscent() + nDiff
) / 2);
1232 nQuoWidth
= nQuoWidth
+ nLastLeft
;
1236 nQuoWidth
= nQuoWidth
+ nLastLeft
;
1239 pGlue
= new SwGluePortion(0);
1240 pGlue
->Width( nLastLeft
);
1241 pPor
->Append( pGlue
);
1242 pPor
= pPor
->GetNextPortion();
1246 // Finally: we insert the QuoVadis Portion
1250 // pPor->Append deletes the pPortion pointer of pPor.
1251 // Therefore we have to keep a pointer to the next portion
1252 pQuo
= static_cast<SwQuoVadisPortion
*>(pCurrPor
->GetNextPortion());
1253 pPor
->Append( pCurrPor
);
1254 pPor
= pPor
->GetNextPortion();
1258 m_pCurr
->Width( m_pCurr
->Width() + nQuoWidth
);
1260 // And adjust again, due to the adjustment and due to the following special
1262 // The DummyUser has set a smaller Font in the Line than the one used
1263 // by the QuoVadis text ...
1264 CalcAdjustLine( m_pCurr
);
1270 * This function creates a Line that reaches to the other Page Margin.
1271 * DummyLines or DummyPortions make sure, that oscillations stop, because
1272 * there's no way to flow back.
1273 * They are used for Footnotes in paragraph-bound Frames and for Footnote
1276 void SwTextFormatter::MakeDummyLine()
1278 sal_uInt16 nRstHeight
= GetFrameRstHeight();
1279 if( m_pCurr
&& nRstHeight
> m_pCurr
->Height() )
1281 SwLineLayout
*pLay
= new SwLineLayout
;
1282 nRstHeight
= nRstHeight
- m_pCurr
->Height();
1283 pLay
->Height( nRstHeight
);
1284 pLay
->SetAscent( nRstHeight
);
1292 class SwFootnoteSave
1294 SwTextSizeInfo
* m_pInf
;
1296 std::unique_ptr
<SwFont
> m_pOld
;
1298 SwFootnoteSave(const SwFootnoteSave
&) = delete;
1299 SwFootnoteSave
& operator=(const SwFootnoteSave
&) = delete;
1302 SwFootnoteSave( const SwTextSizeInfo
&rInf
,
1303 const SwTextFootnote
*pTextFootnote
,
1304 const bool bApplyGivenScriptType
,
1305 const SwFontScript nGivenScriptType
);
1306 ~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
;
1311 SwFootnoteSave::SwFootnoteSave(const SwTextSizeInfo
& rInf
, const SwTextFootnote
* pTextFootnote
,
1312 const bool bApplyGivenScriptType
,
1313 const SwFontScript nGivenScriptType
)
1314 : m_pInf(&const_cast<SwTextSizeInfo
&>(rInf
))
1317 if( pTextFootnote
&& rInf
.GetTextFrame() )
1319 m_pFnt
= const_cast<SwTextSizeInfo
&>(rInf
).GetFont();
1320 m_pOld
.reset(new SwFont(*m_pFnt
));
1321 m_pOld
->GetTox() = m_pFnt
->GetTox();
1322 m_pFnt
->GetTox() = 0;
1323 SwFormatFootnote
& rFootnote
= const_cast<SwFormatFootnote
&>(pTextFootnote
->GetFootnote());
1324 const SwDoc
*const pDoc
= &rInf
.GetTextFrame()->GetDoc();
1327 if ( bApplyGivenScriptType
)
1329 m_pFnt
->SetActual(nGivenScriptType
);
1333 // examine text and set script
1334 OUString
aTmpStr(rFootnote
.GetViewNumStr(*pDoc
, rInf
.GetTextFrame()->getRootFrame()));
1335 m_pFnt
->SetActual(SwScriptInfo::WhichFont(0, aTmpStr
));
1338 const SwEndNoteInfo
* pInfo
;
1339 if( rFootnote
.IsEndNote() )
1340 pInfo
= &pDoc
->GetEndNoteInfo();
1342 pInfo
= &pDoc
->GetFootnoteInfo();
1343 const SwAttrSet
& rSet
= pInfo
->GetAnchorCharFormat(const_cast<SwDoc
&>(*pDoc
))->GetAttrSet();
1344 m_pFnt
->SetDiffFnt(&rSet
, &pDoc
->getIDocumentSettingAccess());
1346 // we reduce footnote size, if we are inside a double line portion
1347 if (!m_pOld
->GetEscapement() && 50 == m_pOld
->GetPropr())
1349 Size aSize
= m_pFnt
->GetSize(m_pFnt
->GetActual());
1350 m_pFnt
->SetSize(Size(aSize
.Width() / 2, aSize
.Height() / 2), m_pFnt
->GetActual());
1353 // set the correct rotation at the footnote font
1354 if( const SvxCharRotateItem
* pItem
= rSet
.GetItemIfSet( RES_CHRATR_ROTATE
) )
1355 m_pFnt
->SetVertical(pItem
->GetValue(),
1356 rInf
.GetTextFrame()->IsVertical());
1358 m_pFnt
->ChgPhysFnt(m_pInf
->GetVsh(), *m_pInf
->GetOut());
1360 if( const SvxBrushItem
* pItem
= rSet
.GetItemIfSet( RES_CHRATR_BACKGROUND
) )
1361 m_pFnt
->SetBackColor(pItem
->GetColor());
1367 SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
1373 m_pFnt
->GetTox() = m_pOld
->GetTox();
1374 m_pFnt
->ChgPhysFnt(m_pInf
->GetVsh(), *m_pInf
->GetOut());
1379 SwFootnotePortion::SwFootnotePortion( const OUString
&rExpand
,
1380 SwTextFootnote
*pFootn
, sal_uInt16 nReal
)
1381 : SwFieldPortion( rExpand
, nullptr )
1382 , m_pFootnote(pFootn
)
1383 , m_nOrigHeight( nReal
)
1385 , mbPreferredScriptTypeSet( false )
1386 , mnPreferredScriptType( SwFontScript::Latin
)
1388 SetLen(TextFrameIndex(1));
1389 SetWhichPor( PortionType::Footnote
);
1392 bool SwFootnotePortion::GetExpText( const SwTextSizeInfo
&, OUString
&rText
) const
1398 bool SwFootnotePortion::Format( SwTextFormatInfo
&rInf
)
1401 // SwFootnoteSave aFootnoteSave( rInf, pFootnote );
1402 SwFootnoteSave
aFootnoteSave( rInf
, m_pFootnote
, mbPreferredScriptTypeSet
, mnPreferredScriptType
);
1403 // the idx is manipulated in SwExpandPortion::Format
1404 // this flag indicates, that a footnote is allowed to trigger
1405 // an underflow during SwTextGuess::Guess
1406 rInf
.SetFakeLineStart( rInf
.GetIdx() > rInf
.GetLineStart() );
1407 const bool bFull
= SwFieldPortion::Format( rInf
);
1408 rInf
.SetFakeLineStart( false );
1409 SetAscent( rInf
.GetAscent() );
1410 Height( rInf
.GetTextHeight() );
1411 rInf
.SetFootnoteDone( !bFull
);
1413 rInf
.SetParaFootnote();
1417 void SwFootnotePortion::Paint( const SwTextPaintInfo
&rInf
) const
1420 // SwFootnoteSave aFootnoteSave( rInf, pFootnote );
1421 SwFootnoteSave
aFootnoteSave( rInf
, m_pFootnote
, mbPreferredScriptTypeSet
, mnPreferredScriptType
);
1422 rInf
.DrawViewOpt( *this, PortionType::Footnote
);
1423 SwExpandPortion::Paint( rInf
);
1426 SwPosSize
SwFootnotePortion::GetTextSize( const SwTextSizeInfo
&rInfo
) const
1429 // SwFootnoteSave aFootnoteSave( rInfo, pFootnote );
1430 SwFootnoteSave
aFootnoteSave( rInfo
, m_pFootnote
, mbPreferredScriptTypeSet
, mnPreferredScriptType
);
1431 return SwExpandPortion::GetTextSize( rInfo
);
1435 void SwFootnotePortion::SetPreferredScriptType( SwFontScript nPreferredScriptType
)
1437 mbPreferredScriptTypeSet
= true;
1438 mnPreferredScriptType
= nPreferredScriptType
;
1441 SwFieldPortion
*SwQuoVadisPortion::Clone( const OUString
&rExpand
) const
1443 return new SwQuoVadisPortion( rExpand
, m_aErgo
);
1446 SwQuoVadisPortion::SwQuoVadisPortion( const OUString
&rExp
, OUString aStr
)
1447 : SwFieldPortion( rExp
), m_aErgo(std::move(aStr
))
1449 SetLen(TextFrameIndex(0));
1450 SetWhichPor( PortionType::QuoVadis
);
1453 bool SwQuoVadisPortion::Format( SwTextFormatInfo
&rInf
)
1455 // First try; maybe the Text fits
1456 CheckScript( rInf
);
1457 bool bFull
= SwFieldPortion::Format( rInf
);
1458 SetLen(TextFrameIndex(0));
1462 // Second try; we make the String shorter
1464 bFull
= SwFieldPortion::Format( rInf
);
1465 SetLen(TextFrameIndex(0));
1467 // Third try; we're done: we crush
1468 Width( sal_uInt16(rInf
.Width() - rInf
.X()) );
1470 // No multiline Fields for QuoVadis and ErgoSum
1471 if( rInf
.GetRest() )
1473 delete rInf
.GetRest();
1474 rInf
.SetRest( nullptr );
1480 bool SwQuoVadisPortion::GetExpText( const SwTextSizeInfo
&, OUString
&rText
) const
1483 // if this QuoVadisPortion has a follow, the follow is responsible for
1485 if ( ! HasFollow() )
1490 void SwQuoVadisPortion::HandlePortion( SwPortionHandler
& rPH
) const
1492 rPH
.Special( GetLen(), m_aExpand
+ m_aErgo
, GetWhichPor() );
1495 void SwQuoVadisPortion::Paint( const SwTextPaintInfo
&rInf
) const
1497 // We _always_ want to output per DrawStretchText, because nErgo
1498 // can quickly switch
1501 rInf
.DrawViewOpt( *this, PortionType::QuoVadis
);
1502 SwTextSlot
aDiffText( &rInf
, this, true, false );
1503 SwFontSave
aSave( rInf
, m_pFont
.get() );
1504 rInf
.DrawText( *this, rInf
.GetLen(), true );
1508 SwFieldPortion
*SwErgoSumPortion::Clone( const OUString
&rExpand
) const
1510 return new SwErgoSumPortion( rExpand
, std::u16string_view() );
1513 SwErgoSumPortion::SwErgoSumPortion(const OUString
&rExp
, std::u16string_view rStr
)
1514 : SwFieldPortion( rExp
)
1516 SetLen(TextFrameIndex(0));
1519 // One blank distance to the text
1521 SetWhichPor( PortionType::ErgoSum
);
1524 TextFrameIndex
SwErgoSumPortion::GetModelPositionForViewPoint(const sal_uInt16
) const
1526 return TextFrameIndex(0);
1529 bool SwErgoSumPortion::Format( SwTextFormatInfo
&rInf
)
1531 const bool bFull
= SwFieldPortion::Format( rInf
);
1532 SetLen(TextFrameIndex(0));
1533 rInf
.SetErgoDone( true );
1535 // No multiline Fields for QuoVadis and ErgoSum
1536 if( bFull
&& rInf
.GetRest() )
1538 delete rInf
.GetRest();
1539 rInf
.SetRest( nullptr );
1542 // We return false in order to get some text into the current line,
1543 // even if it's full (better than looping)
1547 void SwParaPortion::SetErgoSumNum( const OUString
& rErgo
)
1549 SwLineLayout
*pLay
= this;
1550 while( pLay
->GetNext() )
1552 pLay
= pLay
->GetNext();
1554 SwLinePortion
*pPor
= pLay
;
1555 SwQuoVadisPortion
*pQuo
= nullptr;
1556 while( pPor
&& !pQuo
)
1558 if ( pPor
->IsQuoVadisPortion() )
1559 pQuo
= static_cast<SwQuoVadisPortion
*>(pPor
);
1560 pPor
= pPor
->GetNextPortion();
1563 pQuo
->SetNumber( rErgo
);
1567 * Is called in SwTextFrame::Prepare()
1569 bool SwParaPortion::UpdateQuoVadis( std::u16string_view rQuo
)
1571 SwLineLayout
*pLay
= this;
1572 while( pLay
->GetNext() )
1574 pLay
= pLay
->GetNext();
1576 SwLinePortion
*pPor
= pLay
;
1577 SwQuoVadisPortion
*pQuo
= nullptr;
1578 while( pPor
&& !pQuo
)
1580 if ( pPor
->IsQuoVadisPortion() )
1581 pQuo
= static_cast<SwQuoVadisPortion
*>(pPor
);
1582 pPor
= pPor
->GetNextPortion();
1588 return pQuo
->GetQuoText() == rQuo
;
1591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */