sc: factor out some more code
[LibreOffice.git] / sw / source / core / text / txtftn.cxx
blob09289408fc4c6dcdae830d7a8f0e0cbdc65123ba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
24 #include <utility>
25 #include <viewsh.hxx>
26 #include <doc.hxx>
27 #include <IDocumentLayoutAccess.hxx>
28 #include <pagefrm.hxx>
29 #include <rootfrm.hxx>
30 #include <ndtxt.hxx>
31 #include <SwPortionHandler.hxx>
32 #include <txtftn.hxx>
33 #include <flyfrm.hxx>
34 #include <fmtftn.hxx>
35 #include <fmtsrnd.hxx>
36 #include <ftninfo.hxx>
37 #include <charfmt.hxx>
38 #include <rowfrm.hxx>
39 #include <editeng/brushitem.hxx>
40 #include <editeng/charrotateitem.hxx>
41 #include <tabfrm.hxx>
42 #include <sortedobjs.hxx>
44 #include <swfont.hxx>
45 #include "porftn.hxx"
46 #include "porfly.hxx"
47 #include "porlay.hxx"
48 #include <txtfrm.hxx>
49 #include "itrform2.hxx"
50 #include <ftnfrm.hxx>
51 #include <pagedesc.hxx>
52 #include "redlnitr.hxx"
53 #include <sectfrm.hxx>
54 #include <layouter.hxx>
55 #include <frmtool.hxx>
56 #include <ndindex.hxx>
57 #include <IDocumentSettingAccess.hxx>
58 #include <IDocumentRedlineAccess.hxx>
59 #include <swmodule.hxx>
60 #include <unotextrange.hxx>
61 #include <redline.hxx>
62 #include <editeng/colritem.hxx>
63 #include <editeng/udlnitem.hxx>
64 #include <editeng/crossedoutitem.hxx>
66 #include <com/sun/star/beans/XPropertySet.hpp>
67 #include <com/sun/star/awt/CharSet.hpp>
68 #include <com/sun/star/text/XTextRange.hpp>
70 using namespace ::com::sun::star;
72 bool SwTextFrame::IsFootnoteNumFrame_() const
74 if (IsInTab())
75 return false; // tdf#102073 first frame in cell doesn't have mpPrev set
76 const SwFootnoteFrame* pFootnote = FindFootnoteFrame()->GetMaster();
77 while( pFootnote && !pFootnote->ContainsContent() )
78 pFootnote = pFootnote->GetMaster();
79 return !pFootnote;
82 /**
83 * Looks for the TextFrame matching the SwTextFootnote within a master-follow chain
85 SwTextFrame *SwTextFrame::FindFootnoteRef( const SwTextFootnote *pFootnote )
87 SwTextFrame *pFrame = this;
88 const bool bFwd = MapModelToView(&pFootnote->GetTextNode(), pFootnote->GetStart()) >= GetOffset();
89 while( pFrame )
91 if( SwFootnoteBossFrame::FindFootnote( pFrame, pFootnote ) )
92 return pFrame;
93 pFrame = bFwd ? pFrame->GetFollow() :
94 pFrame->IsFollow() ? pFrame->FindMaster() : nullptr;
96 return pFrame;
99 void SwTextFrame::SetHasRotatedPortions(bool bHasRotatedPortions)
101 mbHasRotatedPortions = bHasRotatedPortions;
104 #ifdef DBG_UTIL
105 void SwTextFrame::CalcFootnoteFlag(TextFrameIndex nStop) // For testing the SplitFrame
106 #else
107 void SwTextFrame::CalcFootnoteFlag()
108 #endif
110 mbFootnote = false;
112 #ifdef DBG_UTIL
113 const TextFrameIndex nEnd = nStop != TextFrameIndex(COMPLETE_STRING)
114 ? nStop
115 : GetFollow() ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING);
116 #else
117 const TextFrameIndex nEnd = GetFollow()
118 ? GetFollow()->GetOffset()
119 : TextFrameIndex(COMPLETE_STRING);
120 #endif
122 SwTextNode const* pNode(nullptr);
123 sw::MergedAttrIter iter(*this);
124 for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
126 if ( pHt->Which() == RES_TXTATR_FTN )
128 TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
129 if ( nEnd < nIdx )
130 break;
131 if( GetOffset() <= nIdx )
133 mbFootnote = true;
134 break;
140 bool SwTextFrame::CalcPrepFootnoteAdjust()
142 OSL_ENSURE( HasFootnote(), "Who´s calling me?" );
143 SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame( true );
144 const SwFootnoteFrame *pFootnote = pBoss->FindFirstFootnote( this );
145 if (pFootnote && FTNPOS_CHAPTER != GetDoc().GetFootnoteInfo().m_ePos &&
146 ( !pBoss->GetUpper()->IsSctFrame() ||
147 !static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsFootnoteAtEnd() ) )
149 const SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
150 bool bReArrange = true;
152 SwRectFnSet aRectFnSet(this);
153 if ( pCont && aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
154 aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
156 pBoss->RearrangeFootnotes( aRectFnSet.GetBottom(getFrameArea()), false,
157 pFootnote->GetAttr() );
158 ValidateBodyFrame();
159 ValidateFrame();
160 pFootnote = pBoss->FindFirstFootnote( this );
162 else
163 bReArrange = false;
164 if( !pCont || !pFootnote || bReArrange != (pFootnote->FindFootnoteBossFrame() == pBoss) )
166 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
167 SwTextFormatter aLine( this, &aInf );
168 aLine.TruncLines();
169 SetPara( nullptr ); // May be deleted!
170 ResetPreps();
171 return false;
174 return true;
178 * Local helper function. Checks if nLower should be taken as the boundary
179 * for the footnote.
181 static SwTwips lcl_GetFootnoteLower( const SwTextFrame* pFrame, SwTwips nLower )
183 // nLower is an absolute value. It denotes the bottom of the line
184 // containing the footnote.
185 SwRectFnSet aRectFnSet(pFrame);
187 OSL_ENSURE( !pFrame->IsVertical() || !pFrame->IsSwapped(),
188 "lcl_GetFootnoteLower with swapped frame" );
190 SwTwips nAdd;
191 SwTwips nRet = nLower;
193 // Check if text is inside a table.
194 if ( pFrame->IsInTab() )
196 // If pFrame is inside a table, we have to check if
197 // a) The table is not allowed to split or
198 // b) The table row is not allowed to split
200 // Inside a table, there are no footnotes,
201 // see SwFrame::FindFootnoteBossFrame. So we don't have to check
202 // the case that pFrame is inside a (footnote collecting) section
203 // within the table.
204 const SwFrame* pRow = pFrame;
205 while( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() )
206 pRow = pRow->GetUpper();
207 const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
209 OSL_ENSURE( pTabFrame && pRow &&
210 pRow->GetUpper()->IsTabFrame(), "Upper of row should be tab" );
212 const bool bDontSplit = !pTabFrame->IsFollow() &&
213 !pTabFrame->IsLayoutSplitAllowed();
215 SwTwips nMin = 0;
216 if ( bDontSplit )
217 nMin = aRectFnSet.GetBottom(pTabFrame->getFrameArea());
218 else if ( !static_cast<const SwRowFrame*>(pRow)->IsRowSplitAllowed() )
219 nMin = aRectFnSet.GetBottom(pRow->getFrameArea());
221 if ( nMin && aRectFnSet.YDiff( nMin, nLower ) > 0 )
222 nRet = nMin;
224 nAdd = aRectFnSet.GetBottomMargin(*pRow->GetUpper());
226 else
227 nAdd = aRectFnSet.GetBottomMargin(*pFrame);
229 if( nAdd > 0 )
231 if ( aRectFnSet.IsVert() )
232 nRet -= nAdd;
233 else
234 nRet += nAdd;
237 // #i10770#: If there are fly frames anchored at previous paragraphs,
238 // the deadline should consider their lower borders.
239 const SwFrame* pStartFrame = pFrame->GetUpper()->GetLower();
240 OSL_ENSURE( pStartFrame, "Upper has no lower" );
241 SwTwips nFlyLower = aRectFnSet.IsVert() ? LONG_MAX : 0;
242 while ( pStartFrame != pFrame )
244 assert(pStartFrame && "Frame chain is broken");
245 if ( pStartFrame->GetDrawObjs() )
247 const SwSortedObjs &rObjs = *pStartFrame->GetDrawObjs();
248 for (SwAnchoredObject* pAnchoredObj : rObjs)
250 if (pAnchoredObj->GetFrameFormat()->GetSurround().GetSurround()
251 == text::WrapTextMode_THROUGH)
253 continue; // tdf#161718 no effect on text flow, skip
256 SwRect aRect( pAnchoredObj->GetObjRect() );
258 auto pFlyFrame = pAnchoredObj->DynCastFlyFrame();
259 if ( !pFlyFrame ||
260 pFlyFrame->isFrameAreaDefinitionValid() )
262 const SwTwips nBottom = aRectFnSet.GetBottom(aRect);
263 if ( aRectFnSet.YDiff( nBottom, nFlyLower ) > 0 )
264 nFlyLower = nBottom;
269 pStartFrame = pStartFrame->GetNext();
272 if ( aRectFnSet.IsVert() )
273 nRet = std::min( nRet, nFlyLower );
274 else
275 nRet = std::max( nRet, nFlyLower );
277 return nRet;
280 SwTwips SwTextFrame::GetFootnoteLine( const SwTextFootnote *pFootnote ) const
282 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
283 "SwTextFrame::GetFootnoteLine with swapped frame" );
285 SwTextFrame *pThis = const_cast<SwTextFrame*>(this);
287 if( !HasPara() )
289 // #109071# GetFormatted() does not work here, because most probably
290 // the frame is currently locked. We return the previous value.
291 return pThis->mnFootnoteLine > 0 ?
292 pThis->mnFootnoteLine :
293 IsVertical() ? getFrameArea().Left() : getFrameArea().Bottom();
296 SwTwips nRet;
298 SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
300 SwTextInfo aInf( pThis );
301 SwTextIter aLine( pThis, &aInf );
302 TextFrameIndex const nPos(MapModelToView(
303 &pFootnote->GetTextNode(), pFootnote->GetStart()));
304 aLine.CharToLine( nPos );
306 nRet = aLine.Y() + aLine.GetLineHeight();
307 if( IsVertical() )
308 nRet = SwitchHorizontalToVertical( nRet );
311 nRet = lcl_GetFootnoteLower( pThis, nRet );
313 pThis->mnFootnoteLine = nRet;
314 return nRet;
318 * Calculates the maximum reachable height for the TextFrame in the Footnote Area.
319 * The cell's bottom margin with the Footnote Reference limit's this height.
321 SwTwips SwTextFrame::GetFootnoteFrameHeight_() const
323 OSL_ENSURE( !IsFollow() && IsInFootnote(), "SwTextFrame::SetFootnoteLine: moon walk" );
325 const SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame();
326 const SwTextFrame *pRef = static_cast<const SwTextFrame *>(pFootnoteFrame->GetRef());
327 const SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame();
328 if( pBoss != pRef->FindFootnoteBossFrame( !pFootnoteFrame->GetAttr()->
329 GetFootnote().IsEndNote() ) )
330 return 0;
332 SwSwapIfSwapped swap(const_cast<SwTextFrame *>(this));
334 SwTwips nHeight = pRef->IsInFootnoteConnect() ?
335 1 : pRef->GetFootnoteLine( pFootnoteFrame->GetAttr() );
336 if( nHeight )
338 // As odd as it may seem: the first Footnote on the page may not touch the
339 // Footnote Reference, when entering text in the Footnote Area.
340 const SwFrame *pCont = pFootnoteFrame->GetUpper();
342 // Height within the Container which we're allowed to consume anyways
343 SwRectFnSet aRectFnSet(pCont);
344 SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pCont),
345 aRectFnSet.GetTop(getFrameArea()) );
347 #if OSL_DEBUG_LEVEL > 0
348 if( nTmp < 0 )
350 bool bInvalidPos = false;
351 const SwLayoutFrame* pTmp = GetUpper();
352 while( !bInvalidPos && pTmp )
354 const SwFrame* pLower = pTmp->Lower();
355 bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
356 !pLower || !pLower->isFrameAreaPositionValid();
357 if( pTmp == pCont )
358 break;
359 pTmp = pTmp->GetUpper();
361 OSL_ENSURE( bInvalidPos, "Hanging below FootnoteCont" );
363 #endif
365 if ( aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight) > 0 )
367 // Growth potential of the container
368 if ( !pRef->IsInFootnoteConnect() )
370 SwSaveFootnoteHeight aSave( const_cast<SwFootnoteBossFrame*>(pBoss), nHeight );
371 nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
373 else
374 nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
376 nHeight += nTmp;
377 if( nHeight < 0 )
378 nHeight = 0;
380 else
381 { // The container has to shrink
382 nTmp += aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight);
383 if( nTmp > 0 )
384 nHeight = nTmp;
385 else
386 nHeight = 0;
390 return nHeight;
393 SwTextFrame *SwTextFrame::FindQuoVadisFrame()
395 // Check whether we're in a FootnoteFrame
396 if( GetIndPrev() || !IsInFootnote() )
397 return nullptr;
399 // To the preceding FootnoteFrame
400 SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame()->GetMaster();
401 if( !pFootnoteFrame )
402 return nullptr;
404 // Now the last Content
405 SwContentFrame *pCnt = pFootnoteFrame->ContainsContent();
406 if( !pCnt )
407 return nullptr;
408 SwContentFrame *pLast;
410 { pLast = pCnt;
411 pCnt = pCnt->GetNextContentFrame();
412 } while( pCnt && pFootnoteFrame->IsAnLower( pCnt ) );
413 return static_cast<SwTextFrame*>(pLast);
416 void SwTextFrame::RemoveFootnote(TextFrameIndex const nStart, TextFrameIndex const nLen)
418 if ( !IsFootnoteAllowed() )
419 return;
421 bool bRollBack = nLen != TextFrameIndex(COMPLETE_STRING);
422 TextFrameIndex nEnd;
423 SwTextFrame* pSource;
424 if( bRollBack )
426 nEnd = nStart + nLen;
427 pSource = GetFollow();
428 if( !pSource )
429 return;
431 else
433 nEnd = TextFrameIndex(COMPLETE_STRING);
434 pSource = this;
437 SwPageFrame* pUpdate = nullptr;
438 bool bRemove = false;
439 SwFootnoteBossFrame *pFootnoteBoss = nullptr;
440 SwFootnoteBossFrame *pEndBoss = nullptr;
441 bool bFootnoteEndDoc = FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos;
442 SwTextNode const* pNode(nullptr);
443 sw::MergedAttrIterReverse iter(*this);
444 for (SwTextAttr const* pHt = iter.PrevAttr(&pNode); pHt; pHt = iter.PrevAttr(&pNode))
446 if (RES_TXTATR_FTN != pHt->Which())
447 continue;
449 TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
450 if (nStart > nIdx)
451 break;
453 if (nEnd >= nIdx)
455 SwTextFootnote const*const pFootnote(static_cast<SwTextFootnote const*>(pHt));
456 const bool bEndn = pFootnote->GetFootnote().IsEndNote();
458 if (bEndn)
460 if (!pEndBoss)
461 pEndBoss = pSource->FindFootnoteBossFrame();
463 else
465 if (!pFootnoteBoss)
467 pFootnoteBoss = pSource->FindFootnoteBossFrame( true );
468 if( pFootnoteBoss->GetUpper()->IsSctFrame() )
470 SwSectionFrame* pSect = static_cast<SwSectionFrame*>(
471 pFootnoteBoss->GetUpper());
472 if (pSect->IsFootnoteAtEnd())
473 bFootnoteEndDoc = false;
478 // We don't delete, but move instead.
479 // Three cases are to be considered:
480 // 1) There's neither Follow nor PrevFollow:
481 // -> RemoveFootnote() (maybe even a OSL_ENSURE(value))
483 // 2) nStart > GetOffset, I have a Follow
484 // -> Footnote moves into Follow
486 // 3) nStart < GetOffset, I am a Follow
487 // -> Footnote moves into the PrevFollow
489 // Both need to be on one Page/in one Column
490 SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote(pSource, pFootnote);
492 if (pFootnoteFrame)
494 const bool bEndDoc = bEndn || bFootnoteEndDoc;
495 if( bRollBack )
497 while (pFootnoteFrame)
499 pFootnoteFrame->SetRef( this );
500 pFootnoteFrame = pFootnoteFrame->GetFollow();
501 SetFootnote( true );
504 else if (GetFollow())
506 SwContentFrame *pDest = GetFollow();
507 while (pDest->GetFollow() && static_cast<SwTextFrame*>(pDest->
508 GetFollow())->GetOffset() <= nIdx)
509 pDest = pDest->GetFollow();
510 OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote(
511 pDest,pFootnote),"SwTextFrame::RemoveFootnote: footnote exists");
513 // Never deregister; always move
514 if (bEndDoc ||
515 !pFootnoteFrame->FindFootnoteBossFrame()->IsBefore(pDest->FindFootnoteBossFrame(!bEndn))
518 SwPageFrame* pTmp = pFootnoteFrame->FindPageFrame();
519 if( pUpdate && pUpdate != pTmp )
520 pUpdate->UpdateFootnoteNum();
521 pUpdate = pTmp;
522 while ( pFootnoteFrame )
524 pFootnoteFrame->SetRef( pDest );
525 pFootnoteFrame = pFootnoteFrame->GetFollow();
528 else
530 pFootnoteBoss->MoveFootnotes( this, pDest, pFootnote );
531 bRemove = true;
533 static_cast<SwTextFrame*>(pDest)->SetFootnote( true );
535 OSL_ENSURE( SwFootnoteBossFrame::FindFootnote( pDest,
536 pFootnote),"SwTextFrame::RemoveFootnote: footnote ChgRef failed");
538 else
540 if (!bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
541 !SwLayouter::Collecting( &GetDoc(),
542 pEndBoss->FindSctFrame(), nullptr ) ))
544 if( bEndn )
545 pEndBoss->RemoveFootnote( this, pFootnote );
546 else
547 pFootnoteBoss->RemoveFootnote( this, pFootnote );
548 bRemove = bRemove || !bEndDoc;
549 OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote( this, pFootnote ),
550 "SwTextFrame::RemoveFootnote: can't get off that footnote" );
556 if (pUpdate)
557 pUpdate->UpdateFootnoteNum();
559 // We break the oscillation
560 if (bRemove && !bFootnoteEndDoc && HasPara())
562 ValidateBodyFrame();
563 ValidateFrame();
566 // We call the RemoveFootnote from within the FindBreak, because the last line is
567 // to be passed to the Follow. The Offset of the Follow is, however, outdated;
568 // it'll be set soon. CalcFntFlag depends on a correctly set Follow Offset.
569 // Therefore we temporarily calculate the Follow Offset here
570 TextFrameIndex nOldOfst(COMPLETE_STRING);
571 if( HasFollow() && nStart > GetOffset() )
573 nOldOfst = GetFollow()->GetOffset();
574 GetFollow()->ManipOfst(nStart + (bRollBack ? nLen : TextFrameIndex(0)));
576 pSource->CalcFootnoteFlag();
577 if (nOldOfst < TextFrameIndex(COMPLETE_STRING))
578 GetFollow()->ManipOfst( nOldOfst );
583 * We basically only have two possibilities:
585 * a) The Footnote is already present
586 * => we move it, if another pSrcFrame has been found
588 * b) The Footnote is not present
589 * => we have it created for us
591 * Whether the Footnote ends up on our Page/Column, doesn't matter in this
592 * context.
594 * Optimization for Endnotes.
596 * Another problem: if the Deadline falls within the Footnote Area, we need
597 * to move the Footnote.
599 * @returns false on any type of error
601 void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDeadLine )
603 OSL_ENSURE( !IsVertical() || !IsSwapped(),
604 "SwTextFrame::ConnectFootnote with swapped frame" );
606 mbFootnote = true;
607 mbInFootnoteConnect = true; // Just reset!
608 // See if pFootnote is an endnote on a separate endnote page.
609 const bool bEnd = pFootnote->GetFootnote().IsEndNote();
611 // We want to store this value, because it is needed as a fallback
612 // in GetFootnoteLine(), if there is no paragraph information available
613 mnFootnoteLine = nDeadLine;
615 // We always need a parent (Page/Column)
616 SwSectionFrame *pSect;
617 SwContentFrame *pContent = this;
618 if( bEnd && IsInSct() )
620 pSect = FindSctFrame();
621 if( pSect->IsEndnAtEnd() )
622 pContent = pSect->FindLastContent( SwFindMode::EndNote );
623 if( !pContent )
624 pContent = this;
627 SwFootnoteBossFrame *pBoss = pContent->FindFootnoteBossFrame( !bEnd );
629 pSect = pBoss->FindSctFrame();
630 bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) :
631 ( !( pSect && pSect->IsFootnoteAtEnd() ) &&
632 FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos);
634 // Footnote can be registered with the Follow
635 SwContentFrame *pSrcFrame = FindFootnoteRef( pFootnote );
637 if( bDocEnd )
639 if (pSect && pSrcFrame)
641 SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
642 if (pFootnoteFrame && pFootnoteFrame->IsInSct())
644 pBoss->RemoveFootnote( pSrcFrame, pFootnote );
645 pSrcFrame = nullptr;
649 else if( bEnd && pSect )
651 SwFootnoteFrame *pFootnoteFrame = pSrcFrame ? SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote ) : nullptr;
652 if( pFootnoteFrame && !pFootnoteFrame->GetUpper() )
653 pFootnoteFrame = nullptr;
654 SwDoc *const pDoc = &GetDoc();
655 if( SwLayouter::Collecting( pDoc, pSect, pFootnoteFrame ) )
657 if( !pSrcFrame )
659 SwFootnoteFrame *pNew = new SwFootnoteFrame(pDoc->GetDfltFrameFormat(),this,this,pFootnote);
660 SwNodeIndex aIdx( *pFootnote->GetStartNode(), 1 );
661 ::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
662 pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew );
664 else if( pSrcFrame != this )
665 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
666 mbInFootnoteConnect = false;
667 return;
669 else if (pSrcFrame && pFootnoteFrame)
671 SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
672 if( !pFootnoteBoss->IsInSct() ||
673 pFootnoteBoss->ImplFindSctFrame()->GetSection()!=pSect->GetSection() )
675 pBoss->RemoveFootnote( pSrcFrame, pFootnote );
676 pSrcFrame = nullptr;
681 if( bDocEnd || bEnd )
683 if( !pSrcFrame )
684 pBoss->AppendFootnote( this, pFootnote );
685 else if( pSrcFrame != this )
686 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
687 mbInFootnoteConnect = false;
688 return;
691 SwSaveFootnoteHeight aHeight( pBoss, nDeadLine );
693 if( !pSrcFrame ) // No Footnote was found at all
694 pBoss->AppendFootnote( this, pFootnote );
695 else
697 SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
698 SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
700 bool bBrutal = false;
702 if( pFootnoteBoss == pBoss ) // Ref and Footnote are on the same Page/Column
704 SwFrame *pCont = pFootnoteFrame->GetUpper();
706 SwRectFnSet aRectFnSet(pCont);
707 tools::Long nDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
708 nDeadLine );
710 if( nDiff >= 0 )
712 // If the Footnote has been registered to a Follow, we need to
713 // rewire it now too
714 if ( pSrcFrame != this )
715 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
717 // We have some room left, so the Footnote can grow
718 if ( pFootnoteFrame->GetFollow() && nDiff > 0 )
720 SwFrameDeleteGuard aDeleteGuard(pCont);
721 SwTwips nHeight = aRectFnSet.GetHeight(pCont->getFrameArea());
722 pBoss->RearrangeFootnotes( nDeadLine, false, pFootnote );
723 ValidateBodyFrame();
724 ValidateFrame();
725 SwViewShell *pSh = getRootFrame()->GetCurrShell();
726 if ( pSh && nHeight == aRectFnSet.GetHeight(pCont->getFrameArea()) )
727 // So that we don't miss anything
728 pSh->InvalidateWindows( pCont->getFrameArea() );
730 mbInFootnoteConnect = false;
731 return;
733 else
734 bBrutal = true;
736 else
738 // Ref and Footnote are not on one Page; attempt to move is necessary
739 SwFrame* pTmp = this;
740 while( pTmp->GetNext() && pSrcFrame != pTmp )
741 pTmp = pTmp->GetNext();
742 if( pSrcFrame == pTmp )
743 bBrutal = true;
744 else
745 { // If our Parent is in a column Area, but the Page already has a
746 // FootnoteContainer, we can only brute force it
747 if( pSect && pSect->FindFootnoteBossFrame( !bEnd )->FindFootnoteCont() )
748 bBrutal = true;
750 else if ( !pFootnoteFrame->GetPrev() ||
751 pFootnoteBoss->IsBefore( pBoss )
754 SwFootnoteBossFrame *pSrcBoss = pSrcFrame->FindFootnoteBossFrame( !bEnd );
755 pSrcBoss->MoveFootnotes( pSrcFrame, this, pFootnote );
757 else
758 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
762 // The brute force method: Remove Footnote and append.
763 // We need to call SetFootnoteDeadLine(), as we can more easily adapt the
764 // nMaxFootnoteHeight after RemoveFootnote
765 if( bBrutal )
767 pBoss->RemoveFootnote( pSrcFrame, pFootnote, false );
768 std::unique_ptr<SwSaveFootnoteHeight> pHeight(bEnd ? nullptr : new SwSaveFootnoteHeight( pBoss, nDeadLine ));
769 pBoss->AppendFootnote( this, pFootnote );
773 // In column Areas, that not yet reach the Page's border a RearrangeFootnotes is not
774 // useful yet, as the Footnote container has not yet been calculated
775 if( !pSect || !pSect->Growable() )
777 // Validate environment, to avoid oscillation
778 SwSaveFootnoteHeight aNochmal( pBoss, nDeadLine );
779 ValidateBodyFrame();
780 pBoss->RearrangeFootnotes( nDeadLine, true );
781 ValidateFrame();
783 else if( pSect->IsFootnoteAtEnd() )
785 ValidateBodyFrame();
786 ValidateFrame();
789 mbInFootnoteConnect = false;
793 * The portion for the Footnote Reference in the Text
795 SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
796 SwTextAttr *pHint )
798 OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
799 "NewFootnotePortion with unswapped frame" );
801 if (!m_pFrame->IsFootnoteAllowed())
802 return new SwFootnotePortion(u""_ustr, nullptr);
804 SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint);
806 const SwFormatFootnote& rFootnote = pFootnote->GetFootnote();
807 SwDoc *const pDoc = &m_pFrame->GetDoc();
809 if( rInf.IsTest() )
810 return new SwFootnotePortion(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()), pFootnote);
812 SwSwapIfSwapped swap(m_pFrame);
814 SwTwips nReal;
816 SwTwips nOldReal = m_pCurr->GetRealHeight();
817 SwTwips nOldAscent = m_pCurr->GetAscent();
818 SwTwips nOldHeight = m_pCurr->Height();
819 CalcRealHeight();
820 nReal = m_pCurr->GetRealHeight();
821 if( nReal < nOldReal )
822 nReal = nOldReal;
823 m_pCurr->SetRealHeight( nOldReal );
824 m_pCurr->Height( nOldHeight );
825 m_pCurr->SetAscent( nOldAscent );
828 SwTwips nLower = Y() + nReal;
830 const bool bVertical = m_pFrame->IsVertical();
831 if( bVertical )
832 nLower = m_pFrame->SwitchHorizontalToVertical( nLower );
834 nLower = lcl_GetFootnoteLower( m_pFrame, nLower );
836 // We just refresh.
837 // The Connect does not do anything useful in this case, but will
838 // mostly throw away the Footnote and create it anew.
839 if( !rInf.IsQuick() )
840 m_pFrame->ConnectFootnote( pFootnote, nLower );
842 SwTextFrame *pScrFrame = m_pFrame->FindFootnoteRef( pFootnote );
843 SwFootnoteBossFrame *pBoss = m_pFrame->FindFootnoteBossFrame( !rFootnote.IsEndNote() );
844 SwFootnoteFrame *pFootnoteFrame = nullptr;
845 if( pScrFrame )
846 pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pScrFrame, pFootnote );
848 // We see whether our Append has caused some Footnote to
849 // still be on the Page/Column. If not, our line disappears too,
850 // which will lead to the following undesired behaviour:
851 // Footnote1 still fits onto the Page/Column, but Footnote2 doesn't.
852 // The Footnote2 Reference remains on the Page/Column. The Footnote itself
853 // is on the next Page/Column.
855 // Exception: If the Page/Column cannot accommodate another line,
856 // the Footnote Reference should be moved to the next one.
857 if( !rFootnote.IsEndNote() )
859 SwSectionFrame *pSct = pBoss->FindSctFrame();
860 bool bAtSctEnd = pSct && pSct->IsFootnoteAtEnd();
861 if( FTNPOS_CHAPTER != pDoc->GetFootnoteInfo().m_ePos || bAtSctEnd )
863 SwFrame* pFootnoteCont = pBoss->FindFootnoteCont();
864 // If the Parent is within an Area, it can only be a Column of this
865 // Area. If this one is not the first Column, we can avoid it.
866 if( !m_pFrame->IsInTab() && ( GetLineNr() > 1 || m_pFrame->GetPrev() ||
867 ( !bAtSctEnd && m_pFrame->GetIndPrev() ) ||
868 ( pSct && pBoss->GetPrev() ) ) )
870 if( !pFootnoteCont )
872 rInf.SetStop( true );
873 return nullptr;
875 else
877 // There must not be any Footnote Containers in column Areas and at the same time on the
878 // Page/Page column
879 if( pSct && !bAtSctEnd ) // Is the Container in a (column) Area?
881 SwFootnoteBossFrame* pTmp = pBoss->FindSctFrame()->FindFootnoteBossFrame( true );
882 SwFootnoteContFrame* pFootnoteC = pTmp->FindFootnoteCont();
883 if( pFootnoteC )
885 SwFootnoteFrame* pTmpFrame = static_cast<SwFootnoteFrame*>(pFootnoteC->Lower());
886 if( pTmpFrame && *pTmpFrame < pFootnote )
888 rInf.SetStop( true );
889 return nullptr;
893 // Is this the last Line that fits?
894 SwTwips nTmpBot = Y() + nReal * 2;
896 if( bVertical )
897 nTmpBot = m_pFrame->SwitchHorizontalToVertical( nTmpBot );
899 SwRectFnSet aRectFnSet(pFootnoteCont);
901 const tools::Long nDiff = aRectFnSet.YDiff(
902 aRectFnSet.GetTop(pFootnoteCont->getFrameArea()),
903 nTmpBot );
905 if( pScrFrame && nDiff < 0 )
907 if( pFootnoteFrame )
909 SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
910 if( pFootnoteBoss != pBoss )
912 // We're in the last Line and the Footnote has moved
913 // to another Page. We also want to be on that Page!
914 rInf.SetStop( true );
915 return nullptr;
923 // Finally: Create FootnotePortion and exit ...
924 SwFootnotePortion *pRet = new SwFootnotePortion(
925 rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()),
926 pFootnote, nReal );
927 rInf.SetFootnoteInside( true );
929 return pRet;
933 * The portion for the Footnote Numbering in the Footnote Area
935 SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const &rInf ) const
937 OSL_ENSURE( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() && !rInf.IsFootnoteDone(),
938 "This is the wrong place for a ftnnumber" );
939 if( rInf.GetTextStart() != m_nStart ||
940 rInf.GetTextStart() != rInf.GetIdx() )
941 return nullptr;
943 const SwFootnoteFrame* pFootnoteFrame = m_pFrame->FindFootnoteFrame();
944 const SwTextFootnote* pFootnote = pFootnoteFrame->GetAttr();
946 // Aha! So we're in the Footnote Area!
947 SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
949 SwDoc *const pDoc = &m_pFrame->GetDoc();
950 OUString aFootnoteText(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame(), true));
952 const SwEndNoteInfo* pInfo;
953 if( rFootnote.IsEndNote() )
954 pInfo = &pDoc->GetEndNoteInfo();
955 else
956 pInfo = &pDoc->GetFootnoteInfo();
958 const SwAttrSet* pParSet = &rInf.GetCharAttr();
959 const IDocumentSettingAccess* pIDSA = &pDoc->getIDocumentSettingAccess();
960 std::unique_ptr<SwFont> pNumFnt(new SwFont( pParSet, pIDSA ));
962 // #i37142#
963 // Underline style of paragraph font should not be considered
964 // Overline style of paragraph font should not be considered
965 // Weight style of paragraph font should not be considered
966 // Posture style of paragraph font should not be considered
967 // See also #i18463# and SwTextFormatter::NewNumberPortion()
968 pNumFnt->SetUnderline( LINESTYLE_NONE );
969 pNumFnt->SetOverline( LINESTYLE_NONE );
970 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin );
971 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK );
972 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL );
973 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin );
974 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK );
975 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL );
977 const rtl::Reference<SwXTextRange> xAnchor = rFootnote.getAnchor(*pDoc);
978 if (xAnchor.is())
980 auto aAny = xAnchor->getPropertyValue(u"CharFontCharSet"_ustr);
981 sal_Int16 eCharSet;
982 if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
984 OUString aFontName;
985 aAny = xAnchor->getPropertyValue(u"CharFontName"_ustr);
986 if (aAny >>= aFontName)
988 pNumFnt->SetName(aFontName, SwFontScript::Latin);
989 pNumFnt->SetName(aFontName, SwFontScript::CJK);
990 pNumFnt->SetName(aFontName, SwFontScript::CTL);
991 pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin);
992 pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CJK);
993 pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CTL);
998 const SwAttrSet& rSet = pInfo->GetCharFormat(*pDoc)->GetAttrSet();
999 pNumFnt->SetDiffFnt(&rSet, pIDSA );
1000 pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
1002 // tdf#85610 apply redline coloring to the footnote numbering in the footnote area
1003 SwUnoInternalPaM aPam(*pDoc);
1004 if ( ::sw::XTextRangeToSwPaM(aPam, xAnchor) )
1006 SwRedlineTable::size_type nRedlinePos = 0;
1007 const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
1008 const SwRangeRedline* pRedline = rTable.FindAtPosition( *aPam.Start(), nRedlinePos );
1009 if (pRedline)
1011 SwAttrPool& rPool = pDoc->GetAttrPool();
1012 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
1014 std::size_t aAuthor = (1 < pRedline->GetStackCount())
1015 ? pRedline->GetAuthor( 1 )
1016 : pRedline->GetAuthor();
1018 if ( RedlineType::Delete == pRedline->GetType() )
1019 SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet);
1020 else
1021 SwModule::get()->GetInsertAuthorAttr(aAuthor, aSet);
1023 if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
1024 pNumFnt->SetColor(pItem->GetValue());
1025 if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
1026 pNumFnt->SetUnderline(pItem->GetLineStyle());
1027 if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
1028 pNumFnt->SetStrikeout( pItem->GetStrikeout() );
1032 SwFootnoteNumPortion* pNewPor = new SwFootnoteNumPortion( aFootnoteText, std::move(pNumFnt) );
1033 pNewPor->SetLeft( !m_pFrame->IsRightToLeft() );
1034 return pNewPor;
1037 static OUString lcl_GetPageNumber( const SwPageFrame* pPage )
1039 assert(pPage && "GetPageNumber: Homeless TextFrame");
1040 const sal_uInt16 nVirtNum = pPage->GetVirtPageNum();
1041 const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
1042 return rNum.GetNumStr( nVirtNum );
1045 SwErgoSumPortion *SwTextFormatter::NewErgoSumPortion( SwTextFormatInfo const &rInf ) const
1047 // We cannot assume we're a Follow
1048 if( !m_pFrame->IsInFootnote() || m_pFrame->GetPrev() ||
1049 rInf.IsErgoDone() || rInf.GetIdx() != m_pFrame->GetOffset() ||
1050 m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
1051 return nullptr;
1053 // we are in the footnote container
1054 const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
1055 SwTextFrame *pQuoFrame = m_pFrame->FindQuoVadisFrame();
1056 if( !pQuoFrame )
1057 return nullptr;
1058 const SwPageFrame* pPage = m_pFrame->FindPageFrame();
1059 const SwPageFrame* pQuoPage = pQuoFrame->FindPageFrame();
1060 if( pPage == pQuoFrame->FindPageFrame() )
1061 return nullptr; // If the QuoVadis is on the same Column/Page
1062 const OUString aPage = lcl_GetPageNumber( pPage );
1063 SwParaPortion *pPara = pQuoFrame->GetPara();
1064 if( pPara )
1065 pPara->SetErgoSumNum( aPage );
1066 if( rFootnoteInfo.m_aErgoSum.isEmpty() )
1067 return nullptr;
1068 SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFootnoteInfo.m_aErgoSum,
1069 lcl_GetPageNumber( pQuoPage ) );
1070 return pErgo;
1073 TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
1075 OSL_ENSURE( ! m_pFrame->IsVertical() || ! m_pFrame->IsSwapped(),
1076 "SwTextFormatter::FormatQuoVadis with swapped frame" );
1078 if( !m_pFrame->IsInFootnote() || m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
1079 return nOffset;
1081 const SwFrame* pErgoFrame = m_pFrame->FindFootnoteFrame()->GetFollow();
1082 if( !pErgoFrame && m_pFrame->HasFollow() )
1083 pErgoFrame = m_pFrame->GetFollow();
1084 if( !pErgoFrame )
1085 return nOffset;
1087 if( pErgoFrame == m_pFrame->GetNext() )
1089 SwFrame *pCol = m_pFrame->FindColFrame();
1090 while( pCol && !pCol->GetNext() )
1091 pCol = pCol->GetUpper()->FindColFrame();
1092 if( pCol )
1093 return nOffset;
1095 else
1097 const SwPageFrame* pPage = m_pFrame->FindPageFrame();
1098 const SwPageFrame* pErgoPage = pErgoFrame->FindPageFrame();
1099 if( pPage == pErgoPage )
1100 return nOffset; // If the ErgoSum is on the same Page
1103 SwTextFormatInfo &rInf = GetInfo();
1104 const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
1105 if( rFootnoteInfo.m_aQuoVadis.isEmpty() )
1106 return nOffset;
1108 // A remark on QuoVadis/ErgoSum:
1109 // We use the Font set for the Paragraph for these texts.
1110 // Thus, we initialize:
1111 // TODO: ResetFont();
1112 FeedInf( rInf );
1113 SeekStartAndChg( rInf, true );
1114 if( GetRedln() && m_pCurr->HasRedline() )
1116 std::pair<SwTextNode const*, sal_Int32> const pos(
1117 GetTextFrame()->MapViewToModel(nOffset));
1118 GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
1121 // A tricky special case: Flyfrms extend into the Line and are at the
1122 // position we want to insert the Quovadis text
1123 // Let's see if it is that bad indeed:
1124 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
1125 SwTwips nLastLeft = 0;
1126 while( pPor )
1128 if ( pPor->IsFlyPortion() )
1129 nLastLeft = static_cast<SwFlyPortion*>(pPor)->GetFix() +
1130 static_cast<SwFlyPortion*>(pPor)->Width();
1131 pPor = pPor->GetNextPortion();
1134 // The old game all over again: we want the Line to wrap around
1135 // at a certain point, so we adjust the width.
1136 // nLastLeft is now basically the right margin
1137 const SwTwips nOldRealWidth = rInf.RealWidth();
1138 rInf.RealWidth( nOldRealWidth - nLastLeft );
1140 OUString aErgo = lcl_GetPageNumber( pErgoFrame->FindPageFrame() );
1141 SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFootnoteInfo.m_aQuoVadis, aErgo );
1142 pQuo->SetAscent( rInf.GetAscent() );
1143 pQuo->Height( rInf.GetTextHeight() );
1144 pQuo->Format( rInf );
1145 SwTwips nQuoWidth = pQuo->Width();
1146 SwLinePortion* pCurrPor = pQuo;
1148 while ( rInf.GetRest() )
1150 SwLinePortion* pFollow = rInf.GetRest();
1151 rInf.SetRest( nullptr );
1152 pCurrPor->Move( rInf );
1154 OSL_ENSURE( pFollow->IsQuoVadisPortion(),
1155 "Quo Vadis, rest of QuoVadisPortion" );
1157 // format the rest and append it to the other QuoVadis parts
1158 pFollow->Format( rInf );
1159 nQuoWidth = nQuoWidth + pFollow->Width();
1161 pCurrPor->Append( pFollow );
1162 pCurrPor = pFollow;
1165 Right( Right() - nQuoWidth );
1167 TextFrameIndex nRet;
1169 SwSwapIfNotSwapped swap(m_pFrame);
1171 nRet = FormatLine( m_nStart );
1174 Right( rInf.Left() + nOldRealWidth - 1 );
1176 nLastLeft = nOldRealWidth - m_pCurr->Width();
1177 FeedInf( rInf );
1179 // It's possible that there's a Margin Portion at the end, which would
1180 // just cause a lot of trouble, when respanning
1181 pPor = m_pCurr->FindLastPortion();
1182 SwGluePortion *pGlue = pPor->IsMarginPortion() ? static_cast<SwMarginPortion*>(pPor) : nullptr;
1183 if( pGlue )
1185 pGlue->Height( 0 );
1186 pGlue->Width( 0 );
1187 pGlue->SetLen(TextFrameIndex(0));
1188 pGlue->SetAscent( 0 );
1189 pGlue->SetNextPortion( nullptr );
1190 pGlue->SetFixWidth(0);
1193 // Luxury: We make sure the QuoVadis text appears on the right, by
1194 // using Glues.
1195 nLastLeft = nLastLeft - nQuoWidth;
1196 if( nLastLeft )
1198 if( nLastLeft > pQuo->GetAscent() ) // Minimum distance
1200 switch( GetAdjust() )
1202 case SvxAdjust::Block:
1204 if( !m_pCurr->GetLen() ||
1205 CH_BREAK != GetInfo().GetChar(m_nStart + m_pCurr->GetLen() - TextFrameIndex(1)))
1206 nLastLeft = pQuo->GetAscent();
1207 nQuoWidth = nQuoWidth + nLastLeft;
1208 break;
1210 case SvxAdjust::Right:
1212 nLastLeft = pQuo->GetAscent();
1213 nQuoWidth = nQuoWidth + nLastLeft;
1214 break;
1216 case SvxAdjust::Center:
1218 nQuoWidth = nQuoWidth + pQuo->GetAscent();
1219 tools::Long nDiff = nLastLeft - nQuoWidth;
1220 if( nDiff < 0 )
1222 nLastLeft = pQuo->GetAscent();
1223 nQuoWidth = -nDiff + nLastLeft;
1225 else
1227 nQuoWidth = 0;
1228 nLastLeft = (pQuo->GetAscent() + nDiff) / 2;
1230 break;
1232 default:
1233 nQuoWidth = nQuoWidth + nLastLeft;
1236 else
1237 nQuoWidth = nQuoWidth + nLastLeft;
1238 if( nLastLeft )
1240 pGlue = new SwGluePortion(0);
1241 pGlue->Width( nLastLeft );
1242 pPor->Append( pGlue );
1243 pPor = pPor->GetNextPortion();
1247 // Finally: we insert the QuoVadis Portion
1248 pCurrPor = pQuo;
1249 while ( pCurrPor )
1251 // pPor->Append deletes the pPortion pointer of pPor.
1252 // Therefore we have to keep a pointer to the next portion
1253 pQuo = static_cast<SwQuoVadisPortion*>(pCurrPor->GetNextPortion());
1254 pPor->Append( pCurrPor );
1255 pPor = pPor->GetNextPortion();
1256 pCurrPor = pQuo;
1259 m_pCurr->Width( m_pCurr->Width() + nQuoWidth );
1261 // And adjust again, due to the adjustment and due to the following special
1262 // case:
1263 // The DummyUser has set a smaller Font in the Line than the one used
1264 // by the QuoVadis text ...
1265 CalcAdjustLine( m_pCurr );
1267 return nRet;
1271 * This function creates a Line that reaches to the other Page Margin.
1272 * DummyLines or DummyPortions make sure, that oscillations stop, because
1273 * there's no way to flow back.
1274 * They are used for Footnotes in paragraph-bound Frames and for Footnote
1275 * oscillations
1277 void SwTextFormatter::MakeDummyLine()
1279 SwTwips nRstHeight = GetFrameRstHeight();
1280 if( m_pCurr && nRstHeight > m_pCurr->Height() )
1282 SwLineLayout *pLay = new SwLineLayout;
1283 nRstHeight = nRstHeight - m_pCurr->Height();
1284 pLay->Height( nRstHeight );
1285 pLay->SetAscent( nRstHeight );
1286 Insert( pLay );
1287 Next();
1291 namespace {
1293 class SwFootnoteSave
1295 SwTextSizeInfo* m_pInf;
1296 SwFont* m_pFnt;
1297 std::unique_ptr<SwFont> m_pOld;
1299 SwFootnoteSave(const SwFootnoteSave&) = delete;
1300 SwFootnoteSave& operator=(const SwFootnoteSave&) = delete;
1302 public:
1303 SwFootnoteSave( const SwTextSizeInfo &rInf,
1304 const SwTextFootnote *pTextFootnote,
1305 const bool bApplyGivenScriptType,
1306 const SwFontScript nGivenScriptType );
1307 ~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE;
1312 SwFootnoteSave::SwFootnoteSave(const SwTextSizeInfo& rInf, const SwTextFootnote* pTextFootnote,
1313 const bool bApplyGivenScriptType,
1314 const SwFontScript nGivenScriptType)
1315 : m_pInf(&const_cast<SwTextSizeInfo&>(rInf))
1316 , m_pFnt(nullptr)
1318 if( pTextFootnote && rInf.GetTextFrame() )
1320 m_pFnt = const_cast<SwTextSizeInfo&>(rInf).GetFont();
1321 m_pOld.reset(new SwFont(*m_pFnt));
1322 m_pOld->GetTox() = m_pFnt->GetTox();
1323 m_pFnt->GetTox() = 0;
1324 SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pTextFootnote->GetFootnote());
1325 const SwDoc *const pDoc = &rInf.GetTextFrame()->GetDoc();
1327 // #i98418#
1328 if ( bApplyGivenScriptType )
1330 m_pFnt->SetActual(nGivenScriptType);
1332 else
1334 // examine text and set script
1335 OUString aTmpStr(rFootnote.GetViewNumStr(*pDoc, rInf.GetTextFrame()->getRootFrame()));
1336 m_pFnt->SetActual(SwScriptInfo::WhichFont(0, aTmpStr));
1339 const SwEndNoteInfo* pInfo;
1340 if( rFootnote.IsEndNote() )
1341 pInfo = &pDoc->GetEndNoteInfo();
1342 else
1343 pInfo = &pDoc->GetFootnoteInfo();
1344 const SwAttrSet& rSet = pInfo->GetAnchorCharFormat(const_cast<SwDoc&>(*pDoc))->GetAttrSet();
1345 m_pFnt->SetDiffFnt(&rSet, &pDoc->getIDocumentSettingAccess());
1347 // we reduce footnote size, if we are inside a double line portion
1348 if (!m_pOld->GetEscapement() && 50 == m_pOld->GetPropr())
1350 Size aSize = m_pFnt->GetSize(m_pFnt->GetActual());
1351 m_pFnt->SetSize(Size(aSize.Width() / 2, aSize.Height() / 2), m_pFnt->GetActual());
1354 // set the correct rotation at the footnote font
1355 if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
1356 m_pFnt->SetVertical(pItem->GetValue(),
1357 rInf.GetTextFrame()->IsVertical());
1359 m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
1361 if( const SvxBrushItem* pItem = rSet.GetItemIfSet( RES_CHRATR_BACKGROUND ) )
1362 m_pFnt->SetBackColor(pItem->GetColor());
1364 else
1365 m_pFnt = nullptr;
1368 SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
1370 if (m_pFnt)
1372 // Put back SwFont
1373 *m_pFnt = *m_pOld;
1374 m_pFnt->GetTox() = m_pOld->GetTox();
1375 m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
1376 m_pOld.reset();
1380 SwFootnotePortion::SwFootnotePortion(const OUString& rExpand, SwTextFootnote* pFootn, SwTwips nReal)
1381 : SwFieldPortion( rExpand, nullptr )
1382 , m_pFootnote(pFootn)
1383 , m_nOrigHeight( nReal )
1384 // #i98418#
1385 , mbPreferredScriptTypeSet( false )
1386 , mnPreferredScriptType( SwFontScript::Latin )
1388 SetLen(TextFrameIndex(1));
1389 SetWhichPor( PortionType::Footnote );
1392 bool SwFootnotePortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
1394 rText = m_aExpand;
1395 return true;
1398 bool SwFootnotePortion::Format( SwTextFormatInfo &rInf )
1400 // #i98418#
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 );
1412 if (!bFull && m_pFootnote)
1413 rInf.SetParaFootnote();
1414 return bFull;
1417 void SwFootnotePortion::Paint( const SwTextPaintInfo &rInf ) const
1419 // #i98418#
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
1428 // #i98418#
1429 // SwFootnoteSave aFootnoteSave( rInfo, pFootnote );
1430 SwFootnoteSave aFootnoteSave( rInfo, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
1431 return SwExpandPortion::GetTextSize( rInfo );
1434 // #i98418#
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));
1460 if( bFull )
1462 // Second try; we make the String shorter
1463 m_aExpand = "...";
1464 bFull = SwFieldPortion::Format( rInf );
1465 SetLen(TextFrameIndex(0));
1466 if( bFull )
1467 // Third try; we're done: we crush
1468 Width(rInf.Width() - rInf.X());
1470 // No multiline Fields for QuoVadis and ErgoSum
1471 if( rInf.GetRest() )
1473 delete rInf.GetRest();
1474 rInf.SetRest( nullptr );
1477 return bFull;
1480 bool SwQuoVadisPortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
1482 rText = m_aExpand;
1483 // if this QuoVadisPortion has a follow, the follow is responsible for
1484 // the ergo text.
1485 if ( ! HasFollow() )
1486 rText += m_aErgo;
1487 return true;
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
1499 if( PrtWidth() )
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));
1517 m_aExpand += rStr;
1519 // One blank distance to the text
1520 m_aExpand += " ";
1521 SetWhichPor( PortionType::ErgoSum );
1524 TextFrameIndex SwErgoSumPortion::GetModelPositionForViewPoint(const SwTwips) 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)
1544 return false;
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();
1562 if( pQuo )
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();
1585 if( !pQuo )
1586 return false;
1588 return pQuo->GetQuoText() == rQuo;
1591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */