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 <config_feature_desktop.h>
21 
22 #include <ctime>
23 #include <rootfrm.hxx>
24 #include <pagefrm.hxx>
25 #include <viewimp.hxx>
26 #include <crsrsh.hxx>
27 #include <dflyobj.hxx>
28 #include <frmatr.hxx>
29 #include <frmtool.hxx>
30 #include <viewopt.hxx>
31 #include <dbg_lay.hxx>
32 #include <layouter.hxx>
33 #include <docstat.hxx>
34 #include <swevent.hxx>
35 #include <IDocumentDrawModelAccess.hxx>
36 #include <IDocumentStatistics.hxx>
37 #include <IDocumentLayoutAccess.hxx>
38 
39 #include <sfx2/event.hxx>
40 
41 #include <ftnidx.hxx>
42 #include <vcl/svapp.hxx>
43 #include <editeng/opaqitem.hxx>
44 #include <SwSmartTagMgr.hxx>
45 #include <sal/log.hxx>
46 
47 #include <layact.hxx>
48 #include <swwait.hxx>
49 #include <fmtsrnd.hxx>
50 #include <docsh.hxx>
51 
52 #include <anchoreddrawobject.hxx>
53 #include <ndtxt.hxx>
54 #include <tabfrm.hxx>
55 #include <ftnfrm.hxx>
56 #include <txtfrm.hxx>
57 #include <notxtfrm.hxx>
58 #include <flyfrms.hxx>
59 #include <mdiexp.hxx>
60 #include <sectfrm.hxx>
61 #include <acmplwrd.hxx>
62 #include <sortedobjs.hxx>
63 #include <objectformatter.hxx>
64 #include <fntcache.hxx>
65 #include <fmtanchr.hxx>
66 #include <comphelper/scopeguard.hxx>
67 #include <vector>
68 #include <tools/diagnose_ex.h>
69 
CheckWaitCursor()70 void SwLayAction::CheckWaitCursor()
71 {
72     if (IsReschedule())
73     {
74         ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
75     }
76     if ( !m_pWait && IsWaitAllowed() && IsPaint() &&
77          ((std::clock() - m_nStartTicks) * 1000 / CLOCKS_PER_SEC >= CLOCKS_PER_SEC/2) )
78     {
79         m_pWait.reset( new SwWait( *m_pRoot->GetFormat()->GetDoc()->GetDocShell(), true ) );
80     }
81 }
82 
83 // Time over already?
CheckIdleEnd()84 inline void SwLayAction::CheckIdleEnd()
85 {
86     if (!IsInterrupt())
87         m_bInterrupt = bool(GetInputType()) && Application::AnyInput(GetInputType());
88 }
89 
SetStatBar(bool bNew)90 void SwLayAction::SetStatBar( bool bNew )
91 {
92     if ( bNew )
93     {
94         m_nEndPage = m_pRoot->GetPageNum();
95         m_nEndPage += m_nEndPage * 10 / 100;
96     }
97     else
98         m_nEndPage = USHRT_MAX;
99 }
100 
PaintWithoutFlys(const SwRect & rRect,const SwContentFrame * pCnt,const SwPageFrame * pPage)101 bool SwLayAction::PaintWithoutFlys( const SwRect &rRect, const SwContentFrame *pCnt,
102                                     const SwPageFrame *pPage )
103 {
104     SwRegionRects aTmp( rRect );
105     const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
106     const SwFlyFrame *pSelfFly = pCnt->FindFlyFrame();
107 
108     for ( size_t i = 0; i < rObjs.size() && !aTmp.empty(); ++i )
109     {
110         SwVirtFlyDrawObj *pVirtFly = dynamic_cast<SwVirtFlyDrawObj*>(rObjs[i]->DrawObj());
111         if ( !pVirtFly )
112             continue;
113 
114         // do not consider invisible objects
115         const IDocumentDrawModelAccess& rIDDMA = pPage->GetFormat()->getIDocumentDrawModelAccess();
116         if ( !rIDDMA.IsVisibleLayerId( pVirtFly->GetLayer() ) )
117         {
118             continue;
119         }
120 
121         SwFlyFrame *pFly = pVirtFly->GetFlyFrame();
122 
123         if ( pFly == pSelfFly || !rRect.IsOver( pFly->getFrameArea() ) )
124             continue;
125 
126         if ( pSelfFly && pSelfFly->IsLowerOf( pFly ) )
127             continue;
128 
129         if ( pFly->GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() )
130             continue;
131 
132         if ( pSelfFly )
133         {
134             const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
135             if ( pVirtFly->GetLayer() == pTmp->GetLayer() )
136             {
137                 if ( pVirtFly->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
138                     // Only look at things above us, if inside the same layer
139                     continue;
140             }
141             else
142             {
143                 const bool bLowerOfSelf = pFly->IsLowerOf( pSelfFly );
144                 if ( !bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue() )
145                     // Things from other layers are only interesting to us if
146                     // they're not transparent or lie inwards
147                     continue;
148             }
149         }
150 
151         //     Fly frame without a lower have to be subtracted from paint region.
152         //     For checking, if fly frame contains transparent graphic or
153         //     has surrounded contour, assure that fly frame has a lower
154         if ( pFly->Lower() &&
155              pFly->Lower()->IsNoTextFrame() &&
156              ( static_cast<SwNoTextFrame*>(pFly->Lower())->IsTransparent() ||
157                pFly->GetFormat()->GetSurround().IsContour() )
158            )
159         {
160             continue;
161         }
162 
163         //     vcl::Region of a fly frame with transparent background or a transparent
164         //     shadow have not to be subtracted from paint region
165         if ( pFly->IsBackgroundTransparent() )
166         {
167             continue;
168         }
169 
170         aTmp -= pFly->getFrameArea();
171     }
172 
173     bool bRetPaint = false;
174     for ( const auto& rRegionRect : aTmp )
175         bRetPaint |= m_pImp->GetShell()->AddPaintRect( rRegionRect );
176     return bRetPaint;
177 }
178 
PaintContent_(const SwContentFrame * pContent,const SwPageFrame * pPage,const SwRect & rRect)179 inline bool SwLayAction::PaintContent_( const SwContentFrame *pContent,
180                                       const SwPageFrame *pPage,
181                                       const SwRect &rRect )
182 {
183     if ( rRect.HasArea() )
184     {
185         if ( pPage->GetSortedObjs() )
186             return PaintWithoutFlys( rRect, pContent, pPage );
187         else
188             return m_pImp->GetShell()->AddPaintRect( rRect );
189     }
190     return false;
191 }
192 
193 /**
194  * Depending of the type, the Content is output according to its changes, or the area
195  * to be outputted is registered with the region, respectively.
196  */
PaintContent(const SwContentFrame * pCnt,const SwPageFrame * pPage,const SwRect & rOldRect,tools::Long nOldBottom)197 void SwLayAction::PaintContent( const SwContentFrame *pCnt,
198                               const SwPageFrame *pPage,
199                               const SwRect &rOldRect,
200                               tools::Long nOldBottom )
201 {
202     SwRectFnSet aRectFnSet(pCnt);
203 
204     if ( pCnt->IsCompletePaint() || !pCnt->IsTextFrame() )
205     {
206         SwRect aPaint( pCnt->GetPaintArea() );
207         if ( !PaintContent_( pCnt, pPage, aPaint ) )
208             pCnt->ResetCompletePaint();
209     }
210     else
211     {
212         // paint the area between printing bottom and frame bottom and
213         // the area left and right beside the frame, if its height changed.
214         tools::Long nOldHeight = aRectFnSet.GetHeight(rOldRect);
215         tools::Long nNewHeight = aRectFnSet.GetHeight(pCnt->getFrameArea());
216         const bool bHeightDiff = nOldHeight != nNewHeight;
217         if( bHeightDiff )
218         {
219             // consider whole potential paint area.
220             SwRect aDrawRect( pCnt->GetPaintArea() );
221             if( nOldHeight > nNewHeight )
222                 nOldBottom = aRectFnSet.GetPrtBottom(*pCnt);
223             aRectFnSet.SetTop( aDrawRect, nOldBottom );
224             PaintContent_( pCnt, pPage, aDrawRect );
225         }
226         // paint content area
227         SwRect aPaintRect = static_cast<SwTextFrame*>(const_cast<SwContentFrame*>(pCnt))->GetPaintSwRect();
228         PaintContent_( pCnt, pPage, aPaintRect );
229     }
230 
231     if ( !pCnt->IsRetouche() || pCnt->GetNext() )
232         return;
233 
234     const SwFrame *pTmp = pCnt;
235     if( pCnt->IsInSct() )
236     {
237         const SwSectionFrame* pSct = pCnt->FindSctFrame();
238         if( pSct->IsRetouche() && !pSct->GetNext() )
239             pTmp = pSct;
240     }
241     SwRect aRect( pTmp->GetUpper()->GetPaintArea() );
242     aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTmp) );
243     if ( !PaintContent_( pCnt, pPage, aRect ) )
244         pCnt->ResetRetouche();
245 }
246 
SwLayAction(SwRootFrame * pRt,SwViewShellImp * pI)247 SwLayAction::SwLayAction( SwRootFrame *pRt, SwViewShellImp *pI ) :
248     m_pRoot( pRt ),
249     m_pImp( pI ),
250     m_pOptTab( nullptr ),
251     m_nPreInvaPage( USHRT_MAX ),
252     m_nStartTicks( std::clock() ),
253     m_nInputType( VclInputFlags::NONE ),
254     m_nEndPage( USHRT_MAX ),
255     m_nCheckPageNum( USHRT_MAX )
256 {
257     m_bPaintExtraData = ::IsExtraData( m_pImp->GetShell()->GetDoc() );
258     m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
259     m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
260     m_bUpdateExpFields = m_bBrowseActionStop = m_bActionInProgress = false;
261     // init new flag <mbFormatContentOnInterrupt>.
262     mbFormatContentOnInterrupt = false;
263 
264     assert(!m_pImp->m_pLayAction); // there can be only one SwLayAction
265     m_pImp->m_pLayAction = this;   // register there
266 }
267 
~SwLayAction()268 SwLayAction::~SwLayAction()
269 {
270     OSL_ENSURE( !m_pWait, "Wait object not destroyed" );
271     m_pImp->m_pLayAction = nullptr;      // unregister
272 }
273 
Reset()274 void SwLayAction::Reset()
275 {
276     m_pOptTab = nullptr;
277     m_nStartTicks = std::clock();
278     m_nInputType = VclInputFlags::NONE;
279     m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX;
280     m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
281     m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
282     m_bUpdateExpFields = m_bBrowseActionStop = false;
283 }
284 
RemoveEmptyBrowserPages()285 bool SwLayAction::RemoveEmptyBrowserPages()
286 {
287     // switching from the normal to the browser mode, empty pages may be
288     // retained for an annoyingly long time, so delete them here
289     bool bRet = false;
290     const SwViewShell *pSh = m_pRoot->GetCurrShell();
291     if( pSh && pSh->GetViewOptions()->getBrowseMode() )
292     {
293         SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
294         do
295         {
296             if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) ||
297                  pPage->ContainsContent() )
298                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
299             else
300             {
301                 bRet = true;
302                 SwPageFrame *pDel = pPage;
303                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
304                 pDel->Cut();
305                 SwFrame::DestroyFrame(pDel);
306             }
307         } while ( pPage );
308     }
309     return bRet;
310 }
311 
Action(OutputDevice * pRenderContext)312 void SwLayAction::Action(OutputDevice* pRenderContext)
313 {
314     m_bActionInProgress = true;
315 
316     //TurboMode? Hands-off during idle-format
317     if ( IsPaint() && !IsIdle() && TurboAction() )
318     {
319         m_pWait.reset();
320         m_pRoot->ResetTurboFlag();
321         m_bActionInProgress = false;
322         m_pRoot->DeleteEmptySct();
323         return;
324     }
325     else if ( m_pRoot->GetTurbo() )
326     {
327         m_pRoot->DisallowTurbo();
328         const SwFrame *pFrame = m_pRoot->GetTurbo();
329         m_pRoot->ResetTurbo();
330         pFrame->InvalidatePage();
331     }
332     m_pRoot->DisallowTurbo();
333 
334     if ( IsCalcLayout() )
335         SetCheckPages( false );
336 
337     InternalAction(pRenderContext);
338     m_bAgain |= RemoveEmptyBrowserPages();
339     while ( IsAgain() )
340     {
341         m_bAgain = m_bNextCycle = false;
342         InternalAction(pRenderContext);
343         m_bAgain |= RemoveEmptyBrowserPages();
344     }
345     m_pRoot->DeleteEmptySct();
346 
347     m_pWait.reset();
348 
349     //Turbo-Action permitted again for all cases.
350     m_pRoot->ResetTurboFlag();
351     m_pRoot->ResetTurbo();
352 
353     SetCheckPages( true );
354 
355     m_bActionInProgress = false;
356 }
357 
CheckFirstVisPage(SwPageFrame * pPage)358 SwPageFrame* SwLayAction::CheckFirstVisPage( SwPageFrame *pPage )
359 {
360     SwContentFrame *pCnt = pPage->FindFirstBodyContent();
361     SwContentFrame *pChk = pCnt;
362     bool bPageChgd = false;
363     while ( pCnt && pCnt->IsFollow() )
364         pCnt = pCnt->FindMaster();
365     if ( pCnt && pChk != pCnt )
366     {   bPageChgd = true;
367         pPage = pCnt->FindPageFrame();
368     }
369 
370     if ( !pPage->GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
371     {
372         SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
373         if ( pCont )
374         {
375             pCnt = pCont->ContainsContent();
376             pChk = pCnt;
377             while ( pCnt && pCnt->IsFollow() )
378                 pCnt = static_cast<SwContentFrame*>(pCnt->FindPrev());
379             if ( pCnt && pCnt != pChk )
380             {
381                 if ( bPageChgd )
382                 {
383                     // Use the 'topmost' page
384                     SwPageFrame *pTmp = pCnt->FindPageFrame();
385                     if ( pPage->GetPhyPageNum() > pTmp->GetPhyPageNum() )
386                         pPage = pTmp;
387                 }
388                 else
389                     pPage = pCnt->FindPageFrame();
390             }
391         }
392     }
393     return pPage;
394 }
395 
396 // unlock position on start and end of page
397 // layout process.
unlockPositionOfObjects(SwPageFrame * pPageFrame)398 static void unlockPositionOfObjects( SwPageFrame *pPageFrame )
399 {
400     assert( pPageFrame );
401 
402     SwSortedObjs* pObjs = pPageFrame->GetSortedObjs();
403     if ( pObjs )
404     {
405         for (SwAnchoredObject* pObj : *pObjs)
406         {
407             pObj->UnlockPosition();
408         }
409     }
410 }
411 
InternalAction(OutputDevice * pRenderContext)412 void SwLayAction::InternalAction(OutputDevice* pRenderContext)
413 {
414     OSL_ENSURE( m_pRoot->Lower()->IsPageFrame(), ":-( No page below the root.");
415 
416     m_pRoot->Calc(pRenderContext);
417 
418     // Figure out the first invalid page or the first one to be formatted,
419     // respectively. A complete-action means the first invalid page.
420     // However, the first page to be formatted might be the one having the
421     // number 1.  If we're doing a fake formatting, the number of the first
422     // page is the number of the first visible page.
423     SwPageFrame *pPage = IsComplete() ? static_cast<SwPageFrame*>(m_pRoot->Lower()) :
424                 m_pImp->GetFirstVisPage(pRenderContext);
425     if ( !pPage )
426         pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
427 
428     // If there's a first-flow-Content in the first visible page that's also a Follow,
429     // we switch the page back to the original master of that Content.
430     if ( !IsComplete() )
431         pPage = CheckFirstVisPage( pPage );
432     sal_uInt16 nFirstPageNum = pPage->GetPhyPageNum();
433 
434     while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
435         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
436 
437     IDocumentLayoutAccess& rLayoutAccess = m_pRoot->GetFormat()->getIDocumentLayoutAccess();
438     const bool bNoLoop = pPage && SwLayouter::StartLoopControl(m_pRoot->GetFormat()->GetDoc(), pPage);
439     sal_uInt16 nPercentPageNum = 0;
440 
441     auto lcl_isLayoutLooping = [&]()
442     {
443         const bool bAgain = this->IsAgain();
444         if (bAgain && bNoLoop)
445             rLayoutAccess.GetLayouter()->EndLoopControl();
446         return bAgain;
447     };
448 
449     while ( (pPage && !IsInterrupt()) || m_nCheckPageNum != USHRT_MAX )
450     {
451         // note: this is the only place that consumes and resets m_nCheckPageNum
452         if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
453         {
454             if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
455             {
456                 SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
457                 while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
458                     pPg = static_cast<SwPageFrame*>(pPg->GetNext());
459                 if (pPg)
460                     pPage = pPg;
461                 if (!pPage)
462                     break;
463             }
464 
465             SwPageFrame *pTmp = pPage->GetPrev() ?
466                                         static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
467             SetCheckPages( true );
468             SwFrame::CheckPageDescs( pPage, true, &pTmp );
469             SetCheckPages( false );
470             m_nCheckPageNum = USHRT_MAX;
471             pPage = pTmp;
472             continue;
473         }
474 
475         if ( m_nEndPage != USHRT_MAX && pPage->GetPhyPageNum() > nPercentPageNum )
476         {
477             nPercentPageNum = pPage->GetPhyPageNum();
478             ::SetProgressState( nPercentPageNum, m_pImp->GetShell()->GetDoc()->GetDocShell());
479         }
480         m_pOptTab = nullptr;
481 
482         // No Shortcut for Idle or CalcLayout
483         const bool bTakeShortcut = !IsIdle() && !IsComplete() && IsShortCut(pPage);
484 
485         m_pRoot->DeleteEmptySct();
486         if (lcl_isLayoutLooping()) return;
487 
488         if (!bTakeShortcut)
489         {
490             while ( !IsInterrupt() && !IsNextCycle() &&
491                     ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid()) )
492             {
493                 unlockPositionOfObjects( pPage );
494 
495                 SwObjectFormatter::FormatObjsAtFrame( *pPage, *pPage, this );
496                 if ( !pPage->GetSortedObjs() )
497                 {
498                     // If there are no (more) Flys, the flags are superfluous.
499                     pPage->ValidateFlyLayout();
500                     pPage->ValidateFlyContent();
501                 }
502                 // change condition
503                 while ( !IsInterrupt() && !IsNextCycle() &&
504                         ( pPage->IsInvalid() ||
505                           (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
506                 {
507                     PROTOCOL( pPage, PROT::FileInit, DbgAction::NONE, nullptr)
508                     if (lcl_isLayoutLooping()) return;
509 
510                     // new loop control
511                     int nLoopControlRuns_1 = 0;
512                     const int nLoopControlMax = 20;
513 
514                     while ( !IsNextCycle() && pPage->IsInvalidLayout() )
515                     {
516                         pPage->ValidateLayout();
517 
518                         if ( ++nLoopControlRuns_1 > nLoopControlMax )
519                         {
520                             OSL_FAIL( "LoopControl_1 in SwLayAction::InternalAction" );
521                             break;
522                         }
523 
524                         FormatLayout( pRenderContext, pPage );
525                         if (lcl_isLayoutLooping()) return;
526                     }
527                     // change condition
528                     if ( !IsNextCycle() &&
529                          ( pPage->IsInvalidContent() ||
530                            (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
531                     {
532                         pPage->ValidateFlyInCnt();
533                         pPage->ValidateContent();
534                         pPage->ValidateFlyLayout();
535                         pPage->ValidateFlyContent();
536                         if ( !FormatContent( pPage ) )
537                         {
538                             if (lcl_isLayoutLooping()) return;
539                             pPage->InvalidateContent();
540                             pPage->InvalidateFlyInCnt();
541                             pPage->InvalidateFlyLayout();
542                             pPage->InvalidateFlyContent();
543                             if ( IsBrowseActionStop() )
544                                 m_bInterrupt = true;
545                         }
546                     }
547                     if( bNoLoop )
548                         rLayoutAccess.GetLayouter()->LoopControl( pPage );
549                 }
550 
551                 unlockPositionOfObjects( pPage );
552             }
553 
554             // A previous page may be invalid again.
555             if (lcl_isLayoutLooping()) return;
556             if ( !pPage->GetSortedObjs() )
557             {
558                 // If there are no (more) Flys, the flags are superfluous.
559                 pPage->ValidateFlyLayout();
560                 pPage->ValidateFlyContent();
561             }
562             if ( !IsInterrupt() )
563             {
564                 SetNextCycle( false );
565 
566                 if ( m_nPreInvaPage != USHRT_MAX )
567                 {
568                     if( !IsComplete() && m_nPreInvaPage + 2 < nFirstPageNum )
569                     {
570                         m_pImp->SetFirstVisPageInvalid();
571                         SwPageFrame *pTmpPage = m_pImp->GetFirstVisPage(pRenderContext);
572                         nFirstPageNum = pTmpPage->GetPhyPageNum();
573                         if( m_nPreInvaPage < nFirstPageNum )
574                         {
575                             m_nPreInvaPage = nFirstPageNum;
576                             pPage = pTmpPage;
577                         }
578                     }
579                     while ( pPage->GetPrev() && pPage->GetPhyPageNum() > m_nPreInvaPage )
580                         pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
581                     m_nPreInvaPage = USHRT_MAX;
582                 }
583 
584                 while ( pPage->GetPrev() &&
585                         ( static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalid() ||
586                           ( static_cast<SwPageFrame*>(pPage->GetPrev())->GetSortedObjs() &&
587                             static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalidFly())) &&
588                         (static_cast<SwPageFrame*>(pPage->GetPrev())->GetPhyPageNum() >=
589                             nFirstPageNum) )
590                 {
591                     pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
592                 }
593 
594                 // Continue to the next invalid page
595                 while ( pPage && !pPage->IsInvalid() &&
596                         (!pPage->GetSortedObjs() || !pPage->IsInvalidFly()) )
597                 {
598                     pPage = static_cast<SwPageFrame*>(pPage->GetNext());
599                 }
600                 if( bNoLoop )
601                     rLayoutAccess.GetLayouter()->LoopControl( pPage );
602             }
603             CheckIdleEnd();
604         }
605 
606         if ((bTakeShortcut || !pPage) && !IsInterrupt() &&
607              (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) )
608         {
609             // tdf#139426 allow supression of AssertFlyPages
610             if ( m_pRoot->IsAssertFlyPages() && !m_pRoot->IsTableUpdateInProgress())
611             {
612                 m_pRoot->AssertFlyPages();
613             }
614             if ( m_pRoot->IsSuperfluous() )
615             {
616                 bool bOld = IsAgain();
617                 m_pRoot->RemoveSuperfluous();
618                 m_bAgain = bOld;
619             }
620             if (lcl_isLayoutLooping()) return;
621             pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
622             while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
623                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
624             while ( pPage && pPage->GetNext() &&
625                     pPage->GetPhyPageNum() < nFirstPageNum )
626                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
627         }
628         else if (bTakeShortcut)
629             break;
630     }
631     if ( IsInterrupt() && pPage )
632     {
633         // If we have input, we don't want to format content anymore, but
634         // we still should clean the layout.
635         // Otherwise, the following situation might arise:
636         // The user enters some text at the end of the paragraph of the last
637         // page, causing the paragraph to create a Follow for the next page.
638         // Meanwhile the user continues typing, so we have input while
639         // still formatting.
640         // The paragraph on the new page has already been partially formatted,
641         // and the new page has been fully formatted and is set to CompletePaint,
642         // but hasn't added itself to the area to be output. Then we paint,
643         // the CompletePaint of the page is reset because the new paragraph
644         // already added itself, but the borders of the page haven't been painted
645         // yet.
646         // Oh well, with the inevitable following LayAction, the page doesn't
647         // register itself, because it's (LayoutFrame) flags have been reset
648         // already - the border of the page will never be painted.
649         SwPageFrame *pPg = pPage;
650         if (lcl_isLayoutLooping()) return;
651         const SwRect &rVis = m_pImp->GetShell()->VisArea();
652 
653         while( pPg && pPg->getFrameArea().Bottom() < rVis.Top() )
654             pPg = static_cast<SwPageFrame*>(pPg->GetNext());
655         if( pPg != pPage )
656             pPg = pPg ? static_cast<SwPageFrame*>(pPg->GetPrev()) : pPage;
657 
658         // set flag for interrupt content formatting
659         mbFormatContentOnInterrupt = true;
660         tools::Long nBottom = rVis.Bottom();
661         // #i42586# - format current page, if idle action is active
662         // This is an optimization for the case that the interrupt is created by
663         // the move of a form control object, which is represented by a window.
664         while ( pPg && ( pPg->getFrameArea().Top() < nBottom ||
665                          ( IsIdle() && pPg == pPage ) ) )
666         {
667             unlockPositionOfObjects( pPg );
668 
669             if (lcl_isLayoutLooping()) return;
670 
671             // new loop control
672             int nLoopControlRuns_2 = 0;
673             const int nLoopControlMax = 20;
674 
675             // special case: interrupt content formatting
676             // conditions are incorrect and are too strict.
677             // adjust interrupt formatting to normal page formatting - see above.
678             while ( ( mbFormatContentOnInterrupt &&
679                       ( pPg->IsInvalid() ||
680                         ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) ) ||
681                     ( !mbFormatContentOnInterrupt && pPg->IsInvalidLayout() ) )
682             {
683                 if (lcl_isLayoutLooping()) return;
684                 // format also at-page anchored objects
685                 SwObjectFormatter::FormatObjsAtFrame( *pPg, *pPg, this );
686                 if ( !pPg->GetSortedObjs() )
687                 {
688                     pPg->ValidateFlyLayout();
689                     pPg->ValidateFlyContent();
690                 }
691 
692                 // new loop control
693                 int nLoopControlRuns_3 = 0;
694 
695                 while ( pPg->IsInvalidLayout() )
696                 {
697                     pPg->ValidateLayout();
698 
699                     if ( ++nLoopControlRuns_3 > nLoopControlMax )
700                     {
701                         OSL_FAIL( "LoopControl_3 in Interrupt formatting in SwLayAction::InternalAction" );
702                         break;
703                     }
704 
705                     FormatLayout( pRenderContext, pPg );
706                     if (lcl_isLayoutLooping()) return;
707                 }
708 
709                 if ( mbFormatContentOnInterrupt &&
710                      ( pPg->IsInvalidContent() ||
711                        ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) )
712                 {
713                     pPg->ValidateFlyInCnt();
714                     pPg->ValidateContent();
715                     pPg->ValidateFlyLayout();
716                     pPg->ValidateFlyContent();
717 
718                     if ( ++nLoopControlRuns_2 > nLoopControlMax )
719                     {
720                         OSL_FAIL( "LoopControl_2 in Interrupt formatting in SwLayAction::InternalAction" );
721                         break;
722                     }
723 
724                     if ( !FormatContent( pPg ) )
725                     {
726                         if (lcl_isLayoutLooping()) return;
727                         pPg->InvalidateContent();
728                         pPg->InvalidateFlyInCnt();
729                         pPg->InvalidateFlyLayout();
730                         pPg->InvalidateFlyContent();
731                     }
732                     // we are satisfied if the content is formatted once complete.
733                     else
734                     {
735                         break;
736                     }
737                 }
738             }
739 
740             unlockPositionOfObjects( pPg );
741             pPg = static_cast<SwPageFrame*>(pPg->GetNext());
742         }
743         // reset flag for special interrupt content formatting.
744         mbFormatContentOnInterrupt = false;
745     }
746     m_pOptTab = nullptr;
747     if( bNoLoop )
748         rLayoutAccess.GetLayouter()->EndLoopControl();
749 }
750 
TurboAction_(const SwContentFrame * pCnt)751 bool SwLayAction::TurboAction_( const SwContentFrame *pCnt )
752 {
753 
754     const SwPageFrame *pPage = nullptr;
755     if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() || pCnt->IsRetouche() )
756     {
757         const SwRect aOldRect( pCnt->UnionFrame( true ) );
758         const tools::Long   nOldBottom = pCnt->getFrameArea().Top() + pCnt->getFramePrintArea().Bottom();
759         pCnt->Calc(m_pImp->GetShell()->GetOut());
760         if ( pCnt->getFrameArea().Bottom() < aOldRect.Bottom() )
761             pCnt->SetRetouche();
762 
763         pPage = pCnt->FindPageFrame();
764         PaintContent( pCnt, pPage, aOldRect, nOldBottom );
765 
766         if ( !pCnt->GetValidLineNumFlag() && pCnt->IsTextFrame() )
767         {
768             const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pCnt)->GetAllLines();
769             const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->RecalcAllLines();
770             if ( nAllLines != static_cast<const SwTextFrame*>(pCnt)->GetAllLines() )
771             {
772                 if ( IsPaintExtraData() )
773                     m_pImp->GetShell()->AddPaintRect( pCnt->getFrameArea() );
774                 // This is to calculate the remaining LineNums on the page,
775                 // and we don't stop processing here. To perform this inside RecalcAllLines
776                 // would be expensive, because we would have to notify the page even
777                 // in unnecessary cases (normal actions).
778                 const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
779                 while ( pNxt &&
780                         (pNxt->IsInTab() || pNxt->IsInDocBody() != pCnt->IsInDocBody()) )
781                     pNxt = pNxt->GetNextContentFrame();
782                 if ( pNxt )
783                     pNxt->InvalidatePage();
784             }
785             return false;
786         }
787 
788         if ( pPage->IsInvalidLayout() || (pPage->GetSortedObjs() && pPage->IsInvalidFly()) )
789             return false;
790     }
791     if ( !pPage )
792         pPage = pCnt->FindPageFrame();
793 
794     // format floating screen objects at content frame.
795     if ( pCnt->IsTextFrame() &&
796          !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pCnt),
797                                               *pPage, this ) )
798     {
799         return false;
800     }
801 
802     if ( pPage->IsInvalidContent() )
803         return false;
804     return true;
805 }
806 
TurboAction()807 bool SwLayAction::TurboAction()
808 {
809     bool bRet = true;
810 
811     if ( m_pRoot->GetTurbo() )
812     {
813         if ( !TurboAction_( m_pRoot->GetTurbo() ) )
814         {
815             CheckIdleEnd();
816             bRet = false;
817         }
818         m_pRoot->ResetTurbo();
819     }
820     else
821         bRet = false;
822     return bRet;
823 }
824 
lcl_IsInvaLay(const SwFrame * pFrame,tools::Long nBottom)825 static bool lcl_IsInvaLay( const SwFrame *pFrame, tools::Long nBottom )
826 {
827     return !pFrame->isFrameAreaDefinitionValid() ||
828          (pFrame->IsCompletePaint() && ( pFrame->getFrameArea().Top() < nBottom ) );
829 }
830 
lcl_FindFirstInvaLay(const SwFrame * pFrame,tools::Long nBottom)831 static const SwFrame *lcl_FindFirstInvaLay( const SwFrame *pFrame, tools::Long nBottom )
832 {
833     OSL_ENSURE( pFrame->IsLayoutFrame(), "FindFirstInvaLay, no LayFrame" );
834 
835     if (lcl_IsInvaLay(pFrame, nBottom))
836         return pFrame;
837     pFrame = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
838     while ( pFrame )
839     {
840         if ( pFrame->IsLayoutFrame() )
841         {
842             if (lcl_IsInvaLay(pFrame, nBottom))
843                 return pFrame;
844             const SwFrame *pTmp = lcl_FindFirstInvaLay( pFrame, nBottom );
845             if ( nullptr != pTmp )
846                 return pTmp;
847         }
848         pFrame = pFrame->GetNext();
849     }
850     return nullptr;
851 }
852 
lcl_FindFirstInvaContent(const SwLayoutFrame * pLay,tools::Long nBottom,const SwContentFrame * pFirst)853 static const SwFrame *lcl_FindFirstInvaContent( const SwLayoutFrame *pLay, tools::Long nBottom,
854                                      const SwContentFrame *pFirst )
855 {
856     const SwContentFrame *pCnt = pFirst ? pFirst->GetNextContentFrame() :
857                                       pLay->ContainsContent();
858     while ( pCnt )
859     {
860         if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() )
861         {
862             if ( pCnt->getFrameArea().Top() <= nBottom )
863                 return pCnt;
864         }
865 
866         if ( pCnt->GetDrawObjs() )
867         {
868             const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
869             for (SwAnchoredObject* pObj : rObjs)
870             {
871                 if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pObj ) )
872                 {
873                     if ( pFly->IsFlyInContentFrame() )
874                     {
875                         if ( static_cast<const SwFlyInContentFrame*>(pFly)->IsInvalid() ||
876                              pFly->IsCompletePaint() )
877                         {
878                             if ( pFly->getFrameArea().Top() <= nBottom )
879                                 return pFly;
880                         }
881                         const SwFrame *pFrame = lcl_FindFirstInvaContent( pFly, nBottom, nullptr );
882                         if ( pFrame && pFrame->getFrameArea().Bottom() <= nBottom )
883                             return pFrame;
884                     }
885                 }
886             }
887         }
888         if ( pCnt->getFrameArea().Top() > nBottom && !pCnt->IsInTab() )
889             return nullptr;
890         pCnt = pCnt->GetNextContentFrame();
891         if ( !pLay->IsAnLower( pCnt ) )
892             break;
893     }
894     return nullptr;
895 }
896 
897 // consider drawing objects
lcl_FindFirstInvaObj(const SwPageFrame * _pPage,tools::Long _nBottom)898 static const SwAnchoredObject* lcl_FindFirstInvaObj( const SwPageFrame* _pPage,
899                                               tools::Long _nBottom )
900 {
901     OSL_ENSURE( _pPage->GetSortedObjs(), "FindFirstInvaObj, no Objs" );
902 
903     for (SwAnchoredObject* pObj : *_pPage->GetSortedObjs())
904     {
905         if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pObj )  )
906         {
907             if ( pFly->getFrameArea().Top() <= _nBottom )
908             {
909                 if ( pFly->IsInvalid() || pFly->IsCompletePaint() )
910                     return pFly;
911 
912                 const SwFrame* pTmp;
913                 if ( nullptr != (pTmp = lcl_FindFirstInvaContent( pFly, _nBottom, nullptr )) &&
914                      pTmp->getFrameArea().Top() <= _nBottom )
915                     return pFly;
916             }
917         }
918         else if ( auto pDrawObject = dynamic_cast< const SwAnchoredDrawObject *>( pObj ) )
919         {
920             if ( !pDrawObject->IsValidPos() )
921             {
922                 return pObj;
923             }
924         }
925     }
926     return nullptr;
927 }
928 
929 /* Returns True if the page lies directly below or right of the visible area.
930  *
931  * It's possible for things to change in such a way that the processing
932  * (of the caller!) has to continue with the predecessor of the passed page.
933  * The parameter might therefore get modified!
934  * For BrowseMode, you may even activate the ShortCut if the invalid content
935  * of the page lies below the visible area.
936  */
IsShortCut(SwPageFrame * & prPage)937 bool SwLayAction::IsShortCut( SwPageFrame *&prPage )
938 {
939     vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
940     bool bRet = false;
941     const SwViewShell *pSh = m_pRoot->GetCurrShell();
942     const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
943 
944     // If the page is not valid, we quickly format it, otherwise
945     // there's gonna be no end of trouble
946     if ( !prPage->isFrameAreaDefinitionValid() )
947     {
948         if ( bBrowse )
949         {
950             // format complete page
951             // Thus, loop on all lowers of the page <prPage>, instead of only
952             // format its first lower.
953             // NOTE: In online layout (bBrowse == true) a page can contain
954             //     a header frame and/or a footer frame beside the body frame.
955             prPage->Calc(pRenderContext);
956             SwFrame* pPageLowerFrame = prPage->Lower();
957             while ( pPageLowerFrame )
958             {
959                 pPageLowerFrame->Calc(pRenderContext);
960                 pPageLowerFrame = pPageLowerFrame->GetNext();
961             }
962         }
963         else
964             FormatLayout( pSh ? pSh->GetOut() : nullptr, prPage );
965         if ( IsAgain() )
966             return false;
967     }
968 
969     const SwRect &rVis = m_pImp->GetShell()->VisArea();
970     if ( (prPage->getFrameArea().Top() >= rVis.Bottom()) ||
971          (prPage->getFrameArea().Left()>= rVis.Right()) )
972     {
973         bRet = true;
974 
975         // This is going to be a bit nasty: The first ContentFrame of this
976         // page in the Body text needs formatting; if it changes the page during
977         // that process, I need to start over a page further back, because we
978         // have been processing a PageBreak.
979         // Even more uncomfortable: The next ContentFrame must be formatted,
980         // because it's possible for empty pages to exist temporarily (for example
981         // a paragraph across multiple pages gets deleted or reduced in size).
982 
983         // This is irrelevant for the browser, if the last Cnt above it
984         // isn't visible anymore.
985 
986         const SwPageFrame *p2ndPage = prPage;
987         const SwContentFrame *pContent;
988         const SwLayoutFrame* pBody = p2ndPage->FindBodyCont();
989         if( p2ndPage->IsFootnotePage() && pBody )
990             pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
991         pContent = pBody ? pBody->ContainsContent() : nullptr;
992         while ( p2ndPage && !pContent )
993         {
994             p2ndPage = static_cast<const SwPageFrame*>(p2ndPage->GetNext());
995             if( p2ndPage )
996             {
997                 pBody = p2ndPage->FindBodyCont();
998                 if( p2ndPage->IsFootnotePage() && pBody )
999                     pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
1000                 pContent = pBody ? pBody->ContainsContent() : nullptr;
1001             }
1002         }
1003         if ( pContent )
1004         {
1005             bool bTstCnt = true;
1006             if ( bBrowse )
1007             {
1008                 // Is the Cnt before already invisible?
1009                 const SwFrame *pLst = pContent;
1010                 if ( pLst->IsInTab() )
1011                     pLst = pContent->FindTabFrame();
1012                 if ( pLst->IsInSct() )
1013                     pLst = pContent->FindSctFrame();
1014                 pLst = pLst->FindPrev();
1015                 if ( pLst &&
1016                      (pLst->getFrameArea().Top() >= rVis.Bottom() ||
1017                       pLst->getFrameArea().Left()>= rVis.Right()) )
1018                 {
1019                     bTstCnt = false;
1020                 }
1021             }
1022 
1023             if ( bTstCnt )
1024             {
1025                 // check after each frame calculation,
1026                 // if the content frame has changed the page. If yes, no other
1027                 // frame calculation is performed
1028                 bool bPageChg = false;
1029 
1030                 if ( pContent->IsInSct() )
1031                 {
1032                     const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
1033                     if ( !pSct->isFrameAreaDefinitionValid() )
1034                     {
1035                         pSct->Calc(pRenderContext);
1036                         pSct->SetCompletePaint();
1037                         if ( IsAgain() )
1038                             return false;
1039 
1040                         bPageChg = pContent->FindPageFrame() != p2ndPage &&
1041                                    prPage->GetPrev();
1042                     }
1043                 }
1044 
1045                 if ( !bPageChg && !pContent->isFrameAreaDefinitionValid() )
1046                 {
1047                     pContent->Calc(pRenderContext);
1048                     pContent->SetCompletePaint();
1049                     if ( IsAgain() )
1050                         return false;
1051 
1052                     bPageChg = pContent->FindPageFrame() != p2ndPage &&
1053                                prPage->GetPrev();
1054                 }
1055 
1056                 if ( !bPageChg && pContent->IsInTab() )
1057                 {
1058                     const SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindTabFrame();
1059                     if ( !pTab->isFrameAreaDefinitionValid() )
1060                     {
1061                         pTab->Calc(pRenderContext);
1062                         pTab->SetCompletePaint();
1063                         if ( IsAgain() )
1064                             return false;
1065 
1066                         bPageChg = pContent->FindPageFrame() != p2ndPage &&
1067                                    prPage->GetPrev();
1068                     }
1069                 }
1070 
1071                 if ( !bPageChg && pContent->IsInSct() )
1072                 {
1073                     const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
1074                     if ( !pSct->isFrameAreaDefinitionValid() )
1075                     {
1076                         pSct->Calc(pRenderContext);
1077                         pSct->SetCompletePaint();
1078                         if ( IsAgain() )
1079                             return false;
1080 
1081                         bPageChg = pContent->FindPageFrame() != p2ndPage &&
1082                                    prPage->GetPrev();
1083                     }
1084                 }
1085 
1086                 if ( bPageChg )
1087                 {
1088                     bRet = false;
1089                     const SwPageFrame* pTmp = pContent->FindPageFrame();
1090                     if ( pTmp->GetPhyPageNum() < prPage->GetPhyPageNum() &&
1091                          pTmp->IsInvalid() )
1092                     {
1093                         prPage = const_cast<SwPageFrame*>(pTmp);
1094                     }
1095                     else
1096                     {
1097                         prPage = static_cast<SwPageFrame*>(prPage->GetPrev());
1098                     }
1099                 }
1100                 // no shortcut, if at previous page
1101                 // an anchored object is registered, whose anchor is <pContent>.
1102                 else if ( prPage->GetPrev() )
1103                 {
1104                     SwSortedObjs* pObjs =
1105                         static_cast<SwPageFrame*>(prPage->GetPrev())->GetSortedObjs();
1106                     if ( pObjs )
1107                     {
1108                         for (SwAnchoredObject* pObj : *pObjs)
1109                         {
1110                             if ( pObj->GetAnchorFrameContainingAnchPos() == pContent )
1111                             {
1112                                 bRet = false;
1113                                 break;
1114                             }
1115                         }
1116                     }
1117                 }
1118             }
1119         }
1120     }
1121 
1122     if ( !bRet && bBrowse )
1123     {
1124         const tools::Long nBottom = rVis.Bottom();
1125         const SwAnchoredObject* pObj( nullptr );
1126         if ( prPage->GetSortedObjs() &&
1127              (prPage->IsInvalidFlyLayout() || prPage->IsInvalidFlyContent()) &&
1128              nullptr != (pObj = lcl_FindFirstInvaObj( prPage, nBottom )) &&
1129              pObj->GetObjRect().Top() <= nBottom )
1130         {
1131             return false;
1132         }
1133         const SwFrame* pFrame( nullptr );
1134         if ( prPage->IsInvalidLayout() &&
1135              nullptr != (pFrame = lcl_FindFirstInvaLay( prPage, nBottom )) &&
1136              pFrame->getFrameArea().Top() <= nBottom )
1137         {
1138             return false;
1139         }
1140         if ( (prPage->IsInvalidContent() || prPage->IsInvalidFlyInCnt()) &&
1141              nullptr != (pFrame = lcl_FindFirstInvaContent( prPage, nBottom, nullptr )) &&
1142              pFrame->getFrameArea().Top() <= nBottom )
1143         {
1144             return false;
1145         }
1146         bRet = true;
1147     }
1148     return bRet;
1149 }
1150 
1151 // introduce support for vertical layout
FormatLayout(OutputDevice * pRenderContext,SwLayoutFrame * pLay,bool bAddRect)1152 bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLay, bool bAddRect )
1153 {
1154     OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
1155     if ( IsAgain() )
1156         return false;
1157 
1158     bool bChanged = false;
1159     bool bAlreadyPainted = false;
1160     // remember frame at complete paint
1161     SwRect aFrameAtCompletePaint;
1162 
1163     if ( !pLay->isFrameAreaDefinitionValid() || pLay->IsCompletePaint() )
1164     {
1165         if ( pLay->GetPrev() && !pLay->GetPrev()->isFrameAreaDefinitionValid() )
1166             pLay->GetPrev()->SetCompletePaint();
1167 
1168         SwRect aOldFrame( pLay->getFrameArea() );
1169         SwRect aOldRect( aOldFrame );
1170         if( pLay->IsPageFrame() )
1171         {
1172             aOldRect = static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext);
1173         }
1174 
1175         {
1176             SwFrameDeleteGuard aDeleteGuard(pLay);
1177             pLay->Calc(pRenderContext);
1178         }
1179 
1180         if ( aOldFrame != pLay->getFrameArea() )
1181             bChanged = true;
1182 
1183         bool bNoPaint = false;
1184         if ( pLay->IsPageBodyFrame() &&
1185              pLay->getFrameArea().Pos() == aOldRect.Pos() &&
1186              pLay->Lower() )
1187         {
1188             const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1189             // Limitations because of headers / footers
1190             if( pSh && pSh->GetViewOptions()->getBrowseMode() &&
1191                 !( pLay->IsCompletePaint() && pLay->FindPageFrame()->FindFootnoteCont() ) )
1192                 bNoPaint = true;
1193         }
1194 
1195         if ( !bNoPaint && IsPaint() && bAddRect && (pLay->IsCompletePaint() || bChanged) )
1196         {
1197             SwRect aPaint( pLay->getFrameArea() );
1198             // consider border and shadow for
1199             // page frames -> enlarge paint rectangle correspondingly.
1200             if ( pLay->IsPageFrame() )
1201             {
1202                 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
1203                 aPaint = pPageFrame->GetBoundRect(pRenderContext);
1204             }
1205 
1206             bool bPageInBrowseMode = pLay->IsPageFrame();
1207             if( bPageInBrowseMode )
1208             {
1209                 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1210                 if( !pSh || !pSh->GetViewOptions()->getBrowseMode() )
1211                     bPageInBrowseMode = false;
1212             }
1213             if( bPageInBrowseMode )
1214             {
1215                 // NOTE: no vertical layout in online layout
1216                 // Is the change even visible?
1217                 if ( pLay->IsCompletePaint() )
1218                 {
1219                     m_pImp->GetShell()->AddPaintRect( aPaint );
1220                     bAddRect = false;
1221                 }
1222                 else
1223                 {
1224                     SwRegionRects aRegion( aOldRect );
1225                     aRegion -= aPaint;
1226                     for ( size_t i = 0; i < aRegion.size(); ++i )
1227                         m_pImp->GetShell()->AddPaintRect( aRegion[i] );
1228                     aRegion.ChangeOrigin( aPaint );
1229                     aRegion.clear();
1230                     aRegion.push_back( aPaint );
1231                     aRegion -= aOldRect;
1232                     for ( size_t i = 0; i < aRegion.size(); ++i )
1233                         m_pImp->GetShell()->AddPaintRect( aRegion[i] );
1234                 }
1235             }
1236             else
1237             {
1238                 m_pImp->GetShell()->AddPaintRect( aPaint );
1239                 bAlreadyPainted = true;
1240                 // remember frame at complete paint
1241                 aFrameAtCompletePaint = pLay->getFrameArea();
1242             }
1243 
1244             // provide paint of spacing
1245             // between pages (not only for in online mode).
1246             if ( pLay->IsPageFrame() )
1247             {
1248                 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
1249                 const SwTwips nHalfDocBorder = pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
1250                                                    : SwViewOption::defGapBetweenPages;
1251                 const bool bLeftToRightViewLayout = m_pRoot->IsLeftToRightViewLayout();
1252                 const bool bPrev = bLeftToRightViewLayout ? pLay->GetPrev() : pLay->GetNext();
1253                 const bool bNext = bLeftToRightViewLayout ? pLay->GetNext() : pLay->GetPrev();
1254                 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
1255                 SwRect aPageRect( pLay->getFrameArea() );
1256 
1257                 if(pSh)
1258                 {
1259                     SwPageFrame::GetBorderAndShadowBoundRect(aPageRect, pSh,
1260                         pRenderContext,
1261                         aPageRect, pPageFrame->IsLeftShadowNeeded(), pPageFrame->IsRightShadowNeeded(),
1262                         pPageFrame->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
1263                 }
1264 
1265                 if ( bPrev )
1266                 {
1267                     // top
1268                     SwRect aSpaceToPrevPage( aPageRect );
1269                     aSpaceToPrevPage.Top( aSpaceToPrevPage.Top() - nHalfDocBorder );
1270                     aSpaceToPrevPage.Bottom( pLay->getFrameArea().Top() );
1271                     if(!aSpaceToPrevPage.IsEmpty())
1272                         m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
1273 
1274                     // left
1275                     aSpaceToPrevPage = aPageRect;
1276                     aSpaceToPrevPage.Left( aSpaceToPrevPage.Left() - nHalfDocBorder );
1277                     aSpaceToPrevPage.Right( pLay->getFrameArea().Left() );
1278                     if(!aSpaceToPrevPage.IsEmpty())
1279                         m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
1280                 }
1281                 if ( bNext )
1282                 {
1283                     // bottom
1284                     SwRect aSpaceToNextPage( aPageRect );
1285                     aSpaceToNextPage.Bottom( aSpaceToNextPage.Bottom() + nHalfDocBorder );
1286                     aSpaceToNextPage.Top( pLay->getFrameArea().Bottom() );
1287                     if(!aSpaceToNextPage.IsEmpty())
1288                         m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
1289 
1290                     // right
1291                     aSpaceToNextPage = aPageRect;
1292                     aSpaceToNextPage.Right( aSpaceToNextPage.Right() + nHalfDocBorder );
1293                     aSpaceToNextPage.Left( pLay->getFrameArea().Right() );
1294                     if(!aSpaceToNextPage.IsEmpty())
1295                         m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
1296                 }
1297             }
1298         }
1299         pLay->ResetCompletePaint();
1300     }
1301 
1302     if ( IsPaint() && bAddRect &&
1303          !pLay->GetNext() && pLay->IsRetoucheFrame() && pLay->IsRetouche() )
1304     {
1305         // vertical layout support
1306         SwRectFnSet aRectFnSet(pLay);
1307         SwRect aRect( pLay->GetUpper()->GetPaintArea() );
1308         aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pLay) );
1309         if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1310             pLay->ResetRetouche();
1311     }
1312 
1313     if( bAlreadyPainted )
1314         bAddRect = false;
1315 
1316     CheckWaitCursor();
1317 
1318     if ( IsAgain() )
1319         return false;
1320 
1321     // Now, deal with the lowers that are LayoutFrames
1322 
1323     if ( pLay->IsFootnoteFrame() ) // no LayFrames as Lower
1324         return bChanged;
1325 
1326     SwFrame *pLow = pLay->Lower();
1327     bool bTabChanged = false;
1328     while ( pLow && pLow->GetUpper() == pLay )
1329     {
1330         SwFrame* pNext = nullptr;
1331         if ( pLow->IsLayoutFrame() )
1332         {
1333             if ( pLow->IsTabFrame() )
1334             {
1335                 // Remember what was the next of the lower. Formatting may move it to the previous
1336                 // page, in which case it looses its next.
1337                 pNext = pLow->GetNext();
1338 
1339                 if (pNext && pNext->IsTabFrame())
1340                 {
1341                     auto pTab = static_cast<SwTabFrame*>(pNext);
1342                     if (pTab->IsFollow())
1343                     {
1344                         // The next frame is a follow of the previous frame, SwTabFrame::Join() will
1345                         // delete this one as part of formatting, so forget about it.
1346                         pNext = nullptr;
1347                     }
1348                 }
1349 
1350                 bTabChanged |= FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
1351             }
1352             // Skip the ones already registered for deletion
1353             else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() )
1354                 bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect );
1355         }
1356         else if ( m_pImp->GetShell()->IsPaintLocked() )
1357             // Shortcut to minimize the cycles. With Lock, the
1358             // paint is coming either way (primarily for browse)
1359             pLow->OptCalc();
1360 
1361         if ( IsAgain() )
1362             return false;
1363         if (!pNext)
1364         {
1365             pNext = pLow->GetNext();
1366         }
1367         pLow = pNext;
1368     }
1369     // add complete frame area as paint area, if frame
1370     // area has been already added and after formatting its lowers the frame area
1371     // is enlarged.
1372     SwRect aBoundRect(pLay->IsPageFrame() ? static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext) : pLay->getFrameArea() );
1373 
1374     if ( bAlreadyPainted &&
1375          ( aBoundRect.Width() > aFrameAtCompletePaint.Width() ||
1376            aBoundRect.Height() > aFrameAtCompletePaint.Height() )
1377        )
1378     {
1379         m_pImp->GetShell()->AddPaintRect( aBoundRect );
1380     }
1381     return bChanged || bTabChanged;
1382 }
1383 
FormatLayoutFly(SwFlyFrame * pFly)1384 void SwLayAction::FormatLayoutFly( SwFlyFrame* pFly )
1385 {
1386     vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
1387     OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
1388     if ( IsAgain() )
1389         return;
1390 
1391     bool bChanged = false;
1392     bool bAddRect = true;
1393 
1394     if ( !pFly->isFrameAreaDefinitionValid() || pFly->IsCompletePaint() || pFly->IsInvalid() )
1395     {
1396         // The Frame has changed, now it's getting formatted.
1397         const SwRect aOldRect( pFly->getFrameArea() );
1398         pFly->Calc(pRenderContext);
1399         bChanged = aOldRect != pFly->getFrameArea();
1400 
1401         if ( IsPaint() && (pFly->IsCompletePaint() || bChanged) &&
1402                     pFly->getFrameArea().Top() > 0 && pFly->getFrameArea().Left() > 0 )
1403             m_pImp->GetShell()->AddPaintRect( pFly->getFrameArea() );
1404 
1405         if ( bChanged )
1406             pFly->Invalidate();
1407         else
1408             pFly->Validate();
1409 
1410         bAddRect = false;
1411         pFly->ResetCompletePaint();
1412     }
1413 
1414     if ( IsAgain() )
1415         return;
1416 
1417     // Now, deal with the lowers that are LayoutFrames
1418     SwFrame *pLow = pFly->Lower();
1419     while ( pLow )
1420     {
1421         if ( pLow->IsLayoutFrame() )
1422         {
1423             if ( pLow->IsTabFrame() )
1424                 FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
1425             else
1426                 FormatLayout( m_pImp->GetShell()->GetOut(), static_cast<SwLayoutFrame*>(pLow), bAddRect );
1427         }
1428         pLow = pLow->GetNext();
1429     }
1430 }
1431 
1432 // Implement vertical layout support
FormatLayoutTab(SwTabFrame * pTab,bool bAddRect)1433 bool SwLayAction::FormatLayoutTab( SwTabFrame *pTab, bool bAddRect )
1434 {
1435     OSL_ENSURE( !IsAgain(), "8-) Attention to the invalid page." );
1436     if ( IsAgain() || !pTab->Lower() )
1437         return false;
1438 
1439     vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
1440     IDocumentTimerAccess& rTimerAccess = m_pRoot->GetFormat()->getIDocumentTimerAccess();
1441     rTimerAccess.BlockIdling();
1442 
1443     bool bChanged = false;
1444     bool bPainted = false;
1445 
1446     const SwPageFrame *pOldPage = pTab->FindPageFrame();
1447 
1448     // vertical layout support
1449     SwRectFnSet aRectFnSet(pTab);
1450 
1451     if ( !pTab->isFrameAreaDefinitionValid() || pTab->IsCompletePaint() || pTab->IsComplete() )
1452     {
1453         if ( pTab->GetPrev() && !pTab->GetPrev()->isFrameAreaDefinitionValid() )
1454         {
1455             pTab->GetPrev()->SetCompletePaint();
1456         }
1457 
1458         const SwRect aOldRect( pTab->getFrameArea() );
1459         pTab->SetLowersFormatted( false );
1460         pTab->Calc(pRenderContext);
1461         if ( aOldRect != pTab->getFrameArea() )
1462         {
1463             bChanged = true;
1464         }
1465         const SwRect aPaintFrame = pTab->GetPaintArea();
1466 
1467         if ( IsPaint() && bAddRect )
1468         {
1469             // add condition <pTab->getFrameArea().HasArea()>
1470             if ( !pTab->IsCompletePaint() &&
1471                  pTab->IsComplete() &&
1472                  ( pTab->getFrameArea().SSize() != pTab->getFramePrintArea().SSize() ||
1473                    // vertical layout support
1474                    aRectFnSet.GetLeftMargin(*pTab) ) &&
1475                  pTab->getFrameArea().HasArea()
1476                )
1477             {
1478                 // re-implement calculation of margin rectangles.
1479                 SwRect aMarginRect;
1480 
1481                 SwTwips nLeftMargin = aRectFnSet.GetLeftMargin(*pTab);
1482                 if ( nLeftMargin > 0)
1483                 {
1484                     aMarginRect = pTab->getFrameArea();
1485                     aRectFnSet.SetWidth( aMarginRect, nLeftMargin );
1486                     m_pImp->GetShell()->AddPaintRect( aMarginRect );
1487                 }
1488 
1489                 if ( aRectFnSet.GetRightMargin(*pTab) > 0)
1490                 {
1491                     aMarginRect = pTab->getFrameArea();
1492                     aRectFnSet.SetLeft( aMarginRect, aRectFnSet.GetPrtRight(*pTab) );
1493                     m_pImp->GetShell()->AddPaintRect( aMarginRect );
1494                 }
1495 
1496                 SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pTab);
1497                 if ( nTopMargin > 0)
1498                 {
1499                     aMarginRect = pTab->getFrameArea();
1500                     aRectFnSet.SetHeight( aMarginRect, nTopMargin );
1501                     m_pImp->GetShell()->AddPaintRect( aMarginRect );
1502                 }
1503 
1504                 if ( aRectFnSet.GetBottomMargin(*pTab) > 0)
1505                 {
1506                     aMarginRect = pTab->getFrameArea();
1507                     aRectFnSet.SetTop( aMarginRect, aRectFnSet.GetPrtBottom(*pTab) );
1508                     m_pImp->GetShell()->AddPaintRect( aMarginRect );
1509                 }
1510             }
1511             else if ( pTab->IsCompletePaint() )
1512             {
1513                 m_pImp->GetShell()->AddPaintRect( aPaintFrame );
1514                 bAddRect = false;
1515                 bPainted = true;
1516             }
1517 
1518             if ( pTab->IsRetouche() && !pTab->GetNext() )
1519             {
1520                 SwRect aRect( pTab->GetUpper()->GetPaintArea() );
1521                 // vertical layout support
1522                 aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
1523                 if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1524                     pTab->ResetRetouche();
1525             }
1526         }
1527         else
1528             bAddRect = false;
1529 
1530         if ( pTab->IsCompletePaint() && !m_pOptTab )
1531             m_pOptTab = pTab;
1532         pTab->ResetCompletePaint();
1533     }
1534     if ( IsPaint() && bAddRect && pTab->IsRetouche() && !pTab->GetNext() )
1535     {
1536         // set correct rectangle for retouche: area between bottom of table frame
1537         // and bottom of paint area of the upper frame.
1538         SwRect aRect( pTab->GetUpper()->GetPaintArea() );
1539         // vertical layout support
1540         aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
1541         if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
1542             pTab->ResetRetouche();
1543     }
1544 
1545     CheckWaitCursor();
1546 
1547     rTimerAccess.UnblockIdling();
1548 
1549     // Ugly shortcut!
1550     if ( pTab->IsLowersFormatted() &&
1551          (bPainted || !m_pImp->GetShell()->VisArea().IsOver( pTab->getFrameArea())) )
1552         return false;
1553 
1554     // Now, deal with the lowers
1555     if ( IsAgain() )
1556         return false;
1557 
1558     // for safety reasons:
1559     // check page number before formatting lowers.
1560     if ( pOldPage->GetPhyPageNum() > (pTab->FindPageFrame()->GetPhyPageNum() + 1) )
1561         SetNextCycle( true );
1562 
1563     // format lowers, only if table frame is valid
1564     if ( pTab->isFrameAreaDefinitionValid() )
1565     {
1566         FlowFrameJoinLockGuard tabG(pTab); // tdf#124675 prevent Join() if pTab becomes empty
1567         SwLayoutFrame *pLow = static_cast<SwLayoutFrame*>(pTab->Lower());
1568         while ( pLow )
1569         {
1570             SwFrameDeleteGuard rowG(pLow); // tdf#124675 prevent RemoveFollowFlowLine()
1571             bChanged |= FormatLayout( m_pImp->GetShell()->GetOut(), pLow, bAddRect );
1572             if ( IsAgain() )
1573                 return false;
1574             pLow = static_cast<SwLayoutFrame*>(pLow->GetNext());
1575         }
1576     }
1577 
1578     return bChanged;
1579 }
1580 
FormatContent(SwPageFrame * const pPage)1581 bool SwLayAction::FormatContent(SwPageFrame *const pPage)
1582 {
1583     ::comphelper::ScopeGuard g([this, pPage]() {
1584         if (IsAgain())
1585         {
1586             return; // pPage probably deleted
1587         }
1588         if (auto const* pObjs = pPage->GetSortedObjs())
1589         {
1590             std::vector<std::pair<SwAnchoredObject*, SwPageFrame*>> moved;
1591             for (auto const pObj : *pObjs)
1592             {
1593                 assert(!pObj->AnchorFrame()->IsTextFrame()
1594                     || !static_cast<SwTextFrame const*>(pObj->AnchorFrame())->IsFollow());
1595                 SwPageFrame *const pAnchorPage(pObj->AnchorFrame()->FindPageFrame());
1596                 assert(pAnchorPage);
1597                 if (pAnchorPage != pPage
1598                     && pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()
1599                     && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
1600                         != RndStdIds::FLY_AS_CHAR)
1601                 {
1602                     moved.emplace_back(pObj, pAnchorPage);
1603                 }
1604             }
1605             for (auto const& [pObj, pAnchorPage] : moved)
1606             {
1607                 SAL_INFO("sw.layout", "SwLayAction::FormatContent: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
1608                 pObj->RegisterAtPage(*pAnchorPage);
1609                 ::Notify_Background(pObj->GetDrawObj(), pPage,
1610                     pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
1611             }
1612             if (!moved.empty())
1613             {
1614                 pPage->InvalidateFlyLayout();
1615                 if (auto *const pContent = pPage->FindLastBodyContent())
1616                 {
1617                     pContent->InvalidateSize();
1618                 }
1619             }
1620         }
1621     });
1622 
1623     const SwContentFrame *pContent = pPage->ContainsContent();
1624     const SwViewShell *pSh = m_pRoot->GetCurrShell();
1625     const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
1626 
1627     while ( pContent && pPage->IsAnLower( pContent ) )
1628     {
1629         // If the content didn't change, we can use a few shortcuts.
1630         const bool bFull = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint() ||
1631                            pContent->IsRetouche() || pContent->GetDrawObjs();
1632         if ( bFull )
1633         {
1634             // We do this so we don't have to search later on.
1635             const bool bNxtCnt = IsCalcLayout() && !pContent->GetFollow();
1636             const SwContentFrame *pContentNext = bNxtCnt ? pContent->GetNextContentFrame() : nullptr;
1637             const SwContentFrame *pContentPrev = pContent->GetPrev() ? pContent->GetPrevContentFrame() : nullptr;
1638 
1639             const SwLayoutFrame*pOldUpper  = pContent->GetUpper();
1640             const SwTabFrame *pTab = pContent->FindTabFrame();
1641             const bool bInValid = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint();
1642             const bool bOldPaint = IsPaint();
1643             m_bPaint = bOldPaint && !(pTab && pTab == m_pOptTab);
1644             FormatContent_( pContent, pPage );
1645             // reset <bPaint> before format objects
1646             m_bPaint = bOldPaint;
1647 
1648             // format floating screen object at content frame.
1649             // No format, if action flag <bAgain> is set or action is interrupted.
1650             // allow format on interruption of action, if
1651             // it's the format for this interrupt
1652             // pass correct page frame
1653             // to the object formatter.
1654             if ( !IsAgain() &&
1655                  ( !IsInterrupt() || mbFormatContentOnInterrupt ) &&
1656                  pContent->IsTextFrame() &&
1657                  !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pContent),
1658                                                       *(pContent->FindPageFrame()), this ) )
1659             {
1660                 return false;
1661             }
1662 
1663             if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1664             {
1665                 const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1666                 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1667                 if ( IsPaintExtraData() && IsPaint() &&
1668                      nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1669                     m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1670             }
1671 
1672             if ( IsAgain() )
1673                 return false;
1674 
1675             // Temporarily interrupt processing if layout or Flys become invalid again.
1676             // However not for the BrowseView: The layout is getting invalid
1677             // all the time because the page height gets adjusted.
1678             // The same applies if the user wants to continue working and at least one
1679             // paragraph has been processed.
1680             if (!pTab || !bInValid)
1681             {
1682                 CheckIdleEnd();
1683                 // consider interrupt formatting.
1684                 if ( ( IsInterrupt() && !mbFormatContentOnInterrupt ) ||
1685                      ( !bBrowse && pPage->IsInvalidLayout() ) ||
1686                      // consider interrupt formatting
1687                      ( pPage->GetSortedObjs() && pPage->IsInvalidFly() && !mbFormatContentOnInterrupt )
1688                    )
1689                     return false;
1690             }
1691             if ( pOldUpper != pContent->GetUpper() )
1692             {
1693                 const sal_uInt16 nCurNum = pContent->FindPageFrame()->GetPhyPageNum();
1694                 if (  nCurNum < pPage->GetPhyPageNum() )
1695                     m_nPreInvaPage = nCurNum;
1696 
1697                 // If the frame flowed backwards more than one page, we need to
1698                 // start over again from the beginning, so nothing gets left out.
1699                 if ( !IsCalcLayout() && pPage->GetPhyPageNum() > nCurNum+1 )
1700                 {
1701                     SetNextCycle( true );
1702                     // consider interrupt formatting
1703                     if ( !mbFormatContentOnInterrupt )
1704                     {
1705                         return false;
1706                     }
1707                 }
1708             }
1709             // If the frame moved forwards to the next page, we re-run through
1710             // the predecessor.
1711             // This way, we catch predecessors which are now responsible for
1712             // retouching, but the footers will be touched also.
1713             bool bSetContent = true;
1714             if ( pContentPrev )
1715             {
1716                 if ( !pContentPrev->isFrameAreaDefinitionValid() && pPage->IsAnLower( pContentPrev ) )
1717                 {
1718                     pPage->InvalidateContent();
1719                 }
1720 
1721                 if ( pOldUpper != pContent->GetUpper() &&
1722                      pPage->GetPhyPageNum() < pContent->FindPageFrame()->GetPhyPageNum() )
1723                 {
1724                     pContent = pContentPrev;
1725                     bSetContent = false;
1726                 }
1727             }
1728             if ( bSetContent )
1729             {
1730                 if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
1731                      pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
1732                 {
1733                     const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
1734                     const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
1735                                                             nBottom, pContent );
1736                     if ( !pTmp )
1737                     {
1738                         if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
1739                               !lcl_FindFirstInvaObj( pPage, nBottom )) &&
1740                               (!pPage->IsInvalidLayout() ||
1741                                !lcl_FindFirstInvaLay( pPage, nBottom )))
1742                             SetBrowseActionStop( true );
1743                         // consider interrupt formatting.
1744                         if ( !mbFormatContentOnInterrupt )
1745                         {
1746                             return false;
1747                         }
1748                     }
1749                 }
1750                 pContent = bNxtCnt ? pContentNext : pContent->GetNextContentFrame();
1751             }
1752 
1753             if (IsReschedule())
1754             {
1755                 ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
1756             }
1757         }
1758         else
1759         {
1760             if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1761             {
1762                 const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1763                 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1764                 if ( IsPaintExtraData() && IsPaint() &&
1765                      nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1766                     m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1767             }
1768 
1769             // Do this if the frame has been formatted before.
1770             if ( pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() &&
1771                   IsPaint() )
1772                 PaintContent( pContent, pPage, pContent->getFrameArea(), pContent->getFrameArea().Bottom());
1773             if ( IsIdle() )
1774             {
1775                 CheckIdleEnd();
1776                 // consider interrupt formatting.
1777                 if ( IsInterrupt() && !mbFormatContentOnInterrupt )
1778                     return false;
1779             }
1780             if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
1781                  pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
1782             {
1783                 const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
1784                 const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
1785                                                     nBottom, pContent );
1786                 if ( !pTmp )
1787                 {
1788                     if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
1789                             !lcl_FindFirstInvaObj( pPage, nBottom )) &&
1790                             (!pPage->IsInvalidLayout() ||
1791                             !lcl_FindFirstInvaLay( pPage, nBottom )))
1792                         SetBrowseActionStop( true );
1793                     // consider interrupt formatting.
1794                     if ( !mbFormatContentOnInterrupt )
1795                     {
1796                         return false;
1797                     }
1798                 }
1799             }
1800             pContent = pContent->GetNextContentFrame();
1801         }
1802     }
1803     CheckWaitCursor();
1804     // consider interrupt formatting.
1805     return !IsInterrupt() || mbFormatContentOnInterrupt;
1806 }
1807 
FormatContent_(const SwContentFrame * pContent,const SwPageFrame * pPage)1808 void SwLayAction::FormatContent_( const SwContentFrame *pContent, const SwPageFrame  *pPage )
1809 {
1810     // We probably only ended up here because the Content holds DrawObjects.
1811     const bool bDrawObjsOnly = pContent->isFrameAreaDefinitionValid() && !pContent->IsCompletePaint() && !pContent->IsRetouche();
1812     SwRectFnSet aRectFnSet(pContent);
1813     if ( !bDrawObjsOnly && IsPaint() )
1814     {
1815         const SwRect aOldRect( pContent->UnionFrame() );
1816         const tools::Long nOldBottom = aRectFnSet.GetPrtBottom(*pContent);
1817         pContent->OptCalc();
1818         if( IsAgain() )
1819             return;
1820         if( aRectFnSet.YDiff( aRectFnSet.GetBottom(pContent->getFrameArea()),
1821                                 aRectFnSet.GetBottom(aOldRect) ) < 0 )
1822         {
1823             pContent->SetRetouche();
1824         }
1825         PaintContent( pContent, pContent->FindPageFrame(), aOldRect, nOldBottom);
1826     }
1827     else
1828     {
1829         if ( IsPaint() && pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() )
1830             PaintContent( pContent, pPage, pContent->getFrameArea(),
1831                         aRectFnSet.GetBottom(pContent->getFrameArea()) );
1832         pContent->OptCalc();
1833     }
1834 }
1835 
FormatFlyContent(const SwFlyFrame * pFly)1836 void SwLayAction::FormatFlyContent( const SwFlyFrame *pFly )
1837 {
1838     const SwContentFrame *pContent = pFly->ContainsContent();
1839 
1840     while ( pContent )
1841     {
1842         FormatContent_( pContent, pContent->FindPageFrame() );
1843 
1844         // format floating screen objects at content text frame
1845         // pass correct page frame to the object formatter.
1846         if ( pContent->IsTextFrame() &&
1847              !SwObjectFormatter::FormatObjsAtFrame(
1848                                             *const_cast<SwContentFrame*>(pContent),
1849                                             *(pContent->FindPageFrame()), this ) )
1850         {
1851             // restart format with first content
1852             pContent = pFly->ContainsContent();
1853             continue;
1854         }
1855 
1856         if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
1857         {
1858             const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
1859             const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
1860             if ( IsPaintExtraData() && IsPaint() &&
1861                  nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
1862                 m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
1863         }
1864 
1865         if ( IsAgain() )
1866             return;
1867 
1868         // If there's input, we interrupt processing.
1869         if ( !pFly->IsFlyInContentFrame() )
1870         {
1871             CheckIdleEnd();
1872             // consider interrupt formatting.
1873             if ( IsInterrupt() && !mbFormatContentOnInterrupt )
1874                 return;
1875         }
1876         pContent = pContent->GetNextContentFrame();
1877     }
1878     CheckWaitCursor();
1879 }
1880 
DoIdleJob_(const SwContentFrame * pCnt,IdleJobType eJob)1881 bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
1882 {
1883     OSL_ENSURE( pCnt->IsTextFrame(), "NoText neighbour of Text" );
1884     // robust against misuse by e.g. #i52542#
1885     if( !pCnt->IsTextFrame() )
1886         return false;
1887 
1888     SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
1889     // sw_redlinehide: spell check only the nodes with visible content?
1890     SwTextNode* pTextNode = const_cast<SwTextNode*>(pTextFrame->GetTextNodeForParaProps());
1891 
1892     bool bProcess = false;
1893     for (size_t i = 0; pTextNode; )
1894     {
1895         switch ( eJob )
1896         {
1897             case ONLINE_SPELLING :
1898                 bProcess = pTextNode->IsWrongDirty(); break;
1899             case AUTOCOMPLETE_WORDS :
1900                 bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
1901             case WORD_COUNT :
1902                 bProcess = pTextNode->IsWordCountDirty(); break;
1903             case SMART_TAGS :
1904                 bProcess = pTextNode->IsSmartTagDirty(); break;
1905         }
1906         if (bProcess)
1907         {
1908             break;
1909         }
1910         if (sw::MergedPara const* pMerged = pTextFrame->GetMergedPara())
1911         {
1912             while (true)
1913             {
1914                 ++i;
1915                 if (i < pMerged->extents.size())
1916                 {
1917                     if (pMerged->extents[i].pNode != pTextNode)
1918                     {
1919                         pTextNode = pMerged->extents[i].pNode;
1920                         break;
1921                     }
1922                 }
1923                 else
1924                 {
1925                     pTextNode = nullptr;
1926                     break;
1927                 }
1928             }
1929         }
1930         else
1931             pTextNode = nullptr;
1932     }
1933 
1934     if( bProcess )
1935     {
1936         assert(pTextNode);
1937         SwViewShell *pSh = m_pImp->GetShell();
1938         if( COMPLETE_STRING == m_nTextPos )
1939         {
1940             --m_nTextPos;
1941             if( auto pCursorShell = dynamic_cast<SwCursorShell *>( pSh ) )
1942                 if( !pCursorShell->IsTableMode() )
1943                 {
1944                     SwPaM *pCursor = pCursorShell->GetCursor();
1945                     if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
1946                     {
1947                         m_pContentNode = pCursor->GetContentNode();
1948                         m_nTextPos =  pCursor->GetPoint()->nContent.GetIndex();
1949                     }
1950                 }
1951         }
1952         sal_Int32 const nPos((m_pContentNode && pTextNode == m_pContentNode)
1953                 ? m_nTextPos
1954                 : COMPLETE_STRING);
1955 
1956         switch ( eJob )
1957         {
1958             case ONLINE_SPELLING :
1959             {
1960                 SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) );
1961                 // PENDING should stop idle spell checking
1962                 m_bPageValid = m_bPageValid && (SwTextNode::WrongState::TODO != pTextNode->GetWrongDirty());
1963                 if ( aRepaint.HasArea() )
1964                     m_pImp->GetShell()->InvalidateWindows( aRepaint );
1965                 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
1966                     return true;
1967                 break;
1968             }
1969             case AUTOCOMPLETE_WORDS :
1970                 const_cast<SwTextFrame*>(pTextFrame)->CollectAutoCmplWrds(*pTextNode, nPos);
1971                 // note: bPageValid remains true here even if the cursor
1972                 // position is skipped, so no PENDING state needed currently
1973                 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
1974                     return true;
1975                 break;
1976             case WORD_COUNT :
1977             {
1978                 const sal_Int32 nEnd = pTextNode->GetText().getLength();
1979                 SwDocStat aStat;
1980                 pTextNode->CountWords( aStat, 0, nEnd );
1981                 if ( Application::AnyInput() )
1982                     return true;
1983                 break;
1984             }
1985             case SMART_TAGS :
1986             {
1987                 try {
1988                     const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) );
1989                     m_bPageValid = m_bPageValid && !pTextNode->IsSmartTagDirty();
1990                     if ( aRepaint.HasArea() )
1991                         m_pImp->GetShell()->InvalidateWindows( aRepaint );
1992                 } catch( const css::uno::RuntimeException&) {
1993                     // handle smarttag problems gracefully and provide diagnostics
1994                     TOOLS_WARN_EXCEPTION( "sw.core", "SMART_TAGS");
1995                 }
1996                 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
1997                     return true;
1998                 break;
1999             }
2000         }
2001     }
2002 
2003     // The Flys that are anchored to the paragraph need to be considered too.
2004     if ( pCnt->GetDrawObjs() )
2005     {
2006         const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
2007         for (SwAnchoredObject* pObj : rObjs)
2008         {
2009             if ( auto pFly = dynamic_cast<SwFlyFrame *>( pObj ) )
2010             {
2011                 if ( pFly->IsFlyInContentFrame() )
2012                 {
2013                     const SwContentFrame *pC = pFly->ContainsContent();
2014                     while( pC )
2015                     {
2016                         if ( pC->IsTextFrame() )
2017                         {
2018                             if ( DoIdleJob_( pC, eJob ) )
2019                                 return true;
2020                         }
2021                         pC = pC->GetNextContentFrame();
2022                     }
2023                 }
2024             }
2025         }
2026     }
2027     return false;
2028 }
2029 
DoIdleJob(IdleJobType eJob,bool bVisAreaOnly)2030 bool SwLayIdle::DoIdleJob( IdleJobType eJob, bool bVisAreaOnly )
2031 {
2032     // Spellcheck all contents of the pages. Either only the
2033     // visible ones or all of them.
2034     const SwViewShell* pViewShell = m_pImp->GetShell();
2035     const SwViewOption* pViewOptions = pViewShell->GetViewOptions();
2036     const SwDoc* pDoc = pViewShell->GetDoc();
2037 
2038     switch ( eJob )
2039     {
2040         case ONLINE_SPELLING :
2041             if( !pViewOptions->IsOnlineSpell() )
2042                 return false;
2043             break;
2044         case AUTOCOMPLETE_WORDS :
2045             if( !SwViewOption::IsAutoCompleteWords() ||
2046                  SwDoc::GetAutoCompleteWords().IsLockWordLstLocked())
2047                 return false;
2048             break;
2049         case WORD_COUNT :
2050             if ( !pViewShell->getIDocumentStatistics().GetDocStat().bModified )
2051                 return false;
2052             break;
2053         case SMART_TAGS :
2054             if ( pDoc->GetDocShell()->IsHelpDocument() ||
2055                  pDoc->isXForms() ||
2056                 !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
2057                 return false;
2058             break;
2059         default: OSL_FAIL( "Unknown idle job type" );
2060     }
2061 
2062     SwPageFrame *pPage;
2063     if ( bVisAreaOnly )
2064         pPage = m_pImp->GetFirstVisPage(pViewShell->GetOut());
2065     else
2066         pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
2067 
2068     m_pContentNode = nullptr;
2069     m_nTextPos = COMPLETE_STRING;
2070 
2071     while ( pPage )
2072     {
2073         m_bPageValid = true;
2074         const SwContentFrame *pCnt = pPage->ContainsContent();
2075         while( pCnt && pPage->IsAnLower( pCnt ) )
2076         {
2077             if ( DoIdleJob_( pCnt, eJob ) )
2078             {
2079                 SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
2080                 return true;
2081             }
2082             pCnt = pCnt->GetNextContentFrame();
2083         }
2084         if ( pPage->GetSortedObjs() )
2085         {
2086             for ( size_t i = 0; pPage->GetSortedObjs() &&
2087                                 i < pPage->GetSortedObjs()->size(); ++i )
2088             {
2089                 const SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
2090                 if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pObj ) )
2091                 {
2092                     const SwContentFrame *pC = pFly->ContainsContent();
2093                     while( pC )
2094                     {
2095                         if ( pC->IsTextFrame() )
2096                         {
2097                             if ( DoIdleJob_( pC, eJob ) )
2098                             {
2099                                 SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
2100                                 return true;
2101                             }
2102                         }
2103                         pC = pC->GetNextContentFrame();
2104                     }
2105                 }
2106             }
2107         }
2108 
2109         if( m_bPageValid )
2110         {
2111             switch ( eJob )
2112             {
2113                 case ONLINE_SPELLING : pPage->ValidateSpelling(); break;
2114                 case AUTOCOMPLETE_WORDS : pPage->ValidateAutoCompleteWords(); break;
2115                 case WORD_COUNT : pPage->ValidateWordCount(); break;
2116                 case SMART_TAGS : pPage->ValidateSmartTags(); break;
2117             }
2118         }
2119 
2120         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
2121         if ( pPage && bVisAreaOnly &&
2122              !pPage->getFrameArea().IsOver( m_pImp->GetShell()->VisArea()))
2123              break;
2124     }
2125     return false;
2126 }
2127 
2128 #if HAVE_FEATURE_DESKTOP && defined DBG_UTIL
ShowIdle(Color eColor)2129 void SwLayIdle::ShowIdle( Color eColor )
2130 {
2131     if ( m_bIndicator )
2132         return;
2133 
2134     m_bIndicator = true;
2135     vcl::Window *pWin = m_pImp->GetShell()->GetWin();
2136     if (pWin && !pWin->SupportsDoubleBuffering()) // FIXME make this work with double-buffering
2137     {
2138         tools::Rectangle aRect( 0, 0, 5, 5 );
2139         aRect = pWin->PixelToLogic( aRect );
2140         // Depending on if idle layout is in progress or not, draw a "red square" or a "green square".
2141         pWin->GetOutDev()->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
2142         pWin->GetOutDev()->SetFillColor( eColor );
2143         pWin->GetOutDev()->SetLineColor();
2144         pWin->GetOutDev()->DrawRect( aRect );
2145         pWin->GetOutDev()->Pop();
2146     }
2147 }
2148 #define SHOW_IDLE( Color ) ShowIdle( Color )
2149 #else
2150 #define SHOW_IDLE( Color )
2151 #endif // DBG_UTIL
2152 
SwLayIdle(SwRootFrame * pRt,SwViewShellImp * pI)2153 SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) :
2154     m_pRoot( pRt ),
2155     m_pImp( pI )
2156 #ifdef DBG_UTIL
2157     , m_bIndicator( false )
2158 #endif
2159 {
2160     SAL_INFO("sw.idle", "SwLayIdle() entry");
2161 
2162     m_pImp->m_pIdleAct = this;
2163 
2164     SHOW_IDLE( COL_LIGHTRED );
2165 
2166     m_pImp->GetShell()->EnableSmooth( false );
2167 
2168     // First, spellcheck the visible area. Only if there's nothing
2169     // to do there, we trigger the IdleFormat.
2170     if ( !DoIdleJob( SMART_TAGS, true ) &&
2171          !DoIdleJob( ONLINE_SPELLING, true ) &&
2172          !DoIdleJob( AUTOCOMPLETE_WORDS, true ) )
2173     {
2174         // Format, then register repaint rectangles with the SwViewShell if necessary.
2175         // This requires running artificial actions, so we don't get undesired
2176         // effects when for instance the page count gets changed.
2177         // We remember the shells where the cursor is visible, so we can make
2178         // it visible again if needed after a document change.
2179         std::vector<bool> aBools;
2180         for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2181         {
2182             ++rSh.mnStartAction;
2183             bool bVis = false;
2184             if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
2185             {
2186                 bVis = pCursorShell->GetCharRect().IsOver(rSh.VisArea());
2187             }
2188             aBools.push_back( bVis );
2189         }
2190 
2191         bool bInterrupt(false);
2192         {
2193             SwLayAction aAction( m_pRoot, m_pImp );
2194             aAction.SetInputType( VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER) );
2195             aAction.SetIdle( true );
2196             aAction.SetWaitAllowed( false );
2197             aAction.Action(m_pImp->GetShell()->GetOut());
2198             bInterrupt = aAction.IsInterrupt();
2199         }
2200 
2201         // Further start/end actions only happen if there were paints started
2202         // somewhere or if the visibility of the CharRects has changed.
2203         bool bActions = false;
2204         size_t nBoolIdx = 0;
2205         for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2206         {
2207             --rSh.mnStartAction;
2208 
2209             if ( rSh.Imp()->GetRegion() )
2210                 bActions = true;
2211             else
2212             {
2213                 SwRect aTmp( rSh.VisArea() );
2214                 rSh.UISizeNotify();
2215 
2216                 // Are we supposed to crash if rSh isn't a cursor shell?!
2217                 // bActions |= aTmp != rSh.VisArea() ||
2218                 //             aBools[nBoolIdx] != ((SwCursorShell*)&rSh)->GetCharRect().IsOver( rSh.VisArea() );
2219 
2220                 // aBools[ i ] is true, if the i-th shell is a cursor shell (!!!)
2221                 // and the cursor is visible.
2222                 bActions |= aTmp != rSh.VisArea();
2223                 if ( aTmp == rSh.VisArea() )
2224                     if ( auto pCursorShell = dynamic_cast< SwCursorShell*>( &rSh) )
2225                         bActions |= aBools[nBoolIdx] != pCursorShell->GetCharRect().IsOver( rSh.VisArea() );
2226             }
2227 
2228             ++nBoolIdx;
2229         }
2230 
2231         if ( bActions )
2232         {
2233             // Prepare start/end actions via CursorShell, so the cursor, selection
2234             // and VisArea can be set correctly.
2235             nBoolIdx = 0;
2236             for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
2237             {
2238                 SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
2239 
2240                 if ( pCursorShell )
2241                     pCursorShell->SttCursorMove();
2242 
2243                 // If there are accrued paints, it's best to simply invalidate
2244                 // the whole window. Otherwise there would arise paint problems whose
2245                 // solution would be disproportionally expensive.
2246                 SwViewShellImp *pViewImp = rSh.Imp();
2247                 bool bUnlock = false;
2248                 if ( pViewImp->GetRegion() )
2249                 {
2250                     pViewImp->DelRegion();
2251 
2252                     // Cause a repaint with virtual device.
2253                     rSh.LockPaint();
2254                     bUnlock = true;
2255                 }
2256 
2257                 if ( pCursorShell )
2258                     // If the Cursor was visible, we need to make it visible again.
2259                     // Otherwise, EndCursorMove with true for IdleEnd
2260                     pCursorShell->EndCursorMove( !aBools[nBoolIdx] );
2261                 if( bUnlock )
2262                 {
2263                     if( pCursorShell )
2264                     {
2265                         // UnlockPaint overwrite the selection from the
2266                         // CursorShell and calls the virtual method paint
2267                         // to fill the virtual device. This fill don't have
2268                         // paint the selection! -> Set the focus flag at
2269                         // CursorShell and it doesn't paint the selection.
2270                         pCursorShell->ShellLoseFocus();
2271                         pCursorShell->UnlockPaint( true );
2272                         pCursorShell->ShellGetFocus();
2273                     }
2274                     else
2275                         rSh.UnlockPaint( true );
2276                 }
2277                 ++nBoolIdx;
2278 
2279             }
2280         }
2281 
2282         if (!bInterrupt)
2283         {
2284             if ( !DoIdleJob( WORD_COUNT, false ) )
2285                 if ( !DoIdleJob( SMART_TAGS, false ) )
2286                     if ( !DoIdleJob( ONLINE_SPELLING, false ) )
2287                         DoIdleJob( AUTOCOMPLETE_WORDS, false );
2288         }
2289 
2290         bool bInValid = false;
2291         const SwViewOption& rVOpt = *m_pImp->GetShell()->GetViewOptions();
2292         const SwViewShell* pViewShell = m_pImp->GetShell();
2293         // See conditions in DoIdleJob()
2294         const bool bSpell     = rVOpt.IsOnlineSpell();
2295         const bool bACmplWrd  = SwViewOption::IsAutoCompleteWords();
2296         const bool bWordCount = pViewShell->getIDocumentStatistics().GetDocStat().bModified;
2297         const bool bSmartTags = !pViewShell->GetDoc()->GetDocShell()->IsHelpDocument() &&
2298                                 !pViewShell->GetDoc()->isXForms() &&
2299                                 SwSmartTagMgr::Get().IsSmartTagsEnabled();
2300 
2301         SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
2302         do
2303         {
2304             bInValid = pPg->IsInvalidContent()    || pPg->IsInvalidLayout() ||
2305                        pPg->IsInvalidFlyContent() || pPg->IsInvalidFlyLayout() ||
2306                        pPg->IsInvalidFlyInCnt() ||
2307                        (bSpell && pPg->IsInvalidSpelling()) ||
2308                        (bACmplWrd && pPg->IsInvalidAutoCompleteWords()) ||
2309                        (bWordCount && pPg->IsInvalidWordCount()) ||
2310                        (bSmartTags && pPg->IsInvalidSmartTags());
2311 
2312             pPg = static_cast<SwPageFrame*>(pPg->GetNext());
2313 
2314         } while ( pPg && !bInValid );
2315 
2316         if ( !bInValid )
2317         {
2318             m_pRoot->ResetIdleFormat();
2319             SfxObjectShell* pDocShell = m_pImp->GetShell()->GetDoc()->GetDocShell();
2320             pDocShell->Broadcast( SfxEventHint( SfxEventHintId::SwEventLayoutFinished, SwDocShell::GetEventName(STR_SW_EVENT_LAYOUT_FINISHED), pDocShell ) );
2321             // Limit lifetime of the text glyphs cache to a single run of the
2322             // layout.
2323             SwClearFntCacheTextGlyphs();
2324         }
2325     }
2326 
2327     m_pImp->GetShell()->EnableSmooth( true );
2328 
2329     if( m_pImp->IsAccessible() )
2330         m_pImp->FireAccessibleEvents();
2331 
2332     SAL_INFO("sw.idle", "SwLayIdle() return");
2333 
2334 #ifdef DBG_UTIL
2335     if ( m_bIndicator && m_pImp->GetShell()->GetWin() )
2336     {
2337         // Do not invalidate indicator, this may cause an endless loop. Instead, just repaint it
2338         // This should be replaced by an overlay object in the future, anyways. Since it's only for debug
2339         // purposes, it is not urgent.
2340             m_bIndicator = false; SHOW_IDLE( COL_LIGHTGREEN );
2341     }
2342 #endif
2343 }
2344 
~SwLayIdle()2345 SwLayIdle::~SwLayIdle()
2346 {
2347     m_pImp->m_pIdleAct = nullptr;
2348 }
2349 
2350 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2351