Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / layact.cxx
blob84481981fd6e725304118ee668868c54de2eb077
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_feature_desktop.h>
21 #include <config_wasm_strip.h>
23 #include <ctime>
24 #include <rootfrm.hxx>
25 #include <pagefrm.hxx>
26 #include <viewimp.hxx>
27 #include <crsrsh.hxx>
28 #include <dflyobj.hxx>
29 #include <frmatr.hxx>
30 #include <frmtool.hxx>
31 #include <viewopt.hxx>
32 #include <dbg_lay.hxx>
33 #include <layouter.hxx>
34 #include <docstat.hxx>
35 #include <swevent.hxx>
36 #include <IDocumentDrawModelAccess.hxx>
37 #include <IDocumentStatistics.hxx>
38 #include <IDocumentLayoutAccess.hxx>
40 #include <sfx2/event.hxx>
42 #include <ftnidx.hxx>
43 #include <vcl/svapp.hxx>
44 #include <editeng/opaqitem.hxx>
45 #include <SwSmartTagMgr.hxx>
46 #include <sal/log.hxx>
48 #include <layact.hxx>
49 #include <swwait.hxx>
50 #include <fmtsrnd.hxx>
51 #include <docsh.hxx>
53 #include <anchoreddrawobject.hxx>
54 #include <ndtxt.hxx>
55 #include <tabfrm.hxx>
56 #include <ftnfrm.hxx>
57 #include <txtfrm.hxx>
58 #include <notxtfrm.hxx>
59 #include <flyfrms.hxx>
60 #include <mdiexp.hxx>
61 #include <sectfrm.hxx>
62 #include <acmplwrd.hxx>
63 #include <deletelistener.hxx>
64 #include <sortedobjs.hxx>
65 #include <objectformatter.hxx>
66 #include <fntcache.hxx>
67 #include <fmtanchr.hxx>
68 #include <comphelper/scopeguard.hxx>
69 #include <vector>
70 #include <comphelper/diagnose_ex.hxx>
72 void SwLayAction::CheckWaitCursor()
74 if (IsReschedule())
76 ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
78 if ( !m_pWait && IsWaitAllowed() && IsPaint() &&
79 ((std::clock() - m_nStartTicks) * 1000 / CLOCKS_PER_SEC >= CLOCKS_PER_SEC/2) )
81 m_pWait.reset( new SwWait( *m_pRoot->GetFormat()->GetDoc()->GetDocShell(), true ) );
85 // Time over already?
86 inline void SwLayAction::CheckIdleEnd()
88 if (!IsInterrupt())
89 m_bInterrupt = bool(GetInputType()) && Application::AnyInput(GetInputType());
92 void SwLayAction::SetStatBar( bool bNew )
94 if ( bNew )
96 m_nEndPage = m_pRoot->GetPageNum();
97 m_nEndPage += m_nEndPage * 10 / 100;
99 else
100 m_nEndPage = USHRT_MAX;
103 bool SwLayAction::PaintWithoutFlys( const SwRect &rRect, const SwContentFrame *pCnt,
104 const SwPageFrame *pPage )
106 SwRegionRects aTmp( rRect );
107 const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
108 const SwFlyFrame *pSelfFly = pCnt->FindFlyFrame();
110 for ( size_t i = 0; i < rObjs.size() && !aTmp.empty(); ++i )
112 SwVirtFlyDrawObj *pVirtFly = dynamic_cast<SwVirtFlyDrawObj*>(rObjs[i]->DrawObj());
113 if ( !pVirtFly )
114 continue;
116 // do not consider invisible objects
117 const IDocumentDrawModelAccess& rIDDMA = pPage->GetFormat()->getIDocumentDrawModelAccess();
118 if ( !rIDDMA.IsVisibleLayerId( pVirtFly->GetLayer() ) )
120 continue;
123 SwFlyFrame *pFly = pVirtFly->GetFlyFrame();
125 if ( pFly == pSelfFly || !rRect.Overlaps( pFly->getFrameArea() ) )
126 continue;
128 if ( pSelfFly && pSelfFly->IsLowerOf( pFly ) )
129 continue;
131 if ( pFly->GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() )
132 continue;
134 if ( pSelfFly )
136 const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
137 if ( pVirtFly->GetLayer() == pTmp->GetLayer() )
139 if ( pVirtFly->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
140 // Only look at things above us, if inside the same layer
141 continue;
143 else
145 const bool bLowerOfSelf = pFly->IsLowerOf( pSelfFly );
146 if ( !bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue() )
147 // Things from other layers are only interesting to us if
148 // they're not transparent or lie inwards
149 continue;
153 // Fly frame without a lower have to be subtracted from paint region.
154 // For checking, if fly frame contains transparent graphic or
155 // has surrounded contour, assure that fly frame has a lower
156 if ( pFly->Lower() &&
157 pFly->Lower()->IsNoTextFrame() &&
158 ( static_cast<SwNoTextFrame*>(pFly->Lower())->IsTransparent() ||
159 pFly->GetFormat()->GetSurround().IsContour() )
162 continue;
165 // vcl::Region of a fly frame with transparent background or a transparent
166 // shadow have not to be subtracted from paint region
167 if ( pFly->IsBackgroundTransparent() )
169 continue;
172 aTmp -= pFly->getFrameArea();
175 bool bRetPaint = false;
176 for ( const auto& rRegionRect : aTmp )
177 bRetPaint |= m_pImp->GetShell()->AddPaintRect( rRegionRect );
178 return bRetPaint;
181 inline bool SwLayAction::PaintContent_( const SwContentFrame *pContent,
182 const SwPageFrame *pPage,
183 const SwRect &rRect )
185 if ( rRect.HasArea() )
187 if ( pPage->GetSortedObjs() )
188 return PaintWithoutFlys( rRect, pContent, pPage );
189 else
190 return m_pImp->GetShell()->AddPaintRect( rRect );
192 return false;
196 * Depending of the type, the Content is output according to its changes, or the area
197 * to be outputted is registered with the region, respectively.
199 void SwLayAction::PaintContent( const SwContentFrame *pCnt,
200 const SwPageFrame *pPage,
201 const SwRect &rOldRect,
202 tools::Long nOldBottom )
204 SwRectFnSet aRectFnSet(pCnt);
206 if ( pCnt->IsCompletePaint() || !pCnt->IsTextFrame() )
208 SwRect aPaint( pCnt->GetPaintArea() );
209 if ( !PaintContent_( pCnt, pPage, aPaint ) )
210 pCnt->ResetCompletePaint();
212 else
214 // paint the area between printing bottom and frame bottom and
215 // the area left and right beside the frame, if its height changed.
216 tools::Long nOldHeight = aRectFnSet.GetHeight(rOldRect);
217 tools::Long nNewHeight = aRectFnSet.GetHeight(pCnt->getFrameArea());
218 const bool bHeightDiff = nOldHeight != nNewHeight;
219 if( bHeightDiff )
221 // consider whole potential paint area.
222 SwRect aDrawRect( pCnt->GetPaintArea() );
223 if( nOldHeight > nNewHeight )
224 nOldBottom = aRectFnSet.GetPrtBottom(*pCnt);
225 aRectFnSet.SetTop( aDrawRect, nOldBottom );
226 PaintContent_( pCnt, pPage, aDrawRect );
228 // paint content area
229 SwRect aPaintRect = static_cast<SwTextFrame*>(const_cast<SwContentFrame*>(pCnt))->GetPaintSwRect();
230 PaintContent_( pCnt, pPage, aPaintRect );
233 if ( !pCnt->IsRetouche() || pCnt->GetNext() )
234 return;
236 const SwFrame *pTmp = pCnt;
237 if( pCnt->IsInSct() )
239 const SwSectionFrame* pSct = pCnt->FindSctFrame();
240 if( pSct->IsRetouche() && !pSct->GetNext() )
241 pTmp = pSct;
243 SwRect aRect( pTmp->GetUpper()->GetPaintArea() );
244 aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTmp) );
245 if ( !PaintContent_( pCnt, pPage, aRect ) )
246 pCnt->ResetRetouche();
249 SwLayAction::SwLayAction( SwRootFrame *pRt, SwViewShellImp *pI ) :
250 m_pRoot( pRt ),
251 m_pImp( pI ),
252 m_pOptTab( nullptr ),
253 m_nPreInvaPage( USHRT_MAX ),
254 m_nStartTicks( std::clock() ),
255 m_nInputType( VclInputFlags::NONE ),
256 m_nEndPage( USHRT_MAX ),
257 m_nCheckPageNum( USHRT_MAX )
259 m_bPaintExtraData = ::IsExtraData( m_pImp->GetShell()->GetDoc() );
260 m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
261 m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
262 m_bUpdateExpFields = m_bBrowseActionStop = m_bActionInProgress = false;
263 // init new flag <mbFormatContentOnInterrupt>.
264 mbFormatContentOnInterrupt = false;
266 assert(!m_pImp->m_pLayAction); // there can be only one SwLayAction
267 m_pImp->m_pLayAction = this; // register there
270 SwLayAction::~SwLayAction()
272 OSL_ENSURE( !m_pWait, "Wait object not destroyed" );
273 m_pImp->m_pLayAction = nullptr; // unregister
276 void SwLayAction::Reset()
278 SetAgain(false);
279 m_pOptTab = nullptr;
280 m_nStartTicks = std::clock();
281 m_nInputType = VclInputFlags::NONE;
282 m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX;
283 m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
284 m_bInterrupt = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
285 m_bUpdateExpFields = m_bBrowseActionStop = false;
288 bool SwLayAction::RemoveEmptyBrowserPages()
290 // switching from the normal to the browser mode, empty pages may be
291 // retained for an annoyingly long time, so delete them here
292 bool bRet = false;
293 const SwViewShell *pSh = m_pRoot->GetCurrShell();
294 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
296 SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
299 if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) ||
300 pPage->ContainsContent() ||
301 pPage->FindFootnoteCont() )
302 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
303 else
305 bRet = true;
306 SwPageFrame *pDel = pPage;
307 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
308 pDel->Cut();
309 SwFrame::DestroyFrame(pDel);
311 } while ( pPage );
313 return bRet;
316 void SwLayAction::SetAgain(bool bAgain)
318 if (bAgain == m_bAgain)
319 return;
321 m_bAgain = bAgain;
323 assert(m_aFrameStack.size() == m_aFrameDeleteGuards.size());
324 size_t nCount = m_aFrameStack.size();
325 if (m_bAgain)
327 // LayAction::FormatLayout is now flagged to exit early and will avoid
328 // dereferencing any SwFrames in the stack of FormatLayouts so allow
329 // their deletion
330 for (size_t i = 0; i < nCount; ++i)
331 m_aFrameDeleteGuards[i].reset();
333 else
335 // LayAction::FormatLayout is now continue normally and will
336 // dereference the top SwFrame in the stack of m_aFrameStack as each
337 // FormatLevel returns so disallow their deletion
338 for (size_t i = 0; i < nCount; ++i)
339 m_aFrameDeleteGuards[i] = std::make_unique<SwFrameDeleteGuard>(m_aFrameStack[i]);
343 void SwLayAction::PushFormatLayout(SwFrame* pLow)
345 /* Workaround crash seen in crashtesting with fdo53985-1.docx
347 Lock pLow against getting deleted when it will be dereferenced
348 after FormatLayout
350 If SetAgain is called to make SwLayAction exit early to avoid that
351 dereference, then it clears these guards
353 m_aFrameStack.push_back(pLow);
354 m_aFrameDeleteGuards.push_back(std::make_unique<SwFrameDeleteGuard>(pLow));
357 void SwLayAction::PopFormatLayout()
359 m_aFrameDeleteGuards.pop_back();
360 m_aFrameStack.pop_back();
363 void SwLayAction::Action(OutputDevice* pRenderContext)
365 m_bActionInProgress = true;
367 //TurboMode? Hands-off during idle-format
368 if ( IsPaint() && !IsIdle() && TurboAction() )
370 m_pWait.reset();
371 m_pRoot->ResetTurboFlag();
372 m_bActionInProgress = false;
373 m_pRoot->DeleteEmptySct();
374 m_pRoot->DeleteEmptyFlys();
375 return;
377 else if ( m_pRoot->GetTurbo() )
379 m_pRoot->DisallowTurbo();
380 const SwFrame *pFrame = m_pRoot->GetTurbo();
381 m_pRoot->ResetTurbo();
382 pFrame->InvalidatePage();
384 m_pRoot->DisallowTurbo();
386 if ( IsCalcLayout() )
387 SetCheckPages( false );
389 InternalAction(pRenderContext);
390 if (RemoveEmptyBrowserPages())
391 SetAgain(true);
392 while ( IsAgain() )
394 SetAgain(false);
395 m_bNextCycle = false;
396 InternalAction(pRenderContext);
397 if (RemoveEmptyBrowserPages())
398 SetAgain(true);
400 m_pRoot->DeleteEmptySct();
401 m_pRoot->DeleteEmptyFlys();
403 m_pWait.reset();
405 //Turbo-Action permitted again for all cases.
406 m_pRoot->ResetTurboFlag();
407 m_pRoot->ResetTurbo();
409 SetCheckPages( true );
411 m_bActionInProgress = false;
414 SwPageFrame* SwLayAction::CheckFirstVisPage( SwPageFrame *pPage )
416 SwContentFrame *pCnt = pPage->FindFirstBodyContent();
417 SwContentFrame *pChk = pCnt;
418 bool bPageChgd = false;
419 while ( pCnt && pCnt->IsFollow() )
420 pCnt = pCnt->FindMaster();
421 if ( pCnt && pChk != pCnt )
422 { bPageChgd = true;
423 pPage = pCnt->FindPageFrame();
426 if ( !pPage->GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
428 SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
429 if ( pCont )
431 pCnt = pCont->ContainsContent();
432 pChk = pCnt;
433 while ( pCnt && pCnt->IsFollow() )
434 pCnt = static_cast<SwContentFrame*>(pCnt->FindPrev());
435 if ( pCnt && pCnt != pChk )
437 if ( bPageChgd )
439 // Use the 'topmost' page
440 SwPageFrame *pTmp = pCnt->FindPageFrame();
441 if ( pPage->GetPhyPageNum() > pTmp->GetPhyPageNum() )
442 pPage = pTmp;
444 else
445 pPage = pCnt->FindPageFrame();
449 return pPage;
452 // unlock position on start and end of page
453 // layout process.
454 static void unlockPositionOfObjects( SwPageFrame *pPageFrame )
456 assert( pPageFrame );
458 SwSortedObjs* pObjs = pPageFrame->GetSortedObjs();
459 if ( pObjs )
461 for (SwAnchoredObject* pObj : *pObjs)
463 pObj->UnlockPosition();
468 void SwLayAction::InternalAction(OutputDevice* pRenderContext)
470 OSL_ENSURE( m_pRoot->Lower()->IsPageFrame(), ":-( No page below the root.");
472 m_pRoot->Calc(pRenderContext);
474 // Figure out the first invalid page or the first one to be formatted,
475 // respectively. A complete-action means the first invalid page.
476 // However, the first page to be formatted might be the one having the
477 // number 1. If we're doing a fake formatting, the number of the first
478 // page is the number of the first visible page.
479 SwPageFrame *pPage = IsComplete() ? static_cast<SwPageFrame*>(m_pRoot->Lower()) :
480 m_pImp->GetFirstVisPage(pRenderContext);
481 if ( !pPage )
482 pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
484 // If there's a first-flow-Content in the first visible page that's also a Follow,
485 // we switch the page back to the original master of that Content.
486 if ( !IsComplete() )
487 pPage = CheckFirstVisPage( pPage );
488 sal_uInt16 nFirstPageNum = pPage->GetPhyPageNum();
490 while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
491 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
493 IDocumentLayoutAccess& rLayoutAccess = m_pRoot->GetFormat()->getIDocumentLayoutAccess();
494 const bool bNoLoop = pPage && SwLayouter::StartLoopControl(m_pRoot->GetFormat()->GetDoc(), pPage);
495 sal_uInt16 nPercentPageNum = 0;
497 auto lcl_isLayoutLooping = [&]()
499 const bool bAgain = this->IsAgain();
500 if (bAgain && bNoLoop)
501 rLayoutAccess.GetLayouter()->EndLoopControl();
502 return bAgain;
505 int nOuterLoopControlRuns = 0;
506 const int nOutermoopControlMax = 10000;
507 while ( (pPage && !IsInterrupt()) || m_nCheckPageNum != USHRT_MAX )
509 // Fix infinite loop in sw_ooxmlexport17 unit test
510 // When running the sw_ooxmlexport17 unit test on slower macOS Intel
511 // machines, This loop will never end even after 1M+ loops so set a
512 // maximum number of loops like is done in the nested while loops.
513 if (++nOuterLoopControlRuns > nOutermoopControlMax)
515 SAL_WARN("sw", "SwLayAction::InternalAction has run too many loops");
516 m_bInterrupt = true;
519 // note: this is the only place that consumes and resets m_nCheckPageNum
520 if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
522 if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
524 SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
525 while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
526 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
527 if (pPg)
528 pPage = pPg;
529 if (!pPage)
530 break;
533 SwPageFrame *pTmp = pPage->GetPrev() ?
534 static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
535 SetCheckPages( true );
536 SwFrame::CheckPageDescs( pPage, true, &pTmp );
537 SetCheckPages( false );
538 m_nCheckPageNum = USHRT_MAX;
539 pPage = pTmp;
540 continue;
543 if ( m_nEndPage != USHRT_MAX && pPage->GetPhyPageNum() > nPercentPageNum )
545 nPercentPageNum = pPage->GetPhyPageNum();
546 ::SetProgressState( nPercentPageNum, m_pImp->GetShell()->GetDoc()->GetDocShell());
548 m_pOptTab = nullptr;
550 // No Shortcut for Idle or CalcLayout
551 const bool bTakeShortcut = !IsIdle() && !IsComplete() && IsShortCut(pPage);
553 m_pRoot->DeleteEmptySct();
554 m_pRoot->DeleteEmptyFlys();
555 if (lcl_isLayoutLooping()) return;
557 if (!bTakeShortcut)
559 while ( !IsInterrupt() && !IsNextCycle() &&
560 ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid()) )
562 unlockPositionOfObjects( pPage );
564 SwObjectFormatter::FormatObjsAtFrame( *pPage, *pPage, this );
565 if ( !pPage->GetSortedObjs() )
567 // If there are no (more) Flys, the flags are superfluous.
568 pPage->ValidateFlyLayout();
569 pPage->ValidateFlyContent();
571 // change condition
572 while ( !IsInterrupt() && !IsNextCycle() &&
573 ( pPage->IsInvalid() ||
574 (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
576 PROTOCOL( pPage, PROT::FileInit, DbgAction::NONE, nullptr)
577 if (lcl_isLayoutLooping()) return;
579 // new loop control
580 int nLoopControlRuns_1 = 0;
581 const int nLoopControlMax = 20;
583 while ( !IsNextCycle() && pPage->IsInvalidLayout() )
585 pPage->ValidateLayout();
587 if ( ++nLoopControlRuns_1 > nLoopControlMax )
589 OSL_FAIL( "LoopControl_1 in SwLayAction::InternalAction" );
590 break;
593 FormatLayout( pRenderContext, pPage );
594 if (lcl_isLayoutLooping()) return;
596 // change condition
597 if ( !IsNextCycle() &&
598 ( pPage->IsInvalidContent() ||
599 (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
601 pPage->ValidateFlyInCnt();
602 pPage->ValidateContent();
603 pPage->ValidateFlyLayout();
604 pPage->ValidateFlyContent();
605 if ( !FormatContent( pPage ) )
607 if (lcl_isLayoutLooping()) return;
608 pPage->InvalidateContent();
609 pPage->InvalidateFlyInCnt();
610 pPage->InvalidateFlyLayout();
611 pPage->InvalidateFlyContent();
612 if ( IsBrowseActionStop() )
613 m_bInterrupt = true;
616 if( bNoLoop )
617 rLayoutAccess.GetLayouter()->LoopControl( pPage );
620 unlockPositionOfObjects( pPage );
623 // A previous page may be invalid again.
624 if (lcl_isLayoutLooping()) return;
625 if ( !pPage->GetSortedObjs() )
627 // If there are no (more) Flys, the flags are superfluous.
628 pPage->ValidateFlyLayout();
629 pPage->ValidateFlyContent();
631 if ( !IsInterrupt() )
633 SetNextCycle( false );
635 if ( m_nPreInvaPage != USHRT_MAX )
637 if( !IsComplete() && m_nPreInvaPage + 2 < nFirstPageNum )
639 m_pImp->SetFirstVisPageInvalid();
640 SwPageFrame *pTmpPage = m_pImp->GetFirstVisPage(pRenderContext);
641 nFirstPageNum = pTmpPage->GetPhyPageNum();
642 if( m_nPreInvaPage < nFirstPageNum )
644 m_nPreInvaPage = nFirstPageNum;
645 pPage = pTmpPage;
648 while ( pPage->GetPrev() && pPage->GetPhyPageNum() > m_nPreInvaPage )
649 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
650 m_nPreInvaPage = USHRT_MAX;
653 while ( pPage->GetPrev() &&
654 ( static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalid() ||
655 ( static_cast<SwPageFrame*>(pPage->GetPrev())->GetSortedObjs() &&
656 static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalidFly())) &&
657 (static_cast<SwPageFrame*>(pPage->GetPrev())->GetPhyPageNum() >=
658 nFirstPageNum) )
660 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
663 // Continue to the next invalid page
664 while ( pPage && !pPage->IsInvalid() &&
665 (!pPage->GetSortedObjs() || !pPage->IsInvalidFly()) )
667 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
669 if( bNoLoop )
670 rLayoutAccess.GetLayouter()->LoopControl( pPage );
672 CheckIdleEnd();
675 if ((bTakeShortcut || !pPage) && !IsInterrupt() &&
676 (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) )
678 // tdf#139426 allow suppression of AssertFlyPages
679 if ( m_pRoot->IsAssertFlyPages() && !m_pRoot->IsTableUpdateInProgress())
681 m_pRoot->AssertFlyPages();
683 if ( m_pRoot->IsSuperfluous() )
685 bool bOld = IsAgain();
686 m_pRoot->RemoveSuperfluous();
687 SetAgain(bOld);
689 if (lcl_isLayoutLooping()) return;
690 pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
691 while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
692 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
693 while ( pPage && pPage->GetNext() &&
694 pPage->GetPhyPageNum() < nFirstPageNum )
695 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
697 else if (bTakeShortcut)
698 break;
700 if ( IsInterrupt() && pPage )
702 // If we have input, we don't want to format content anymore, but
703 // we still should clean the layout.
704 // Otherwise, the following situation might arise:
705 // The user enters some text at the end of the paragraph of the last
706 // page, causing the paragraph to create a Follow for the next page.
707 // Meanwhile the user continues typing, so we have input while
708 // still formatting.
709 // The paragraph on the new page has already been partially formatted,
710 // and the new page has been fully formatted and is set to CompletePaint,
711 // but hasn't added itself to the area to be output. Then we paint,
712 // the CompletePaint of the page is reset because the new paragraph
713 // already added itself, but the borders of the page haven't been painted
714 // yet.
715 // Oh well, with the inevitable following LayAction, the page doesn't
716 // register itself, because it's (LayoutFrame) flags have been reset
717 // already - the border of the page will never be painted.
718 SwPageFrame *pPg = pPage;
719 if (lcl_isLayoutLooping()) return;
720 const SwRect &rVis = m_pImp->GetShell()->VisArea();
722 while( pPg && pPg->getFrameArea().Bottom() < rVis.Top() )
723 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
724 if( pPg != pPage )
725 pPg = pPg ? static_cast<SwPageFrame*>(pPg->GetPrev()) : pPage;
727 // set flag for interrupt content formatting
728 mbFormatContentOnInterrupt = true;
729 tools::Long nBottom = rVis.Bottom();
730 // #i42586# - format current page, if idle action is active
731 // This is an optimization for the case that the interrupt is created by
732 // the move of a form control object, which is represented by a window.
733 while ( pPg && ( pPg->getFrameArea().Top() < nBottom ||
734 ( IsIdle() && pPg == pPage ) ) )
736 unlockPositionOfObjects( pPg );
738 if (lcl_isLayoutLooping()) return;
740 // new loop control
741 int nLoopControlRuns_2 = 0;
742 const int nLoopControlMax = 20;
744 // special case: interrupt content formatting
745 // conditions are incorrect and are too strict.
746 // adjust interrupt formatting to normal page formatting - see above.
747 while ( ( mbFormatContentOnInterrupt &&
748 ( pPg->IsInvalid() ||
749 ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) ) ||
750 ( !mbFormatContentOnInterrupt && pPg->IsInvalidLayout() ) )
752 if (lcl_isLayoutLooping()) return;
753 // format also at-page anchored objects
754 SwObjectFormatter::FormatObjsAtFrame( *pPg, *pPg, this );
755 if ( !pPg->GetSortedObjs() )
757 pPg->ValidateFlyLayout();
758 pPg->ValidateFlyContent();
761 // new loop control
762 int nLoopControlRuns_3 = 0;
764 while ( pPg->IsInvalidLayout() )
766 pPg->ValidateLayout();
768 if ( ++nLoopControlRuns_3 > nLoopControlMax )
770 OSL_FAIL( "LoopControl_3 in Interrupt formatting in SwLayAction::InternalAction" );
771 break;
774 FormatLayout( pRenderContext, pPg );
775 if (lcl_isLayoutLooping()) return;
778 if ( mbFormatContentOnInterrupt &&
779 ( pPg->IsInvalidContent() ||
780 ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) )
782 pPg->ValidateFlyInCnt();
783 pPg->ValidateContent();
784 pPg->ValidateFlyLayout();
785 pPg->ValidateFlyContent();
787 if ( ++nLoopControlRuns_2 > nLoopControlMax )
789 OSL_FAIL( "LoopControl_2 in Interrupt formatting in SwLayAction::InternalAction" );
790 break;
793 if ( !FormatContent( pPg ) )
795 if (lcl_isLayoutLooping()) return;
796 pPg->InvalidateContent();
797 pPg->InvalidateFlyInCnt();
798 pPg->InvalidateFlyLayout();
799 pPg->InvalidateFlyContent();
801 // we are satisfied if the content is formatted once complete.
802 else
804 break;
809 unlockPositionOfObjects( pPg );
810 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
812 // reset flag for special interrupt content formatting.
813 mbFormatContentOnInterrupt = false;
815 m_pOptTab = nullptr;
816 if( bNoLoop )
817 rLayoutAccess.GetLayouter()->EndLoopControl();
820 bool SwLayAction::TurboAction_( const SwContentFrame *pCnt )
823 const SwPageFrame *pPage = nullptr;
824 if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() || pCnt->IsRetouche() )
826 const SwRect aOldRect( pCnt->UnionFrame( true ) );
827 const tools::Long nOldBottom = pCnt->getFrameArea().Top() + pCnt->getFramePrintArea().Bottom();
828 pCnt->Calc(m_pImp->GetShell()->GetOut());
829 if ( pCnt->getFrameArea().Bottom() < aOldRect.Bottom() )
830 pCnt->SetRetouche();
832 pPage = pCnt->FindPageFrame();
833 PaintContent( pCnt, pPage, aOldRect, nOldBottom );
835 if ( !pCnt->GetValidLineNumFlag() && pCnt->IsTextFrame() )
837 const sal_Int32 nAllLines = static_cast<const SwTextFrame*>(pCnt)->GetAllLines();
838 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->RecalcAllLines();
839 if ( nAllLines != static_cast<const SwTextFrame*>(pCnt)->GetAllLines() )
841 if ( IsPaintExtraData() )
842 m_pImp->GetShell()->AddPaintRect( pCnt->getFrameArea() );
843 // This is to calculate the remaining LineNums on the page,
844 // and we don't stop processing here. To perform this inside RecalcAllLines
845 // would be expensive, because we would have to notify the page even
846 // in unnecessary cases (normal actions).
847 const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
848 while ( pNxt &&
849 (pNxt->IsInTab() || pNxt->IsInDocBody() != pCnt->IsInDocBody()) )
850 pNxt = pNxt->GetNextContentFrame();
851 if ( pNxt )
852 pNxt->InvalidatePage();
854 return false;
857 if ( pPage->IsInvalidLayout() || (pPage->GetSortedObjs() && pPage->IsInvalidFly()) )
858 return false;
860 if ( !pPage )
861 pPage = pCnt->FindPageFrame();
863 // format floating screen objects at content frame.
864 if ( pCnt->IsTextFrame() &&
865 !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pCnt),
866 *pPage, this ) )
868 return false;
871 if ( pPage->IsInvalidContent() )
872 return false;
873 return true;
876 bool SwLayAction::TurboAction()
878 bool bRet = true;
880 if ( m_pRoot->GetTurbo() )
882 if ( !TurboAction_( m_pRoot->GetTurbo() ) )
884 CheckIdleEnd();
885 bRet = false;
887 m_pRoot->ResetTurbo();
889 else
890 bRet = false;
891 return bRet;
894 static bool lcl_IsInvaLay( const SwFrame *pFrame, tools::Long nBottom )
896 return !pFrame->isFrameAreaDefinitionValid() ||
897 (pFrame->IsCompletePaint() && ( pFrame->getFrameArea().Top() < nBottom ) );
900 static const SwFrame *lcl_FindFirstInvaLay( const SwFrame *pFrame, tools::Long nBottom )
902 OSL_ENSURE( pFrame->IsLayoutFrame(), "FindFirstInvaLay, no LayFrame" );
904 if (lcl_IsInvaLay(pFrame, nBottom))
905 return pFrame;
906 pFrame = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
907 while ( pFrame )
909 if ( pFrame->IsLayoutFrame() )
911 if (lcl_IsInvaLay(pFrame, nBottom))
912 return pFrame;
913 const SwFrame *pTmp = lcl_FindFirstInvaLay( pFrame, nBottom );
914 if ( nullptr != pTmp )
915 return pTmp;
917 pFrame = pFrame->GetNext();
919 return nullptr;
922 static const SwFrame *lcl_FindFirstInvaContent( const SwLayoutFrame *pLay, tools::Long nBottom,
923 const SwContentFrame *pFirst )
925 const SwContentFrame *pCnt = pFirst ? pFirst->GetNextContentFrame() :
926 pLay->ContainsContent();
927 while ( pCnt )
929 if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() )
931 if ( pCnt->getFrameArea().Top() <= nBottom )
932 return pCnt;
935 if ( pCnt->GetDrawObjs() )
937 const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
938 for (SwAnchoredObject* pObj : rObjs)
940 if ( auto pFly = pObj->DynCastFlyFrame() )
942 if ( pFly->IsFlyInContentFrame() )
944 if ( static_cast<const SwFlyInContentFrame*>(pFly)->IsInvalid() ||
945 pFly->IsCompletePaint() )
947 if ( pFly->getFrameArea().Top() <= nBottom )
948 return pFly;
950 const SwFrame *pFrame = lcl_FindFirstInvaContent( pFly, nBottom, nullptr );
951 if ( pFrame && pFrame->getFrameArea().Bottom() <= nBottom )
952 return pFrame;
957 if ( pCnt->getFrameArea().Top() > nBottom && !pCnt->IsInTab() )
958 return nullptr;
959 pCnt = pCnt->GetNextContentFrame();
960 if ( !pLay->IsAnLower( pCnt ) )
961 break;
963 return nullptr;
966 // consider drawing objects
967 static const SwAnchoredObject* lcl_FindFirstInvaObj( const SwPageFrame* _pPage,
968 tools::Long _nBottom )
970 OSL_ENSURE( _pPage->GetSortedObjs(), "FindFirstInvaObj, no Objs" );
972 for (SwAnchoredObject* pObj : *_pPage->GetSortedObjs())
974 if ( auto pFly = pObj->DynCastFlyFrame() )
976 if ( pFly->getFrameArea().Top() <= _nBottom )
978 if ( pFly->IsInvalid() || pFly->IsCompletePaint() )
979 return pFly;
981 const SwFrame* pTmp;
982 if ( nullptr != (pTmp = lcl_FindFirstInvaContent( pFly, _nBottom, nullptr )) &&
983 pTmp->getFrameArea().Top() <= _nBottom )
984 return pFly;
987 else if ( auto pDrawObject = dynamic_cast< const SwAnchoredDrawObject *>( pObj ) )
989 if ( !pDrawObject->IsValidPos() )
991 return pObj;
995 return nullptr;
998 /* Returns True if the page lies directly below or right of the visible area.
1000 * It's possible for things to change in such a way that the processing
1001 * (of the caller!) has to continue with the predecessor of the passed page.
1002 * The parameter might therefore get modified!
1003 * For BrowseMode, you may even activate the ShortCut if the invalid content
1004 * of the page lies below the visible area.
1006 bool SwLayAction::IsShortCut( SwPageFrame *&prPage )
1008 vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
1009 bool bRet = false;
1010 const SwViewShell *pSh = m_pRoot->GetCurrShell();
1011 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
1013 // If the page is not valid, we quickly format it, otherwise
1014 // there's gonna be no end of trouble
1015 if ( !prPage->isFrameAreaDefinitionValid() )
1017 if ( bBrowse )
1019 // format complete page
1020 // Thus, loop on all lowers of the page <prPage>, instead of only
1021 // format its first lower.
1022 // NOTE: In online layout (bBrowse == true) a page can contain
1023 // a header frame and/or a footer frame beside the body frame.
1024 prPage->Calc(pRenderContext);
1025 SwFrame* pPageLowerFrame = prPage->Lower();
1026 while ( pPageLowerFrame )
1028 pPageLowerFrame->Calc(pRenderContext);
1029 pPageLowerFrame = pPageLowerFrame->GetNext();
1032 else
1033 FormatLayout( pSh ? pSh->GetOut() : nullptr, prPage );
1034 if ( IsAgain() )
1035 return false;
1038 const SwRect &rVis = m_pImp->GetShell()->VisArea();
1039 if ( (prPage->getFrameArea().Top() >= rVis.Bottom()) ||
1040 (prPage->getFrameArea().Left()>= rVis.Right()) )
1042 bRet = true;
1044 // This is going to be a bit nasty: The first ContentFrame of this
1045 // page in the Body text needs formatting; if it changes the page during
1046 // that process, I need to start over a page further back, because we
1047 // have been processing a PageBreak.
1048 // Even more uncomfortable: The next ContentFrame must be formatted,
1049 // because it's possible for empty pages to exist temporarily (for example
1050 // a paragraph across multiple pages gets deleted or reduced in size).
1052 // This is irrelevant for the browser, if the last Cnt above it
1053 // isn't visible anymore.
1055 const SwPageFrame *p2ndPage = prPage;
1056 const SwContentFrame *pContent;
1057 const SwLayoutFrame* pBody = p2ndPage->FindBodyCont();
1058 if( p2ndPage->IsFootnotePage() && pBody )
1059 pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
1060 pContent = pBody ? pBody->ContainsContent() : nullptr;
1061 while ( p2ndPage && !pContent )
1063 p2ndPage = static_cast<const SwPageFrame*>(p2ndPage->GetNext());
1064 if( p2ndPage )
1066 pBody = p2ndPage->FindBodyCont();
1067 if( p2ndPage->IsFootnotePage() && pBody )
1068 pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
1069 pContent = pBody ? pBody->ContainsContent() : nullptr;
1072 if ( pContent )
1074 bool bTstCnt = true;
1075 if ( bBrowse )
1077 // Is the Cnt before already invisible?
1078 const SwFrame *pLst = pContent;
1079 if ( pLst->IsInTab() )
1080 pLst = pContent->FindTabFrame();
1081 if ( pLst->IsInSct() )
1082 pLst = pContent->FindSctFrame();
1083 pLst = pLst->FindPrev();
1084 if ( pLst &&
1085 (pLst->getFrameArea().Top() >= rVis.Bottom() ||
1086 pLst->getFrameArea().Left()>= rVis.Right()) )
1088 bTstCnt = false;
1092 if ( bTstCnt )
1094 // check after each frame calculation,
1095 // if the content frame has changed the page. If yes, no other
1096 // frame calculation is performed
1097 bool bPageChg = false;
1099 if ( pContent->IsInSct() )
1101 const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
1102 if ( !pSct->isFrameAreaDefinitionValid() )
1104 pSct->Calc(pRenderContext);
1105 pSct->SetCompletePaint();
1106 if ( IsAgain() )
1107 return false;
1109 bPageChg = pContent->FindPageFrame() != p2ndPage &&
1110 prPage->GetPrev();
1114 if ( !bPageChg && !pContent->isFrameAreaDefinitionValid() )
1116 pContent->Calc(pRenderContext);
1117 pContent->SetCompletePaint();
1118 if ( IsAgain() )
1119 return false;
1121 bPageChg = pContent->FindPageFrame() != p2ndPage &&
1122 prPage->GetPrev();
1125 if ( !bPageChg && pContent->IsInTab() )
1127 const SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindTabFrame();
1128 if ( !pTab->isFrameAreaDefinitionValid() )
1130 pTab->Calc(pRenderContext);
1131 pTab->SetCompletePaint();
1132 if ( IsAgain() )
1133 return false;
1135 bPageChg = pContent->FindPageFrame() != p2ndPage &&
1136 prPage->GetPrev();
1140 if ( !bPageChg && pContent->IsInSct() )
1142 const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
1143 if ( !pSct->isFrameAreaDefinitionValid() )
1145 pSct->Calc(pRenderContext);
1146 pSct->SetCompletePaint();
1147 if ( IsAgain() )
1148 return false;
1150 bPageChg = pContent->FindPageFrame() != p2ndPage &&
1151 prPage->GetPrev();
1155 if ( bPageChg )
1157 bRet = false;
1158 const SwPageFrame* pTmp = pContent->FindPageFrame();
1159 if ( pTmp->GetPhyPageNum() < prPage->GetPhyPageNum() &&
1160 pTmp->IsInvalid() )
1162 prPage = const_cast<SwPageFrame*>(pTmp);
1164 else
1166 prPage = static_cast<SwPageFrame*>(prPage->GetPrev());
1169 // no shortcut, if at previous page
1170 // an anchored object is registered, whose anchor is <pContent>.
1171 else
1173 auto const CheckFlys = [&bRet,pContent](SwPageFrame & rPage)
1175 SwSortedObjs *const pObjs(rPage.GetSortedObjs());
1176 if (pObjs)
1178 for (SwAnchoredObject *const pObj : *pObjs)
1180 if (pObj->GetAnchorFrameContainingAnchPos() == pContent)
1182 bRet = false;
1183 break;
1188 if (prPage->GetPrev())
1190 CheckFlys(*static_cast<SwPageFrame*>(prPage->GetPrev()));
1192 CheckFlys(*prPage); // tdf#147666 also check this page
1198 if ( !bRet && bBrowse )
1200 const tools::Long nBottom = rVis.Bottom();
1201 const SwAnchoredObject* pObj( nullptr );
1202 if ( prPage->GetSortedObjs() &&
1203 (prPage->IsInvalidFlyLayout() || prPage->IsInvalidFlyContent()) &&
1204 nullptr != (pObj = lcl_FindFirstInvaObj( prPage, nBottom )) &&
1205 pObj->GetObjRect().Top() <= nBottom )
1207 return false;
1209 const SwFrame* pFrame( nullptr );
1210 if ( prPage->IsInvalidLayout() &&
1211 nullptr != (pFrame = lcl_FindFirstInvaLay( prPage, nBottom )) &&
1212 pFrame->getFrameArea().Top() <= nBottom )
1214 return false;
1216 if ( (prPage->IsInvalidContent() || prPage->IsInvalidFlyInCnt()) &&
1217 nullptr != (pFrame = lcl_FindFirstInvaContent( prPage, nBottom, nullptr )) &&
1218 pFrame->getFrameArea().Top() <= nBottom )
1220 return false;
1222 bRet = true;
1224 return bRet;
1227 // introduce support for vertical layout
1228 bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLay, bool bAddRect )
1230 OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
1231 if ( IsAgain() )
1232 return false;
1234 bool bChanged = false;
1235 bool bAlreadyPainted = false;
1236 // remember frame at complete paint
1237 SwRect aFrameAtCompletePaint;
1239 if ( !pLay->isFrameAreaDefinitionValid() || pLay->IsCompletePaint() )
1241 if ( pLay->GetPrev() && !pLay->GetPrev()->isFrameAreaDefinitionValid() )
1242 pLay->GetPrev()->SetCompletePaint();
1244 SwRect aOldFrame( pLay->getFrameArea() );
1245 SwRect aOldRect( aOldFrame );
1246 if( pLay->IsPageFrame() )
1248 aOldRect = static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext);
1252 SwFrameDeleteGuard aDeleteGuard(pLay);
1253 pLay->Calc(pRenderContext);
1256 if ( aOldFrame != pLay->getFrameArea() )
1257 bChanged = true;
1259 bool bNoPaint = false;
1260 if ( pLay->IsPageBodyFrame() &&
1261 pLay->getFrameArea().Pos() == aOldRect.Pos() &&
1262 pLay->Lower() )
1264 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1265 // Limitations because of headers / footers
1266 if( pSh && pSh->GetViewOptions()->getBrowseMode() &&
1267 !( pLay->IsCompletePaint() && pLay->FindPageFrame()->FindFootnoteCont() ) )
1268 bNoPaint = true;
1271 if ( !bNoPaint && IsPaint() && bAddRect && (pLay->IsCompletePaint() || bChanged) )
1273 SwRect aPaint( pLay->getFrameArea() );
1274 // consider border and shadow for
1275 // page frames -> enlarge paint rectangle correspondingly.
1276 if ( pLay->IsPageFrame() )
1278 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
1279 aPaint = pPageFrame->GetBoundRect(pRenderContext);
1282 bool bPageInBrowseMode = pLay->IsPageFrame();
1283 if( bPageInBrowseMode )
1285 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1286 if( !pSh || !pSh->GetViewOptions()->getBrowseMode() )
1287 bPageInBrowseMode = false;
1289 if( bPageInBrowseMode )
1291 // NOTE: no vertical layout in online layout
1292 // Is the change even visible?
1293 if ( pLay->IsCompletePaint() )
1295 m_pImp->GetShell()->AddPaintRect( aPaint );
1296 bAddRect = false;
1298 else
1300 SwRegionRects aRegion( aOldRect );
1301 aRegion -= aPaint;
1302 for ( size_t i = 0; i < aRegion.size(); ++i )
1303 m_pImp->GetShell()->AddPaintRect( aRegion[i] );
1304 aRegion.ChangeOrigin( aPaint );
1305 aRegion.clear();
1306 aRegion.push_back( aPaint );
1307 aRegion -= aOldRect;
1308 for ( size_t i = 0; i < aRegion.size(); ++i )
1309 m_pImp->GetShell()->AddPaintRect( aRegion[i] );
1312 else
1314 m_pImp->GetShell()->AddPaintRect( aPaint );
1315 bAlreadyPainted = true;
1316 // remember frame at complete paint
1317 aFrameAtCompletePaint = pLay->getFrameArea();
1320 // provide paint of spacing
1321 // between pages (not only for in online mode).
1322 if ( pLay->IsPageFrame() )
1324 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1325 const SwTwips nHalfDocBorder = pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
1326 : SwViewOption::defGapBetweenPages;
1327 const bool bLeftToRightViewLayout = m_pRoot->IsLeftToRightViewLayout();
1328 const bool bPrev = bLeftToRightViewLayout ? pLay->GetPrev() : pLay->GetNext();
1329 const bool bNext = bLeftToRightViewLayout ? pLay->GetNext() : pLay->GetPrev();
1330 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
1331 SwRect aPageRect( pLay->getFrameArea() );
1333 if(pSh)
1335 SwPageFrame::GetBorderAndShadowBoundRect(aPageRect, pSh,
1336 pRenderContext,
1337 aPageRect, pPageFrame->IsLeftShadowNeeded(), pPageFrame->IsRightShadowNeeded(),
1338 pPageFrame->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
1341 if ( bPrev )
1343 // top
1344 SwRect aSpaceToPrevPage( aPageRect );
1345 aSpaceToPrevPage.Top( aSpaceToPrevPage.Top() - nHalfDocBorder );
1346 aSpaceToPrevPage.Bottom( pLay->getFrameArea().Top() );
1347 if(!aSpaceToPrevPage.IsEmpty())
1348 m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
1350 // left
1351 aSpaceToPrevPage = aPageRect;
1352 aSpaceToPrevPage.Left( aSpaceToPrevPage.Left() - nHalfDocBorder );
1353 aSpaceToPrevPage.Right( pLay->getFrameArea().Left() );
1354 if(!aSpaceToPrevPage.IsEmpty())
1355 m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
1357 if ( bNext )
1359 // bottom
1360 SwRect aSpaceToNextPage( aPageRect );
1361 aSpaceToNextPage.Bottom( aSpaceToNextPage.Bottom() + nHalfDocBorder );
1362 aSpaceToNextPage.Top( pLay->getFrameArea().Bottom() );
1363 if(!aSpaceToNextPage.IsEmpty())
1364 m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
1366 // right
1367 aSpaceToNextPage = aPageRect;
1368 aSpaceToNextPage.Right( aSpaceToNextPage.Right() + nHalfDocBorder );
1369 aSpaceToNextPage.Left( pLay->getFrameArea().Right() );
1370 if(!aSpaceToNextPage.IsEmpty())
1371 m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
1375 pLay->ResetCompletePaint();
1378 if ( IsPaint() && bAddRect &&
1379 !pLay->GetNext() && pLay->IsRetoucheFrame() && pLay->IsRetouche() )
1381 // vertical layout support
1382 SwRectFnSet aRectFnSet(pLay);
1383 SwRect aRect( pLay->GetUpper()->GetPaintArea() );
1384 aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pLay) );
1385 if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1386 pLay->ResetRetouche();
1389 if( bAlreadyPainted )
1390 bAddRect = false;
1392 CheckWaitCursor();
1394 if ( IsAgain() )
1395 return false;
1397 // Now, deal with the lowers that are LayoutFrames
1399 if ( pLay->IsFootnoteFrame() ) // no LayFrames as Lower
1400 return bChanged;
1402 SwFrame *pLow = pLay->Lower();
1403 bool bTabChanged = false;
1404 while ( pLow && pLow->GetUpper() == pLay )
1406 SwFrame* pNext = nullptr;
1407 if ( pLow->IsLayoutFrame() )
1409 if ( pLow->IsTabFrame() )
1411 // Remember what was the next of the lower. Formatting may move it to the previous
1412 // page, in which case it looses its next.
1413 pNext = pLow->GetNext();
1415 if (pNext && pNext->IsTabFrame())
1417 auto pTab = static_cast<SwTabFrame*>(pNext);
1418 if (pTab->IsFollow())
1420 // The next frame is a follow of the previous frame, SwTabFrame::Join() will
1421 // delete this one as part of formatting, so forget about it.
1422 pNext = nullptr;
1426 bTabChanged |= FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
1428 // Skip the ones already registered for deletion
1429 else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() )
1431 PushFormatLayout(pLow);
1432 bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect );
1433 PopFormatLayout();
1436 // else: don't calc content frames any more
1438 if ( IsAgain() )
1439 return false;
1440 if (!pNext)
1442 pNext = pLow->GetNext();
1444 pLow = pNext;
1446 // add complete frame area as paint area, if frame
1447 // area has been already added and after formatting its lowers the frame area
1448 // is enlarged.
1449 SwRect aBoundRect(pLay->IsPageFrame() ? static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext) : pLay->getFrameArea() );
1451 if ( bAlreadyPainted &&
1452 ( aBoundRect.Width() > aFrameAtCompletePaint.Width() ||
1453 aBoundRect.Height() > aFrameAtCompletePaint.Height() )
1456 m_pImp->GetShell()->AddPaintRect( aBoundRect );
1458 return bChanged || bTabChanged;
1461 void SwLayAction::FormatLayoutFly( SwFlyFrame* pFly )
1463 vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
1464 OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
1465 if ( IsAgain() )
1466 return;
1468 bool bChanged = false;
1469 bool bAddRect = true;
1471 if ( !pFly->isFrameAreaDefinitionValid() || pFly->IsCompletePaint() || pFly->IsInvalid() )
1473 // The Frame has changed, now it's getting formatted.
1474 const SwRect aOldRect( pFly->getFrameArea() );
1475 pFly->Calc(pRenderContext);
1476 bChanged = aOldRect != pFly->getFrameArea();
1478 if ( IsPaint() && (pFly->IsCompletePaint() || bChanged) &&
1479 pFly->getFrameArea().Top() > 0 && pFly->getFrameArea().Left() > 0 )
1480 m_pImp->GetShell()->AddPaintRect( pFly->getFrameArea() );
1482 if ( bChanged )
1483 pFly->Invalidate();
1484 else
1485 pFly->Validate();
1487 bAddRect = false;
1488 pFly->ResetCompletePaint();
1491 if ( IsAgain() )
1492 return;
1494 // Now, deal with the lowers that are LayoutFrames
1495 SwFrame *pLow = pFly->Lower();
1496 while ( pLow )
1498 if ( pLow->IsLayoutFrame() )
1500 if ( pLow->IsTabFrame() )
1501 FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
1502 else
1503 FormatLayout( m_pImp->GetShell()->GetOut(), static_cast<SwLayoutFrame*>(pLow), bAddRect );
1505 pLow = pLow->GetNext();
1509 // Implement vertical layout support
1510 bool SwLayAction::FormatLayoutTab( SwTabFrame *pTab, bool bAddRect )
1512 OSL_ENSURE( !IsAgain(), "8-) Attention to the invalid page." );
1513 if ( IsAgain() || !pTab->Lower() )
1514 return false;
1516 vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
1517 IDocumentTimerAccess& rTimerAccess = m_pRoot->GetFormat()->getIDocumentTimerAccess();
1518 rTimerAccess.BlockIdling();
1520 bool bChanged = false;
1521 bool bPainted = false;
1523 const SwPageFrame *pOldPage = pTab->FindPageFrame();
1525 // vertical layout support
1526 SwRectFnSet aRectFnSet(pTab);
1528 if ( !pTab->isFrameAreaDefinitionValid() || pTab->IsCompletePaint() || pTab->IsComplete() )
1530 if ( pTab->GetPrev() && !pTab->GetPrev()->isFrameAreaDefinitionValid() )
1532 pTab->GetPrev()->SetCompletePaint();
1535 const SwRect aOldRect( pTab->getFrameArea() );
1536 pTab->SetLowersFormatted( false );
1537 pTab->Calc(pRenderContext);
1538 if ( aOldRect != pTab->getFrameArea() )
1540 bChanged = true;
1542 const SwRect aPaintFrame = pTab->GetPaintArea();
1544 if ( IsPaint() && bAddRect )
1546 // add condition <pTab->getFrameArea().HasArea()>
1547 if ( !pTab->IsCompletePaint() &&
1548 pTab->IsComplete() &&
1549 ( pTab->getFrameArea().SSize() != pTab->getFramePrintArea().SSize() ||
1550 // vertical layout support
1551 aRectFnSet.GetLeftMargin(*pTab) ) &&
1552 pTab->getFrameArea().HasArea()
1555 // re-implement calculation of margin rectangles.
1556 SwRect aMarginRect;
1558 SwTwips nLeftMargin = aRectFnSet.GetLeftMargin(*pTab);
1559 if ( nLeftMargin > 0)
1561 aMarginRect = pTab->getFrameArea();
1562 aRectFnSet.SetWidth( aMarginRect, nLeftMargin );
1563 m_pImp->GetShell()->AddPaintRect( aMarginRect );
1566 if ( aRectFnSet.GetRightMargin(*pTab) > 0)
1568 aMarginRect = pTab->getFrameArea();
1569 aRectFnSet.SetLeft( aMarginRect, aRectFnSet.GetPrtRight(*pTab) );
1570 m_pImp->GetShell()->AddPaintRect( aMarginRect );
1573 SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pTab);
1574 if ( nTopMargin > 0)
1576 aMarginRect = pTab->getFrameArea();
1577 aRectFnSet.SetHeight( aMarginRect, nTopMargin );
1578 m_pImp->GetShell()->AddPaintRect( aMarginRect );
1581 if ( aRectFnSet.GetBottomMargin(*pTab) > 0)
1583 aMarginRect = pTab->getFrameArea();
1584 aRectFnSet.SetTop( aMarginRect, aRectFnSet.GetPrtBottom(*pTab) );
1585 m_pImp->GetShell()->AddPaintRect( aMarginRect );
1588 else if ( pTab->IsCompletePaint() )
1590 m_pImp->GetShell()->AddPaintRect( aPaintFrame );
1591 bAddRect = false;
1592 bPainted = true;
1595 if ( pTab->IsRetouche() && !pTab->GetNext() )
1597 SwRect aRect( pTab->GetUpper()->GetPaintArea() );
1598 // vertical layout support
1599 aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
1600 if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1601 pTab->ResetRetouche();
1604 else
1605 bAddRect = false;
1607 if ( pTab->IsCompletePaint() && !m_pOptTab )
1608 m_pOptTab = pTab;
1609 pTab->ResetCompletePaint();
1611 if ( IsPaint() && bAddRect && pTab->IsRetouche() && !pTab->GetNext() )
1613 // set correct rectangle for retouche: area between bottom of table frame
1614 // and bottom of paint area of the upper frame.
1615 SwRect aRect( pTab->GetUpper()->GetPaintArea() );
1616 // vertical layout support
1617 aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
1618 if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1619 pTab->ResetRetouche();
1622 CheckWaitCursor();
1624 rTimerAccess.UnblockIdling();
1626 // Ugly shortcut!
1627 if ( pTab->IsLowersFormatted() &&
1628 (bPainted || !m_pImp->GetShell()->VisArea().Overlaps( pTab->getFrameArea())) )
1629 return false;
1631 // Now, deal with the lowers
1632 if ( IsAgain() )
1633 return false;
1635 // for safety reasons:
1636 // check page number before formatting lowers.
1637 if ( pOldPage->GetPhyPageNum() > (pTab->FindPageFrame()->GetPhyPageNum() + 1) )
1638 SetNextCycle( true );
1640 // format lowers, only if table frame is valid
1641 if ( pTab->isFrameAreaDefinitionValid() )
1643 // tdf#128437 FlowFrameJoinLockGuard on pTab caused a problem here
1644 SwLayoutFrame *pLow = static_cast<SwLayoutFrame*>(pTab->Lower());
1645 while ( pLow )
1647 SwFrameDeleteGuard rowG(pLow); // tdf#124675 prevent RemoveFollowFlowLine()
1648 bChanged |= FormatLayout( m_pImp->GetShell()->GetOut(), pLow, bAddRect );
1649 if ( IsAgain() )
1650 return false;
1651 pLow = static_cast<SwLayoutFrame*>(pLow->GetNext());
1655 return bChanged;
1658 bool SwLayAction::FormatContent(SwPageFrame *const pPage)
1660 ::comphelper::ScopeGuard g([this, pPage]() {
1661 if (IsAgain())
1663 return; // pPage probably deleted
1665 if (auto const* pObjs = pPage->GetSortedObjs())
1667 std::vector<std::pair<SwAnchoredObject*, SwPageFrame*>> moved;
1668 for (auto const pObj : *pObjs)
1670 assert(!pObj->AnchorFrame()->IsTextFrame()
1671 || !static_cast<SwTextFrame const*>(pObj->AnchorFrame())->IsFollow());
1672 SwPageFrame *const pAnchorPage(pObj->AnchorFrame()->FindPageFrame());
1673 assert(pAnchorPage);
1674 if (pAnchorPage != pPage
1675 && pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()
1676 && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
1677 != RndStdIds::FLY_AS_CHAR)
1679 moved.emplace_back(pObj, pAnchorPage);
1682 for (auto const& [pObj, pAnchorPage] : moved)
1684 SAL_INFO("sw.layout", "SwLayAction::FormatContent: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
1685 pObj->RegisterAtPage(*pAnchorPage);
1686 // tdf#143239 if the position remains valid, it may not be
1687 // positioned again so would remain on the wrong page!
1688 pObj->InvalidateObjPos();
1689 ::Notify_Background(pObj->GetDrawObj(), pPage,
1690 pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
1691 // tdf#148897 in case the fly moves back to this page before
1692 // being positioned again, the SwFlyNotify / ::Notify() could
1693 // conclude that it didn't move at all and not call
1694 // NotifyBackground(); note: pObj->SetObjTop(FAR_AWAY) results
1695 // in wrong positions, use different approach!
1696 pObj->SetForceNotifyNewBackground(true);
1698 if (!moved.empty())
1700 pPage->InvalidateFlyLayout();
1701 if (auto *const pContent = pPage->FindLastBodyContent())
1703 pContent->InvalidateSize();
1709 const SwContentFrame *pContent = pPage->ContainsContent();
1710 const SwViewShell *pSh = m_pRoot->GetCurrShell();
1711 const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
1713 while ( pContent && pPage->IsAnLower( pContent ) )
1715 // If the content didn't change, we can use a few shortcuts.
1716 bool bFull = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint() ||
1717 pContent->IsRetouche() || pContent->GetDrawObjs();
1719 auto pText = pContent->DynCastTextFrame();
1720 if (!bFull && !pContent->GetDrawObjs() && pContent->IsFollow() && pText)
1722 // This content frame doesn't have to-para anchored objects, but it's a follow, check
1723 // the master.
1724 const SwTextFrame* pMaster = pText;
1725 while (pMaster->IsFollow())
1727 pMaster = pMaster->FindMaster();
1729 if (pMaster && pMaster->GetDrawObjs())
1731 for (SwAnchoredObject* pDrawObj : *pMaster->GetDrawObjs())
1733 auto pFly = pDrawObj->DynCastFlyFrame();
1734 if (!pFly)
1736 continue;
1739 if (!pFly->IsFlySplitAllowed())
1741 continue;
1744 if (pFly->GetAnchorFrameContainingAnchPos() != pContent)
1746 continue;
1749 // This fly is effectively anchored to pContent, still format pContent.
1750 bFull = true;
1751 break;
1756 if ( bFull )
1758 // We do this so we don't have to search later on.
1759 const bool bNxtCnt = IsCalcLayout() && !pContent->GetFollow();
1760 const SwContentFrame *pContentNext = bNxtCnt ? pContent->GetNextContentFrame() : nullptr;
1761 SwContentFrame* const pContentPrev = pContent->GetPrev() ? pContent->GetPrevContentFrame() : nullptr;
1762 std::optional<SfxDeleteListener> oPrevDeleteListener;
1763 if (pContentPrev)
1764 oPrevDeleteListener.emplace(*pContentPrev);
1766 const SwLayoutFrame*pOldUpper = pContent->GetUpper();
1767 const SwTabFrame *pTab = pContent->FindTabFrame();
1768 const bool bInValid = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint();
1769 const bool bOldPaint = IsPaint();
1770 m_bPaint = bOldPaint && !(pTab && pTab == m_pOptTab);
1771 FormatContent_( pContent, pPage );
1772 // reset <bPaint> before format objects
1773 m_bPaint = bOldPaint;
1775 // format floating screen object at content frame.
1776 // No format, if action flag <bAgain> is set or action is interrupted.
1777 // allow format on interruption of action, if
1778 // it's the format for this interrupt
1779 // pass correct page frame
1780 // to the object formatter.
1781 if ( !IsAgain() &&
1782 ( !IsInterrupt() || mbFormatContentOnInterrupt ) &&
1783 pContent->IsTextFrame() &&
1784 !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pContent),
1785 *(pContent->FindPageFrame()), this ) )
1787 return false;
1790 if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1792 const sal_Int32 nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1793 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1794 if ( IsPaintExtraData() && IsPaint() &&
1795 nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1796 m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1799 if ( IsAgain() )
1800 return false;
1802 // Temporarily interrupt processing if layout or Flys become invalid again.
1803 // However not for the BrowseView: The layout is getting invalid
1804 // all the time because the page height gets adjusted.
1805 // The same applies if the user wants to continue working and at least one
1806 // paragraph has been processed.
1807 if (!pTab || !bInValid)
1809 CheckIdleEnd();
1810 // consider interrupt formatting.
1811 if ( ( IsInterrupt() && !mbFormatContentOnInterrupt ) ||
1812 ( !bBrowse && pPage->IsInvalidLayout() ) ||
1813 // consider interrupt formatting
1814 ( pPage->GetSortedObjs() && pPage->IsInvalidFly() && !mbFormatContentOnInterrupt )
1816 return false;
1818 if ( pOldUpper != pContent->GetUpper() )
1820 const sal_uInt16 nCurNum = pContent->FindPageFrame()->GetPhyPageNum();
1821 if ( nCurNum < pPage->GetPhyPageNum() )
1822 m_nPreInvaPage = nCurNum;
1824 // If the frame flowed backwards more than one page, we need to
1825 // start over again from the beginning, so nothing gets left out.
1826 if ( !IsCalcLayout() && pPage->GetPhyPageNum() > nCurNum+1 )
1828 SetNextCycle( true );
1829 // consider interrupt formatting
1830 if ( !mbFormatContentOnInterrupt )
1832 return false;
1836 // If the frame moved forwards to the next page, we re-run through
1837 // the predecessor.
1838 // This way, we catch predecessors which are now responsible for
1839 // retouching, but the footers will be touched also.
1840 bool bSetContent = true;
1841 if ( pContentPrev )
1843 if (oPrevDeleteListener->WasDeleted())
1845 SAL_WARN("sw", "ContentPrev was deleted");
1846 return false;
1849 if ( !pContentPrev->isFrameAreaDefinitionValid() && pPage->IsAnLower( pContentPrev ) )
1851 pPage->InvalidateContent();
1854 if ( pOldUpper != pContent->GetUpper() &&
1855 pPage->GetPhyPageNum() < pContent->FindPageFrame()->GetPhyPageNum() )
1857 pContent = pContentPrev;
1858 bSetContent = false;
1861 if ( bSetContent )
1863 if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
1864 pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
1866 const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
1867 const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
1868 nBottom, pContent );
1869 if ( !pTmp )
1871 if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
1872 !lcl_FindFirstInvaObj( pPage, nBottom )) &&
1873 (!pPage->IsInvalidLayout() ||
1874 !lcl_FindFirstInvaLay( pPage, nBottom )))
1875 SetBrowseActionStop( true );
1876 // consider interrupt formatting.
1877 if ( !mbFormatContentOnInterrupt )
1879 return false;
1883 pContent = bNxtCnt ? pContentNext : pContent->GetNextContentFrame();
1886 if (IsReschedule())
1888 ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
1891 else
1893 if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1895 const sal_Int32 nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1896 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1897 if ( IsPaintExtraData() && IsPaint() &&
1898 nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1899 m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1902 // Do this if the frame has been formatted before.
1903 if ( pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() &&
1904 IsPaint() )
1905 PaintContent( pContent, pPage, pContent->getFrameArea(), pContent->getFrameArea().Bottom());
1906 if ( IsIdle() )
1908 CheckIdleEnd();
1909 // consider interrupt formatting.
1910 if ( IsInterrupt() && !mbFormatContentOnInterrupt )
1911 return false;
1913 if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
1914 pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
1916 const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
1917 const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
1918 nBottom, pContent );
1919 if ( !pTmp )
1921 if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
1922 !lcl_FindFirstInvaObj( pPage, nBottom )) &&
1923 (!pPage->IsInvalidLayout() ||
1924 !lcl_FindFirstInvaLay( pPage, nBottom )))
1925 SetBrowseActionStop( true );
1926 // consider interrupt formatting.
1927 if ( !mbFormatContentOnInterrupt )
1929 return false;
1933 pContent = pContent->GetNextContentFrame();
1936 CheckWaitCursor();
1937 // consider interrupt formatting.
1938 return !IsInterrupt() || mbFormatContentOnInterrupt;
1941 void SwLayAction::FormatContent_( const SwContentFrame *pContent, const SwPageFrame *pPage )
1943 // We probably only ended up here because the Content holds DrawObjects.
1944 const bool bDrawObjsOnly = pContent->isFrameAreaDefinitionValid() && !pContent->IsCompletePaint() && !pContent->IsRetouche();
1945 SwRectFnSet aRectFnSet(pContent);
1946 if ( !bDrawObjsOnly && IsPaint() )
1948 const SwRect aOldRect( pContent->UnionFrame() );
1949 const tools::Long nOldBottom = aRectFnSet.GetPrtBottom(*pContent);
1950 pContent->OptCalc();
1951 if( IsAgain() )
1952 return;
1953 if( aRectFnSet.YDiff( aRectFnSet.GetBottom(pContent->getFrameArea()),
1954 aRectFnSet.GetBottom(aOldRect) ) < 0 )
1956 pContent->SetRetouche();
1958 PaintContent( pContent, pContent->FindPageFrame(), aOldRect, nOldBottom);
1960 else
1962 if ( IsPaint() && pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() )
1963 PaintContent( pContent, pPage, pContent->getFrameArea(),
1964 aRectFnSet.GetBottom(pContent->getFrameArea()) );
1965 pContent->OptCalc();
1969 void SwLayAction::FormatFlyContent( const SwFlyFrame *pFly )
1971 const SwContentFrame *pContent = pFly->ContainsContent();
1973 while ( pContent )
1975 FormatContent_( pContent, pContent->FindPageFrame() );
1977 // format floating screen objects at content text frame
1978 // pass correct page frame to the object formatter.
1979 if ( pContent->IsTextFrame() &&
1980 !SwObjectFormatter::FormatObjsAtFrame(
1981 *const_cast<SwContentFrame*>(pContent),
1982 *(pContent->FindPageFrame()), this ) )
1984 // restart format with first content
1985 pContent = pFly->ContainsContent();
1986 continue;
1989 if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1991 const sal_Int32 nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1992 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1993 if ( IsPaintExtraData() && IsPaint() &&
1994 nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1995 m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1998 if ( IsAgain() )
1999 return;
2001 // If there's input, we interrupt processing.
2002 if ( !pFly->IsFlyInContentFrame() )
2004 CheckIdleEnd();
2005 // consider interrupt formatting.
2006 if ( IsInterrupt() && !mbFormatContentOnInterrupt )
2007 return;
2009 pContent = pContent->GetNextContentFrame();
2011 CheckWaitCursor();
2014 bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
2016 OSL_ENSURE( pCnt->IsTextFrame(), "NoText neighbour of Text" );
2017 // robust against misuse by e.g. #i52542#
2018 if( !pCnt->IsTextFrame() )
2019 return false;
2021 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
2022 // sw_redlinehide: spell check only the nodes with visible content?
2023 SwTextNode* pTextNode = const_cast<SwTextNode*>(pTextFrame->GetTextNodeForParaProps());
2025 bool bProcess = false;
2026 for (size_t i = 0; pTextNode; )
2028 switch ( eJob )
2030 case IdleJobType::ONLINE_SPELLING:
2031 bProcess = pTextNode->IsWrongDirty(); break;
2032 case IdleJobType::AUTOCOMPLETE_WORDS:
2033 bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
2034 case IdleJobType::WORD_COUNT:
2035 bProcess = pTextNode->IsWordCountDirty(); break;
2036 case IdleJobType::SMART_TAGS:
2037 bProcess = pTextNode->IsSmartTagDirty(); break;
2039 if (bProcess)
2041 break;
2043 if (sw::MergedPara const* pMerged = pTextFrame->GetMergedPara())
2045 while (true)
2047 ++i;
2048 if (i < pMerged->extents.size())
2050 if (pMerged->extents[i].pNode != pTextNode)
2052 pTextNode = pMerged->extents[i].pNode;
2053 break;
2056 else
2058 pTextNode = nullptr;
2059 break;
2063 else
2064 pTextNode = nullptr;
2067 if( bProcess )
2069 assert(pTextNode);
2070 SwViewShell *pSh = m_pImp->GetShell();
2071 if( COMPLETE_STRING == m_nTextPos )
2073 --m_nTextPos;
2074 if( auto pCursorShell = dynamic_cast<SwCursorShell *>( pSh ) )
2075 if( !pCursorShell->IsTableMode() )
2077 SwPaM *pCursor = pCursorShell->GetCursor();
2078 if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
2080 m_pContentNode = pCursor->GetPointContentNode();
2081 m_nTextPos = pCursor->GetPoint()->GetContentIndex();
2085 sal_Int32 const nPos((m_pContentNode && pTextNode == m_pContentNode)
2086 ? m_nTextPos
2087 : COMPLETE_STRING);
2089 switch ( eJob )
2091 case IdleJobType::ONLINE_SPELLING:
2093 SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) );
2094 // PENDING should stop idle spell checking
2095 m_bPageValid = m_bPageValid && (sw::WrongState::TODO != pTextNode->GetWrongDirty());
2096 if ( aRepaint.HasArea() )
2097 m_pImp->GetShell()->InvalidateWindows( aRepaint );
2098 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
2099 return true;
2100 break;
2102 case IdleJobType::AUTOCOMPLETE_WORDS:
2103 const_cast<SwTextFrame*>(pTextFrame)->CollectAutoCmplWrds(*pTextNode, nPos);
2104 // note: bPageValid remains true here even if the cursor
2105 // position is skipped, so no PENDING state needed currently
2106 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
2107 return true;
2108 break;
2109 case IdleJobType::WORD_COUNT:
2111 const sal_Int32 nEnd = pTextNode->GetText().getLength();
2112 SwDocStat aStat;
2113 pTextNode->CountWords( aStat, 0, nEnd );
2114 if ( Application::AnyInput() )
2115 return true;
2116 break;
2118 case IdleJobType::SMART_TAGS:
2120 try {
2121 const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) );
2122 m_bPageValid = m_bPageValid && !pTextNode->IsSmartTagDirty();
2123 if ( aRepaint.HasArea() )
2124 m_pImp->GetShell()->InvalidateWindows( aRepaint );
2125 } catch( const css::uno::RuntimeException&) {
2126 // handle smarttag problems gracefully and provide diagnostics
2127 TOOLS_WARN_EXCEPTION( "sw.core", "SMART_TAGS");
2129 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
2130 return true;
2131 break;
2136 // The Flys that are anchored to the paragraph need to be considered too.
2137 if ( pCnt->GetDrawObjs() )
2139 const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
2140 for (SwAnchoredObject* pObj : rObjs)
2142 if ( auto pFly = pObj->DynCastFlyFrame() )
2144 if ( pFly->IsFlyInContentFrame() )
2146 const SwContentFrame *pC = pFly->ContainsContent();
2147 while( pC )
2149 if ( pC->IsTextFrame() )
2151 if ( DoIdleJob_( pC, eJob ) )
2152 return true;
2154 pC = pC->GetNextContentFrame();
2160 return false;
2163 bool SwLayIdle::isJobEnabled(IdleJobType eJob, const SwViewShell* pViewShell)
2165 switch (eJob)
2167 case IdleJobType::ONLINE_SPELLING:
2169 const SwViewOption* pViewOptions = pViewShell->GetViewOptions();
2170 return pViewOptions->IsOnlineSpell();
2173 case IdleJobType::AUTOCOMPLETE_WORDS:
2175 if (!SwViewOption::IsAutoCompleteWords() || SwDoc::GetAutoCompleteWords().IsLockWordLstLocked())
2176 return false;
2177 return true;
2180 case IdleJobType::WORD_COUNT:
2182 return pViewShell->getIDocumentStatistics().GetDocStat().bModified;
2185 case IdleJobType::SMART_TAGS:
2187 const SwDoc* pDoc = pViewShell->GetDoc();
2188 if (!pDoc->GetDocShell()->IsHelpDocument() || pDoc->isXForms() || !SwSmartTagMgr::Get().IsSmartTagsEnabled())
2189 return false;
2190 return true;
2194 return false;
2197 bool SwLayIdle::DoIdleJob(IdleJobType eJob, IdleJobArea eJobArea)
2199 // Spellcheck all contents of the pages. Either only the
2200 // visible ones or all of them.
2201 const SwViewShell* pViewShell = m_pImp->GetShell();
2203 // Check if job ius enabled and can run
2204 if (!isJobEnabled(eJob, pViewShell))
2205 return false;
2207 SwPageFrame *pPage;
2208 if (eJobArea == IdleJobArea::VISIBLE)
2209 pPage = m_pImp->GetFirstVisPage(pViewShell->GetOut());
2210 else
2211 pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
2213 m_pContentNode = nullptr;
2214 m_nTextPos = COMPLETE_STRING;
2216 while ( pPage )
2218 m_bPageValid = true;
2219 const SwContentFrame* pContentFrame = pPage->ContainsContent();
2220 while (pContentFrame && pPage->IsAnLower(pContentFrame))
2222 if (DoIdleJob_(pContentFrame, eJob))
2224 SAL_INFO("sw.idle", "DoIdleJob " << sal_Int32(eJob) << " interrupted on page " << pPage->GetPhyPageNum());
2225 return true;
2227 pContentFrame = pContentFrame->GetNextContentFrame();
2229 if ( pPage->GetSortedObjs() )
2231 for ( size_t i = 0; pPage->GetSortedObjs() &&
2232 i < pPage->GetSortedObjs()->size(); ++i )
2234 const SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
2235 if ( auto pFly = pObj->DynCastFlyFrame() )
2237 const SwContentFrame *pC = pFly->ContainsContent();
2238 while( pC )
2240 if ( pC->IsTextFrame() )
2242 if ( DoIdleJob_( pC, eJob ) )
2244 SAL_INFO("sw.idle", "DoIdleJob " << sal_Int32(eJob) << " interrupted on page " << pPage->GetPhyPageNum());
2245 return true;
2248 pC = pC->GetNextContentFrame();
2254 if( m_bPageValid )
2256 switch (eJob)
2258 case IdleJobType::ONLINE_SPELLING:
2259 pPage->ValidateSpelling();
2260 break;
2261 case IdleJobType::AUTOCOMPLETE_WORDS:
2262 pPage->ValidateAutoCompleteWords();
2263 break;
2264 case IdleJobType::WORD_COUNT:
2265 pPage->ValidateWordCount();
2266 break;
2267 case IdleJobType::SMART_TAGS:
2268 pPage->ValidateSmartTags();
2269 break;
2273 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
2274 if (pPage && eJobArea == IdleJobArea::VISIBLE &&
2275 !pPage->getFrameArea().Overlaps( m_pImp->GetShell()->VisArea()))
2277 break;
2280 return false;
2283 #if HAVE_FEATURE_DESKTOP && defined DBG_UTIL
2284 void SwLayIdle::ShowIdle( Color eColor )
2286 if ( m_bIndicator )
2287 return;
2289 m_bIndicator = true;
2290 vcl::Window *pWin = m_pImp->GetShell()->GetWin();
2291 if (pWin && !pWin->SupportsDoubleBuffering()) // FIXME make this work with double-buffering
2293 tools::Rectangle aRect( 0, 0, 5, 5 );
2294 aRect = pWin->PixelToLogic( aRect );
2295 // Depending on if idle layout is in progress or not, draw a "red square" or a "green square".
2296 pWin->GetOutDev()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
2297 pWin->GetOutDev()->SetFillColor( eColor );
2298 pWin->GetOutDev()->SetLineColor();
2299 pWin->GetOutDev()->DrawRect( aRect );
2300 pWin->GetOutDev()->Pop();
2303 #define SHOW_IDLE( Color ) ShowIdle( Color )
2304 #else
2305 #define SHOW_IDLE( Color )
2306 #endif // DBG_UTIL
2308 SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) :
2309 m_pRoot( pRt ),
2310 m_pImp( pI )
2311 #ifdef DBG_UTIL
2312 , m_bIndicator( false )
2313 #endif
2315 SAL_INFO("sw.idle", "SwLayIdle() entry");
2317 m_pImp->m_pIdleAct = this;
2319 SHOW_IDLE( COL_LIGHTRED );
2321 m_pImp->GetShell()->EnableSmooth( false );
2323 // First, spellcheck the visible area. Only if there's nothing
2324 // to do there, we trigger the IdleFormat.
2325 if ( !DoIdleJob(IdleJobType::SMART_TAGS, IdleJobArea::VISIBLE) &&
2326 !DoIdleJob(IdleJobType::ONLINE_SPELLING, IdleJobArea::VISIBLE) &&
2327 !DoIdleJob(IdleJobType::AUTOCOMPLETE_WORDS, IdleJobArea::VISIBLE) )
2329 // Format, then register repaint rectangles with the SwViewShell if necessary.
2330 // This requires running artificial actions, so we don't get undesired
2331 // effects when for instance the page count gets changed.
2332 // We remember the shells where the cursor is visible, so we can make
2333 // it visible again if needed after a document change.
2334 std::vector<bool> aBools;
2335 for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2337 ++rSh.mnStartAction;
2338 bool bVis = false;
2339 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
2341 bVis = pCursorShell->GetCharRect().Overlaps(rSh.VisArea());
2343 aBools.push_back( bVis );
2346 bool bInterrupt(false);
2348 SwLayAction aAction( m_pRoot, m_pImp );
2349 aAction.SetInputType( VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER) );
2350 aAction.SetIdle( true );
2351 aAction.SetWaitAllowed( false );
2352 aAction.Action(m_pImp->GetShell()->GetOut());
2353 bInterrupt = aAction.IsInterrupt();
2356 // Further start/end actions only happen if there were paints started
2357 // somewhere or if the visibility of the CharRects has changed.
2358 bool bActions = false;
2359 size_t nBoolIdx = 0;
2360 for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2362 --rSh.mnStartAction;
2364 if ( rSh.Imp()->HasPaintRegion() )
2365 bActions = true;
2366 else
2368 SwRect aTmp( rSh.VisArea() );
2369 rSh.UISizeNotify();
2371 // Are we supposed to crash if rSh isn't a cursor shell?!
2372 // bActions |= aTmp != rSh.VisArea() ||
2373 // aBools[nBoolIdx] != ((SwCursorShell*)&rSh)->GetCharRect().IsOver( rSh.VisArea() );
2375 // aBools[ i ] is true, if the i-th shell is a cursor shell (!!!)
2376 // and the cursor is visible.
2377 bActions |= aTmp != rSh.VisArea();
2378 if ( aTmp == rSh.VisArea() )
2379 if ( auto pCursorShell = dynamic_cast< SwCursorShell*>( &rSh) )
2380 bActions |= aBools[nBoolIdx] != pCursorShell->GetCharRect().Overlaps( rSh.VisArea() );
2383 ++nBoolIdx;
2386 if ( bActions )
2388 // Prepare start/end actions via CursorShell, so the cursor, selection
2389 // and VisArea can be set correctly.
2390 nBoolIdx = 0;
2391 for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2393 SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
2395 if ( pCursorShell )
2396 pCursorShell->SttCursorMove();
2398 // If there are accrued paints, it's best to simply invalidate
2399 // the whole window. Otherwise there would arise paint problems whose
2400 // solution would be disproportionally expensive.
2401 SwViewShellImp *pViewImp = rSh.Imp();
2402 bool bUnlock = false;
2403 if ( pViewImp->HasPaintRegion() )
2405 SAL_INFO("sw.idle", "Disappointing full document invalidation");
2406 pViewImp->DeletePaintRegion();
2408 // Cause a repaint with virtual device.
2409 rSh.LockPaint(LockPaintReason::SwLayIdle);
2410 bUnlock = true;
2413 if ( pCursorShell )
2414 // If the Cursor was visible, we need to make it visible again.
2415 // Otherwise, EndCursorMove with true for IdleEnd
2416 pCursorShell->EndCursorMove( !aBools[nBoolIdx] );
2417 if( bUnlock )
2419 if( pCursorShell )
2421 // UnlockPaint overwrite the selection from the
2422 // CursorShell and calls the virtual method paint
2423 // to fill the virtual device. This fill don't have
2424 // paint the selection! -> Set the focus flag at
2425 // CursorShell and it doesn't paint the selection.
2426 pCursorShell->ShellLoseFocus();
2427 pCursorShell->UnlockPaint( true );
2428 pCursorShell->ShellGetFocus();
2430 else
2431 rSh.UnlockPaint( true );
2433 ++nBoolIdx;
2438 if (!bInterrupt)
2440 if (!DoIdleJob(IdleJobType::WORD_COUNT, IdleJobArea::ALL))
2441 if (!DoIdleJob(IdleJobType::SMART_TAGS, IdleJobArea::ALL))
2442 if (!DoIdleJob(IdleJobType::ONLINE_SPELLING, IdleJobArea::ALL))
2443 DoIdleJob(IdleJobType::AUTOCOMPLETE_WORDS, IdleJobArea::ALL);
2446 bool bInValid = false;
2447 const SwViewOption& rVOpt = *m_pImp->GetShell()->GetViewOptions();
2448 const SwViewShell* pViewShell = m_pImp->GetShell();
2449 // See conditions in DoIdleJob()
2450 const bool bSpell = rVOpt.IsOnlineSpell();
2451 const bool bACmplWrd = SwViewOption::IsAutoCompleteWords();
2452 const bool bWordCount = pViewShell->getIDocumentStatistics().GetDocStat().bModified;
2453 const bool bSmartTags = !pViewShell->GetDoc()->GetDocShell()->IsHelpDocument() &&
2454 !pViewShell->GetDoc()->isXForms() &&
2455 SwSmartTagMgr::Get().IsSmartTagsEnabled();
2457 SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
2460 bInValid = pPg->IsInvalidContent() || pPg->IsInvalidLayout() ||
2461 pPg->IsInvalidFlyContent() || pPg->IsInvalidFlyLayout() ||
2462 pPg->IsInvalidFlyInCnt() ||
2463 (bSpell && pPg->IsInvalidSpelling()) ||
2464 (bACmplWrd && pPg->IsInvalidAutoCompleteWords()) ||
2465 (bWordCount && pPg->IsInvalidWordCount()) ||
2466 (bSmartTags && pPg->IsInvalidSmartTags());
2468 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
2470 } while ( pPg && !bInValid );
2472 if ( !bInValid )
2474 m_pRoot->ResetIdleFormat();
2475 SfxObjectShell* pDocShell = m_pImp->GetShell()->GetDoc()->GetDocShell();
2476 pDocShell->Broadcast( SfxEventHint( SfxEventHintId::SwEventLayoutFinished, SwDocShell::GetEventName(STR_SW_EVENT_LAYOUT_FINISHED), pDocShell ) );
2480 m_pImp->GetShell()->EnableSmooth( true );
2482 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2483 if( m_pImp->IsAccessible() )
2484 m_pImp->FireAccessibleEvents();
2485 #endif
2487 SAL_INFO("sw.idle", "SwLayIdle() return");
2489 #ifdef DBG_UTIL
2490 if ( m_bIndicator && m_pImp->GetShell()->GetWin() )
2492 // Do not invalidate indicator, this may cause an endless loop. Instead, just repaint it
2493 // This should be replaced by an overlay object in the future, anyways. Since it's only for debug
2494 // purposes, it is not urgent.
2495 m_bIndicator = false; SHOW_IDLE( COL_LIGHTGREEN );
2497 #endif
2500 SwLayIdle::~SwLayIdle()
2502 m_pImp->m_pIdleAct = nullptr;
2505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */