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