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