1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <bodyfrm.hxx>
24 #include <hintids.hxx>
25 #include <editeng/keepitem.hxx>
26 #include <editeng/hyphenzoneitem.hxx>
27 #include <pagefrm.hxx>
28 #include <ndtxt.hxx>
29 #include <dcontact.hxx>
30 #include <dflyobj.hxx>
31 #include <flyfrm.hxx>
32 #include <ftnfrm.hxx>
33 #include <txtftn.hxx>
34 #include <fmtftn.hxx>
35 #include <paratr.hxx>
36 #include <viewopt.hxx>
37 #include <viewsh.hxx>
38 #include <frmatr.hxx>
39 #include <pam.hxx>
40 #include <flyfrms.hxx>
41 #include <fmtanchr.hxx>
42 #include "itrform2.hxx"
43 #include "widorp.hxx"
44 #include "txtcache.hxx"
45 #include "porrst.hxx"
46 #include <blink.hxx>
47 #include "porfld.hxx"
48 #include <sectfrm.hxx>
49 #include "pormulti.hxx"
50 #include <rootfrm.hxx>
51 #include <frmfmt.hxx>
52 #include <sortedobjs.hxx>
53 #include "portab.hxx"
54 #include <editeng/lrspitem.hxx>
55 #include <editeng/tstpitem.hxx>
56 #include <redline.hxx>
57 #include <comphelper/lok.hxx>
58 
59 // Tolerance in formatting and text output
60 #define SLOPPY_TWIPS    5
61 
62 class FormatLevel
63 {
64     static sal_uInt16 nLevel;
65 public:
FormatLevel()66     FormatLevel()  { ++nLevel; }
~FormatLevel()67     ~FormatLevel() { --nLevel; }
GetLevel()68     static sal_uInt16 GetLevel() { return nLevel; }
LastLevel()69     static bool LastLevel() { return 10 < nLevel; }
70 };
71 sal_uInt16 FormatLevel::nLevel = 0;
72 
ValidateText(SwFrame * pFrame)73 void ValidateText( SwFrame *pFrame )     // Friend of frame
74 {
75     if ( ( ! pFrame->IsVertical() &&
76              pFrame->getFrameArea().Width() == pFrame->GetUpper()->getFramePrintArea().Width() ) ||
77          (   pFrame->IsVertical() &&
78              pFrame->getFrameArea().Height() == pFrame->GetUpper()->getFramePrintArea().Height() ) )
79     {
80         pFrame->setFrameAreaSizeValid(true);
81     }
82 }
83 
ValidateFrame()84 void SwTextFrame::ValidateFrame()
85 {
86     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
87     // Validate surroundings to avoid oscillation
88     SwSwapIfSwapped swap( this );
89 
90     if ( !IsInFly() && !IsInTab() )
91     {   // Only validate 'this' when inside a fly, the rest should actually only be
92         // needed for footnotes, which do not exist in flys.
93         SwSectionFrame* pSct = FindSctFrame();
94         if( pSct )
95         {
96             if( !pSct->IsColLocked() )
97                 pSct->ColLock();
98             else
99                 pSct = nullptr;
100         }
101 
102         SwFrame *pUp = GetUpper();
103         pUp->Calc(pRenderContext);
104         if( pSct )
105             pSct->ColUnlock();
106     }
107     ValidateText( this );
108 
109     // We at least have to save the MustFit flag!
110     assert(HasPara() && "ResetPreps(), missing ParaPortion, SwCache bug?");
111     SwParaPortion *pPara = GetPara();
112     const bool bMustFit = pPara->IsPrepMustFit();
113     ResetPreps();
114     pPara->SetPrepMustFit( bMustFit );
115 }
116 
117 // After a RemoveFootnote the BodyFrame and all Frames contained within it, need to be
118 // recalculated, so that the DeadLine is right.
119 // First we search outwards, on the way back we calculate everything.
ValidateBodyFrame_(SwFrame * pFrame)120 static void ValidateBodyFrame_( SwFrame *pFrame )
121 {
122     vcl::RenderContext* pRenderContext = pFrame ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
123     if( pFrame && !pFrame->IsCellFrame() )
124     {
125         if( !pFrame->IsBodyFrame() && pFrame->GetUpper() )
126             ValidateBodyFrame_( pFrame->GetUpper() );
127         if( !pFrame->IsSctFrame() )
128             pFrame->Calc(pRenderContext);
129         else
130         {
131             const bool bOld = static_cast<SwSectionFrame*>(pFrame)->IsContentLocked();
132             static_cast<SwSectionFrame*>(pFrame)->SetContentLock( true );
133             pFrame->Calc(pRenderContext);
134             if( !bOld )
135                 static_cast<SwSectionFrame*>(pFrame)->SetContentLock( false );
136         }
137     }
138 }
139 
ValidateBodyFrame()140 void SwTextFrame::ValidateBodyFrame()
141 {
142     SwSwapIfSwapped swap( this );
143 
144      // See comment in ValidateFrame()
145     if ( !IsInFly() && !IsInTab() &&
146          !( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() ) )
147         ValidateBodyFrame_( GetUpper() );
148 }
149 
GetDropRect_(SwRect & rRect) const150 bool SwTextFrame::GetDropRect_( SwRect &rRect ) const
151 {
152     SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
153 
154     OSL_ENSURE( HasPara(), "SwTextFrame::GetDropRect_: try again next year." );
155     SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
156     SwTextMargin aLine( const_cast<SwTextFrame*>(this), &aInf );
157     if( aLine.GetDropLines() )
158     {
159         rRect.Top( aLine.Y() );
160         rRect.Left( aLine.GetLineStart() );
161         rRect.Height( aLine.GetDropHeight() );
162         rRect.Width( aLine.GetDropLeft() );
163 
164         if ( IsRightToLeft() )
165             SwitchLTRtoRTL( rRect );
166 
167         if ( IsVertical() )
168             SwitchHorizontalToVertical( rRect );
169         return true;
170     }
171 
172     return false;
173 }
174 
CalcFollow(TextFrameIndex const nTextOfst)175 bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
176 {
177     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
178     SwSwapIfSwapped swap( this );
179 
180     OSL_ENSURE( HasFollow(), "CalcFollow: missing Follow." );
181 
182     SwTextFrame* pMyFollow = GetFollow();
183 
184     SwParaPortion *pPara = GetPara();
185     const bool bFollowField = pPara && pPara->IsFollowField();
186 
187     if( !pMyFollow->GetOfst() || pMyFollow->GetOfst() != nTextOfst ||
188         bFollowField || pMyFollow->IsFieldFollow() ||
189         ( pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Width() ) ||
190         ( ! pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Height() ) )
191     {
192 #if OSL_DEBUG_LEVEL > 0
193         const SwFrame *pOldUp = GetUpper();
194 #endif
195 
196         SwRectFnSet aRectFnSet(this);
197         SwTwips nOldBottom = aRectFnSet.GetBottom(GetUpper()->getFrameArea());
198         SwTwips nMyPos = aRectFnSet.GetTop(getFrameArea());
199 
200         const SwPageFrame *pPage = nullptr;
201         bool bOldInvaContent = true;
202         if ( !IsInFly() && GetNext() )
203         {
204             pPage = FindPageFrame();
205             // Minimize (reset if possible) invalidations: see below
206             bOldInvaContent  = pPage->IsInvalidContent();
207         }
208 
209         pMyFollow->SetOfst_( nTextOfst );
210         pMyFollow->SetFieldFollow( bFollowField );
211         if( HasFootnote() || pMyFollow->HasFootnote() )
212         {
213             ValidateFrame();
214             ValidateBodyFrame();
215             if( pPara )
216             {
217                 pPara->GetReformat() = SwCharRange();
218                 pPara->GetDelta() = 0;
219             }
220         }
221 
222         // The footnote area must not get larger
223         SwSaveFootnoteHeight aSave( FindFootnoteBossFrame( true ), LONG_MAX );
224 
225         pMyFollow->CalcFootnoteFlag();
226         if ( !pMyFollow->GetNext() && !pMyFollow->HasFootnote() )
227             nOldBottom =  aRectFnSet.IsVert() ? 0 : LONG_MAX;
228 
229         // tdf#122892 check flag:
230         // 1. WidowsAndOrphans::FindWidows() determines follow is a widow
231         // 2. SwTextFrame::PrepWidows() calls SetPrepWidows() on master;
232         //    if it can spare lines, master truncates one line
233         // 3. SwTextFrame::CalcPreps() on master (below);
234         //    unless IsPrepMustFit(), if master hasn't shrunk via 2., it will SetWidow()
235         // 4. loop must exit then, because the follow didn't grow so nothing will ever change
236         while (!IsWidow())
237         {
238             if( !FormatLevel::LastLevel() )
239             {
240                 // If the follow is contained within a column section or column
241                 // frame, we need to calculate that first. This is because the
242                 // FormatWidthCols() does not work if it is called from MakeAll
243                 // of the _locked_ follow.
244                 SwSectionFrame* pSct = pMyFollow->FindSctFrame();
245                 if( pSct && !pSct->IsAnLower( this ) )
246                 {
247                     if( pSct->GetFollow() )
248                         pSct->SimpleFormat();
249                     else if( ( pSct->IsVertical() && !pSct->getFrameArea().Width() ) ||
250                              ( ! pSct->IsVertical() && !pSct->getFrameArea().Height() ) )
251                         break;
252                 }
253                 // i#11760 - Intrinsic format of follow is controlled.
254                 if ( FollowFormatAllowed() )
255                 {
256                     // i#11760 - No nested format of follows, if
257                     // text frame is contained in a column frame.
258                     // Thus, forbid intrinsic format of follow.
259                     {
260                         bool bIsFollowInColumn = false;
261                         SwFrame* pFollowUpper = pMyFollow->GetUpper();
262                         while ( pFollowUpper )
263                         {
264                             if ( pFollowUpper->IsColumnFrame() )
265                             {
266                                 bIsFollowInColumn = true;
267                                 break;
268                             }
269                             if ( pFollowUpper->IsPageFrame() ||
270                                  pFollowUpper->IsFlyFrame() )
271                             {
272                                 break;
273                             }
274                             pFollowUpper = pFollowUpper->GetUpper();
275                         }
276                         if ( bIsFollowInColumn )
277                         {
278                             pMyFollow->ForbidFollowFormat();
279                         }
280                     }
281 
282                     pMyFollow->Calc(pRenderContext);
283                     // The Follow can tell from its getFrameArea().Height() that something went wrong
284                     OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: cheesy follow" );
285                     if( pMyFollow->GetPrev() )
286                     {
287                         pMyFollow->Prepare();
288                         pMyFollow->Calc(pRenderContext);
289                         OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: very cheesy follow" );
290                     }
291 
292                     // i#11760 - Reset control flag for follow format.
293                     pMyFollow->AllowFollowFormat();
294                 }
295 
296                 // Make sure that the Follow gets painted
297                 pMyFollow->SetCompletePaint();
298             }
299 
300             pPara = GetPara();
301             // As long as the Follow requests lines due to Orphans, it is
302             // passed these and is formatted again if possible
303             if( pPara && pPara->IsPrepWidows() )
304                 CalcPreps();
305             else
306                 break;
307         }
308 
309         if( HasFootnote() || pMyFollow->HasFootnote() )
310         {
311             ValidateBodyFrame();
312             ValidateFrame();
313             if( pPara )
314             {
315                 pPara->GetReformat() = SwCharRange();
316                 pPara->GetDelta() = 0;
317             }
318         }
319 
320         if ( pPage  && !bOldInvaContent )
321             pPage->ValidateContent();
322 
323 #if OSL_DEBUG_LEVEL > 0
324         OSL_ENSURE( pOldUp == GetUpper(), "SwTextFrame::CalcFollow: heavy follow" );
325 #endif
326 
327         const long nRemaining =
328                  - aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom );
329         if (  nRemaining > 0 && !GetUpper()->IsSctFrame() &&
330               nRemaining != ( aRectFnSet.IsVert() ?
331                               nMyPos - getFrameArea().Right() :
332                               getFrameArea().Top() - nMyPos ) )
333         {
334             return true;
335         }
336     }
337 
338     return false;
339 }
340 
MakePos()341 void SwTextFrame::MakePos()
342 {
343     SwFrame::MakePos();
344     // Inform LOK clients about change in position of redlines (if any)
345     if(comphelper::LibreOfficeKit::isActive())
346     {
347         SwTextNode const* pTextNode = GetTextNodeFirst();
348         const SwRedlineTable& rTable = pTextNode->getIDocumentRedlineAccess().GetRedlineTable();
349         for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
350         {
351             SwRangeRedline* pRedln = rTable[nRedlnPos];
352             if (pTextNode->GetIndex() == pRedln->GetPoint()->nNode.GetNode().GetIndex())
353             {
354                 pRedln->MaybeNotifyRedlinePositionModification(getFrameArea().Top());
355                 if (GetMergedPara()
356                     && pRedln->GetType() == RedlineType::Delete
357                     && pRedln->GetPoint()->nNode != pRedln->GetMark()->nNode)
358                 {
359                     pTextNode = pRedln->End()->nNode.GetNode().GetTextNode();
360                 }
361             }
362         }
363     }
364 }
365 
AdjustFrame(const SwTwips nChgHght,bool bHasToFit)366 void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
367 {
368     vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
369     if( IsUndersized() )
370     {
371         if( GetOfst() && !IsFollow() ) // A scrolled paragraph (undersized)
372             return;
373         SetUndersized( nChgHght == 0 || bHasToFit );
374     }
375 
376     // AdjustFrame is called with a swapped frame during
377     // formatting but the frame is not swapped during FormatEmpty
378     SwSwapIfSwapped swap( this );
379     SwRectFnSet aRectFnSet(this);
380 
381     // The Frame's size variable is incremented by Grow or decremented by Shrink.
382     // If the size cannot change, nothing should happen!
383     if( nChgHght >= 0)
384     {
385         SwTwips nChgHeight = nChgHght;
386         if( nChgHght && !bHasToFit )
387         {
388             if( IsInFootnote() && !IsInSct() )
389             {
390                 SwTwips nReal = Grow( nChgHght, true );
391                 if( nReal < nChgHght )
392                 {
393                     SwTwips nBot = aRectFnSet.YInc( aRectFnSet.GetBottom(getFrameArea()),
394                                                       nChgHght - nReal );
395                     SwFrame* pCont = FindFootnoteFrame()->GetUpper();
396 
397                     if( aRectFnSet.BottomDist( pCont->getFrameArea(), nBot ) > 0 )
398                     {
399                         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
400                         aRectFnSet.AddBottom( aFrm, nChgHght );
401 
402                         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
403 
404                         if( aRectFnSet.IsVert() )
405                         {
406                             aPrt.SSize().AdjustWidth(nChgHght );
407                         }
408                         else
409                         {
410                             aPrt.SSize().AdjustHeight(nChgHght );
411                         }
412 
413                         return;
414                     }
415                 }
416             }
417 
418             Grow( nChgHght );
419 
420             if ( IsInFly() )
421             {
422                 // If one of the Upper is a Fly, it's very likely that this fly changes its
423                 // position by the Grow. Therefore, my position has to be corrected also or
424                 // the check further down is not meaningful.
425                 // The predecessors need to be calculated, so that the position can be
426                 // calculated correctly.
427                 if ( GetPrev() )
428                 {
429                     SwFrame *pPre = GetUpper()->Lower();
430                     do
431                     {   pPre->Calc(pRenderContext);
432                         pPre = pPre->GetNext();
433                     } while ( pPre && pPre != this );
434                 }
435                 const Point aOldPos( getFrameArea().Pos() );
436                 MakePos();
437                 if ( aOldPos != getFrameArea().Pos() )
438                 {
439                     InvalidateObjs(false);
440                 }
441             }
442             nChgHeight = 0;
443         }
444         // A Grow() is always accepted by the Layout, even if the
445         // FixSize of the surrounding layout frame should not allow it.
446         // We text for this case and correct the values.
447         // The Frame must NOT be shrunk further than its size permits
448         // even in the case of an emergency.
449         SwTwips nRstHeight;
450         if ( IsVertical() )
451         {
452             OSL_ENSURE( ! IsSwapped(),"Swapped frame while calculating nRstHeight" );
453 
454             if ( IsVertLR() )
455                     nRstHeight = GetUpper()->getFrameArea().Left()
456                                + GetUpper()->getFramePrintArea().Left()
457                                + GetUpper()->getFramePrintArea().Width()
458                                - getFrameArea().Left();
459             else
460                 nRstHeight = getFrameArea().Left() + getFrameArea().Width() -
461                             ( GetUpper()->getFrameArea().Left() + GetUpper()->getFramePrintArea().Left() );
462          }
463         else
464             nRstHeight = GetUpper()->getFrameArea().Top()
465                        + GetUpper()->getFramePrintArea().Top()
466                        + GetUpper()->getFramePrintArea().Height()
467                        - getFrameArea().Top();
468 
469         // We can get a bit of space in table cells, because there could be some
470         // left through a vertical alignment to the top.
471         // Assure that first lower in upper is the current one or is valid.
472         if ( IsInTab() &&
473              ( GetUpper()->Lower() == this ||
474                GetUpper()->Lower()->isFrameAreaDefinitionValid() ) )
475         {
476             long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(GetUpper()->Lower()->getFrameArea()),
477                                             aRectFnSet.GetPrtTop(*GetUpper()) );
478             OSL_ENSURE( nAdd >= 0, "Ey" );
479             nRstHeight += nAdd;
480         }
481 
482         // nRstHeight < 0 means that the TextFrame is located completely outside of its Upper.
483         // This can happen, if it's located within a FlyAtContentFrame, which changed sides by a
484         // Grow(). In such a case, it's wrong to execute the following Grow().
485         // In the case of a bug, we end up with an infinite loop.
486         SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
487         SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
488 
489         if( nRstHeight < nFrameHeight )
490         {
491             // It can be that I have the right size, but the Upper is too small and can get me some room
492             if( ( nRstHeight >= 0 || ( IsInFootnote() && IsInSct() ) ) && !bHasToFit )
493                 nRstHeight += GetUpper()->Grow( nFrameHeight - nRstHeight );
494             // In column sections we do not want to get too big or else more areas are created by
495             // GetNextSctLeaf. Instead, we shrink and remember bUndersized, so that FormatWidthCols
496             // can calculate the right column size.
497             if ( nRstHeight < nFrameHeight )
498             {
499                 if( bHasToFit || !IsMoveable() ||
500                     ( IsInSct() && !FindSctFrame()->MoveAllowed(this) ) )
501                 {
502                     SetUndersized( true );
503                     Shrink( std::min( ( nFrameHeight - nRstHeight), nPrtHeight ) );
504                 }
505                 else
506                     SetUndersized( false );
507             }
508         }
509         else if( nChgHeight )
510         {
511             if( nRstHeight - nFrameHeight < nChgHeight )
512                 nChgHeight = nRstHeight - nFrameHeight;
513             if( nChgHeight )
514                 Grow( nChgHeight );
515         }
516     }
517     else
518         Shrink( -nChgHght );
519 }
520 
GetTabStopInfo(SwTwips CurrentPos)521 css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips CurrentPos )
522 {
523     css::uno::Sequence< css::style::TabStop > tabs(1);
524     css::style::TabStop ts;
525 
526     SwTextFormatInfo     aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
527     SwTextFormatter      aLine( this, &aInf );
528     SwTextCursor         TextCursor( this, &aInf );
529     const Point aCharPos( TextCursor.GetTopLeft() );
530 
531     SwTwips nRight = aLine.Right();
532     CurrentPos -= aCharPos.X();
533 
534     // get current tab stop information stored in the Frame
535     const SvxTabStop *pTS = aLine.GetLineInfo().GetTabStop( CurrentPos, nRight );
536 
537     if( !pTS )
538     {
539         return css::uno::Sequence< css::style::TabStop >();
540     }
541 
542     // copy tab stop information into a Sequence, which only contains one element.
543     ts.Position = pTS->GetTabPos();
544     ts.DecimalChar = pTS->GetDecimal();
545     ts.FillChar = pTS->GetFill();
546     switch( pTS->GetAdjustment() )
547     {
548     case SvxTabAdjust::Left   : ts.Alignment = css::style::TabAlign_LEFT; break;
549     case SvxTabAdjust::Center : ts.Alignment = css::style::TabAlign_CENTER; break;
550     case SvxTabAdjust::Right  : ts.Alignment = css::style::TabAlign_RIGHT; break;
551     case SvxTabAdjust::Decimal: ts.Alignment = css::style::TabAlign_DECIMAL; break;
552     case SvxTabAdjust::Default: ts.Alignment = css::style::TabAlign_DEFAULT; break;
553     default: break; // prevent warning
554     }
555 
556     tabs[0] = ts;
557     return tabs;
558 }
559 
560 // AdjustFollow expects the following situation:
561 // The SwTextIter points to the lower end of the Master, the Offset is set in the Follow.
562 // nOffset holds the Offset in the text string, from which the Master closes
563 // and the Follow starts.
564 // If it's 0, the FollowFrame is deleted.
AdjustFollow_(SwTextFormatter & rLine,const TextFrameIndex nOffset,const TextFrameIndex nEnd,const sal_uInt8 nMode)565 void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
566                  const TextFrameIndex nOffset, const TextFrameIndex nEnd,
567                              const sal_uInt8 nMode )
568 {
569     SwFrameSwapper aSwapper( this, false );
570 
571     // We got the rest of the text mass: Delete all Follows
572     // DummyPortions() are a special case.
573     // Special cases are controlled by parameter <nMode>.
574     if( HasFollow() && !(nMode & 1) && nOffset == nEnd )
575     {
576         while( GetFollow() )
577         {
578             if( GetFollow()->IsLocked() )
579             {
580                 OSL_FAIL( "+SwTextFrame::JoinFrame: Follow is locked." );
581                 return;
582             }
583             if (GetFollow()->IsDeleteForbidden())
584                 return;
585             JoinFrame();
586         }
587 
588         return;
589     }
590 
591     // Dancing on the volcano: We'll just format the last line quickly
592     // for the QuoVadis stuff.
593     // The Offset can move of course:
594     const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
595         ? rLine.FormatQuoVadis(nOffset) : nOffset;
596 
597     if( !(nMode & 1) )
598     {
599         // We steal text mass from our Follows
600         // It can happen that we have to join some of them
601         while( GetFollow() && GetFollow()->GetFollow() &&
602                nNewOfst >= GetFollow()->GetFollow()->GetOfst() )
603         {
604             JoinFrame();
605         }
606     }
607 
608     // The Offset moved
609     if( GetFollow() )
610     {
611         if ( nMode )
612             GetFollow()->ManipOfst(TextFrameIndex(0));
613 
614         if ( CalcFollow( nNewOfst ) )   // CalcFollow only at the end, we do a SetOfst there
615             rLine.SetOnceMore( true );
616     }
617 }
618 
JoinFrame()619 SwContentFrame *SwTextFrame::JoinFrame()
620 {
621     OSL_ENSURE( GetFollow(), "+SwTextFrame::JoinFrame: no follow" );
622     SwTextFrame  *pFoll = GetFollow();
623 
624     SwTextFrame *pNxt = pFoll->GetFollow();
625 
626     // All footnotes of the to-be-destroyed Follow are relocated to us
627     TextFrameIndex nStart = pFoll->GetOfst();
628     if ( pFoll->HasFootnote() )
629     {
630         SwFootnoteBossFrame *pFootnoteBoss = nullptr;
631         SwFootnoteBossFrame *pEndBoss = nullptr;
632         SwTextNode const* pNode(nullptr);
633         sw::MergedAttrIter iter(*pFoll);
634         for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
635         {
636             if (RES_TXTATR_FTN == pHt->Which()
637                 && nStart <= pFoll->MapModelToView(pNode, pHt->GetStart()))
638             {
639                 if (pHt->GetFootnote().IsEndNote())
640                 {
641                     if (!pEndBoss)
642                         pEndBoss = pFoll->FindFootnoteBossFrame();
643                     SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
644                 }
645                 else
646                 {
647                     if (!pFootnoteBoss)
648                         pFootnoteBoss = pFoll->FindFootnoteBossFrame( true );
649                     SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
650                 }
651                 SetFootnote( true );
652             }
653         }
654     }
655 
656 #ifdef DBG_UTIL
657     else if ( pFoll->isFramePrintAreaValid() ||
658               pFoll->isFrameAreaSizeValid() )
659     {
660         pFoll->CalcFootnoteFlag();
661         OSL_ENSURE( !pFoll->HasFootnote(), "Missing FootnoteFlag." );
662     }
663 #endif
664 
665     pFoll->MoveFlyInCnt( this, nStart, TextFrameIndex(COMPLETE_STRING) );
666     pFoll->SetFootnote( false );
667     // i#27138
668     // Notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
669     // Relation CONTENT_FLOWS_FROM for current next paragraph will change
670     // and relation CONTENT_FLOWS_TO for current previous paragraph, which
671     // is <this>, will change.
672     {
673         SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() );
674         if ( pViewShell && pViewShell->GetLayout() &&
675              pViewShell->GetLayout()->IsAnyShellAccessible() )
676         {
677             pViewShell->InvalidateAccessibleParaFlowRelation(
678                             dynamic_cast<SwTextFrame*>(pFoll->FindNextCnt( true )),
679                             this );
680         }
681     }
682     pFoll->Cut();
683     SetFollow(pNxt);
684     SwFrame::DestroyFrame(pFoll);
685     return pNxt;
686 }
687 
SplitFrame(TextFrameIndex const nTextPos)688 void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
689 {
690     SwSwapIfSwapped swap( this );
691 
692     // The Paste sends a Modify() to me
693     // I lock myself, so that my data does not disappear
694     TextFrameLockGuard aLock( this );
695     SwTextFrame *const pNew = static_cast<SwTextFrame *>(GetTextNodeFirst()->MakeFrame(this));
696 
697     pNew->SetFollow( GetFollow() );
698     SetFollow( pNew );
699 
700     pNew->Paste( GetUpper(), GetNext() );
701     // i#27138
702     // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
703     // Relation CONTENT_FLOWS_FROM for current next paragraph will change
704     // and relation CONTENT_FLOWS_TO for current previous paragraph, which
705     // is <this>, will change.
706     {
707         SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
708         if ( pViewShell && pViewShell->GetLayout() &&
709              pViewShell->GetLayout()->IsAnyShellAccessible() )
710         {
711             pViewShell->InvalidateAccessibleParaFlowRelation(
712                             dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )),
713                             this );
714         }
715     }
716 
717     // If footnotes end up in pNew bz our actions, we need
718     // to re-register them
719     if ( HasFootnote() )
720     {
721         SwFootnoteBossFrame *pFootnoteBoss = nullptr;
722         SwFootnoteBossFrame *pEndBoss = nullptr;
723         SwTextNode const* pNode(nullptr);
724         sw::MergedAttrIter iter(*this);
725         for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
726         {
727             if (RES_TXTATR_FTN == pHt->Which()
728                 && nTextPos <= MapModelToView(pNode, pHt->GetStart()))
729             {
730                 if (pHt->GetFootnote().IsEndNote())
731                 {
732                     if (!pEndBoss)
733                         pEndBoss = FindFootnoteBossFrame();
734                     SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
735                 }
736                 else
737                 {
738                     if (!pFootnoteBoss)
739                         pFootnoteBoss = FindFootnoteBossFrame( true );
740                     SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
741                 }
742                 pNew->SetFootnote( true );
743             }
744         }
745     }
746 
747 #ifdef DBG_UTIL
748     else
749     {
750         CalcFootnoteFlag( nTextPos - TextFrameIndex(1) );
751         OSL_ENSURE( !HasFootnote(), "Missing FootnoteFlag." );
752     }
753 #endif
754 
755     MoveFlyInCnt( pNew, nTextPos, TextFrameIndex(COMPLETE_STRING) );
756 
757     // No SetOfst or CalcFollow, because an AdjustFollow follows immediately anyways
758 
759     pNew->ManipOfst( nTextPos );
760 }
761 
SetOfst_(TextFrameIndex const nNewOfst)762 void SwTextFrame::SetOfst_(TextFrameIndex const nNewOfst)
763 {
764     // We do not need to invalidate out Follow.
765     // We are a Follow, get formatted right away and call
766     // SetOfst() from there
767     mnOffset = nNewOfst;
768     SwParaPortion *pPara = GetPara();
769     if( pPara )
770     {
771         SwCharRange &rReformat = pPara->GetReformat();
772         rReformat.Start() = TextFrameIndex(0);
773         rReformat.Len() = TextFrameIndex(GetText().getLength());
774         pPara->GetDelta() = sal_Int32(rReformat.Len());
775     }
776     InvalidateSize();
777 }
778 
CalcPreps()779 bool SwTextFrame::CalcPreps()
780 {
781     OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::CalcPreps with swapped frame" );
782     SwRectFnSet aRectFnSet(this);
783 
784     SwParaPortion *pPara = GetPara();
785     if ( !pPara )
786         return false;
787     const bool bPrep = pPara->IsPrep();
788     const bool bPrepWidows = pPara->IsPrepWidows();
789     const bool bPrepAdjust = pPara->IsPrepAdjust();
790     const bool bPrepMustFit = pPara->IsPrepMustFit();
791     ResetPreps();
792 
793     bool bRet = false;
794     if( bPrep && !pPara->GetReformat().Len() )
795     {
796         // PREP_WIDOWS means that the orphans rule got activated in the Follow.
797         // In unfortunate cases we could also have a PrepAdjust!
798         if( bPrepWidows )
799         {
800             if( !GetFollow() )
801             {
802                 OSL_ENSURE( GetFollow(), "+SwTextFrame::CalcPreps: no credits" );
803                 return false;
804             }
805 
806             // We need to prepare for two cases:
807             // We were able to hand over a few lines to the Follow
808             // -> we need to shrink
809             // or we need to go on the next page
810             // -> we let our Frame become too big
811 
812             SwTwips nChgHeight = GetParHeight();
813             if( nChgHeight >= aRectFnSet.GetHeight(getFramePrintArea()) )
814             {
815                 if( bPrepMustFit )
816                 {
817                     GetFollow()->SetJustWidow( true );
818                     GetFollow()->Prepare();
819                 }
820                 else if ( aRectFnSet.IsVert() )
821                 {
822                     {
823                         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
824                         aFrm.Width( aFrm.Width() + aFrm.Left() );
825                         aFrm.Left( 0 );
826                     }
827 
828                     {
829                         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
830                         aPrt.Width( aPrt.Width() + getFrameArea().Left() );
831                     }
832 
833                     SetWidow( true );
834                 }
835                 else
836                 {
837                     // nTmp should be very large, but not so large as to cause overflow later (e.g.,
838                     // GetFrameOfModify in sw/source/core/layout/frmtool.cxx calculates nCurrentDist
839                     // from, among others, the square of aDiff.getY(), which can be close to nTmp);
840                     // the previously used value TWIPS_MAX/2 (i.e., (LONG_MAX - 1)/2) depended on
841                     // the range of 'long', while the value (SAL_MAX_INT32 - 1)/2 (which matches the
842                     // old value on platforms where 'long' is 'sal_Int32') is empirically shown to
843                     // be large enough in practice even on platforms where 'long' is 'sal_Int64':
844                     SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Top()+10000);
845                     SwTwips nDiff = nTmp - getFrameArea().Height();
846 
847                     {
848                         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
849                         aFrm.Height( nTmp );
850                     }
851 
852                     {
853                         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
854                         aPrt.Height( aPrt.Height() + nDiff );
855                     }
856 
857                     SetWidow( true );
858                 }
859             }
860             else
861             {
862                 OSL_ENSURE( nChgHeight < aRectFnSet.GetHeight(getFramePrintArea()),
863                         "+SwTextFrame::CalcPrep: want to shrink" );
864 
865                 nChgHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nChgHeight;
866 
867                 GetFollow()->SetJustWidow( true );
868                 GetFollow()->Prepare();
869                 Shrink( nChgHeight );
870                 SwRect &rRepaint = pPara->GetRepaint();
871 
872                 if ( aRectFnSet.IsVert() )
873                 {
874                     SwRect aRepaint( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
875                     SwitchVerticalToHorizontal( aRepaint );
876                     rRepaint.Chg( aRepaint.Pos(), aRepaint.SSize() );
877                 }
878                 else
879                     rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
880 
881                 if( 0 >= rRepaint.Width() )
882                     rRepaint.Width(1);
883             }
884             bRet = true;
885         }
886         else if ( bPrepAdjust )
887         {
888             if ( HasFootnote() )
889             {
890                 if( !CalcPrepFootnoteAdjust() )
891                 {
892                     if( bPrepMustFit )
893                     {
894                         SwTextLineAccess aAccess( this );
895                         aAccess.GetPara()->SetPrepMustFit(true);
896                     }
897                     return false;
898                 }
899             }
900 
901             {
902                 SwSwapIfNotSwapped swap( this );
903 
904                 SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
905                 SwTextFormatter aLine( this, &aInf );
906 
907                 WidowsAndOrphans aFrameBreak( this );
908                 // Whatever the attributes say: we split the paragraph in
909                 // MustFit case if necessary
910                 if( bPrepMustFit )
911                 {
912                     aFrameBreak.SetKeep( false );
913                     aFrameBreak.ClrOrphLines();
914                 }
915                 // Before calling FormatAdjust, we need to make sure
916                 // that the lines protruding at the bottom get indeed
917                 // truncated
918                 bool bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
919                 bRet = true;
920                 while( !bBreak && aLine.Next() )
921                 {
922                     bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
923                 }
924                 if( bBreak )
925                 {
926                     // We run into troubles: when TruncLines is called, the
927                     // conditions in IsInside change immediately such that
928                     // IsBreakNow can return different results.
929                     // For this reason, we tell rFrameBreak that the
930                     // end is reached at the location of rLine.
931                     // Let's see if it works ...
932                     aLine.TruncLines();
933                     aFrameBreak.SetRstHeight( aLine );
934                     FormatAdjust( aLine, aFrameBreak, TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
935                 }
936                 else
937                 {
938                     if( !GetFollow() )
939                     {
940                         FormatAdjust( aLine, aFrameBreak,
941                             TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
942                     }
943                     else if ( !aFrameBreak.IsKeepAlways() )
944                     {
945                         // We delete a line before the Master, because the Follow
946                         // could hand over a line
947                         const SwCharRange aFollowRg(GetFollow()->GetOfst(), TextFrameIndex(1));
948                         pPara->GetReformat() += aFollowRg;
949                         // We should continue!
950                         bRet = false;
951                     }
952                 }
953             }
954 
955             // A final check, if FormatAdjust() didn't help we need to
956             // truncate
957             if( bPrepMustFit )
958             {
959                 const SwTwips nMust = aRectFnSet.GetPrtBottom(*GetUpper());
960                 const SwTwips nIs   = aRectFnSet.GetBottom(getFrameArea());
961 
962                 if( aRectFnSet.IsVert() && nIs < nMust )
963                 {
964                     Shrink( nMust - nIs );
965 
966                     if( getFramePrintArea().Width() < 0 )
967                     {
968                         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
969                         aPrt.Width( 0 );
970                     }
971 
972                     SetUndersized( true );
973                 }
974                 else if ( ! aRectFnSet.IsVert() && nIs > nMust )
975                 {
976                     Shrink( nIs - nMust );
977 
978                     if( getFramePrintArea().Height() < 0 )
979                     {
980                         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
981                         aPrt.Height( 0 );
982                     }
983 
984                     SetUndersized( true );
985                 }
986             }
987         }
988     }
989     pPara->SetPrepMustFit( bPrepMustFit );
990     return bRet;
991 }
992 
993 // We rewire the footnotes and the character bound objects
ChangeOffset(SwTextFrame * pFrame,TextFrameIndex nNew)994 void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
995 {
996     if( pFrame->GetOfst() < nNew )
997         pFrame->MoveFlyInCnt( this, TextFrameIndex(0), nNew );
998     else if( pFrame->GetOfst() > nNew )
999         MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );
1000 }
1001 
FormatAdjust(SwTextFormatter & rLine,WidowsAndOrphans & rFrameBreak,TextFrameIndex const nStrLen,const bool bDummy)1002 void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
1003                              WidowsAndOrphans &rFrameBreak,
1004                              TextFrameIndex const nStrLen,
1005                              const bool bDummy )
1006 {
1007     SwSwapIfNotSwapped swap( this );
1008 
1009     SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1010 
1011     TextFrameIndex nEnd = rLine.GetStart();
1012 
1013     const bool bHasToFit = pPara->IsPrepMustFit();
1014 
1015     // The StopFlag is set by footnotes which want to go onto the next page
1016     // Call base class method <SwTextFrameBreak::IsBreakNow(..)>
1017     // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break,
1018     // even if due to widow rule no enough lines exists.
1019     sal_uInt8 nNew = ( !GetFollow() &&
1020                        nEnd < nStrLen &&
1021                        ( rLine.IsStop() ||
1022                          ( bHasToFit
1023                            ? ( rLine.GetLineNr() > 1 &&
1024                                !rFrameBreak.IsInside( rLine ) )
1025                            : rFrameBreak.IsBreakNow( rLine ) ) ) )
1026                      ? 1 : 0;
1027     // i#84870
1028     // no split of text frame, which only contains an as-character anchored object
1029     bool bOnlyContainsAsCharAnchoredObj =
1030             !IsFollow() && nStrLen == TextFrameIndex(1) &&
1031             GetDrawObjs() && GetDrawObjs()->size() == 1 &&
1032             (*GetDrawObjs())[0]->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
1033 
1034     // Still try split text frame if we have columns.
1035     if (FindColFrame())
1036         bOnlyContainsAsCharAnchoredObj = false;
1037 
1038     if ( nNew && bOnlyContainsAsCharAnchoredObj )
1039     {
1040         nNew = 0;
1041     }
1042 
1043     if ( nNew )
1044     {
1045         SplitFrame( nEnd );
1046     }
1047 
1048     const SwFrame *pBodyFrame = FindBodyFrame();
1049 
1050     const long nBodyHeight = pBodyFrame ? ( IsVertical() ?
1051                                           pBodyFrame->getFrameArea().Width() :
1052                                           pBodyFrame->getFrameArea().Height() ) : 0;
1053 
1054     // If the current values have been calculated, show that they
1055     // are valid now
1056     pPara->GetReformat() = SwCharRange();
1057     bool bDelta = pPara->GetDelta() != 0;
1058     pPara->GetDelta() = 0;
1059 
1060     if( rLine.IsStop() )
1061     {
1062         rLine.TruncLines( true );
1063         nNew = 1;
1064     }
1065 
1066     // FindBreak truncates the last line
1067     if( !rFrameBreak.FindBreak( this, rLine, bHasToFit ) )
1068     {
1069         // If we're done formatting, we set nEnd to the end.
1070         // AdjustFollow might execute JoinFrame() because of this.
1071         // Else, nEnd is the end of the last line in the Master.
1072         TextFrameIndex nOld = nEnd;
1073         nEnd = rLine.GetEnd();
1074         if( GetFollow() )
1075         {
1076             if( nNew && nOld < nEnd )
1077                 RemoveFootnote( nOld, nEnd - nOld );
1078             ChangeOffset( GetFollow(), nEnd );
1079             if( !bDelta )
1080                 GetFollow()->ManipOfst( nEnd );
1081         }
1082     }
1083     else
1084     {   // If we pass over lines, we must not call Join in Follows, instead we even
1085         // need to create a Follow.
1086         // We also need to do this if the whole mass of text remains in the Master,
1087         // because a hard line break could necessitate another line (without text mass)!
1088         nEnd = rLine.GetEnd();
1089         if( GetFollow() )
1090         {
1091             // Another case for not joining the follow:
1092             // Text frame has no content, but a numbering. Then, do *not* join.
1093             // Example of this case: When an empty, but numbered paragraph
1094             // at the end of page is completely displaced by a fly frame.
1095             // Thus, the text frame introduced a follow by a
1096             // <SwTextFrame::SplitFrame(..)> - see below. The follow then shows
1097             // the numbering and must stay.
1098             if ( GetFollow()->GetOfst() != nEnd ||
1099                  GetFollow()->IsFieldFollow() ||
1100                  (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
1101             {
1102                 nNew |= 3;
1103             }
1104             else if (FindTabFrame() && nEnd > TextFrameIndex(0) &&
1105                 rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
1106             {
1107                 // We are in a table, the paragraph has a follow and the text
1108                 // ends with a hard line break. Don't join the follow just
1109                 // because the follow would have no content, we may still need it
1110                 // for the paragraph mark.
1111                 nNew |= 1;
1112             }
1113             ChangeOffset( GetFollow(), nEnd );
1114             GetFollow()->ManipOfst( nEnd );
1115         }
1116         else
1117         {
1118             // Only split frame, if the frame contains
1119             // content or contains no content, but has a numbering.
1120             // i#84870 - No split, if text frame only contains one
1121             // as-character anchored object.
1122             if ( !bOnlyContainsAsCharAnchoredObj &&
1123                  (nStrLen > TextFrameIndex(0) ||
1124                    (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
1125                )
1126             {
1127                 SplitFrame( nEnd );
1128                 nNew |= 3;
1129             }
1130         }
1131         // If the remaining height changed e.g by RemoveFootnote() we need to
1132         // fill up in order to avoid oscillation.
1133         if( bDummy && pBodyFrame &&
1134            nBodyHeight < ( IsVertical() ?
1135                            pBodyFrame->getFrameArea().Width() :
1136                            pBodyFrame->getFrameArea().Height() ) )
1137             rLine.MakeDummyLine();
1138     }
1139 
1140     // In AdjustFrame() we set ourselves via Grow/Shrink
1141     // In AdjustFollow() we set our FollowFrame
1142 
1143     const SwTwips nDocPrtTop = getFrameArea().Top() + getFramePrintArea().Top();
1144     const SwTwips nOldHeight = getFramePrintArea().SSize().Height();
1145     SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight;
1146 
1147     //#i84870# - no shrink of text frame, if it only contains one as-character anchored object.
1148     if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj )
1149     {
1150         nChg = 0;
1151     }
1152 
1153     // Vertical Formatting:
1154     // The (rotated) repaint rectangle's x coordinate refers to the frame.
1155     // If the frame grows (or shirks) the repaint rectangle cannot simply
1156     // be rotated back after formatting, because we use the upper left point
1157     // of the frame for rotation. This point changes when growing/shrinking.
1158 
1159     if ( IsVertical() && !IsVertLR() && nChg )
1160     {
1161         SwRect &rRepaint = pPara->GetRepaint();
1162         rRepaint.Left( rRepaint.Left() - nChg );
1163         rRepaint.Width( rRepaint.Width() - nChg );
1164     }
1165 
1166     AdjustFrame( nChg, bHasToFit );
1167 
1168     if( HasFollow() || IsInFootnote() )
1169         AdjustFollow_( rLine, nEnd, nStrLen, nNew );
1170 
1171     pPara->SetPrepMustFit( false );
1172 }
1173 
1174 // bPrev is set whether Reformat.Start() was called because of Prev().
1175 // Else, wo don't know whether we can limit the repaint or not.
FormatLine(SwTextFormatter & rLine,const bool bPrev)1176 bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
1177 {
1178     OSL_ENSURE( ! IsVertical() || IsSwapped(),
1179             "SwTextFrame::FormatLine( rLine, bPrev) with unswapped frame" );
1180     SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1181     const SwLineLayout *pOldCur = rLine.GetCurr();
1182     const TextFrameIndex nOldLen = pOldCur->GetLen();
1183     const sal_uInt16 nOldAscent = pOldCur->GetAscent();
1184     const sal_uInt16 nOldHeight = pOldCur->Height();
1185     const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin();
1186     const bool bOldHyph = pOldCur->IsEndHyph();
1187     SwTwips nOldTop = 0;
1188     SwTwips nOldBottom = 0;
1189     if( rLine.GetCurr()->IsClipping() )
1190         rLine.CalcUnclipped( nOldTop, nOldBottom );
1191 
1192     TextFrameIndex const nNewStart = rLine.FormatLine( rLine.GetStart() );
1193 
1194     OSL_ENSURE( getFrameArea().Pos().Y() + getFramePrintArea().Pos().Y() == rLine.GetFirstPos(),
1195             "SwTextFrame::FormatLine: frame leaves orbit." );
1196     OSL_ENSURE( rLine.GetCurr()->Height(),
1197             "SwTextFrame::FormatLine: line height is zero" );
1198 
1199     // The current line break object
1200     const SwLineLayout *pNew = rLine.GetCurr();
1201 
1202     bool bUnChg = nOldLen == pNew->GetLen() &&
1203                   bOldHyph == pNew->IsEndHyph();
1204     if ( bUnChg && !bPrev )
1205     {
1206         const long nWidthDiff = nOldWidth > pNew->Width()
1207                                 ? nOldWidth - pNew->Width()
1208                                 : pNew->Width() - nOldWidth;
1209 
1210         // we only declare a line as unchanged, if its main values have not
1211         // changed and it is not the last line (!paragraph end symbol!)
1212         bUnChg = nOldHeight == pNew->Height() &&
1213                  nOldAscent == pNew->GetAscent() &&
1214                  nWidthDiff <= SLOPPY_TWIPS &&
1215                  pOldCur->GetNext();
1216     }
1217 
1218     // Calculate rRepaint
1219     const SwTwips nBottom = rLine.Y() + rLine.GetLineHeight();
1220     SwRepaint &rRepaint = pPara->GetRepaint();
1221     if( bUnChg && rRepaint.Top() == rLine.Y()
1222                && (bPrev || nNewStart <= pPara->GetReformat().Start())
1223                && (nNewStart < TextFrameIndex(GetText().getLength())))
1224     {
1225         rRepaint.Top( nBottom );
1226         rRepaint.Height( 0 );
1227     }
1228     else
1229     {
1230         if( nOldTop )
1231         {
1232             if( nOldTop < rRepaint.Top() )
1233                 rRepaint.Top( nOldTop );
1234             if( !rLine.IsUnclipped() || nOldBottom > rRepaint.Bottom() )
1235             {
1236                 rRepaint.Bottom( nOldBottom - 1 );
1237                 rLine.SetUnclipped( true );
1238             }
1239         }
1240         if( rLine.GetCurr()->IsClipping() && rLine.IsFlyInCntBase() )
1241         {
1242             SwTwips nTmpTop, nTmpBottom;
1243             rLine.CalcUnclipped( nTmpTop, nTmpBottom );
1244             if( nTmpTop < rRepaint.Top() )
1245                 rRepaint.Top( nTmpTop );
1246             if( !rLine.IsUnclipped() || nTmpBottom > rRepaint.Bottom() )
1247             {
1248                 rRepaint.Bottom( nTmpBottom - 1 );
1249                 rLine.SetUnclipped( true );
1250             }
1251         }
1252         else
1253         {
1254             if( !rLine.IsUnclipped() || nBottom > rRepaint.Bottom() )
1255             {
1256                 rRepaint.Bottom( nBottom - 1 );
1257                 rLine.SetUnclipped( false );
1258             }
1259         }
1260         SwTwips nRght = std::max( nOldWidth, pNew->Width() +
1261                              pNew->GetHangingMargin() );
1262         SwViewShell *pSh = getRootFrame()->GetCurrShell();
1263         const SwViewOption *pOpt = pSh ? pSh->GetViewOptions() : nullptr;
1264         if( pOpt && (pOpt->IsParagraph() || pOpt->IsLineBreak()) )
1265             nRght += ( std::max( nOldAscent, pNew->GetAscent() ) );
1266         else
1267             nRght += ( std::max( nOldAscent, pNew->GetAscent() ) / 4);
1268         nRght += rLine.GetLeftMargin();
1269         if( rRepaint.GetOfst() || rRepaint.GetRightOfst() < nRght )
1270             rRepaint.SetRightOfst( nRght );
1271 
1272         // Finally we enlarge the repaint rectangle if we found an underscore
1273         // within our line. 40 Twips should be enough
1274         const bool bHasUnderscore =
1275                 ( rLine.GetInfo().GetUnderScorePos() < nNewStart );
1276         if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() )
1277             rRepaint.Bottom( rRepaint.Bottom() + 40 );
1278 
1279         const_cast<SwLineLayout*>(rLine.GetCurr())->SetUnderscore( bHasUnderscore );
1280     }
1281 
1282     // Calculating the good ol' nDelta
1283     pPara->GetDelta() -= sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);
1284 
1285     // Stop!
1286     if( rLine.IsStop() )
1287         return false;
1288 
1289     // Absolutely another line
1290     if( rLine.IsNewLine() )
1291         return true;
1292 
1293     // Until the String's end?
1294     if (nNewStart >= TextFrameIndex(GetText().getLength()))
1295         return false;
1296 
1297     if( rLine.GetInfo().IsShift() )
1298         return true;
1299 
1300     // Reached the Reformat's end?
1301     const TextFrameIndex nEnd = pPara->GetReformat().Start() +
1302                         pPara->GetReformat().Len();
1303 
1304     if( nNewStart <= nEnd )
1305         return true;
1306 
1307     return 0 != pPara->GetDelta();
1308 }
1309 
Format_(SwTextFormatter & rLine,SwTextFormatInfo & rInf,const bool bAdjust)1310 void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
1311                         const bool bAdjust )
1312 {
1313     OSL_ENSURE( ! IsVertical() || IsSwapped(),"SwTextFrame::Format_ with unswapped frame" );
1314 
1315     SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1316     rLine.SetUnclipped( false );
1317 
1318     const OUString & rString = GetText();
1319     const TextFrameIndex nStrLen(rString.getLength());
1320 
1321     SwCharRange &rReformat = pPara->GetReformat();
1322     SwRepaint   &rRepaint = pPara->GetRepaint();
1323     std::unique_ptr<SwRepaint> pFreeze;
1324 
1325     // Due to performance reasons we set rReformat to COMPLETE_STRING in Init()
1326     // In this case we adjust rReformat
1327     if( rReformat.Len() > nStrLen )
1328         rReformat.Len() = nStrLen;
1329 
1330     if( rReformat.Start() + rReformat.Len() > nStrLen )
1331         rReformat.Len() = nStrLen - rReformat.Start();
1332 
1333     SwTwips nOldBottom;
1334     if( GetOfst() && !IsFollow() )
1335     {
1336         rLine.Bottom();
1337         nOldBottom = rLine.Y();
1338         rLine.Top();
1339     }
1340     else
1341         nOldBottom = 0;
1342     rLine.CharToLine( rReformat.Start() );
1343 
1344     // When inserting or removing a Space, words can be moved out of the edited
1345     // line and into the preceding line, hence the preceding line must be
1346     // formatted as well.
1347     // Optimization: If rReformat starts after the first word of the line,
1348     // this line cannot possibly influence the previous one.
1349     // ...Turns out that unfortunately it can: Text size changes + FlyFrames;
1350     // the feedback can affect multiple lines (Frames!)!
1351 
1352     // i#46560
1353     // FME: Yes, consider this case: "(word )" has to go to the next line
1354     // because ")" is a forbidden character at the beginning of a line although
1355     // "(word" would still fit on the previous line. Adding text right in front
1356     // of ")" would not trigger a reformatting of the previous line. Adding 1
1357     // to the result of FindBrk() does not solve the problem in all cases,
1358     // nevertheless it should be sufficient.
1359     bool bPrev = rLine.GetPrev() &&
1360                      (FindBrk(rString, rLine.GetStart(), rReformat.Start() + TextFrameIndex(1))
1361                        // i#46560
1362                        + TextFrameIndex(1)
1363                        >= rReformat.Start() ||
1364                        rLine.GetCurr()->IsRest() );
1365     if( bPrev )
1366     {
1367         while( rLine.Prev() )
1368             if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() )
1369             {
1370                 if( !rLine.GetStart() )
1371                     rLine.Top(); // So that NumDone doesn't get confused
1372                 break;
1373             }
1374         TextFrameIndex nNew = rLine.GetStart() + rLine.GetLength();
1375         if( nNew )
1376         {
1377             --nNew;
1378             if (CH_BREAK == rString[sal_Int32(nNew)])
1379             {
1380                 ++nNew;
1381                 rLine.Next();
1382                 bPrev = false;
1383             }
1384         }
1385         rReformat.Len()  += rReformat.Start() - nNew;
1386         rReformat.Start() = nNew;
1387     }
1388 
1389     rRepaint.SetOfst( 0 );
1390     rRepaint.SetRightOfst( 0 );
1391     rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
1392     if( pPara->IsMargin() )
1393         rRepaint.Width( rRepaint.Width() + pPara->GetHangingMargin() );
1394     rRepaint.Top( rLine.Y() );
1395     if( 0 >= rRepaint.Width() )
1396         rRepaint.Width(1);
1397     WidowsAndOrphans aFrameBreak( this, rInf.IsTest() ? 1 : 0 );
1398 
1399     // rLine is now set to the first line which needs formatting.
1400     // The bFirst flag makes sure that Next() is not called.
1401     // The whole thing looks weird, but we need to make sure that
1402     // rLine stops at the last non-fitting line when calling IsBreakNow.
1403     bool bFirst  = true;
1404     bool bFormat = true;
1405 
1406     // The CharToLine() can also get us into the danger zone.
1407     // In that case we need to walk back until rLine is set
1408     // to the non-fitting line. Or else the mass of text is lost,
1409     // because the Ofst was set wrongly in the Follow.
1410 
1411     bool bBreak = ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 )
1412                     && aFrameBreak.IsBreakNowWidAndOrp( rLine );
1413     if( bBreak )
1414     {
1415         bool bPrevDone = nullptr != rLine.Prev();
1416         while( bPrevDone && aFrameBreak.IsBreakNowWidAndOrp(rLine) )
1417             bPrevDone = nullptr != rLine.Prev();
1418         if( bPrevDone )
1419         {
1420             aFrameBreak.SetKeep( false );
1421             rLine.Next();
1422         }
1423         rLine.TruncLines();
1424 
1425         // Play it safe
1426         aFrameBreak.IsBreakNowWidAndOrp(rLine);
1427     }
1428 
1429  /* Meaning if the following flags are set:
1430 
1431     Watch(End/Mid)Hyph: we need to format if we have a break at
1432     the line end/Fly, as long as MaxHyph is reached
1433 
1434     Jump(End/Mid)Flag: the next line which has no break (line end/Fly),
1435     needs to be formatted, because we could wrap now. This might have been
1436     forbidden earlier by MaxHyph
1437 
1438     Watch(End/Mid)Hyph: if the last formatted line got a cutoff point, but
1439     didn't have one before
1440 
1441     Jump(End/Mid)Hyph: if a cutoff point disappears
1442  */
1443     bool bJumpEndHyph  = false;
1444     bool bWatchEndHyph = false;
1445     bool bJumpMidHyph  = false;
1446     bool bWatchMidHyph = false;
1447 
1448     const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
1449     rInf.MaxHyph() = rAttrSet.GetHyphenZone().GetMaxHyphens();
1450     bool bMaxHyph = 0 != rInf.MaxHyph();
1451     if ( bMaxHyph )
1452         rLine.InitCntHyph();
1453 
1454     if( IsFollow() && IsFieldFollow() && rLine.GetStart() == GetOfst() )
1455     {
1456         SwTextFrame *pMaster = FindMaster();
1457         OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
1458         const SwLineLayout* pLine=nullptr;
1459         if (pMaster)
1460         {
1461             if (!pMaster->HasPara())
1462             {   // master could be locked because it's being formatted upstack
1463                 SAL_WARN("sw", "SwTextFrame::Format_: master not formatted!");
1464             }
1465             else
1466             {
1467                 SwTextSizeInfo aInf( pMaster );
1468                 SwTextIter aMasterLine( pMaster, &aInf );
1469                 aMasterLine.Bottom();
1470                 pLine = aMasterLine.GetCurr();
1471                 assert(aMasterLine.GetEnd() == GetOfst());
1472             }
1473         }
1474         SwLinePortion* pRest = pLine ?
1475             rLine.MakeRestPortion(pLine, GetOfst()) : nullptr;
1476         if( pRest )
1477             rInf.SetRest( pRest );
1478         else
1479             SetFieldFollow( false );
1480     }
1481 
1482     /* Ad cancel criterion:
1483      * In order to recognize, whether a line does not fit onto the page
1484      * anymore, we need to format it. This overflow is removed again in
1485      * e.g. AdjustFollow.
1486      * Another complication: if we are the Master, we need to traverse
1487      * the lines, because it could happen that one line can overflow
1488      * from the Follow to the Master.
1489      */
1490     do
1491     {
1492         if( bFirst )
1493             bFirst = false;
1494         else
1495         {
1496             if ( bMaxHyph )
1497             {
1498                 if ( rLine.GetCurr()->IsEndHyph() )
1499                     rLine.CntEndHyph()++;
1500                 else
1501                     rLine.CntEndHyph() = 0;
1502                 if ( rLine.GetCurr()->IsMidHyph() )
1503                     rLine.CntMidHyph()++;
1504                 else
1505                     rLine.CntMidHyph() = 0;
1506             }
1507             if( !rLine.Next() )
1508             {
1509                 if( !bFormat )
1510                 {
1511                     SwLinePortion* pRest =
1512                         rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() );
1513                     if( pRest )
1514                         rInf.SetRest( pRest );
1515                 }
1516                 rLine.Insert( new SwLineLayout() );
1517                 rLine.Next();
1518                 bFormat = true;
1519             }
1520         }
1521         if ( !bFormat && bMaxHyph &&
1522               (bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) )
1523         {
1524             if ( rLine.GetCurr()->IsEndHyph() )
1525             {
1526                 if ( bWatchEndHyph )
1527                     bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
1528             }
1529             else
1530             {
1531                 bFormat = bJumpEndHyph;
1532                 bWatchEndHyph = false;
1533                 bJumpEndHyph = false;
1534             }
1535             if ( rLine.GetCurr()->IsMidHyph() )
1536             {
1537                 if ( bWatchMidHyph && !bFormat )
1538                     bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
1539             }
1540             else
1541             {
1542                 bFormat |= bJumpMidHyph;
1543                 bWatchMidHyph = false;
1544                 bJumpMidHyph = false;
1545             }
1546         }
1547         if( bFormat )
1548         {
1549             const bool bOldEndHyph = rLine.GetCurr()->IsEndHyph();
1550             const bool bOldMidHyph = rLine.GetCurr()->IsMidHyph();
1551             bFormat = FormatLine( rLine, bPrev );
1552             // There can only be one bPrev ... (???)
1553             bPrev = false;
1554             if ( bMaxHyph )
1555             {
1556                 if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph )
1557                 {
1558                     bWatchEndHyph = !bOldEndHyph;
1559                     bJumpEndHyph = bOldEndHyph;
1560                 }
1561                 if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph )
1562                 {
1563                     bWatchMidHyph = !bOldMidHyph;
1564                     bJumpMidHyph = bOldMidHyph;
1565                 }
1566             }
1567         }
1568 
1569         if( !rInf.IsNewLine() )
1570         {
1571             if( !bFormat )
1572                  bFormat = nullptr != rInf.GetRest();
1573             if( rInf.IsStop() || rInf.GetIdx() >= nStrLen )
1574                 break;
1575             if( !bFormat && ( !bMaxHyph || ( !bWatchEndHyph &&
1576                     !bJumpEndHyph && !bWatchMidHyph && !bJumpMidHyph ) ) )
1577             {
1578                 if( GetFollow() )
1579                 {
1580                     while( rLine.Next() )
1581                         ; //Nothing
1582                     pFreeze.reset(new SwRepaint( rRepaint )); // to minimize painting
1583                 }
1584                 else
1585                     break;
1586             }
1587         }
1588         bBreak = aFrameBreak.IsBreakNowWidAndOrp(rLine);
1589     }while( !bBreak );
1590 
1591     if( pFreeze )
1592     {
1593         rRepaint = *pFreeze;
1594         pFreeze.reset();
1595     }
1596 
1597     if( !rLine.IsStop() )
1598     {
1599         // If we're finished formatting the text and we still
1600         // have other line objects left, these are superfluous
1601         // now because the text has gotten shorter.
1602         if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
1603             rLine.GetCurr()->GetNext() )
1604         {
1605             rLine.TruncLines();
1606             rLine.SetTruncLines( true );
1607         }
1608     }
1609 
1610     if( !rInf.IsTest() )
1611     {
1612         // FormatAdjust does not pay off at OnceMore
1613         if( bAdjust || !rLine.GetDropFormat() || !rLine.CalcOnceMore() )
1614         {
1615             FormatAdjust( rLine, aFrameBreak, nStrLen, rInf.IsStop() );
1616         }
1617         if( rRepaint.HasArea() )
1618             SetRepaint();
1619         rLine.SetTruncLines( false );
1620         if( nOldBottom ) // We check whether paragraphs that need scrolling can
1621                          // be shrunk, so that they don't need scrolling anymore
1622         {
1623             rLine.Bottom();
1624             SwTwips nNewBottom = rLine.Y();
1625             if( nNewBottom < nOldBottom )
1626                 SetOfst_(TextFrameIndex(0));
1627         }
1628     }
1629 }
1630 
FormatOnceMore(SwTextFormatter & rLine,SwTextFormatInfo & rInf)1631 void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
1632 {
1633     OSL_ENSURE( ! IsVertical() || IsSwapped(),
1634             "A frame is not swapped in SwTextFrame::FormatOnceMore" );
1635 
1636     SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
1637     if( !pPara )
1638         return;
1639 
1640     // If necessary the pPara
1641     sal_uInt16 nOld  = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
1642     bool bShrink = false;
1643     bool bGrow   = false;
1644     bool bGoOn   = rLine.IsOnceMore();
1645     sal_uInt8 nGo    = 0;
1646     while( bGoOn )
1647     {
1648         ++nGo;
1649         rInf.Init();
1650         rLine.Top();
1651         if( !rLine.GetDropFormat() )
1652             rLine.SetOnceMore( false );
1653         SwCharRange aRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
1654         pPara->GetReformat() = aRange;
1655         Format_( rLine, rInf );
1656 
1657         bGoOn = rLine.IsOnceMore();
1658         if( bGoOn )
1659         {
1660             const sal_uInt16 nNew = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
1661             if( nOld == nNew )
1662                 bGoOn = false;
1663             else
1664             {
1665                 if( nOld > nNew )
1666                     bShrink = true;
1667                 else
1668                     bGrow = true;
1669 
1670                 if( bShrink == bGrow || 5 < nGo )
1671                     bGoOn = false;
1672 
1673                 nOld = nNew;
1674             }
1675 
1676             // If something went wrong, we need to reformat again
1677             if( !bGoOn )
1678             {
1679                 rInf.CtorInitTextFormatInfo( getRootFrame()->GetCurrShell()->GetOut(), this );
1680                 rLine.CtorInitTextFormatter( this, &rInf );
1681                 rLine.SetDropLines( 1 );
1682                 rLine.CalcDropHeight( 1 );
1683                 SwCharRange aTmpRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
1684                 pPara->GetReformat() = aTmpRange;
1685                 Format_( rLine, rInf, true );
1686                 // We paint everything ...
1687                 SetCompletePaint();
1688             }
1689         }
1690     }
1691 }
1692 
Format_(vcl::RenderContext * pRenderContext,SwParaPortion * pPara)1693 void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara )
1694 {
1695     const bool bIsEmpty = GetText().isEmpty();
1696 
1697     if ( bIsEmpty )
1698     {
1699         // Empty lines do not get tortured for very long:
1700         // pPara is cleared, which is the same as:
1701         // *pPara = SwParaPortion;
1702         const bool bMustFit = pPara->IsPrepMustFit();
1703         pPara->Truncate();
1704         pPara->FormatReset();
1705         if( pBlink && pPara->IsBlinking() )
1706             pBlink->Delete( pPara );
1707 
1708         // delete pSpaceAdd and pKanaComp
1709         pPara->FinishSpaceAdd();
1710         pPara->FinishKanaComp();
1711         pPara->ResetFlags();
1712         pPara->SetPrepMustFit( bMustFit );
1713     }
1714 
1715     OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Format_" );
1716 
1717     if ( IsVertical() )
1718         SwapWidthAndHeight();
1719 
1720     SwTextFormatInfo aInf( pRenderContext, this );
1721     SwTextFormatter  aLine( this, &aInf );
1722 
1723     HideAndShowObjects();
1724 
1725     Format_( aLine, aInf );
1726 
1727     if( aLine.IsOnceMore() )
1728         FormatOnceMore( aLine, aInf );
1729 
1730     if ( IsVertical() )
1731         SwapWidthAndHeight();
1732 
1733     OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Format_" );
1734 
1735     if( 1 < aLine.GetDropLines() )
1736     {
1737         if( SvxAdjust::Left != aLine.GetAdjust() &&
1738             SvxAdjust::Block != aLine.GetAdjust() )
1739         {
1740             aLine.CalcDropAdjust();
1741             aLine.SetPaintDrop( true );
1742         }
1743 
1744         if( aLine.IsPaintDrop() )
1745         {
1746             aLine.CalcDropRepaint();
1747             aLine.SetPaintDrop( false );
1748         }
1749     }
1750 }
1751 
1752 // We calculate the text frame's size and send a notification.
1753 // Shrink() or Grow() to adjust the frame's size to the changed required space.
Format(vcl::RenderContext * pRenderContext,const SwBorderAttrs *)1754 void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
1755 {
1756     SwRectFnSet aRectFnSet(this);
1757 
1758     CalcAdditionalFirstLineOffset();
1759 
1760     // The range autopilot or the BASIC interface pass us TextFrames with
1761     // a width <= 0 from time to time
1762     if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
1763     {
1764         // If MustFit is set, we shrink to the Upper's bottom edge if needed.
1765         // Else we just take a standard size of 12 Pt. (240 twip).
1766         SwTextLineAccess aAccess( this );
1767         long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
1768 
1769         if( aAccess.GetPara()->IsPrepMustFit() )
1770         {
1771             const SwTwips nLimit = aRectFnSet.GetPrtBottom(*GetUpper());
1772             const SwTwips nDiff = - aRectFnSet.BottomDist( getFrameArea(), nLimit );
1773             if( nDiff > 0 )
1774                 Shrink( nDiff );
1775         }
1776         else if( 240 < nFrameHeight )
1777         {
1778             Shrink( nFrameHeight - 240 );
1779         }
1780         else if( 240 > nFrameHeight )
1781         {
1782             Grow( 240 - nFrameHeight );
1783         }
1784 
1785         nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
1786         const long nTop = aRectFnSet.GetTopMargin(*this);
1787 
1788         if( nTop > nFrameHeight )
1789         {
1790             aRectFnSet.SetYMargins( *this, nFrameHeight, 0 );
1791         }
1792         else if( aRectFnSet.GetHeight(getFramePrintArea()) < 0 )
1793         {
1794             SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1795             aRectFnSet.SetHeight( aPrt, 0 );
1796         }
1797 
1798         return;
1799     }
1800 
1801     const TextFrameIndex nStrLen(GetText().getLength());
1802     if ( nStrLen || !FormatEmpty() )
1803     {
1804 
1805         SetEmpty( false );
1806         // In order to not get confused by nested Formats
1807         FormatLevel aLevel;
1808         if( 12 == FormatLevel::GetLevel() )
1809             return;
1810 
1811         // We could be possibly not allowed to alter the format information
1812         if( IsLocked() )
1813             return;
1814 
1815         // Attention: Format() could be triggered by GetFormatted()
1816         if( IsHiddenNow() )
1817         {
1818             long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
1819             if( nPrtHeight )
1820             {
1821                 HideHidden();
1822                 Shrink( nPrtHeight );
1823             }
1824             else
1825             {
1826                 // Assure that objects anchored
1827                 // at paragraph resp. at/as character inside paragraph
1828                 // are hidden.
1829                 HideAndShowObjects();
1830             }
1831             ChgThisLines();
1832             return;
1833         }
1834 
1835         // We do not want to be interrupted during formatting
1836         TextFrameLockGuard aLock(this);
1837 
1838         // this is to ensure that the similar code in SwTextFrame::Format_
1839         // finds the master formatted in case it's needed
1840         if (IsFollow() && IsFieldFollow())
1841         {
1842             SwTextFrame *pMaster = FindMaster();
1843             assert(pMaster);
1844             if (!pMaster->HasPara())
1845             {
1846                 pMaster->GetFormatted();
1847             }
1848             if (!pMaster->HasPara())
1849             {   // master could be locked because it's being formatted upstack
1850                 SAL_WARN("sw", "SwTextFrame::Format: failed to format master!");
1851             }
1852             else
1853             {
1854                 SwTextSizeInfo aInf( pMaster );
1855                 SwTextIter aMasterLine( pMaster, &aInf );
1856                 aMasterLine.Bottom();
1857                 SetOfst(aMasterLine.GetEnd());
1858             }
1859         }
1860 
1861         SwTextLineAccess aAccess( this );
1862         const bool bNew = !aAccess.IsAvailable();
1863         const bool bSetOfst =
1864             (GetOfst() && GetOfst() > TextFrameIndex(GetText().getLength()));
1865 
1866         if( CalcPreps() )
1867             ; // nothing
1868         // We return if already formatted, but if the TextFrame was just created
1869         // and does not have any format information
1870         else if( !bNew && !aAccess.GetPara()->GetReformat().Len() )
1871         {
1872             if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
1873             {
1874                 aAccess.GetPara()->SetPrepAdjust();
1875                 aAccess.GetPara()->SetPrep();
1876                 CalcPreps();
1877             }
1878             SetWidow( false );
1879         }
1880         else if( bSetOfst && IsFollow() )
1881         {
1882             SwTextFrame *pMaster = FindMaster();
1883             OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
1884             if( pMaster )
1885                 pMaster->Prepare( PREP_FOLLOW_FOLLOWS );
1886             SwTwips nMaxY = aRectFnSet.GetPrtBottom(*GetUpper());
1887 
1888             if( aRectFnSet.OverStep( getFrameArea(), nMaxY  ) )
1889             {
1890                 aRectFnSet.SetLimit( *this, nMaxY );
1891             }
1892             else if( aRectFnSet.BottomDist( getFrameArea(), nMaxY  ) < 0 )
1893             {
1894                 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1895                 aRectFnSet.AddBottom( aFrm, -aRectFnSet.GetHeight(aFrm) );
1896             }
1897         }
1898         else
1899         {
1900             // bSetOfst here means that we have the "red arrow situation"
1901             if ( bSetOfst )
1902                 SetOfst_(TextFrameIndex(0));
1903 
1904             const bool bOrphan = IsWidow();
1905             const SwFootnoteBossFrame* pFootnoteBoss = HasFootnote() ? FindFootnoteBossFrame() : nullptr;
1906             SwTwips nFootnoteHeight = 0;
1907             if( pFootnoteBoss )
1908             {
1909                 const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
1910                 nFootnoteHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
1911             }
1912             do
1913             {
1914                 Format_( pRenderContext, aAccess.GetPara() );
1915                 if( pFootnoteBoss && nFootnoteHeight )
1916                 {
1917                     const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
1918                     SwTwips nNewHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
1919                     // If we lost some footnotes, we may have more space
1920                     // for our main text, so we have to format again ...
1921                     if( nNewHeight < nFootnoteHeight )
1922                         nFootnoteHeight = nNewHeight;
1923                     else
1924                         break;
1925                 }
1926                 else
1927                     break;
1928             } while ( pFootnoteBoss );
1929             if( bOrphan )
1930             {
1931                 ValidateFrame();
1932                 SetWidow( false );
1933             }
1934         }
1935         if( IsEmptyMaster() )
1936         {
1937             SwFrame* pPre = GetPrev();
1938             if( pPre &&
1939                 // i#10826 It's the first, it cannot keep!
1940                 pPre->GetIndPrev() &&
1941                 pPre->GetAttrSet()->GetKeep().GetValue() )
1942             {
1943                 pPre->InvalidatePos();
1944             }
1945         }
1946     }
1947 
1948     ChgThisLines();
1949 
1950     // the PrepMustFit should not survive a Format operation
1951     SwParaPortion *pPara = GetPara();
1952     if ( pPara )
1953            pPara->SetPrepMustFit( false );
1954 
1955     CalcBaseOfstForFly();
1956     CalcHeightOfLastLine(); // i#11860 - Adjust spacing implementation for
1957                              // object positioning - Compatibility to MS Word
1958     // tdf#117982 -- Fix cell spacing hides content
1959     // Check if the cell's content has greater size than the row height
1960     if (IsInTab() && GetUpper() && ((GetUpper()->getFramePrintArea().Height() < getFramePrintArea().Height())
1961             || (getFramePrintArea().Height() <= 0)))
1962     {
1963            SAL_INFO("sw.core", "Warn: Cell content has greater size than cell height!");
1964            //get font size...
1965            SwTwips aTmpHeight = getFrameArea().Height();
1966            //...and push it into the text frame
1967            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1968            //if only bottom margin what we have:
1969            if (GetTopMargin() == 0)
1970                //set the frame to its original location
1971                aPrt.SetTopAndHeight(0, aTmpHeight);
1972     }
1973 }
1974 
1975 // bForceQuickFormat is set if GetFormatted() has been called during the
1976 // painting process. Actually I cannot imagine a situation which requires
1977 // a full formatting of the paragraph during painting, on the other hand
1978 // a full formatting can cause the invalidation of other layout frames,
1979 // e.g., if there are footnotes in this paragraph, and invalid layout
1980 // frames will not calculated during the painting. So I actually want to
1981 // avoid a formatting during painting, but since I'm a coward, I'll only
1982 // force the quick formatting in the situation of issue i29062.
FormatQuick(bool bForceQuickFormat)1983 bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
1984 {
1985     OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
1986             "SwTextFrame::FormatQuick with swapped frame" );
1987 
1988     if( IsEmpty() && FormatEmpty() )
1989         return true;
1990 
1991     // We're very picky:
1992     if( HasPara() || IsWidow() || IsLocked()
1993         || !isFrameAreaSizeValid() ||
1994         ( ( IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height() ) && IsHiddenNow() ) )
1995         return false;
1996 
1997     SwTextLineAccess aAccess( this );
1998     SwParaPortion *pPara = aAccess.GetPara();
1999     if( !pPara )
2000         return false;
2001 
2002     SwFrameSwapper aSwapper( this, true );
2003 
2004     TextFrameLockGuard aLock(this);
2005     SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true );
2006     if( 0 != aInf.MaxHyph() )   // Respect MaxHyphen!
2007         return false;
2008 
2009     SwTextFormatter  aLine( this, &aInf );
2010 
2011     // DropCaps are too complicated ...
2012     if( aLine.GetDropFormat() )
2013         return false;
2014 
2015     TextFrameIndex nStart = GetOfst();
2016     const TextFrameIndex nEnd = GetFollow()
2017                   ? GetFollow()->GetOfst()
2018                   : TextFrameIndex(aInf.GetText().getLength());
2019 
2020     int nLoopProtection = 0;
2021     do
2022     {
2023         TextFrameIndex nNewStart = aLine.FormatLine(nStart);
2024         if (nNewStart == nStart)
2025             ++nLoopProtection;
2026         else
2027             nLoopProtection = 0;
2028         nStart = nNewStart;
2029         const bool bWillEndlessInsert = nLoopProtection > 250;
2030         SAL_WARN_IF(bWillEndlessInsert, "sw", "loop detection triggered");
2031         if ((!bWillEndlessInsert) // Check for special case: line is invisible,
2032                                   // like in too thin table cell: tdf#66141
2033          && (aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd)))
2034             aLine.Insert( new SwLineLayout() );
2035     } while( aLine.Next() );
2036 
2037     // Last exit: the heights need to match
2038     Point aTopLeft( getFrameArea().Pos() );
2039     aTopLeft += getFramePrintArea().Pos();
2040     const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight();
2041     const SwTwips nOldHeight = aTopLeft.Y() + getFramePrintArea().Height();
2042 
2043     if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() )
2044     {
2045         // Attention: This situation can occur due to FormatLevel==12. Don't panic!
2046         TextFrameIndex const nStrt = GetOfst();
2047         InvalidateRange_( SwCharRange( nStrt, nEnd - nStrt) );
2048         return false;
2049     }
2050 
2051     if (m_pFollow && nStart != static_cast<SwTextFrame*>(m_pFollow)->GetOfst())
2052         return false; // can be caused by e.g. Orphans
2053 
2054     // We made it!
2055 
2056     // Set repaint
2057     pPara->GetRepaint().Pos( aTopLeft );
2058     pPara->GetRepaint().SSize( getFramePrintArea().SSize() );
2059 
2060     // Delete reformat
2061     pPara->GetReformat() = SwCharRange();
2062     pPara->GetDelta() = 0;
2063 
2064     return true;
2065 }
2066 
2067 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2068