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