android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / txtftn.cxx
blob035158e2bd2583543836a0b461cd91a76bee2f52
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 <ftninfo.hxx>
36 #include <charfmt.hxx>
37 #include <rowfrm.hxx>
38 #include <editeng/brushitem.hxx>
39 #include <editeng/charrotateitem.hxx>
40 #include <tabfrm.hxx>
41 #include <sortedobjs.hxx>
43 #include <swfont.hxx>
44 #include "porftn.hxx"
45 #include "porfly.hxx"
46 #include "porlay.hxx"
47 #include <txtfrm.hxx>
48 #include "itrform2.hxx"
49 #include <ftnfrm.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
73 if (IsInTab())
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();
78 return !pFootnote;
81 /**
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();
88 while( pFrame )
90 if( SwFootnoteBossFrame::FindFootnote( pFrame, pFootnote ) )
91 return pFrame;
92 pFrame = bFwd ? pFrame->GetFollow() :
93 pFrame->IsFollow() ? pFrame->FindMaster() : nullptr;
95 return pFrame;
98 void SwTextFrame::SetHasRotatedPortions(bool bHasRotatedPortions)
100 mbHasRotatedPortions = bHasRotatedPortions;
103 #ifdef DBG_UTIL
104 void SwTextFrame::CalcFootnoteFlag(TextFrameIndex nStop) // For testing the SplitFrame
105 #else
106 void SwTextFrame::CalcFootnoteFlag()
107 #endif
109 mbFootnote = false;
111 #ifdef DBG_UTIL
112 const TextFrameIndex nEnd = nStop != TextFrameIndex(COMPLETE_STRING)
113 ? nStop
114 : GetFollow() ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING);
115 #else
116 const TextFrameIndex nEnd = GetFollow()
117 ? GetFollow()->GetOffset()
118 : TextFrameIndex(COMPLETE_STRING);
119 #endif
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()));
128 if ( nEnd < nIdx )
129 break;
130 if( GetOffset() <= nIdx )
132 mbFootnote = true;
133 break;
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() );
157 ValidateBodyFrame();
158 ValidateFrame();
159 pFootnote = pBoss->FindFirstFootnote( this );
161 else
162 bReArrange = false;
163 if( !pCont || !pFootnote || bReArrange != (pFootnote->FindFootnoteBossFrame() == pBoss) )
165 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
166 SwTextFormatter aLine( this, &aInf );
167 aLine.TruncLines();
168 SetPara( nullptr ); // May be deleted!
169 ResetPreps();
170 return false;
173 return true;
177 * Local helper function. Checks if nLower should be taken as the boundary
178 * for the footnote.
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" );
189 SwTwips nAdd;
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
202 // within the table.
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();
214 SwTwips nMin = 0;
215 if ( bDontSplit )
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 )
221 nRet = nMin;
223 nAdd = aRectFnSet.GetBottomMargin(*pRow->GetUpper());
225 else
226 nAdd = aRectFnSet.GetBottomMargin(*pFrame);
228 if( nAdd > 0 )
230 if ( aRectFnSet.IsVert() )
231 nRet -= nAdd;
232 else
233 nRet += nAdd;
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();
252 if ( !pFlyFrame ||
253 pFlyFrame->isFrameAreaDefinitionValid() )
255 const SwTwips nBottom = aRectFnSet.GetBottom(aRect);
256 if ( aRectFnSet.YDiff( nBottom, nFlyLower ) > 0 )
257 nFlyLower = nBottom;
262 pStartFrame = pStartFrame->GetNext();
265 if ( aRectFnSet.IsVert() )
266 nRet = std::min( nRet, nFlyLower );
267 else
268 nRet = std::max( nRet, nFlyLower );
270 return nRet;
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);
280 if( !HasPara() )
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();
289 SwTwips nRet;
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();
300 if( IsVertical() )
301 nRet = SwitchHorizontalToVertical( nRet );
304 nRet = lcl_GetFootnoteLower( pThis, nRet );
306 pThis->mnFootnoteLine = nRet;
307 return 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() ) )
323 return 0;
325 SwSwapIfSwapped swap(const_cast<SwTextFrame *>(this));
327 SwTwips nHeight = pRef->IsInFootnoteConnect() ?
328 1 : pRef->GetFootnoteLine( pFootnoteFrame->GetAttr() );
329 if( nHeight )
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
341 if( nTmp < 0 )
343 bool bInvalidPos = false;
344 const SwLayoutFrame* pTmp = GetUpper();
345 while( !bInvalidPos && pTmp )
347 bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
348 !pTmp->Lower()->isFrameAreaPositionValid();
349 if( pTmp == pCont )
350 break;
351 pTmp = pTmp->GetUpper();
353 OSL_ENSURE( bInvalidPos, "Hanging below FootnoteCont" );
355 #endif
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 );
365 else
366 nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
368 nHeight += nTmp;
369 if( nHeight < 0 )
370 nHeight = 0;
372 else
373 { // The container has to shrink
374 nTmp += aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight);
375 if( nTmp > 0 )
376 nHeight = nTmp;
377 else
378 nHeight = 0;
382 return nHeight;
385 SwTextFrame *SwTextFrame::FindQuoVadisFrame()
387 // Check whether we're in a FootnoteFrame
388 if( GetIndPrev() || !IsInFootnote() )
389 return nullptr;
391 // To the preceding FootnoteFrame
392 SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame()->GetMaster();
393 if( !pFootnoteFrame )
394 return nullptr;
396 // Now the last Content
397 SwContentFrame *pCnt = pFootnoteFrame->ContainsContent();
398 if( !pCnt )
399 return nullptr;
400 SwContentFrame *pLast;
402 { pLast = pCnt;
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() )
411 return;
413 bool bRollBack = nLen != TextFrameIndex(COMPLETE_STRING);
414 TextFrameIndex nEnd;
415 SwTextFrame* pSource;
416 if( bRollBack )
418 nEnd = nStart + nLen;
419 pSource = GetFollow();
420 if( !pSource )
421 return;
423 else
425 nEnd = TextFrameIndex(COMPLETE_STRING);
426 pSource = this;
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())
439 continue;
441 TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
442 if (nStart > nIdx)
443 break;
445 if (nEnd >= nIdx)
447 SwTextFootnote const*const pFootnote(static_cast<SwTextFootnote const*>(pHt));
448 const bool bEndn = pFootnote->GetFootnote().IsEndNote();
450 if (bEndn)
452 if (!pEndBoss)
453 pEndBoss = pSource->FindFootnoteBossFrame();
455 else
457 if (!pFootnoteBoss)
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);
484 if (pFootnoteFrame)
486 const bool bEndDoc = bEndn || bFootnoteEndDoc;
487 if( bRollBack )
489 while (pFootnoteFrame)
491 pFootnoteFrame->SetRef( this );
492 pFootnoteFrame = pFootnoteFrame->GetFollow();
493 SetFootnote( true );
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
506 if (bEndDoc ||
507 !pFootnoteFrame->FindFootnoteBossFrame()->IsBefore(pDest->FindFootnoteBossFrame(!bEndn))
510 SwPageFrame* pTmp = pFootnoteFrame->FindPageFrame();
511 if( pUpdate && pUpdate != pTmp )
512 pUpdate->UpdateFootnoteNum();
513 pUpdate = pTmp;
514 while ( pFootnoteFrame )
516 pFootnoteFrame->SetRef( pDest );
517 pFootnoteFrame = pFootnoteFrame->GetFollow();
520 else
522 pFootnoteBoss->MoveFootnotes( this, pDest, pFootnote );
523 bRemove = true;
525 static_cast<SwTextFrame*>(pDest)->SetFootnote( true );
527 OSL_ENSURE( SwFootnoteBossFrame::FindFootnote( pDest,
528 pFootnote),"SwTextFrame::RemoveFootnote: footnote ChgRef failed");
530 else
532 if (!bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
533 !SwLayouter::Collecting( &GetDoc(),
534 pEndBoss->FindSctFrame(), nullptr ) ))
536 if( bEndn )
537 pEndBoss->RemoveFootnote( this, pFootnote );
538 else
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" );
548 if (pUpdate)
549 pUpdate->UpdateFootnoteNum();
551 // We break the oscillation
552 if (bRemove && !bFootnoteEndDoc && HasPara())
554 ValidateBodyFrame();
555 ValidateFrame();
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
584 * context.
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" );
598 mbFootnote = true;
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 );
617 if( !pContent )
618 pContent = this;
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 );
631 if( bDocEnd )
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 );
643 pSrcFrame = nullptr;
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 ) )
655 if( !pSrcFrame )
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;
665 return;
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 );
674 pSrcFrame = nullptr;
679 if( bDocEnd || bEnd )
681 if( !pSrcFrame )
682 pBoss->AppendFootnote( this, pFootnote );
683 else if( pSrcFrame != this )
684 SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
685 mbInFootnoteConnect = false;
686 return;
689 SwSaveFootnoteHeight aHeight( pBoss, nDeadLine );
691 if( !pSrcFrame ) // No Footnote was found at all
692 pBoss->AppendFootnote( this, pFootnote );
693 else
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()),
706 nDeadLine );
708 if( nDiff >= 0 )
710 // If the Footnote has been registered to a Follow, we need to
711 // rewire it now too
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 );
721 ValidateBodyFrame();
722 ValidateFrame();
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;
729 return;
731 else
732 bBrutal = true;
734 else
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 )
741 bBrutal = true;
742 else
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() )
746 bBrutal = true;
748 else if ( !pFootnoteFrame->GetPrev() ||
749 pFootnoteBoss->IsBefore( pBoss )
752 SwFootnoteBossFrame *pSrcBoss = pSrcFrame->FindFootnoteBossFrame( !bEnd );
753 pSrcBoss->MoveFootnotes( pSrcFrame, this, pFootnote );
755 else
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
763 if( bBrutal )
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 );
777 ValidateBodyFrame();
778 pBoss->RearrangeFootnotes( nDeadLine, true );
779 ValidateFrame();
781 else if( pSect->IsFootnoteAtEnd() )
783 ValidateBodyFrame();
784 ValidateFrame();
787 mbInFootnoteConnect = false;
791 * The portion for the Footnote Reference in the Text
793 SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
794 SwTextAttr *pHint )
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();
807 if( rInf.IsTest() )
808 return new SwFootnotePortion(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()), pFootnote);
810 SwSwapIfSwapped swap(m_pFrame);
812 sal_uInt16 nReal;
814 sal_uInt16 nOldReal = m_pCurr->GetRealHeight();
815 sal_uInt16 nOldAscent = m_pCurr->GetAscent();
816 sal_uInt16 nOldHeight = m_pCurr->Height();
817 CalcRealHeight();
818 nReal = m_pCurr->GetRealHeight();
819 if( nReal < nOldReal )
820 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();
829 if( bVertical )
830 nLower = m_pFrame->SwitchHorizontalToVertical( nLower );
832 nLower = lcl_GetFootnoteLower( m_pFrame, nLower );
834 // We just refresh.
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;
843 if( pScrFrame )
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() ) ) )
868 if( !pFootnoteCont )
870 rInf.SetStop( true );
871 return nullptr;
873 else
875 // There must not be any Footnote Containers in column Areas and at the same time on the
876 // Page/Page column
877 if( pSct && !bAtSctEnd ) // Is the Container in a (column) Area?
879 SwFootnoteBossFrame* pTmp = pBoss->FindSctFrame()->FindFootnoteBossFrame( true );
880 SwFootnoteContFrame* pFootnoteC = pTmp->FindFootnoteCont();
881 if( pFootnoteC )
883 SwFootnoteFrame* pTmpFrame = static_cast<SwFootnoteFrame*>(pFootnoteC->Lower());
884 if( pTmpFrame && *pTmpFrame < pFootnote )
886 rInf.SetStop( true );
887 return nullptr;
891 // Is this the last Line that fits?
892 SwTwips nTmpBot = Y() + nReal * 2;
894 if( bVertical )
895 nTmpBot = m_pFrame->SwitchHorizontalToVertical( nTmpBot );
897 SwRectFnSet aRectFnSet(pFootnoteCont);
899 const tools::Long nDiff = aRectFnSet.YDiff(
900 aRectFnSet.GetTop(pFootnoteCont->getFrameArea()),
901 nTmpBot );
903 if( pScrFrame && nDiff < 0 )
905 if( pFootnoteFrame )
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 );
913 return nullptr;
921 // Finally: Create FootnotePortion and exit ...
922 SwFootnotePortion *pRet = new SwFootnotePortion(
923 rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()),
924 pFootnote, nReal );
925 rInf.SetFootnoteInside( true );
927 return pRet;
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() )
939 return nullptr;
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();
953 else
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 ));
960 // #i37142#
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");
980 sal_Int16 eCharSet;
981 if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
983 OUString aFontName;
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 );
1008 if (pRedline)
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);
1019 else
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() );
1033 return pNewPor;
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() )
1050 return nullptr;
1052 // we are in the footnote container
1053 const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
1054 SwTextFrame *pQuoFrame = m_pFrame->FindQuoVadisFrame();
1055 if( !pQuoFrame )
1056 return nullptr;
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();
1063 if( pPara )
1064 pPara->SetErgoSumNum( aPage );
1065 if( rFootnoteInfo.m_aErgoSum.isEmpty() )
1066 return nullptr;
1067 SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFootnoteInfo.m_aErgoSum,
1068 lcl_GetPageNumber( pQuoPage ) );
1069 return pErgo;
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() )
1078 return nOffset;
1080 const SwFrame* pErgoFrame = m_pFrame->FindFootnoteFrame()->GetFollow();
1081 if( !pErgoFrame && m_pFrame->HasFollow() )
1082 pErgoFrame = m_pFrame->GetFollow();
1083 if( !pErgoFrame )
1084 return nOffset;
1086 if( pErgoFrame == m_pFrame->GetNext() )
1088 SwFrame *pCol = m_pFrame->FindColFrame();
1089 while( pCol && !pCol->GetNext() )
1090 pCol = pCol->GetUpper()->FindColFrame();
1091 if( pCol )
1092 return nOffset;
1094 else
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() )
1105 return nOffset;
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();
1111 FeedInf( rInf );
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;
1125 while( pPor )
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 );
1161 pCurrPor = 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();
1176 FeedInf( rInf );
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;
1182 if( pGlue )
1184 pGlue->Height( 0 );
1185 pGlue->Width( 0 );
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
1193 // using Glues.
1194 nLastLeft = nLastLeft - nQuoWidth;
1195 if( nLastLeft )
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;
1207 break;
1209 case SvxAdjust::Right:
1211 nLastLeft = pQuo->GetAscent();
1212 nQuoWidth = nQuoWidth + nLastLeft;
1213 break;
1215 case SvxAdjust::Center:
1217 nQuoWidth = nQuoWidth + pQuo->GetAscent();
1218 tools::Long nDiff = nLastLeft - nQuoWidth;
1219 if( nDiff < 0 )
1221 nLastLeft = pQuo->GetAscent();
1222 nQuoWidth = o3tl::narrowing<sal_uInt16>(-nDiff + nLastLeft);
1224 else
1226 nQuoWidth = 0;
1227 nLastLeft = sal_uInt16(( pQuo->GetAscent() + nDiff ) / 2);
1229 break;
1231 default:
1232 nQuoWidth = nQuoWidth + nLastLeft;
1235 else
1236 nQuoWidth = nQuoWidth + nLastLeft;
1237 if( 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
1247 pCurrPor = pQuo;
1248 while ( pCurrPor )
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();
1255 pCurrPor = pQuo;
1258 m_pCurr->Width( m_pCurr->Width() + nQuoWidth );
1260 // And adjust again, due to the adjustment and due to the following special
1261 // case:
1262 // The DummyUser has set a smaller Font in the Line than the one used
1263 // by the QuoVadis text ...
1264 CalcAdjustLine( m_pCurr );
1266 return nRet;
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
1274 * oscillations
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 );
1285 Insert( pLay );
1286 Next();
1290 namespace {
1292 class SwFootnoteSave
1294 SwTextSizeInfo* m_pInf;
1295 SwFont* m_pFnt;
1296 std::unique_ptr<SwFont> m_pOld;
1298 SwFootnoteSave(const SwFootnoteSave&) = delete;
1299 SwFootnoteSave& operator=(const SwFootnoteSave&) = delete;
1301 public:
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))
1315 , m_pFnt(nullptr)
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();
1326 // #i98418#
1327 if ( bApplyGivenScriptType )
1329 m_pFnt->SetActual(nGivenScriptType);
1331 else
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();
1341 else
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());
1363 else
1364 m_pFnt = nullptr;
1367 SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
1369 if (m_pFnt)
1371 // Put back SwFont
1372 *m_pFnt = *m_pOld;
1373 m_pFnt->GetTox() = m_pOld->GetTox();
1374 m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
1375 m_pOld.reset();
1379 SwFootnotePortion::SwFootnotePortion( const OUString &rExpand,
1380 SwTextFootnote *pFootn, sal_uInt16 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 )
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( 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 );
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 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)
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: */