android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / frmform.cxx
blob495820336fb3134ebe7cb995a267e78aa5273fd6
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 <config_wasm_strip.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
25 #include <IDocumentRedlineAccess.hxx>
26 #include <anchoredobject.hxx>
27 #include <bodyfrm.hxx>
28 #include <hintids.hxx>
29 #include <editeng/keepitem.hxx>
30 #include <editeng/hyphenzoneitem.hxx>
31 #include <pagefrm.hxx>
32 #include <ndtxt.hxx>
33 #include <ftnfrm.hxx>
34 #include <txtftn.hxx>
35 #include <fmtftn.hxx>
36 #include <paratr.hxx>
37 #include <viewopt.hxx>
38 #include <viewsh.hxx>
39 #include <frmatr.hxx>
40 #include <pam.hxx>
41 #include <fmtanchr.hxx>
42 #include "itrform2.hxx"
43 #include "widorp.hxx"
44 #include "txtcache.hxx"
45 #include <sectfrm.hxx>
46 #include <rootfrm.hxx>
47 #include <frmfmt.hxx>
48 #include <sortedobjs.hxx>
49 #include <editeng/tstpitem.hxx>
50 #include <redline.hxx>
51 #include <comphelper/lok.hxx>
52 #include <flyfrms.hxx>
53 #include <frmtool.hxx>
54 #include <layouter.hxx>
56 // Tolerance in formatting and text output
57 #define SLOPPY_TWIPS 5
59 namespace {
61 class FormatLevel
63 static sal_uInt16 s_nLevel;
64 public:
65 FormatLevel() { ++s_nLevel; }
66 ~FormatLevel() { --s_nLevel; }
67 static sal_uInt16 GetLevel() { return s_nLevel; }
68 static bool LastLevel() { return 10 < s_nLevel; }
73 sal_uInt16 FormatLevel::s_nLevel = 0;
75 void ValidateText( SwFrame *pFrame ) // Friend of frame
77 if ( ( ! pFrame->IsVertical() &&
78 pFrame->getFrameArea().Width() == pFrame->GetUpper()->getFramePrintArea().Width() ) ||
79 ( pFrame->IsVertical() &&
80 pFrame->getFrameArea().Height() == pFrame->GetUpper()->getFramePrintArea().Height() ) )
82 pFrame->setFrameAreaSizeValid(true);
86 void SwTextFrame::ValidateFrame()
88 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
89 // Validate surroundings to avoid oscillation
90 SwSwapIfSwapped swap( this );
92 if ( !IsInFly() && !IsInTab() )
93 { // Only validate 'this' when inside a fly, the rest should actually only be
94 // needed for footnotes, which do not exist in flys.
95 SwSectionFrame* pSct = FindSctFrame();
96 if( pSct )
98 if( !pSct->IsColLocked() )
99 pSct->ColLock();
100 else
101 pSct = nullptr;
104 SwFrame *pUp = GetUpper();
105 pUp->Calc(pRenderContext);
106 if( pSct )
107 pSct->ColUnlock();
109 ValidateText( this );
111 // We at least have to save the MustFit flag!
112 assert(HasPara() && "ResetPreps(), missing ParaPortion, SwCache bug?");
113 SwParaPortion *pPara = GetPara();
114 const bool bMustFit = pPara->IsPrepMustFit();
115 ResetPreps();
116 pPara->SetPrepMustFit( bMustFit );
119 // After a RemoveFootnote the BodyFrame and all Frames contained within it, need to be
120 // recalculated, so that the DeadLine is right.
121 // First we search outwards, on the way back we calculate everything.
122 static void ValidateBodyFrame_( SwFrame *pFrame )
124 vcl::RenderContext* pRenderContext = pFrame ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
125 if( !pFrame || pFrame->IsCellFrame() )
126 return;
128 if( !pFrame->IsBodyFrame() && pFrame->GetUpper() )
129 ValidateBodyFrame_( pFrame->GetUpper() );
130 if( !pFrame->IsSctFrame() )
131 pFrame->Calc(pRenderContext);
132 else
134 const bool bOld = static_cast<SwSectionFrame*>(pFrame)->IsContentLocked();
135 static_cast<SwSectionFrame*>(pFrame)->SetContentLock( true );
136 pFrame->Calc(pRenderContext);
137 if( !bOld )
138 static_cast<SwSectionFrame*>(pFrame)->SetContentLock( false );
142 void SwTextFrame::ValidateBodyFrame()
144 SwSwapIfSwapped swap( this );
146 // See comment in ValidateFrame()
147 if ( !IsInFly() && !IsInTab() &&
148 !( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() ) )
149 ValidateBodyFrame_( GetUpper() );
152 bool SwTextFrame::GetDropRect_( SwRect &rRect ) const
154 SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
156 OSL_ENSURE( HasPara(), "SwTextFrame::GetDropRect_: try again next year." );
157 SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
158 SwTextMargin aLine( const_cast<SwTextFrame*>(this), &aInf );
159 if( aLine.GetDropLines() )
161 rRect.Top( aLine.Y() );
162 rRect.Left( aLine.GetLineStart() );
163 rRect.Height( aLine.GetDropHeight() );
164 rRect.Width( aLine.GetDropLeft() );
166 if ( IsRightToLeft() )
167 SwitchLTRtoRTL( rRect );
169 if ( IsVertical() )
170 SwitchHorizontalToVertical( rRect );
171 return true;
174 return false;
177 bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
179 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
180 SwSwapIfSwapped swap( this );
182 OSL_ENSURE( HasFollow(), "CalcFollow: missing Follow." );
184 SwTextFrame* pMyFollow = GetFollow();
186 SwParaPortion *pPara = GetPara();
187 const bool bFollowField = pPara && pPara->IsFollowField();
189 if( !pMyFollow->GetOffset() || pMyFollow->GetOffset() != nTextOfst ||
190 bFollowField || pMyFollow->IsFieldFollow() ||
191 ( pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Width() ) ||
192 ( ! pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Height() ) )
194 #if OSL_DEBUG_LEVEL > 0
195 const SwFrame *pOldUp = GetUpper();
196 #endif
198 SwRectFnSet aRectFnSet(this);
199 SwTwips nOldBottom = aRectFnSet.GetBottom(GetUpper()->getFrameArea());
200 SwTwips nMyPos = aRectFnSet.GetTop(getFrameArea());
202 const SwPageFrame *pPage = nullptr;
203 bool bOldInvaContent = true;
204 if ( !IsInFly() && GetNext() )
206 pPage = FindPageFrame();
207 // Minimize (reset if possible) invalidations: see below
208 bOldInvaContent = pPage->IsInvalidContent();
211 pMyFollow->SetOffset_( nTextOfst );
212 pMyFollow->SetFieldFollow( bFollowField );
213 if( HasFootnote() || pMyFollow->HasFootnote() )
215 ValidateFrame();
216 ValidateBodyFrame();
217 if( pPara )
219 pPara->GetReformat() = SwCharRange();
220 pPara->SetDelta(0);
224 // The footnote area must not get larger
225 SwSaveFootnoteHeight aSave( FindFootnoteBossFrame( true ), LONG_MAX );
227 pMyFollow->CalcFootnoteFlag();
228 if ( !pMyFollow->GetNext() && !pMyFollow->HasFootnote() )
229 nOldBottom = aRectFnSet.IsVert() ? 0 : LONG_MAX;
231 // tdf#122892 check flag:
232 // 1. WidowsAndOrphans::FindWidows() determines follow is a widow
233 // 2. SwTextFrame::PrepWidows() calls SetPrepWidows() on master;
234 // if it can spare lines, master truncates one line
235 // 3. SwTextFrame::CalcPreps() on master (below);
236 // unless IsPrepMustFit(), if master hasn't shrunk via 2., it will SetWidow()
237 // 4. loop must exit then, because the follow didn't grow so nothing will ever change
238 while (!IsWidow())
240 if( !FormatLevel::LastLevel() )
242 // If the follow is contained within a column section or column
243 // frame, we need to calculate that first. This is because the
244 // FormatWidthCols() does not work if it is called from MakeAll
245 // of the _locked_ follow.
246 SwSectionFrame* pSct = pMyFollow->FindSctFrame();
247 if( pSct && !pSct->IsAnLower( this ) )
249 if( pSct->GetFollow() )
250 pSct->SimpleFormat();
251 else if( ( pSct->IsVertical() && !pSct->getFrameArea().Width() ) ||
252 ( ! pSct->IsVertical() && !pSct->getFrameArea().Height() ) )
253 break;
255 // i#11760 - Intrinsic format of follow is controlled.
256 if ( FollowFormatAllowed() )
258 // i#11760 - No nested format of follows, if
259 // text frame is contained in a column frame.
260 // Thus, forbid intrinsic format of follow.
262 bool bIsFollowInColumn = false;
263 SwFrame* pFollowUpper = pMyFollow->GetUpper();
264 while ( pFollowUpper )
266 if ( pFollowUpper->IsColumnFrame() )
268 bIsFollowInColumn = true;
269 break;
271 if ( pFollowUpper->IsPageFrame() ||
272 pFollowUpper->IsFlyFrame() )
274 break;
276 pFollowUpper = pFollowUpper->GetUpper();
278 if ( bIsFollowInColumn )
280 pMyFollow->ForbidFollowFormat();
284 pMyFollow->Calc(pRenderContext);
285 // The Follow can tell from its getFrameArea().Height() that something went wrong
286 OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: cheesy follow" );
287 if( pMyFollow->GetPrev() )
289 pMyFollow->Prepare();
290 pMyFollow->Calc(pRenderContext);
291 OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: very cheesy follow" );
294 // i#11760 - Reset control flag for follow format.
295 pMyFollow->AllowFollowFormat();
298 // Make sure that the Follow gets painted
299 pMyFollow->SetCompletePaint();
302 pPara = GetPara();
303 // As long as the Follow requests lines due to Orphans, it is
304 // passed these and is formatted again if possible
305 if( pPara && pPara->IsPrepWidows() )
306 CalcPreps();
307 else
308 break;
311 if( HasFootnote() || pMyFollow->HasFootnote() )
313 ValidateBodyFrame();
314 ValidateFrame();
315 if( pPara )
317 pPara->GetReformat() = SwCharRange();
318 pPara->SetDelta(0);
322 if ( pPage && !bOldInvaContent )
323 pPage->ValidateContent();
325 #if OSL_DEBUG_LEVEL > 0
326 OSL_ENSURE( pOldUp == GetUpper(), "SwTextFrame::CalcFollow: heavy follow" );
327 #endif
329 const tools::Long nRemaining =
330 - aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom );
331 if ( nRemaining > 0 &&
332 nRemaining != ( aRectFnSet.IsVert() ?
333 nMyPos - getFrameArea().Right() :
334 getFrameArea().Top() - nMyPos ) )
336 return true;
340 return false;
343 void SwTextFrame::MakePos()
345 Point aOldPos = getFrameArea().Pos();
346 SwFrame::MakePos();
348 // Recalc split flys if our position changed.
349 if (aOldPos != getFrameArea().Pos())
351 // Find the master frame.
352 const SwTextFrame* pMaster = this;
353 while (pMaster->IsFollow())
355 pMaster = pMaster->FindMaster();
357 // Find which flys are effectively anchored to this frame.
358 for (const auto& pFly : pMaster->GetSplitFlyDrawObjs())
360 SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
361 if (pFlyAnchor != this)
363 continue;
365 // Possibly this fly was positioned relative to us, invalidate its position now that our
366 // position is changed.
367 SwPageFrame* pPageFrame = pFly->FindPageFrame();
368 bool bFlyNeedsPositioning = false;
369 bool bFlyPageMismatch = false;
370 if (pPageFrame)
372 // Was the position just adjusted to be inside the page frame?
373 bFlyNeedsPositioning = pFly->getFrameArea().Pos() == pPageFrame->getFrameArea().Pos();
374 // Is the fly on a page different than the anchor frame?
375 bFlyPageMismatch = pPageFrame != FindPageFrame();
377 if (bFlyNeedsPositioning || bFlyPageMismatch)
379 // Not really positioned, unlock the position once to allow a recalc.
380 pFly->UnlockPosition();
382 pFly->InvalidatePos();
386 // Inform LOK clients about change in position of redlines (if any)
387 if(!comphelper::LibreOfficeKit::isActive())
388 return;
390 SwTextNode const* pTextNode = GetTextNodeFirst();
391 const SwRedlineTable& rTable = pTextNode->getIDocumentRedlineAccess().GetRedlineTable();
392 for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
394 SwRangeRedline* pRedln = rTable[nRedlnPos];
395 if (pTextNode->GetIndex() == pRedln->GetPoint()->GetNode().GetIndex())
397 pRedln->MaybeNotifyRedlinePositionModification(getFrameArea().Top());
398 if (GetMergedPara()
399 && pRedln->GetType() == RedlineType::Delete
400 && pRedln->GetPoint()->GetNode() != pRedln->GetMark()->GetNode())
402 pTextNode = pRedln->End()->GetNode().GetTextNode();
408 void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
410 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
411 if( IsUndersized() )
413 if( GetOffset() && !IsFollow() ) // A scrolled paragraph (undersized)
414 return;
415 SetUndersized( nChgHght == 0 || bHasToFit );
418 // AdjustFrame is called with a swapped frame during
419 // formatting but the frame is not swapped during FormatEmpty
420 SwSwapIfSwapped swap( this );
421 SwRectFnSet aRectFnSet(this);
423 // The Frame's size variable is incremented by Grow or decremented by Shrink.
424 // If the size cannot change, nothing should happen!
425 if( nChgHght >= 0)
427 SwTwips nChgHeight = nChgHght;
428 if( nChgHght && !bHasToFit )
430 if( IsInFootnote() && !IsInSct() )
432 SwTwips nReal = Grow( nChgHght, true );
433 if( nReal < nChgHght )
435 SwTwips nBot = aRectFnSet.YInc( aRectFnSet.GetBottom(getFrameArea()),
436 nChgHght - nReal );
437 SwFrame* pCont = FindFootnoteFrame()->GetUpper();
439 if( aRectFnSet.BottomDist( pCont->getFrameArea(), nBot ) > 0 )
441 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
442 aRectFnSet.AddBottom( aFrm, nChgHght );
444 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
446 if( aRectFnSet.IsVert() )
448 aPrt.AddWidth(nChgHght );
450 else
452 aPrt.AddHeight(nChgHght );
455 return;
460 Grow( nChgHght );
462 if ( IsInFly() )
464 // If one of the Upper is a Fly, it's very likely that this fly changes its
465 // position by the Grow. Therefore, my position has to be corrected also or
466 // the check further down is not meaningful.
467 // The predecessors need to be calculated, so that the position can be
468 // calculated correctly.
469 if ( GetPrev() )
471 SwFrame *pPre = GetUpper()->Lower();
473 { pPre->Calc(pRenderContext);
474 pPre = pPre->GetNext();
475 } while ( pPre && pPre != this );
477 const Point aOldPos( getFrameArea().Pos() );
478 MakePos();
479 if ( aOldPos != getFrameArea().Pos() )
481 InvalidateObjs(false);
484 nChgHeight = 0;
486 // A Grow() is always accepted by the Layout, even if the
487 // FixSize of the surrounding layout frame should not allow it.
488 // We text for this case and correct the values.
489 // The Frame must NOT be shrunk further than its size permits
490 // even in the case of an emergency.
491 SwTwips nRstHeight;
492 if ( IsVertical() )
494 OSL_ENSURE( ! IsSwapped(),"Swapped frame while calculating nRstHeight" );
496 if ( IsVertLR() )
497 nRstHeight = GetUpper()->getFrameArea().Left()
498 + GetUpper()->getFramePrintArea().Left()
499 + GetUpper()->getFramePrintArea().Width()
500 - getFrameArea().Left();
501 else
502 nRstHeight = getFrameArea().Left() + getFrameArea().Width() -
503 ( GetUpper()->getFrameArea().Left() + GetUpper()->getFramePrintArea().Left() );
505 else
506 nRstHeight = GetUpper()->getFrameArea().Top()
507 + GetUpper()->getFramePrintArea().Top()
508 + GetUpper()->getFramePrintArea().Height()
509 - getFrameArea().Top();
511 // We can get a bit of space in table cells, because there could be some
512 // left through a vertical alignment to the top.
513 // Assure that first lower in upper is the current one or is valid.
514 if ( IsInTab() &&
515 ( GetUpper()->Lower() == this ||
516 GetUpper()->Lower()->isFrameAreaDefinitionValid() ) )
518 tools::Long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(GetUpper()->Lower()->getFrameArea()),
519 aRectFnSet.GetPrtTop(*GetUpper()) );
520 OSL_ENSURE( nAdd >= 0, "Ey" );
521 nRstHeight += nAdd;
524 // nRstHeight < 0 means that the TextFrame is located completely outside of its Upper.
525 // This can happen, if it's located within a FlyAtContentFrame, which changed sides by a
526 // Grow(). In such a case, it's wrong to execute the following Grow().
527 // In the case of a bug, we end up with an infinite loop.
528 SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
529 SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
531 if( nRstHeight < nFrameHeight )
533 // It can be that I have the right size, but the Upper is too small and can get me some room
534 if( ( nRstHeight >= 0 || ( IsInFootnote() && IsInSct() ) ) && !bHasToFit )
535 nRstHeight += GetUpper()->Grow( nFrameHeight - nRstHeight );
536 // In column sections we do not want to get too big or else more areas are created by
537 // GetNextSctLeaf. Instead, we shrink and remember bUndersized, so that FormatWidthCols
538 // can calculate the right column size.
539 if ( nRstHeight < nFrameHeight )
541 if( bHasToFit || !IsMoveable() ||
542 ( IsInSct() && !FindSctFrame()->MoveAllowed(this) ) )
544 SetUndersized( true );
545 Shrink( std::min( ( nFrameHeight - nRstHeight), nPrtHeight ) );
547 else
548 SetUndersized( false );
551 else if( nChgHeight )
553 if( nRstHeight - nFrameHeight < nChgHeight )
554 nChgHeight = nRstHeight - nFrameHeight;
555 if( nChgHeight )
556 Grow( nChgHeight );
559 else
560 Shrink( -nChgHght );
563 css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips CurrentPos )
565 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
566 SwTextFormatter aLine( this, &aInf );
567 SwTextCursor TextCursor( this, &aInf );
568 const Point aCharPos( TextCursor.GetTopLeft() );
570 SwTwips nRight = aLine.Right();
571 CurrentPos -= aCharPos.X();
573 // get current tab stop information stored in the Frame
574 const SvxTabStop *pTS = aLine.GetLineInfo().GetTabStop( CurrentPos, nRight );
576 if( !pTS )
578 return {};
581 // copy tab stop information into a Sequence, which only contains one element.
582 css::style::TabStop ts;
583 ts.Position = pTS->GetTabPos();
584 ts.DecimalChar = pTS->GetDecimal();
585 ts.FillChar = pTS->GetFill();
586 switch( pTS->GetAdjustment() )
588 case SvxTabAdjust::Left : ts.Alignment = css::style::TabAlign_LEFT; break;
589 case SvxTabAdjust::Center : ts.Alignment = css::style::TabAlign_CENTER; break;
590 case SvxTabAdjust::Right : ts.Alignment = css::style::TabAlign_RIGHT; break;
591 case SvxTabAdjust::Decimal: ts.Alignment = css::style::TabAlign_DECIMAL; break;
592 case SvxTabAdjust::Default: ts.Alignment = css::style::TabAlign_DEFAULT; break;
593 default: break; // prevent warning
596 return { ts };
599 // AdjustFollow expects the following situation:
600 // The SwTextIter points to the lower end of the Master, the Offset is set in the Follow.
601 // nOffset holds the Offset in the text string, from which the Master closes
602 // and the Follow starts.
603 // If it's 0, the FollowFrame is deleted.
604 void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
605 const TextFrameIndex nOffset, const TextFrameIndex nEnd,
606 const sal_uInt8 nMode )
608 SwFrameSwapper aSwapper( this, false );
610 // We got the rest of the text mass: Delete all Follows
611 // DummyPortions() are a special case.
612 // Special cases are controlled by parameter <nMode>.
613 bool bDontJoin = nMode & 1;
614 if( HasFollow() && !bDontJoin && nOffset == nEnd )
616 while( GetFollow() )
618 if( GetFollow()->IsLocked() )
620 // this can happen when follow calls pMaster->GetFormatted()
621 SAL_INFO("sw.core", "+SwTextFrame::JoinFrame: Follow is locked." );
622 return;
624 if (GetFollow()->IsDeleteForbidden())
625 return;
627 if (HasNonLastSplitFlyDrawObj())
629 // If a fly frame is anchored to us that has a follow, then don't join the anchor.
630 // First those fly frames have to be joined.
631 return;
634 JoinFrame();
637 return;
640 // Dancing on the volcano: We'll just format the last line quickly
641 // for the QuoVadis stuff.
642 // The Offset can move of course:
643 const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
644 ? rLine.FormatQuoVadis(nOffset) : nOffset;
646 bool bHasNonLastSplitFlyDrawObj = false;
647 if (GetFollow() && GetOffset() == GetFollow()->GetOffset())
649 bHasNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
652 if( !bDontJoin )
654 // We steal text mass from our Follows
655 // It can happen that we have to join some of them
656 while( GetFollow() && GetFollow()->GetFollow() &&
657 nNewOfst >= GetFollow()->GetFollow()->GetOffset() )
659 if (bHasNonLastSplitFlyDrawObj)
661 // A non-last split fly is anchored to us, don't move content from the last frame to
662 // this one and don't join.
663 return;
666 JoinFrame();
670 if (IsEmptyMasterWithSplitFly())
672 // A split fly is anchored to us, don't move content from the follow frame to this one.
673 return;
676 // The Offset moved
677 if( GetFollow() )
679 if (!bDontJoin && bHasNonLastSplitFlyDrawObj)
681 // A non-last split fly is anchored to us, our follow is the last one in the text frame
682 // chain. No move of text from that follow to this text frame.
683 return;
686 if ( nMode )
687 GetFollow()->ManipOfst(TextFrameIndex(0));
689 if ( CalcFollow( nNewOfst ) ) // CalcFollow only at the end, we do a SetOffset there
690 rLine.SetOnceMore( true );
694 SwContentFrame *SwTextFrame::JoinFrame()
696 OSL_ENSURE( GetFollow(), "+SwTextFrame::JoinFrame: no follow" );
697 SwTextFrame *pFoll = GetFollow();
699 SwTextFrame *pNxt = pFoll->GetFollow();
701 // All footnotes of the to-be-destroyed Follow are relocated to us
702 TextFrameIndex nStart = pFoll->GetOffset();
703 if ( pFoll->HasFootnote() )
705 SwFootnoteBossFrame *pFootnoteBoss = nullptr;
706 SwFootnoteBossFrame *pEndBoss = nullptr;
707 SwTextNode const* pNode(nullptr);
708 sw::MergedAttrIter iter(*pFoll);
709 for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
711 if (RES_TXTATR_FTN == pHt->Which()
712 && nStart <= pFoll->MapModelToView(pNode, pHt->GetStart()))
714 if (pHt->GetFootnote().IsEndNote())
716 if (!pEndBoss)
717 pEndBoss = pFoll->FindFootnoteBossFrame();
718 SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
720 else
722 if (!pFootnoteBoss)
723 pFootnoteBoss = pFoll->FindFootnoteBossFrame( true );
724 SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
726 SetFootnote( true );
731 #ifdef DBG_UTIL
732 else if ( pFoll->isFramePrintAreaValid() ||
733 pFoll->isFrameAreaSizeValid() )
735 pFoll->CalcFootnoteFlag();
736 OSL_ENSURE( !pFoll->HasFootnote(), "Missing FootnoteFlag." );
738 #endif
740 pFoll->MoveFlyInCnt( this, nStart, TextFrameIndex(COMPLETE_STRING) );
741 pFoll->SetFootnote( false );
742 // i#27138
743 // Notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
744 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
745 // and relation CONTENT_FLOWS_TO for current previous paragraph, which
746 // is <this>, will change.
747 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
749 SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() );
750 if ( pViewShell && pViewShell->GetLayout() &&
751 pViewShell->GetLayout()->IsAnyShellAccessible() )
753 auto pNext = pFoll->FindNextCnt( true );
754 pViewShell->InvalidateAccessibleParaFlowRelation(
755 pNext ? pNext->DynCastTextFrame() : nullptr,
756 this );
759 #endif
761 pFoll->Cut();
762 SetFollow(pNxt);
763 SwFrame::DestroyFrame(pFoll);
764 return pNxt;
767 void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
769 SwSwapIfSwapped swap( this );
771 // The Paste sends a Modify() to me
772 // I lock myself, so that my data does not disappear
773 TextFrameLockGuard aLock( this );
774 SwTextFrame *const pNew = static_cast<SwTextFrame *>(GetTextNodeFirst()->MakeFrame(this));
776 pNew->SetFollow( GetFollow() );
777 SetFollow( pNew );
779 pNew->Paste( GetUpper(), GetNext() );
780 // i#27138
781 // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
782 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
783 // and relation CONTENT_FLOWS_TO for current previous paragraph, which
784 // is <this>, will change.
785 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
787 SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
788 if ( pViewShell && pViewShell->GetLayout() &&
789 pViewShell->GetLayout()->IsAnyShellAccessible() )
791 auto pNext = pNew->FindNextCnt( true );
792 pViewShell->InvalidateAccessibleParaFlowRelation(
793 pNext ? pNext->DynCastTextFrame() : nullptr,
794 this );
797 #endif
799 // If footnotes end up in pNew bz our actions, we need
800 // to re-register them
801 if ( HasFootnote() )
803 SwFootnoteBossFrame *pFootnoteBoss = nullptr;
804 SwFootnoteBossFrame *pEndBoss = nullptr;
805 SwTextNode const* pNode(nullptr);
806 sw::MergedAttrIter iter(*this);
807 for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
809 if (RES_TXTATR_FTN == pHt->Which()
810 && nTextPos <= MapModelToView(pNode, pHt->GetStart()))
812 if (pHt->GetFootnote().IsEndNote())
814 if (!pEndBoss)
815 pEndBoss = FindFootnoteBossFrame();
816 SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
818 else
820 if (!pFootnoteBoss)
821 pFootnoteBoss = FindFootnoteBossFrame( true );
822 SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
824 pNew->SetFootnote( true );
829 #ifdef DBG_UTIL
830 else
832 CalcFootnoteFlag( nTextPos - TextFrameIndex(1) );
833 OSL_ENSURE( !HasFootnote(), "Missing FootnoteFlag." );
835 #endif
837 MoveFlyInCnt( pNew, nTextPos, TextFrameIndex(COMPLETE_STRING) );
839 // No SetOffset or CalcFollow, because an AdjustFollow follows immediately anyways
841 pNew->ManipOfst( nTextPos );
844 void SwTextFrame::SetOffset_(TextFrameIndex const nNewOfst)
846 // We do not need to invalidate our Follow.
847 // We are a Follow, get formatted right away and call
848 // SetOffset() from there
849 mnOffset = nNewOfst;
850 SwParaPortion *pPara = GetPara();
851 if( pPara )
853 SwCharRange &rReformat = pPara->GetReformat();
854 rReformat.Start() = TextFrameIndex(0);
855 rReformat.Len() = TextFrameIndex(GetText().getLength());
856 pPara->SetDelta(sal_Int32(rReformat.Len()));
858 InvalidateSize();
861 bool SwTextFrame::CalcPreps()
863 OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::CalcPreps with swapped frame" );
864 SwRectFnSet aRectFnSet(this);
866 SwParaPortion *pPara = GetPara();
867 if ( !pPara )
868 return false;
869 const bool bPrep = pPara->IsPrep();
870 const bool bPrepWidows = pPara->IsPrepWidows();
871 const bool bPrepAdjust = pPara->IsPrepAdjust();
872 const bool bPrepMustFit = pPara->IsPrepMustFit();
873 ResetPreps();
875 bool bRet = false;
876 if( bPrep && !pPara->GetReformat().Len() )
878 // PrepareHint::Widows means that the orphans rule got activated in the Follow.
879 // In unfortunate cases we could also have a PrepAdjust!
880 if( bPrepWidows )
882 if( !GetFollow() )
884 OSL_ENSURE( GetFollow(), "+SwTextFrame::CalcPreps: no credits" );
885 return false;
888 // We need to prepare for two cases:
889 // We were able to hand over a few lines to the Follow
890 // -> we need to shrink
891 // or we need to go on the next page
892 // -> we let our Frame become too big
894 SwTwips nChgHeight = GetParHeight();
895 if( nChgHeight >= aRectFnSet.GetHeight(getFramePrintArea()) )
897 if( bPrepMustFit )
899 GetFollow()->SetJustWidow( true );
900 GetFollow()->Prepare();
902 else if ( aRectFnSet.IsVert() )
905 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
906 aFrm.Width( aFrm.Width() + aFrm.Left() );
907 aFrm.Left( 0 );
911 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
912 aPrt.Width( aPrt.Width() + getFrameArea().Left() );
915 SetWidow( true );
917 else
919 // nTmp should be very large, but not so large as to cause overflow later (e.g.,
920 // GetFrameOfModify in sw/source/core/layout/frmtool.cxx calculates nCurrentDist
921 // from, among others, the square of aDiff.getY(), which can be close to nTmp);
922 // the previously used value TWIPS_MAX/2 (i.e., (LONG_MAX - 1)/2) depended on
923 // the range of 'long', while the value (SAL_MAX_INT32 - 1)/2 (which matches the
924 // old value on platforms where 'long' is 'sal_Int32') is empirically shown to
925 // be large enough in practice even on platforms where 'long' is 'sal_Int64':
926 SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Top()+10000);
927 SwTwips nDiff = nTmp - getFrameArea().Height();
930 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
931 aFrm.Height( nTmp );
935 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
936 aPrt.Height( aPrt.Height() + nDiff );
939 SetWidow( true );
942 else
944 OSL_ENSURE( nChgHeight < aRectFnSet.GetHeight(getFramePrintArea()),
945 "+SwTextFrame::CalcPrep: want to shrink" );
947 nChgHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nChgHeight;
949 GetFollow()->SetJustWidow( true );
950 GetFollow()->Prepare();
951 Shrink( nChgHeight );
952 SwRect &rRepaint = pPara->GetRepaint();
954 if ( aRectFnSet.IsVert() )
956 SwRect aRepaint( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
957 SwitchVerticalToHorizontal( aRepaint );
958 rRepaint.Chg( aRepaint.Pos(), aRepaint.SSize() );
960 else
961 rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
963 if( 0 >= rRepaint.Width() )
964 rRepaint.Width(1);
966 bRet = true;
968 else if ( bPrepAdjust )
970 if ( HasFootnote() )
972 if( !CalcPrepFootnoteAdjust() )
974 if( bPrepMustFit )
976 SwTextLineAccess aAccess( this );
977 aAccess.GetPara()->SetPrepMustFit(true);
979 return false;
984 SwSwapIfNotSwapped swap( this );
986 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
987 SwTextFormatter aLine( this, &aInf );
989 WidowsAndOrphans aFrameBreak( this );
990 // Whatever the attributes say: we split the paragraph in
991 // MustFit case if necessary
992 if( bPrepMustFit )
994 aFrameBreak.SetKeep( false );
995 aFrameBreak.ClrOrphLines();
997 // Before calling FormatAdjust, we need to make sure
998 // that the lines protruding at the bottom get indeed
999 // truncated
1000 bool bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
1001 bRet = true;
1002 while( !bBreak && aLine.Next() )
1004 bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
1006 if( bBreak )
1008 // We run into troubles: when TruncLines is called, the
1009 // conditions in IsInside change immediately such that
1010 // IsBreakNow can return different results.
1011 // For this reason, we tell rFrameBreak that the
1012 // end is reached at the location of rLine.
1013 // Let's see if it works ...
1014 aLine.TruncLines();
1015 aFrameBreak.SetRstHeight( aLine );
1016 FormatAdjust( aLine, aFrameBreak, TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
1018 else
1020 if( !GetFollow() )
1022 FormatAdjust( aLine, aFrameBreak,
1023 TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
1025 else if ( !aFrameBreak.IsKeepAlways() )
1027 // We delete a line before the Master, because the Follow
1028 // could hand over a line
1029 const SwCharRange aFollowRg(GetFollow()->GetOffset(), TextFrameIndex(1));
1030 pPara->GetReformat() += aFollowRg;
1031 // We should continue!
1032 bRet = false;
1037 // A final check, if FormatAdjust() didn't help we need to
1038 // truncate
1039 if( bPrepMustFit )
1041 const SwTwips nMust = aRectFnSet.GetPrtBottom(*GetUpper());
1042 const SwTwips nIs = aRectFnSet.GetBottom(getFrameArea());
1044 if( aRectFnSet.IsVert() && nIs < nMust )
1046 Shrink( nMust - nIs );
1048 if( getFramePrintArea().Width() < 0 )
1050 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1051 aPrt.Width( 0 );
1054 SetUndersized( true );
1056 else if ( ! aRectFnSet.IsVert() && nIs > nMust )
1058 Shrink( nIs - nMust );
1060 if( getFramePrintArea().Height() < 0 )
1062 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1063 aPrt.Height( 0 );
1066 SetUndersized( true );
1071 pPara->SetPrepMustFit( bPrepMustFit );
1072 return bRet;
1075 // Move the as-character objects - footnotes must be moved by RemoveFootnote!
1076 void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
1078 if( pFrame->GetOffset() < nNew )
1079 pFrame->MoveFlyInCnt( this, TextFrameIndex(0), nNew );
1080 else if( pFrame->GetOffset() > nNew )
1081 MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );
1084 void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
1085 WidowsAndOrphans &rFrameBreak,
1086 TextFrameIndex const nStrLen,
1087 const bool bDummy )
1089 SwSwapIfNotSwapped swap( this );
1091 SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1093 TextFrameIndex nEnd = rLine.GetStart();
1095 const bool bHasToFit = pPara->IsPrepMustFit();
1097 // The StopFlag is set by footnotes which want to go onto the next page
1098 // Call base class method <SwTextFrameBreak::IsBreakNow(..)>
1099 // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break,
1100 // even if due to widow rule no enough lines exists.
1101 sal_uInt8 nNew = ( !GetFollow() &&
1102 nEnd < nStrLen &&
1103 ( rLine.IsStop() ||
1104 ( bHasToFit
1105 ? ( rLine.GetLineNr() > 1 &&
1106 !rFrameBreak.IsInside( rLine ) )
1107 : rFrameBreak.IsBreakNow( rLine ) ) ) )
1108 ? 1 : 0;
1110 SwTextFormatInfo& rInf = rLine.GetInfo();
1111 if (nNew == 0 && !nStrLen && !rInf.GetTextFly().IsOn() && IsEmptyWithSplitFly())
1113 // Empty paragraph, so IsBreakNow() is not called, but we should split the fly portion and
1114 // the paragraph marker.
1115 nNew = 1;
1118 // i#84870
1119 // no split of text frame, which only contains an as-character anchored object
1120 bool bOnlyContainsAsCharAnchoredObj =
1121 !IsFollow() && nStrLen == TextFrameIndex(1) &&
1122 GetDrawObjs() && GetDrawObjs()->size() == 1 &&
1123 (*GetDrawObjs())[0]->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
1125 // Still try split text frame if we have columns.
1126 if (FindColFrame())
1127 bOnlyContainsAsCharAnchoredObj = false;
1129 if ( nNew && bOnlyContainsAsCharAnchoredObj )
1131 nNew = 0;
1134 if ( nNew )
1136 SplitFrame( nEnd );
1139 const SwFrame *pBodyFrame = FindBodyFrame();
1141 const tools::Long nBodyHeight = pBodyFrame ? ( IsVertical() ?
1142 pBodyFrame->getFrameArea().Width() :
1143 pBodyFrame->getFrameArea().Height() ) : 0;
1145 // If the current values have been calculated, show that they
1146 // are valid now
1147 pPara->GetReformat() = SwCharRange();
1148 bool bDelta = pPara->GetDelta() != 0;
1149 pPara->SetDelta(0);
1151 if( rLine.IsStop() )
1153 rLine.TruncLines( true );
1154 nNew = 1;
1157 // FindBreak truncates the last line
1158 if( !rFrameBreak.FindBreak( this, rLine, bHasToFit ) )
1160 // If we're done formatting, we set nEnd to the end.
1161 // AdjustFollow might execute JoinFrame() because of this.
1162 // Else, nEnd is the end of the last line in the Master.
1163 TextFrameIndex nOld = nEnd;
1164 // Make sure content from the last floating table anchor is not shifted to previous anchors.
1165 if (!HasNonLastSplitFlyDrawObj())
1167 nEnd = rLine.GetEnd();
1169 if( GetFollow() )
1171 if( nNew && nOld < nEnd )
1172 RemoveFootnote( nOld, nEnd - nOld );
1173 ChangeOffset( GetFollow(), nEnd );
1174 if( !bDelta )
1175 GetFollow()->ManipOfst( nEnd );
1178 else
1179 { // If we pass over lines, we must not call Join in Follows, instead we even
1180 // need to create a Follow.
1181 // We also need to do this if the whole mass of text remains in the Master,
1182 // because a hard line break could necessitate another line (without text mass)!
1183 TextFrameIndex const nOld(nEnd);
1184 nEnd = rLine.GetEnd();
1185 if( GetFollow() )
1187 // Another case for not joining the follow:
1188 // Text frame has no content, but a numbering. Then, do *not* join.
1189 // Example of this case: When an empty, but numbered paragraph
1190 // at the end of page is completely displaced by a fly frame.
1191 // Thus, the text frame introduced a follow by a
1192 // <SwTextFrame::SplitFrame(..)> - see below. The follow then shows
1193 // the numbering and must stay.
1194 if ( GetFollow()->GetOffset() != nEnd ||
1195 GetFollow()->IsFieldFollow() ||
1196 (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
1198 nNew |= 3;
1200 else if (FindTabFrame() && nEnd > TextFrameIndex(0) &&
1201 rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
1203 // We are in a table, the paragraph has a follow and the text
1204 // ends with a hard line break. Don't join the follow just
1205 // because the follow would have no content, we may still need it
1206 // for the paragraph mark.
1207 nNew |= 1;
1209 // move footnotes if the follow is kept - if RemoveFootnote() is
1210 // called in next format iteration, it will be with the *new*
1211 // offset so no effect!
1212 if (nNew && nOld < nEnd)
1214 RemoveFootnote(nOld, nEnd - nOld);
1216 ChangeOffset( GetFollow(), nEnd );
1218 if (HasNonLastSplitFlyDrawObj())
1220 // Make sure content from the last floating table anchor is not shifted to previous
1221 // anchors.
1222 nEnd = TextFrameIndex(0);
1225 GetFollow()->ManipOfst( nEnd );
1227 else
1229 const SwTextNode* pTextNode = GetTextNodeForParaProps();
1230 bool bHasVisibleNumRule = nStrLen == TextFrameIndex(0) && pTextNode->GetNumRule();
1232 if (!pTextNode->HasVisibleNumberingOrBullet())
1234 bHasVisibleNumRule = false;
1237 // Only split frame, if the frame contains
1238 // content or contains no content, but has a numbering.
1239 // i#84870 - No split, if text frame only contains one
1240 // as-character anchored object.
1241 if ( !bOnlyContainsAsCharAnchoredObj &&
1242 (nStrLen > TextFrameIndex(0) ||
1243 bHasVisibleNumRule )
1246 SplitFrame( nEnd );
1247 nNew |= 3;
1250 // If the remaining height changed e.g by RemoveFootnote() we need to
1251 // fill up in order to avoid oscillation.
1252 if( bDummy && pBodyFrame &&
1253 nBodyHeight < ( IsVertical() ?
1254 pBodyFrame->getFrameArea().Width() :
1255 pBodyFrame->getFrameArea().Height() ) )
1256 rLine.MakeDummyLine();
1259 // In AdjustFrame() we set ourselves via Grow/Shrink
1260 // In AdjustFollow() we set our FollowFrame
1262 const SwTwips nDocPrtTop = getFrameArea().Top() + getFramePrintArea().Top();
1263 const SwTwips nOldHeight = getFramePrintArea().SSize().Height();
1264 SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight;
1266 //#i84870# - no shrink of text frame, if it only contains one as-character anchored object.
1267 if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj )
1269 nChg = 0;
1272 // Vertical Formatting:
1273 // The (rotated) repaint rectangle's x coordinate refers to the frame.
1274 // If the frame grows (or shirks) the repaint rectangle cannot simply
1275 // be rotated back after formatting, because we use the upper left point
1276 // of the frame for rotation. This point changes when growing/shrinking.
1278 if ( IsVertical() && !IsVertLR() && nChg )
1280 SwRect &rRepaint = pPara->GetRepaint();
1281 rRepaint.Left( rRepaint.Left() - nChg );
1282 rRepaint.Width( rRepaint.Width() - nChg );
1285 AdjustFrame( nChg, bHasToFit );
1287 if( HasFollow() || IsInFootnote() )
1288 AdjustFollow_( rLine, nEnd, nStrLen, nNew );
1290 pPara->SetPrepMustFit( false );
1293 // bPrev is set whether Reformat.Start() was called because of Prev().
1294 // Else, we don't know whether we can limit the repaint or not.
1295 bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
1297 OSL_ENSURE( ! IsVertical() || IsSwapped(),
1298 "SwTextFrame::FormatLine( rLine, bPrev) with unswapped frame" );
1299 SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1300 const SwLineLayout *pOldCur = rLine.GetCurr();
1301 const TextFrameIndex nOldLen = pOldCur->GetLen();
1302 const SwTwips nOldAscent = pOldCur->GetAscent();
1303 const SwTwips nOldHeight = pOldCur->Height();
1304 const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin();
1305 const bool bOldHyph = pOldCur->IsEndHyph();
1306 SwTwips nOldTop = 0;
1307 SwTwips nOldBottom = 0;
1308 if( rLine.GetCurr()->IsClipping() )
1309 rLine.CalcUnclipped( nOldTop, nOldBottom );
1311 TextFrameIndex const nNewStart = rLine.FormatLine( rLine.GetStart() );
1313 OSL_ENSURE( getFrameArea().Pos().Y() + getFramePrintArea().Pos().Y() == rLine.GetFirstPos(),
1314 "SwTextFrame::FormatLine: frame leaves orbit." );
1315 OSL_ENSURE( rLine.GetCurr()->Height(),
1316 "SwTextFrame::FormatLine: line height is zero" );
1318 // The current line break object
1319 const SwLineLayout *pNew = rLine.GetCurr();
1321 bool bUnChg = nOldLen == pNew->GetLen() &&
1322 bOldHyph == pNew->IsEndHyph();
1323 if ( bUnChg && !bPrev )
1325 const tools::Long nWidthDiff = nOldWidth > pNew->Width()
1326 ? nOldWidth - pNew->Width()
1327 : pNew->Width() - nOldWidth;
1329 // we only declare a line as unchanged, if its main values have not
1330 // changed and it is not the last line (!paragraph end symbol!)
1331 bUnChg = nOldHeight == pNew->Height() &&
1332 nOldAscent == pNew->GetAscent() &&
1333 nWidthDiff <= SLOPPY_TWIPS &&
1334 pOldCur->GetNext();
1337 // Calculate rRepaint
1338 const SwTwips nBottom = rLine.Y() + rLine.GetLineHeight();
1339 SwRepaint &rRepaint = pPara->GetRepaint();
1340 if( bUnChg && rRepaint.Top() == rLine.Y()
1341 && (bPrev || nNewStart <= pPara->GetReformat().Start())
1342 && (nNewStart < TextFrameIndex(GetText().getLength())))
1344 rRepaint.Top( nBottom );
1345 rRepaint.Height( 0 );
1347 else
1349 if( nOldTop )
1351 if( nOldTop < rRepaint.Top() )
1352 rRepaint.Top( nOldTop );
1353 if( !rLine.IsUnclipped() || nOldBottom > rRepaint.Bottom() )
1355 rRepaint.Bottom( nOldBottom - 1 );
1356 rLine.SetUnclipped( true );
1359 if( rLine.GetCurr()->IsClipping() && rLine.IsFlyInCntBase() )
1361 SwTwips nTmpTop, nTmpBottom;
1362 rLine.CalcUnclipped( nTmpTop, nTmpBottom );
1363 if( nTmpTop < rRepaint.Top() )
1364 rRepaint.Top( nTmpTop );
1365 if( !rLine.IsUnclipped() || nTmpBottom > rRepaint.Bottom() )
1367 rRepaint.Bottom( nTmpBottom - 1 );
1368 rLine.SetUnclipped( true );
1371 else
1373 if( !rLine.IsUnclipped() || nBottom > rRepaint.Bottom() )
1375 rRepaint.Bottom( nBottom - 1 );
1376 rLine.SetUnclipped( false );
1379 SwTwips nRght = std::max( nOldWidth, pNew->Width() +
1380 pNew->GetHangingMargin() );
1381 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1382 const SwViewOption *pOpt = pSh ? pSh->GetViewOptions() : nullptr;
1383 if( pOpt && (pOpt->IsParagraph() || pOpt->IsLineBreak()) )
1384 nRght += ( std::max( nOldAscent, pNew->GetAscent() ) );
1385 else
1386 nRght += ( std::max( nOldAscent, pNew->GetAscent() ) / 4);
1387 nRght += rLine.GetLeftMargin();
1388 if( rRepaint.GetOffset() || rRepaint.GetRightOfst() < nRght )
1389 rRepaint.SetRightOfst( nRght );
1391 // Finally we enlarge the repaint rectangle if we found an underscore
1392 // within our line. 40 Twips should be enough
1393 const bool bHasUnderscore =
1394 ( rLine.GetInfo().GetUnderScorePos() < nNewStart );
1395 if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() )
1396 rRepaint.Bottom( rRepaint.Bottom() + 40 );
1398 const_cast<SwLineLayout*>(rLine.GetCurr())->SetUnderscore( bHasUnderscore );
1401 // Calculating the good ol' nDelta
1402 const sal_Int32 nDiff = sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);
1403 pPara->SetDelta(pPara->GetDelta() - nDiff);
1405 // Stop!
1406 if( rLine.IsStop() )
1407 return false;
1409 // Absolutely another line
1410 if( rLine.IsNewLine() )
1411 return true;
1413 // Until the String's end?
1414 if (nNewStart >= TextFrameIndex(GetText().getLength()))
1415 return false;
1417 if( rLine.GetInfo().IsShift() )
1418 return true;
1420 // Reached the Reformat's end?
1421 const TextFrameIndex nEnd = pPara->GetReformat().Start() +
1422 pPara->GetReformat().Len();
1424 if( nNewStart <= nEnd )
1425 return true;
1427 return 0 != pPara->GetDelta();
1430 void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
1431 const bool bAdjust )
1433 OSL_ENSURE( ! IsVertical() || IsSwapped(),"SwTextFrame::Format_ with unswapped frame" );
1435 SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1436 rLine.SetUnclipped( false );
1438 const OUString & rString = GetText();
1439 const TextFrameIndex nStrLen(rString.getLength());
1441 SwCharRange &rReformat = pPara->GetReformat();
1442 SwRepaint &rRepaint = pPara->GetRepaint();
1443 std::unique_ptr<SwRepaint> pFreeze;
1445 // Due to performance reasons we set rReformat to COMPLETE_STRING in Init()
1446 // In this case we adjust rReformat
1447 if( rReformat.Len() > nStrLen )
1448 rReformat.Len() = nStrLen;
1450 if( rReformat.Start() + rReformat.Len() > nStrLen )
1451 rReformat.Len() = nStrLen - rReformat.Start();
1453 SwTwips nOldBottom;
1454 if( GetOffset() && !IsFollow() )
1456 rLine.Bottom();
1457 nOldBottom = rLine.Y();
1458 rLine.Top();
1460 else
1461 nOldBottom = 0;
1462 rLine.CharToLine( rReformat.Start() );
1464 // When inserting or removing a Space, words can be moved out of the edited
1465 // line and into the preceding line, hence the preceding line must be
1466 // formatted as well.
1467 // Optimization: If rReformat starts after the first word of the line,
1468 // this line cannot possibly influence the previous one.
1469 // ...Turns out that unfortunately it can: Text size changes + FlyFrames;
1470 // the feedback can affect multiple lines (Frames!)!
1472 // i#46560
1473 // FME: Yes, consider this case: "(word )" has to go to the next line
1474 // because ")" is a forbidden character at the beginning of a line although
1475 // "(word" would still fit on the previous line. Adding text right in front
1476 // of ")" would not trigger a reformatting of the previous line. Adding 1
1477 // to the result of FindBrk() does not solve the problem in all cases,
1478 // nevertheless it should be sufficient.
1479 bool bPrev = rLine.GetPrev() &&
1480 (FindBrk(rString, rLine.GetStart(), rReformat.Start() + TextFrameIndex(1))
1481 // i#46560
1482 + TextFrameIndex(1)
1483 >= rReformat.Start() ||
1484 rLine.GetCurr()->IsRest() );
1485 if( bPrev )
1487 while( rLine.Prev() )
1488 if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() )
1490 if( !rLine.GetStart() )
1491 rLine.Top(); // So that NumDone doesn't get confused
1492 break;
1494 TextFrameIndex nNew = rLine.GetStart() + rLine.GetLength();
1495 if( nNew )
1497 --nNew;
1498 if (CH_BREAK == rString[sal_Int32(nNew)])
1500 ++nNew;
1501 rLine.Next();
1502 bPrev = false;
1505 rReformat.Len() += rReformat.Start() - nNew;
1506 rReformat.Start() = nNew;
1509 rRepaint.SetOffset( 0 );
1510 rRepaint.SetRightOfst( 0 );
1511 rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
1512 if( pPara->IsMargin() )
1513 rRepaint.Width( rRepaint.Width() + pPara->GetHangingMargin() );
1514 rRepaint.Top( rLine.Y() );
1515 if( 0 >= rRepaint.Width() )
1516 rRepaint.Width(1);
1517 WidowsAndOrphans aFrameBreak( this, rInf.IsTest() ? 1 : 0 );
1519 // rLine is now set to the first line which needs formatting.
1520 // The bFirst flag makes sure that Next() is not called.
1521 // The whole thing looks weird, but we need to make sure that
1522 // rLine stops at the last non-fitting line when calling IsBreakNow.
1523 bool bFirst = true;
1524 bool bFormat = true;
1526 // The CharToLine() can also get us into the danger zone.
1527 // In that case we need to walk back until rLine is set
1528 // to the non-fitting line. Or else the mass of text is lost,
1529 // because the Ofst was set wrongly in the Follow.
1531 bool bBreak = ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 )
1532 && aFrameBreak.IsBreakNowWidAndOrp( rLine );
1533 if( bBreak )
1535 bool bPrevDone = nullptr != rLine.Prev();
1536 while( bPrevDone && aFrameBreak.IsBreakNowWidAndOrp(rLine) )
1537 bPrevDone = nullptr != rLine.Prev();
1538 if( bPrevDone )
1540 aFrameBreak.SetKeep( false );
1541 rLine.Next();
1543 rLine.TruncLines();
1545 // Play it safe
1546 aFrameBreak.IsBreakNowWidAndOrp(rLine);
1549 /* Meaning if the following flags are set:
1551 Watch(End/Mid)Hyph: we need to format if we have a break at
1552 the line end/Fly, as long as MaxHyph is reached
1554 Jump(End/Mid)Flag: the next line which has no break (line end/Fly),
1555 needs to be formatted, because we could wrap now. This might have been
1556 forbidden earlier by MaxHyph
1558 Watch(End/Mid)Hyph: if the last formatted line got a cutoff point, but
1559 didn't have one before
1561 Jump(End/Mid)Hyph: if a cutoff point disappears
1563 bool bJumpEndHyph = false;
1564 bool bWatchEndHyph = false;
1565 bool bJumpMidHyph = false;
1566 bool bWatchMidHyph = false;
1568 const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
1569 rInf.MaxHyph() = rAttrSet.GetHyphenZone().GetMaxHyphens();
1570 bool bMaxHyph = 0 != rInf.MaxHyph();
1571 if ( bMaxHyph )
1572 rLine.InitCntHyph();
1574 if( IsFollow() && IsFieldFollow() && rLine.GetStart() == GetOffset() )
1576 SwTextFrame *pMaster = FindMaster();
1577 OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
1578 const SwLineLayout* pLine=nullptr;
1579 if (pMaster)
1581 if (!pMaster->HasPara())
1582 { // master could be locked because it's being formatted upstack
1583 SAL_WARN("sw", "SwTextFrame::Format_: master not formatted!");
1585 else
1587 SwTextSizeInfo aInf( pMaster );
1588 SwTextIter aMasterLine( pMaster, &aInf );
1589 aMasterLine.Bottom();
1590 pLine = aMasterLine.GetCurr();
1591 assert(aMasterLine.GetEnd() == GetOffset());
1594 SwLinePortion* pRest = pLine ?
1595 rLine.MakeRestPortion(pLine, GetOffset()) : nullptr;
1596 if( pRest )
1597 rInf.SetRest( pRest );
1598 else
1599 SetFieldFollow( false );
1602 /* Ad cancel criterion:
1603 * In order to recognize, whether a line does not fit onto the page
1604 * anymore, we need to format it. This overflow is removed again in
1605 * e.g. AdjustFollow.
1606 * Another complication: if we are the Master, we need to traverse
1607 * the lines, because it could happen that one line can overflow
1608 * from the Follow to the Master.
1612 if( bFirst )
1613 bFirst = false;
1614 else
1616 if ( bMaxHyph )
1618 if ( rLine.GetCurr()->IsEndHyph() )
1619 rLine.CntEndHyph()++;
1620 else
1621 rLine.CntEndHyph() = 0;
1622 if ( rLine.GetCurr()->IsMidHyph() )
1623 rLine.CntMidHyph()++;
1624 else
1625 rLine.CntMidHyph() = 0;
1627 if( !rLine.Next() )
1629 if( !bFormat )
1631 SwLinePortion* pRest =
1632 rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() );
1633 if( pRest )
1634 rInf.SetRest( pRest );
1636 rLine.Insert( new SwLineLayout() );
1637 rLine.Next();
1638 bFormat = true;
1641 if ( !bFormat && bMaxHyph &&
1642 (bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) )
1644 if ( rLine.GetCurr()->IsEndHyph() )
1646 if ( bWatchEndHyph )
1647 bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
1649 else
1651 bFormat = bJumpEndHyph;
1652 bWatchEndHyph = false;
1653 bJumpEndHyph = false;
1655 if ( rLine.GetCurr()->IsMidHyph() )
1657 if ( bWatchMidHyph && !bFormat )
1658 bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
1660 else
1662 bFormat |= bJumpMidHyph;
1663 bWatchMidHyph = false;
1664 bJumpMidHyph = false;
1667 if( bFormat )
1669 const bool bOldEndHyph = rLine.GetCurr()->IsEndHyph();
1670 const bool bOldMidHyph = rLine.GetCurr()->IsMidHyph();
1671 bFormat = FormatLine( rLine, bPrev );
1672 // There can only be one bPrev ... (???)
1673 bPrev = false;
1674 if ( bMaxHyph )
1676 if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph )
1678 bWatchEndHyph = !bOldEndHyph;
1679 bJumpEndHyph = bOldEndHyph;
1681 if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph )
1683 bWatchMidHyph = !bOldMidHyph;
1684 bJumpMidHyph = bOldMidHyph;
1689 if( !rInf.IsNewLine() )
1691 if( !bFormat )
1692 bFormat = nullptr != rInf.GetRest();
1693 if( rInf.IsStop() || rInf.GetIdx() >= nStrLen )
1694 break;
1695 if( !bFormat && ( !bMaxHyph || ( !bWatchEndHyph &&
1696 !bJumpEndHyph && !bWatchMidHyph && !bJumpMidHyph ) ) )
1698 if( GetFollow() )
1700 while( rLine.Next() )
1701 ; //Nothing
1702 pFreeze.reset(new SwRepaint( rRepaint )); // to minimize painting
1704 else
1705 break;
1708 bBreak = aFrameBreak.IsBreakNowWidAndOrp(rLine);
1709 }while( !bBreak );
1711 if( pFreeze )
1713 rRepaint = *pFreeze;
1714 pFreeze.reset();
1717 if( !rLine.IsStop() )
1719 // If we're finished formatting the text and we still
1720 // have other line objects left, these are superfluous
1721 // now because the text has gotten shorter.
1722 bool bTruncLines = false;
1723 if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
1724 rLine.GetCurr()->GetNext() )
1726 bTruncLines = true;
1728 else if (GetMergedPara() && rLine.GetCurr()->GetNext())
1730 // We can also have superfluous lines with redlining in case the current line is shorter
1731 // than the text length, but the total length of lines is still more than expected.
1732 // Truncate in this case as well.
1733 TextFrameIndex nLen(0);
1734 for (const SwLineLayout* pLine = pPara; pLine; pLine = pLine->GetNext())
1736 nLen += pLine->GetLen();
1738 bTruncLines = nLen > nStrLen;
1741 if (bTruncLines)
1743 rLine.TruncLines();
1744 rLine.SetTruncLines( true );
1748 if( rInf.IsTest() )
1749 return;
1751 // FormatAdjust does not pay off at OnceMore
1752 if( bAdjust || !rLine.GetDropFormat() || !rLine.CalcOnceMore() )
1754 FormatAdjust( rLine, aFrameBreak, nStrLen, rInf.IsStop() );
1756 if( rRepaint.HasArea() )
1757 SetRepaint();
1758 rLine.SetTruncLines( false );
1759 if( nOldBottom ) // We check whether paragraphs that need scrolling can
1760 // be shrunk, so that they don't need scrolling anymore
1762 rLine.Bottom();
1763 SwTwips nNewBottom = rLine.Y();
1764 if( nNewBottom < nOldBottom )
1765 SetOffset_(TextFrameIndex(0));
1769 void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
1771 OSL_ENSURE( ! IsVertical() || IsSwapped(),
1772 "A frame is not swapped in SwTextFrame::FormatOnceMore" );
1774 SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1775 if( !pPara )
1776 return;
1778 // If necessary the pPara
1779 sal_uInt16 nOld = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
1780 bool bShrink = false;
1781 bool bGrow = false;
1782 bool bGoOn = rLine.IsOnceMore();
1783 sal_uInt8 nGo = 0;
1784 while( bGoOn )
1786 ++nGo;
1787 rInf.Init();
1788 rLine.Top();
1789 if( !rLine.GetDropFormat() )
1790 rLine.SetOnceMore( false );
1791 SwCharRange aRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
1792 pPara->GetReformat() = aRange;
1793 Format_( rLine, rInf );
1795 bGoOn = rLine.IsOnceMore();
1796 if( bGoOn )
1798 const sal_uInt16 nNew = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
1799 if( nOld == nNew )
1800 bGoOn = false;
1801 else
1803 if( nOld > nNew )
1804 bShrink = true;
1805 else
1806 bGrow = true;
1808 if( bShrink == bGrow || 5 < nGo )
1809 bGoOn = false;
1811 nOld = nNew;
1814 // If something went wrong, we need to reformat again
1815 if( !bGoOn )
1817 rInf.CtorInitTextFormatInfo( getRootFrame()->GetCurrShell()->GetOut(), this );
1818 rLine.CtorInitTextFormatter( this, &rInf );
1819 rLine.SetDropLines( 1 );
1820 rLine.CalcDropHeight( 1 );
1821 SwCharRange aTmpRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
1822 pPara->GetReformat() = aTmpRange;
1823 Format_( rLine, rInf, true );
1824 // We paint everything ...
1825 SetCompletePaint();
1831 void SwTextFrame::FormatImpl(vcl::RenderContext* pRenderContext, SwParaPortion *pPara,
1832 std::vector<SwAnchoredObject *> & rIntersectingObjs)
1834 const bool bIsEmpty = GetText().isEmpty();
1836 if ( bIsEmpty )
1838 // Empty lines do not get tortured for very long:
1839 // pPara is cleared, which is the same as:
1840 // *pPara = SwParaPortion;
1841 const bool bMustFit = pPara->IsPrepMustFit();
1842 pPara->Truncate();
1843 pPara->FormatReset();
1845 // delete pSpaceAdd and pKanaComp
1846 pPara->FinishSpaceAdd();
1847 pPara->FinishKanaComp();
1848 pPara->ResetFlags();
1849 pPara->SetPrepMustFit( bMustFit );
1852 OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Format_" );
1854 if ( IsVertical() )
1855 SwapWidthAndHeight();
1857 SwTextFormatInfo aInf( pRenderContext, this );
1858 SwTextFormatter aLine( this, &aInf );
1860 HideAndShowObjects();
1862 Format_( aLine, aInf );
1864 if( aLine.IsOnceMore() )
1865 FormatOnceMore( aLine, aInf );
1867 if (aInf.GetTextFly().IsOn())
1869 SwRect const aRect(aInf.GetTextFly().GetFrameArea());
1870 for (SwAnchoredObject *const pObj : *aInf.GetTextFly().GetAnchoredObjList())
1872 if (!aInf.GetTextFly().AnchoredObjToRect(pObj, aRect).IsEmpty())
1874 rIntersectingObjs.push_back(pObj);
1879 if ( IsVertical() )
1880 SwapWidthAndHeight();
1882 OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Format_" );
1884 if( 1 >= aLine.GetDropLines() )
1885 return;
1887 if( SvxAdjust::Left != aLine.GetAdjust() &&
1888 SvxAdjust::Block != aLine.GetAdjust() )
1890 aLine.CalcDropAdjust();
1891 aLine.SetPaintDrop( true );
1894 if( aLine.IsPaintDrop() )
1896 aLine.CalcDropRepaint();
1897 aLine.SetPaintDrop( false );
1901 // We calculate the text frame's size and send a notification.
1902 // Shrink() or Grow() to adjust the frame's size to the changed required space.
1903 void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
1905 SwRectFnSet aRectFnSet(this);
1907 CalcAdditionalFirstLineOffset();
1909 // The range autopilot or the BASIC interface pass us TextFrames with
1910 // a width <= 0 from time to time
1911 if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
1913 // If MustFit is set, we shrink to the Upper's bottom edge if needed.
1914 SwTextLineAccess aAccess( this );
1916 if( aAccess.GetPara()->IsPrepMustFit() )
1918 const SwTwips nLimit = aRectFnSet.GetPrtBottom(*GetUpper());
1919 const SwTwips nDiff = - aRectFnSet.BottomDist( getFrameArea(), nLimit );
1920 if( nDiff > 0 )
1921 Shrink( nDiff );
1924 tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
1925 const tools::Long nTop = aRectFnSet.GetTopMargin(*this);
1927 if( nTop > nFrameHeight )
1929 aRectFnSet.SetYMargins( *this, nFrameHeight, 0 );
1931 else if( aRectFnSet.GetHeight(getFramePrintArea()) < 0 )
1933 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1934 aRectFnSet.SetHeight( aPrt, 0 );
1937 return;
1940 TextFrameIndex nStrLen(GetText().getLength());
1942 if (HasNonLastSplitFlyDrawObj())
1944 // Non-last part of split fly anchor: consider this empty.
1945 nStrLen = TextFrameIndex(0);
1948 if ( nStrLen || !FormatEmpty() )
1951 SetEmpty( false );
1952 // In order to not get confused by nested Formats
1953 FormatLevel aLevel;
1954 if( 12 == FormatLevel::GetLevel() )
1955 return;
1957 // We could be possibly not allowed to alter the format information
1958 if( IsLocked() )
1959 return;
1961 // Attention: Format() could be triggered by GetFormatted()
1962 if( IsHiddenNow() )
1964 tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
1965 if( nPrtHeight )
1967 HideHidden();
1968 Shrink( nPrtHeight );
1970 else
1972 // Assure that objects anchored
1973 // at paragraph resp. at/as character inside paragraph
1974 // are hidden.
1975 HideAndShowObjects();
1977 ChgThisLines();
1978 return;
1981 // We do not want to be interrupted during formatting
1982 TextFrameLockGuard aLock(this);
1984 // this is to ensure that the similar code in SwTextFrame::Format_
1985 // finds the master formatted in case it's needed
1986 if (IsFollow() && IsFieldFollow())
1988 SwTextFrame *pMaster = FindMaster();
1989 assert(pMaster);
1990 if (!pMaster->HasPara())
1992 pMaster->GetFormatted();
1994 if (!pMaster->HasPara())
1995 { // master could be locked because it's being formatted upstack
1996 SAL_WARN("sw", "SwTextFrame::Format: failed to format master!");
1998 else
2000 SwTextSizeInfo aInf( pMaster );
2001 SwTextIter aMasterLine( pMaster, &aInf );
2002 aMasterLine.Bottom();
2003 SetOffset(aMasterLine.GetEnd());
2007 SwTextLineAccess aAccess( this );
2008 const bool bNew = !aAccess.IsAvailable();
2009 const bool bSetOffset =
2010 (GetOffset() && GetOffset() > TextFrameIndex(GetText().getLength()));
2012 if( CalcPreps() )
2013 ; // nothing
2014 // We return if already formatted, but if the TextFrame was just created
2015 // and does not have any format information
2016 else if( !bNew && !aAccess.GetPara()->GetReformat().Len() )
2018 if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
2020 aAccess.GetPara()->SetPrepAdjust();
2021 aAccess.GetPara()->SetPrep();
2022 CalcPreps();
2024 SetWidow( false );
2026 else if( bSetOffset && IsFollow() )
2028 SwTextFrame *pMaster = FindMaster();
2029 OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
2030 if( pMaster )
2031 pMaster->Prepare( PrepareHint::FollowFollows );
2032 SwTwips nMaxY = aRectFnSet.GetPrtBottom(*GetUpper());
2034 if( aRectFnSet.OverStep( getFrameArea(), nMaxY ) )
2036 aRectFnSet.SetLimit( *this, nMaxY );
2038 else if( aRectFnSet.BottomDist( getFrameArea(), nMaxY ) < 0 )
2040 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2041 aRectFnSet.AddBottom( aFrm, -aRectFnSet.GetHeight(aFrm) );
2044 else
2046 // bSetOffset here means that we have the "red arrow situation"
2047 if ( bSetOffset )
2048 SetOffset_(TextFrameIndex(0));
2050 const bool bOrphan = IsWidow();
2051 const SwFootnoteBossFrame* pFootnoteBoss = HasFootnote() ? FindFootnoteBossFrame() : nullptr;
2052 SwTwips nFootnoteHeight = 0;
2053 if( pFootnoteBoss )
2055 const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
2056 nFootnoteHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
2060 ::std::vector<SwAnchoredObject *> intersectingObjs;
2061 ::std::vector<SwFrame const*> nexts;
2062 for (SwFrame const* pNext = GetNext(); pNext; pNext = pNext->GetNext())
2064 nexts.push_back(pNext);
2066 FormatImpl(pRenderContext, aAccess.GetPara(), intersectingObjs);
2067 if( pFootnoteBoss && nFootnoteHeight )
2069 const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
2070 SwTwips nNewHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
2071 // If we lost some footnotes, we may have more space
2072 // for our main text, so we have to format again ...
2073 if( nNewHeight < nFootnoteHeight )
2075 nFootnoteHeight = nNewHeight;
2076 continue;
2079 if (!intersectingObjs.empty())
2081 // assumption is that FormatImpl() only moves frames
2082 // in the next-chain to next page
2083 SwPageFrame *const pPage(FindPageFrame());
2084 SwTextFrame * pLastMovedAnchor(nullptr);
2085 auto lastIter(nexts.end());
2086 for (SwAnchoredObject *const pObj : intersectingObjs)
2088 SwFrame *const pAnchor(pObj->AnchorFrame());
2089 SwPageFrame *const pAnchorPage(pAnchor->FindPageFrame());
2090 if (pAnchorPage != pPage)
2092 auto const iter(::std::find(nexts.begin(), nexts.end(), pAnchor));
2093 if (iter != nexts.end())
2095 assert(pAnchor->IsTextFrame());
2096 // (can't check SwOszControl::IsInProgress()?)
2097 // called in loop in FormatAnchorFrameAndItsPrevs()
2098 if (static_cast<SwTextFrame const*>(pAnchor)->IsJoinLocked()
2099 // called in loop in SwFrame::PrepareMake()
2100 || pAnchor->IsDeleteForbidden())
2102 // when called via FormatAnchorFrameAndItsPrevs():
2103 // don't do anything, caller will handle it
2104 pLastMovedAnchor = nullptr;
2105 break;
2107 assert(pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()); // how could it move backward?
2109 if (!pLastMovedAnchor || iter < lastIter)
2111 pLastMovedAnchor = static_cast<SwTextFrame *>(pAnchor);
2112 lastIter = iter;
2117 SwPageFrame const*const pPrevPage(static_cast<SwPageFrame const*>(pPage->GetPrev()));
2118 if (pLastMovedAnchor)
2120 for (SwAnchoredObject *const pObj : intersectingObjs)
2122 if (pObj->AnchorFrame() == pLastMovedAnchor)
2124 SwPageFrame *const pAnchorPage(pLastMovedAnchor->FindPageFrame());
2125 SAL_INFO("sw.layout", "SwTextFrame::Format: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
2126 pObj->RegisterAtPage(*pAnchorPage);
2127 // tdf#143239 if the position remains valid, it may not be
2128 // positioned again so would remain on the wrong page!
2129 pObj->InvalidateObjPos();
2130 ::Notify_Background(pObj->GetDrawObj(), pPage,
2131 pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
2132 pObj->SetForceNotifyNewBackground(true);
2135 if (GetFollow() // this frame was split
2136 && (!pPrevPage // prev page is still valid
2137 || (!pPrevPage->IsInvalid()
2138 && (!pPrevPage->GetSortedObjs() || !pPrevPage->IsInvalidFly()))))
2139 { // this seems a bit risky...
2140 SwLayouter::InsertMovedFwdFrame(GetTextNodeFirst()->GetDoc(),
2141 *pLastMovedAnchor, FindPageFrame()->GetPhyPageNum() + 1);
2143 continue; // try again without the fly
2146 break;
2147 } while ( pFootnoteBoss );
2148 if( bOrphan )
2150 ValidateFrame();
2151 SetWidow( false );
2154 if( IsEmptyMaster() )
2156 SwFrame* pPre = GetPrev();
2157 if( pPre &&
2158 // i#10826 It's the first, it cannot keep!
2159 pPre->GetIndPrev() &&
2160 pPre->GetAttrSet()->GetKeep().GetValue() )
2162 pPre->InvalidatePos();
2165 if (IsEmptyMasterWithSplitFly())
2167 // A fly is anchored to us, reduce size, so we definitely still fit the current
2168 // page.
2169 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2170 aRectFnSet.SetHeight(aFrm, 0);
2172 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2173 aRectFnSet.SetTop(aPrt, 0);
2174 aRectFnSet.SetHeight(aPrt, 0);
2179 ChgThisLines();
2181 // the PrepMustFit should not survive a Format operation
2182 SwParaPortion *pPara = GetPara();
2183 if ( pPara )
2184 pPara->SetPrepMustFit( false );
2186 CalcBaseOfstForFly();
2187 CalcHeightOfLastLine(); // i#11860 - Adjust spacing implementation for
2188 // object positioning - Compatibility to MS Word
2189 // tdf#117982 -- Fix cell spacing hides content
2190 // Check if the cell's content has greater size than the row height
2191 if (IsInTab() && GetUpper() && ((GetUpper()->getFramePrintArea().Height() < getFramePrintArea().Height())
2192 || (getFramePrintArea().Height() <= 0)))
2194 SAL_INFO("sw.core", "Warn: Cell content has greater size than cell height!");
2195 //get font size...
2196 SwTwips aTmpHeight = getFrameArea().Height();
2197 //...and push it into the text frame
2198 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2199 //if only bottom margin what we have:
2200 if (GetTopMargin() == 0)
2201 //set the frame to its original location
2202 aPrt.SetTopAndHeight(0, aTmpHeight);
2206 // bForceQuickFormat is set if GetFormatted() has been called during the
2207 // painting process. Actually I cannot imagine a situation which requires
2208 // a full formatting of the paragraph during painting, on the other hand
2209 // a full formatting can cause the invalidation of other layout frames,
2210 // e.g., if there are footnotes in this paragraph, and invalid layout
2211 // frames will not calculated during the painting. So I actually want to
2212 // avoid a formatting during painting, but since I'm a coward, I'll only
2213 // force the quick formatting in the situation of issue i29062.
2214 bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
2216 OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
2217 "SwTextFrame::FormatQuick with swapped frame" );
2219 if( IsEmpty() && FormatEmpty() )
2220 return true;
2222 // We're very picky:
2223 if( HasPara() || IsWidow() || IsLocked()
2224 || !isFrameAreaSizeValid() ||
2225 ( ( IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height() ) && IsHiddenNow() ) )
2226 return false;
2228 SwTextLineAccess aAccess( this );
2229 SwParaPortion *pPara = aAccess.GetPara();
2230 if( !pPara )
2231 return false;
2233 SwFrameSwapper aSwapper( this, true );
2235 TextFrameLockGuard aLock(this);
2236 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true );
2237 if( 0 != aInf.MaxHyph() ) // Respect MaxHyphen!
2238 return false;
2240 SwTextFormatter aLine( this, &aInf );
2242 // DropCaps are too complicated ...
2243 if( aLine.GetDropFormat() )
2244 return false;
2246 TextFrameIndex nStart = GetOffset();
2247 const TextFrameIndex nEnd = GetFollow()
2248 ? GetFollow()->GetOffset()
2249 : TextFrameIndex(aInf.GetText().getLength());
2251 int nLoopProtection = 0;
2254 TextFrameIndex nNewStart = aLine.FormatLine(nStart);
2255 if (nNewStart == nStart)
2256 ++nLoopProtection;
2257 else
2258 nLoopProtection = 0;
2259 nStart = nNewStart;
2260 const bool bWillEndlessInsert = nLoopProtection > 250;
2261 SAL_WARN_IF(bWillEndlessInsert, "sw", "loop detection triggered");
2262 if ((!bWillEndlessInsert) // Check for special case: line is invisible,
2263 // like in too thin table cell: tdf#66141
2264 && (aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd)))
2265 aLine.Insert( new SwLineLayout() );
2266 } while( aLine.Next() );
2268 // Last exit: the heights need to match
2269 Point aTopLeft( getFrameArea().Pos() );
2270 aTopLeft += getFramePrintArea().Pos();
2271 const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight();
2272 const SwTwips nOldHeight = aTopLeft.Y() + getFramePrintArea().Height();
2274 if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() )
2276 // Attention: This situation can occur due to FormatLevel==12. Don't panic!
2277 TextFrameIndex const nStrt = GetOffset();
2278 InvalidateRange_( SwCharRange( nStrt, nEnd - nStrt) );
2279 return false;
2282 if (m_pFollow && nStart != static_cast<SwTextFrame*>(m_pFollow)->GetOffset())
2283 return false; // can be caused by e.g. Orphans
2285 // We made it!
2287 // Set repaint
2288 pPara->GetRepaint().Pos( aTopLeft );
2289 pPara->GetRepaint().SSize( getFramePrintArea().SSize() );
2291 // Delete reformat
2292 pPara->GetReformat() = SwCharRange();
2293 pPara->SetDelta(0);
2295 return true;
2298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */