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