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