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 <comphelper/lok.hxx>
21 #include <ndole.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <svl/itemiter.hxx>
25 #include <fmtfsize.hxx>
26 #include <fmthdft.hxx>
27 #include <fmtclds.hxx>
28 #include <fmtpdsc.hxx>
29 #include <fmtornt.hxx>
30 #include <fmtsrnd.hxx>
31 #include <ftninfo.hxx>
32 #include <frmtool.hxx>
33 #include <tgrditem.hxx>
34 #include <viewopt.hxx>
35 #include <docsh.hxx>
36 #include <wrtsh.hxx>
37 #include <view.hxx>
38 #include <edtwin.hxx>
39 #include <frameformats.hxx>
40 
41 #include <viewimp.hxx>
42 #include <pagefrm.hxx>
43 #include <rootfrm.hxx>
44 #include <IDocumentDrawModelAccess.hxx>
45 #include <IDocumentSettingAccess.hxx>
46 #include <IDocumentFieldsAccess.hxx>
47 #include <dcontact.hxx>
48 #include <hints.hxx>
49 #include <FrameControlsManager.hxx>
50 
51 #include <ftnidx.hxx>
52 #include <bodyfrm.hxx>
53 #include <ftnfrm.hxx>
54 #include <tabfrm.hxx>
55 #include <txtfrm.hxx>
56 #include <notxtfrm.hxx>
57 #include <layact.hxx>
58 #include <flyfrms.hxx>
59 #include <htmltbl.hxx>
60 #include <pagedesc.hxx>
61 #include <editeng/frmdiritem.hxx>
62 #include <sortedobjs.hxx>
63 #include <calbck.hxx>
64 #include <txtfly.hxx>
65 
66 using namespace ::com::sun::star;
67 
SwBodyFrame(SwFrameFormat * pFormat,SwFrame * pSib)68 SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
69     SwLayoutFrame( pFormat, pSib )
70 {
71     mnFrameType = SwFrameType::Body;
72 }
73 
Format(vcl::RenderContext *,const SwBorderAttrs *)74 void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
75 {
76     // Formatting of the body is too simple, thus, it gets its own format method.
77     // Borders etc. are not taken into account here.
78     // Width is taken from the PrtArea of the Upper. Height is the height of the
79     // PrtArea of the Upper minus any neighbors (for robustness).
80     // The PrtArea has always the size of the frame.
81 
82     if ( !isFrameAreaSizeValid() )
83     {
84         SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
85         SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
86         const SwFrame *pFrame = GetUpper()->Lower();
87         do
88         {
89             if ( pFrame != this )
90             {
91                 if( pFrame->IsVertical() )
92                     nWidth -= pFrame->getFrameArea().Width();
93                 else
94                     nHeight -= pFrame->getFrameArea().Height();
95             }
96             pFrame = pFrame->GetNext();
97         } while ( pFrame );
98 
99         if ( nHeight < 0 )
100         {
101             nHeight = 0;
102         }
103 
104         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
105         aFrm.Height( nHeight );
106 
107         if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
108         {
109             aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
110         }
111 
112         aFrm.Width( nWidth );
113     }
114 
115     bool bNoGrid = true;
116     if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
117     {
118         SwTextGridItem const*const pGrid(
119                 GetGridItem(static_cast<SwPageFrame*>(GetUpper())));
120         if( pGrid )
121         {
122             bNoGrid = false;
123             tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
124             SwRectFnSet aRectFnSet(this);
125             tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
126             tools::Long nBorder = 0;
127             if( GRID_LINES_CHARS == pGrid->GetGridType() )
128             {
129                 //for textgrid refactor
130                 SwDoc *pDoc = GetFormat()->GetDoc();
131                 nBorder = nSize % (GetGridWidth(*pGrid, *pDoc));
132                 nSize -= nBorder;
133                 nBorder /= 2;
134             }
135 
136             SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
137             aRectFnSet.SetPosX( aPrt, nBorder );
138             aRectFnSet.SetWidth( aPrt, nSize );
139 
140             // Height of body frame:
141             nBorder = aRectFnSet.GetHeight(getFrameArea());
142 
143             // Number of possible lines in area of body frame:
144             tools::Long nNumberOfLines = nBorder / nSum;
145             if( nNumberOfLines > pGrid->GetLines() )
146                 nNumberOfLines = pGrid->GetLines();
147 
148             // Space required for nNumberOfLines lines:
149             nSize = nNumberOfLines * nSum;
150             nBorder -= nSize;
151             nBorder /= 2;
152 
153             // #i21774# Footnotes and centering the grid does not work together:
154             const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc()->
155                                         GetFootnoteIdxs().empty();
156 
157             aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
158             aRectFnSet.SetHeight( aPrt, nSize );
159         }
160     }
161 
162     if( bNoGrid )
163     {
164         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
165         aPrt.Pos().setX(0);
166         aPrt.Pos().setY(0);
167         aPrt.Height( getFrameArea().Height() );
168         aPrt.Width( getFrameArea().Width() );
169     }
170 
171     setFrameAreaSizeValid(true);
172     setFramePrintAreaValid(true);
173 }
174 
SwPageFrame(SwFrameFormat * pFormat,SwFrame * pSib,SwPageDesc * pPgDsc)175 SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
176     SwFootnoteBossFrame( pFormat, pSib ),
177     m_pDesc( pPgDsc ),
178     m_nPhyPageNum( 0 )
179 {
180     SetDerivedVert( false );
181     SetDerivedR2L( false );
182     if( m_pDesc )
183     {
184         m_bHasGrid = true;
185         SwTextGridItem const*const pGrid(GetGridItem(this));
186         if( !pGrid )
187             m_bHasGrid = false;
188     }
189     else
190         m_bHasGrid = false;
191     SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
192                      pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
193     mnFrameType = SwFrameType::Page;
194     m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
195     m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;
196 
197     SwViewShell *pSh = getRootFrame()->GetCurrShell();
198     const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
199     vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
200 
201     {
202         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
203 
204         if ( bBrowseMode )
205         {
206             aFrm.Height( 0 );
207             tools::Long nWidth = pSh->VisArea().Width();
208 
209             if ( !nWidth )
210             {
211                 nWidth = 5000;     // changes anyway
212             }
213 
214             aFrm.Width ( nWidth );
215         }
216         else
217         {
218             aFrm.SSize( pFormat->GetFrameSize().GetSize() );
219         }
220     }
221 
222     // create and insert body area if it is not a blank page
223     SwDoc* pDoc(pFormat->GetDoc());
224     m_bEmptyPage = (pFormat == pDoc->GetEmptyPageFormat());
225 
226     if(m_bEmptyPage)
227     {
228         return;
229     }
230 
231     Calc(pRenderContext); // so that the PrtArea is correct
232     SwBodyFrame *pBodyFrame = new SwBodyFrame( pDoc->GetDfltFrameFormat(), this );
233     pBodyFrame->ChgSize( getFramePrintArea().SSize() );
234     pBodyFrame->Paste( this );
235     pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
236     pBodyFrame->InvalidatePos();
237 
238     if ( bBrowseMode )
239         InvalidateSize_();
240 
241     // insert header/footer,, but only if active.
242     if ( pFormat->GetHeader().IsActive() )
243         PrepareHeader();
244     if ( pFormat->GetFooter().IsActive() )
245         PrepareFooter();
246 
247     const SwFormatCol &rCol = pFormat->GetCol();
248     if ( rCol.GetNumCols() > 1 )
249     {
250         const SwFormatCol aOld; //ChgColumns() needs an old value
251         pBodyFrame->ChgColumns( aOld, rCol );
252     }
253 
254 }
255 
DestroyImpl()256 void SwPageFrame::DestroyImpl()
257 {
258     // Cleanup the header-footer controls in the SwEditWin
259     SwViewShell* pSh = getRootFrame()->GetCurrShell();
260     SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( pSh );
261     if ( pWrtSh )
262     {
263         SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
264         rEditWin.GetFrameControlsManager( ).RemoveControls( this );
265     }
266 
267     // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
268     if (m_pSortedObjs)
269     {
270         // Objects can be anchored at pages that are before their anchors (why ever...).
271         // In such cases, we would access already freed memory.
272         for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
273         {
274             pAnchoredObj->SetPageFrame( nullptr );
275         }
276         m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
277     }
278 
279     // prevent access to destroyed pages
280     SwDoc *pDoc = GetFormat() ? GetFormat()->GetDoc() : nullptr;
281     if( pDoc && !pDoc->IsInDtor() )
282     {
283         if ( pSh )
284         {
285             SwViewShellImp *pImp = pSh->Imp();
286             pImp->SetFirstVisPageInvalid();
287             if ( pImp->IsAction() )
288                 pImp->GetLayAction().SetAgain();
289             // #i9719# - retouche area of page
290             // including border and shadow area.
291             const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
292             SwRect aRetoucheRect;
293             SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
294             pSh->AddPaintRect( aRetoucheRect );
295         }
296     }
297 
298     SwFootnoteBossFrame::DestroyImpl();
299 }
300 
~SwPageFrame()301 SwPageFrame::~SwPageFrame()
302 {
303 }
304 
CheckGrid(bool bInvalidate)305 void SwPageFrame::CheckGrid( bool bInvalidate )
306 {
307     bool bOld = m_bHasGrid;
308     m_bHasGrid = true;
309     SwTextGridItem const*const pGrid(GetGridItem(this));
310     m_bHasGrid = nullptr != pGrid;
311     if( !(bInvalidate || bOld != m_bHasGrid) )
312         return;
313 
314     SwLayoutFrame* pBody = FindBodyCont();
315     if( pBody )
316     {
317         pBody->InvalidatePrt();
318         SwContentFrame* pFrame = pBody->ContainsContent();
319         while( pBody->IsAnLower( pFrame ) )
320         {
321             static_cast<SwTextFrame*>(pFrame)->Prepare();
322             pFrame = pFrame->GetNextContentFrame();
323         }
324     }
325     SetCompletePaint();
326 }
327 
CheckDirection(bool bVert)328 void SwPageFrame::CheckDirection( bool bVert )
329 {
330     SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
331     if( bVert )
332     {
333         if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
334         {
335             mbVertLR = false;
336             mbVertical = false;
337         }
338         else
339         {
340             const SwViewShell *pSh = getRootFrame()->GetCurrShell();
341             if( pSh && pSh->GetViewOptions()->getBrowseMode() )
342             {
343                 mbVertLR = false;
344                 mbVertical = false;
345             }
346             else
347             {
348                 mbVertical = true;
349 
350                 if(SvxFrameDirection::Vertical_RL_TB == nDir)
351                     mbVertLR = false;
352                 else if(SvxFrameDirection::Vertical_LR_TB==nDir)
353                     mbVertLR = true;
354             }
355         }
356 
357         mbInvalidVert = false;
358     }
359     else
360     {
361         if( SvxFrameDirection::Horizontal_RL_TB == nDir )
362             mbRightToLeft = true;
363         else
364             mbRightToLeft = false;
365         mbInvalidR2L = false;
366     }
367 }
368 
369 /// create specific Flys for this page and format generic content
lcl_FormatLay(SwLayoutFrame * pLay)370 static void lcl_FormatLay( SwLayoutFrame *pLay )
371 {
372     vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
373     // format all LayoutFrames - no tables, Flys etc.
374 
375     SwFrame *pTmp = pLay->Lower();
376     // first the low-level ones
377     while ( pTmp )
378     {
379         const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
380                            | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont
381                            | SwFrameType::Ftn | SwFrameType::Body;
382         if ( pTmp->GetType() & nTypes )
383             ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
384         pTmp = pTmp->GetNext();
385     }
386     pLay->Calc(pRenderContext);
387 }
388 
389 /// Create Flys or register draw objects
lcl_MakeObjs(const SwFrameFormats & rTable,SwPageFrame * pPage)390 static void lcl_MakeObjs( const SwFrameFormats &rTable, SwPageFrame *pPage )
391 {
392     // formats are in the special table of the document
393 
394     for ( size_t i = 0; i < rTable.size(); ++i )
395     {
396         SwFrameFormat *pFormat = rTable[i];
397         const SwFormatAnchor &rAnch = pFormat->GetAnchor();
398         if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() )
399         {
400             if( rAnch.GetContentAnchor() )
401             {
402                 if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
403                 {
404                     SwFormatAnchor aAnch( rAnch );
405                     aAnch.SetAnchor( nullptr );
406                     pFormat->SetFormatAttr( aAnch );
407                 }
408                 else
409                     continue;
410             }
411 
412             // is it a border or a SdrObject?
413             bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
414             SdrObject *pSdrObj = nullptr;
415             if ( bSdrObj  && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
416             {
417                 OSL_FAIL( "DrawObject not found." );
418                 pFormat->GetDoc()->DelFrameFormat( pFormat );
419                 --i;
420                 continue;
421             }
422             // The object might be anchored to another page, e.g. when inserting
423             // a new page due to a page descriptor change. In such cases, the
424             // object needs to be moved.
425             // In some cases the object is already anchored to the correct page.
426             // This will be handled here and does not need to be coded extra.
427             SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
428             if ( bSdrObj )
429             {
430                 // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
431                 SwDrawContact *pContact =
432                             static_cast<SwDrawContact*>(::GetUserCall(pSdrObj));
433                 if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
434                 {
435                     if ( pContact )
436                     {
437                         pDrawVirtObj->RemoveFromWriterLayout();
438                         pDrawVirtObj->RemoveFromDrawingPage();
439                         pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
440                     }
441                 }
442                 else
443                 {
444                     if ( pContact->GetAnchorFrame() )
445                         pContact->DisconnectFromLayout( false );
446                     pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
447                 }
448             }
449             else
450             {
451                 SwIterator<SwFlyFrame,SwFormat> aIter( *pFormat );
452                 SwFlyFrame *pFly = aIter.First();
453                 if ( pFly)
454                 {
455                     if( pFly->GetAnchorFrame() )
456                         pFly->AnchorFrame()->RemoveFly( pFly );
457                 }
458                 else
459                     pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pPg, pPg );
460                 pPg->AppendFly( pFly );
461                 ::RegistFlys( pPg, pFly );
462             }
463         }
464     }
465 }
466 
PreparePage(bool bFootnote)467 void SwPageFrame::PreparePage( bool bFootnote )
468 {
469     SetFootnotePage( bFootnote );
470 
471     // #i82258#
472     // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
473     // the side effect, that the content of page header and footer are formatted.
474     // For this formatting it is needed that the anchored objects are registered
475     // at the <SwPageFrame> instance.
476     // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
477     ::RegistFlys( this, this );
478 
479     if ( Lower() )
480     {
481                 ::lcl_FormatLay( this );
482     }
483 
484     // Flys and draw objects that are still attached to the document.
485     // Footnote pages do not have page-bound Flys!
486     // There might be Flys or draw objects that want to be placed on
487     // empty pages, however, the empty pages ignore that and the following
488     // pages take care of them.
489     if ( !bFootnote && !IsEmptyPage() )
490     {
491         SwDoc *pDoc = GetFormat()->GetDoc();
492 
493         if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
494             lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
495         lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), this );
496     }
497 }
498 
SwClientNotify(const SwModify & rModify,const SfxHint & rHint)499 void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
500 {
501     if(typeid(sw::PageFootnoteHint) == typeid(rHint))
502     {
503         // currently the savest way:
504         static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
505         SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
506         if(!GetMaxFootnoteHeight())
507             SetMaxFootnoteHeight(LONG_MAX);
508         SetColMaxFootnoteHeight();
509         // here, the page might be destroyed:
510         static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
511     }
512     else if (rHint.GetId() == SfxHintId::SwLegacyModify)
513     {
514         auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
515         if(auto pSh = getRootFrame()->GetCurrShell())
516             pSh->SetFirstVisPageInvalid();
517 
518         SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
519         if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
520         {
521             auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
522             auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
523             SfxItemIter aOIter(*rOldSetChg.GetChgSet());
524             SfxItemIter aNIter(*rNewSetChg.GetChgSet());
525             const SfxPoolItem* pOItem = aOIter.GetCurItem();
526             const SfxPoolItem* pNItem = aNIter.GetCurItem();
527             SwAttrSetChg aOldSet(rOldSetChg);
528             SwAttrSetChg aNewSet(rNewSetChg);
529             do
530             {
531                 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
532                 pOItem = aOIter.NextItem();
533                 pNItem = aNIter.NextItem();
534             } while(pNItem);
535             if(aOldSet.Count() || aNewSet.Count())
536                 SwLayoutFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
537         }
538         else
539             UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
540 
541         if (eInvFlags == SwPageFrameInvFlags::NONE)
542             return;
543 
544         InvalidatePage( this );
545         if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
546             InvalidatePrt_();
547         if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
548             SetCompletePaint();
549         if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
550             GetNext()->InvalidatePos();
551         if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
552             PrepareHeader();
553         if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
554             PrepareFooter();
555         if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
556             CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
557     } else
558         SwFrame::SwClientNotify(rModify, rHint);
559 }
560 
UpdateAttr_(const SfxPoolItem * pOld,const SfxPoolItem * pNew,SwPageFrameInvFlags & rInvFlags,SwAttrSetChg * pOldSet,SwAttrSetChg * pNewSet)561 void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
562                              SwPageFrameInvFlags &rInvFlags,
563                              SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
564 {
565     bool bClear = true;
566     const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
567     switch( nWhich )
568     {
569         case RES_FMT_CHG:
570         {
571             // state of m_bEmptyPage needs to be determined newly
572             const bool bNewState(GetFormat() == GetFormat()->GetDoc()->GetEmptyPageFormat());
573 
574             if(m_bEmptyPage != bNewState)
575             {
576                 // copy new state
577                 m_bEmptyPage = bNewState;
578 
579                 if(nullptr == GetLower())
580                 {
581                     // if we were an empty page before there is not yet a BodyArea in the
582                     // form of a SwBodyFrame, see constructor
583                     SwViewShell* pSh(getRootFrame()->GetCurrShell());
584                     vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
585                     Calc(pRenderContext); // so that the PrtArea is correct
586                     SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
587                     pBodyFrame->ChgSize(getFramePrintArea().SSize());
588                     pBodyFrame->Paste(this);
589                     pBodyFrame->InvalidatePos();
590                 }
591             }
592 
593             // If the frame format is changed, several things might also change:
594             // 1. columns:
595             assert(pOld && pNew); //FMT_CHG Missing Format
596             const SwFormat *const pOldFormat = static_cast<const SwFormatChg*>(pOld)->pChangedFormat;
597             const SwFormat *const pNewFormat = static_cast<const SwFormatChg*>(pNew)->pChangedFormat;
598             assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
599             const SwFormatCol &rOldCol = pOldFormat->GetCol();
600             const SwFormatCol &rNewCol = pNewFormat->GetCol();
601             if( rOldCol != rNewCol )
602             {
603                 SwLayoutFrame *pB = FindBodyCont();
604                 assert(pB && "Page without Body.");
605                 pB->ChgColumns( rOldCol, rNewCol );
606                 rInvFlags |= SwPageFrameInvFlags::CheckGrid;
607             }
608 
609             // 2. header and footer:
610             const SwFormatHeader &rOldH = pOldFormat->GetHeader();
611             const SwFormatHeader &rNewH = pNewFormat->GetHeader();
612             if( rOldH != rNewH )
613                 rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
614 
615             const SwFormatFooter &rOldF = pOldFormat->GetFooter();
616             const SwFormatFooter &rNewF = pNewFormat->GetFooter();
617             if( rOldF != rNewF )
618                 rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
619             CheckDirChange();
620 
621             [[fallthrough]];
622         }
623         case RES_FRM_SIZE:
624         {
625             const SwRect aOldPageFrameRect( getFrameArea() );
626             SwViewShell *pSh = getRootFrame()->GetCurrShell();
627             if( pSh && pSh->GetViewOptions()->getBrowseMode() )
628             {
629                 setFrameAreaSizeValid(false);
630                 // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
631                 // Calculation of the page is not necessary, because its size is
632                 // invalidated here and further invalidation is done in the
633                 // calling method <SwPageFrame::Modify(..)> and probably by calling
634                 // <SwLayoutFrame::SwClientNotify(..)> at the end.
635                 // It can also causes inconsistences, because the lowers are
636                 // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
637                 // a next page is called. This is performed on the switch to the
638                 // online layout.
639                 //MakeAll();
640             }
641             else if (pNew)
642             {
643                 const SwFormatFrameSize &rSz = nWhich == RES_FMT_CHG ?
644                         static_cast<const SwFormatChg*>(pNew)->pChangedFormat->GetFrameSize() :
645                         static_cast<const SwFormatFrameSize&>(*pNew);
646 
647                 {
648                     SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
649                     aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
650                     aFrm.Width ( std::max( rSz.GetWidth(),  tools::Long(MINLAY) ) );
651                 }
652 
653                 if ( GetUpper() )
654                 {
655                     static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
656                 }
657             }
658             // cleanup Window
659             if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
660             {
661                 // #i9719# - consider border and shadow of
662                 // page frame for determine 'old' rectangle - it's used for invalidating.
663                 const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
664                 SwRect aOldRectWithBorderAndShadow;
665                 SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
666                     IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
667                 pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
668             }
669             rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
670             if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
671                 rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
672         }
673         break;
674 
675         case RES_COL:
676             assert(pOld && pNew); //COL Missing Format
677             if (pOld && pNew)
678             {
679                 SwLayoutFrame *pB = FindBodyCont();
680                 assert(pB); //page without body
681                 pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
682                 rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
683             }
684         break;
685 
686         case RES_HEADER:
687             rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
688             break;
689 
690         case RES_FOOTER:
691             rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
692             break;
693         case RES_TEXTGRID:
694             rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
695             break;
696         case RES_FRAMEDIR :
697             CheckDirChange();
698             break;
699 
700         default:
701             bClear = false;
702     }
703     if ( !bClear )
704         return;
705 
706     if ( pOldSet || pNewSet )
707     {
708         if ( pOldSet )
709             pOldSet->ClearItem( nWhich );
710         if ( pNewSet )
711             pNewSet->ClearItem( nWhich );
712     }
713     else
714     {
715         SwModify aMod;
716         SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
717     }
718 }
719 
720 /// get information from Modify
GetInfo(SfxPoolItem & rInfo) const721 bool SwPageFrame::GetInfo( SfxPoolItem & rInfo ) const
722 {
723     if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
724     {
725         // a page frame exists, so use this one
726         return false;
727     }
728     return true; // continue searching
729 }
730 
SetPageDesc(SwPageDesc * pNew,SwFrameFormat * pFormat)731 void  SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
732 {
733     m_pDesc = pNew;
734     if ( pFormat )
735         SetFrameFormat( pFormat );
736 }
737 
738 /* determine the right PageDesc:
739  *  0.  from the document for footnote and endnote pages
740  *  1.  from the first BodyContent below a page
741  *  2.  from PageDesc of the predecessor page
742  *  3.  from PageDesc of the previous page if blank page
743  *  3.1 from PageDesc of the next page if no predecessor exists
744  *  4.  default PageDesc
745  *  5.  In BrowseMode use the first paragraph or default PageDesc.
746  */
FindPageDesc()747 SwPageDesc *SwPageFrame::FindPageDesc()
748 {
749     // 0.
750     if ( IsFootnotePage() )
751     {
752         SwDoc *pDoc = GetFormat()->GetDoc();
753         if ( IsEndNotePage() )
754             return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
755         else
756             return pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
757     }
758 
759     SwPageDesc *pRet = nullptr;
760 
761     //5.
762     const SwViewShell *pSh = getRootFrame()->GetCurrShell();
763     if( pSh && pSh->GetViewOptions()->getBrowseMode() )
764     {
765         SwContentFrame *pFrame = GetUpper()->ContainsContent();
766         while (pFrame && !pFrame->IsInDocBody())
767             pFrame = pFrame->GetNextContentFrame();
768         if (pFrame)
769         {
770             SwFrame *pFlow = pFrame;
771             if ( pFlow->IsInTab() )
772                 pFlow = pFlow->FindTabFrame();
773             pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
774         }
775         if ( !pRet )
776             pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
777         return pRet;
778     }
779 
780     SwFrame *pFlow = FindFirstBodyContent();
781     if ( pFlow && pFlow->IsInTab() )
782         pFlow = pFlow->FindTabFrame();
783 
784     //1.
785     if ( pFlow )
786     {
787         SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
788         if ( !pTmp->IsFollow() )
789             pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
790     }
791 
792     //3. and 3.1
793     if ( !pRet && IsEmptyPage() )
794             // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
795             // the same page description as its prev, just like after construction
796             // of the empty page.
797         pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
798                GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;
799 
800     //2.
801     if ( !pRet )
802         pRet = GetPrev() ?
803                     static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;
804 
805     //4.
806     if ( !pRet )
807         pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
808 
809     OSL_ENSURE( pRet, "could not find page descriptor." );
810     return pRet;
811 }
812 
813 // Notify if the RootFrame changes its size
AdjustSizeChgNotify(SwRootFrame * pRoot)814 void AdjustSizeChgNotify( SwRootFrame *pRoot )
815 {
816     const bool bOld = pRoot->IsSuperfluous();
817     pRoot->mbCheckSuperfluous = false;
818     if ( pRoot->GetCurrShell() )
819     {
820         for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
821         {
822             if( pRoot == rSh.GetLayout() )
823             {
824                 rSh.SizeChgNotify();
825                 if ( rSh.Imp() )
826                     rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
827             }
828         }
829     }
830     pRoot->mbCheckSuperfluous = bOld;
831 }
832 
SetLastPage(SwPageFrame * pPage)833 inline void SetLastPage( SwPageFrame *pPage )
834 {
835     static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
836 }
837 
Cut()838 void SwPageFrame::Cut()
839 {
840     SwViewShell *pSh = getRootFrame()->GetCurrShell();
841     if ( !IsEmptyPage() )
842     {
843         if ( GetNext() )
844             GetNext()->InvalidatePos();
845 
846         // move Flys whose anchor is on a different page (draw objects are not relevant here)
847         if ( GetSortedObjs() )
848         {
849             size_t i = 0;
850             while ( GetSortedObjs() && i < GetSortedObjs()->size() )
851             {
852                 // #i28701#
853                 SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];
854 
855                 if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
856                 {
857                     SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
858                                 pFly->AnchorFrame()->FindPageFrame() : nullptr;
859                     if ( pAnchPage && (pAnchPage != this) )
860                     {
861                         MoveFly( pFly, pAnchPage );
862                         pFly->InvalidateSize();
863                         pFly->InvalidatePos_();
864                         // Do not increment index, in this case
865                         continue;
866                     }
867                 }
868                 ++i;
869             }
870         }
871         // cleanup Window
872         if ( pSh && pSh->GetWin() )
873             pSh->InvalidateWindows( getFrameArea() );
874     }
875 
876     // decrease the root's page number
877     static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
878     SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
879     if ( pPg )
880     {
881         while ( pPg )
882         {
883             --pPg->m_nPhyPageNum;
884             pPg = static_cast<SwPageFrame*>(pPg->GetNext());
885         }
886     }
887     else
888         ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );
889 
890     SwFrame* pRootFrame = GetUpper();
891 
892     // cut all connections
893     RemoveFromLayout();
894 
895     if ( pRootFrame )
896         static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
897 }
898 
Paste(SwFrame * pParent,SwFrame * pSibling)899 void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
900 {
901     OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
902     OSL_ENSURE( pParent, "No parent for Paste()." );
903     OSL_ENSURE( pParent != this, "I'm my own parent." );
904     OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
905     OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
906             "I am still registered somewhere." );
907 
908     // insert into tree structure
909     InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
910 
911     // increase the root's page number
912     static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
913     if( GetPrev() )
914         SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
915     else
916         SetPhyPageNum( 1 );
917     SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
918     if ( pPg )
919     {
920         while ( pPg )
921         {
922             ++pPg->m_nPhyPageNum;
923             pPg->InvalidatePos_();
924             pPg->InvalidateLayout();
925             pPg = static_cast<SwPageFrame*>(pPg->GetNext());
926         }
927     }
928     else
929         ::SetLastPage( this );
930 
931     if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
932         InvalidateSize_();
933 
934     InvalidatePos();
935 
936     SwViewShell *pSh = getRootFrame()->GetCurrShell();
937     if ( pSh )
938         pSh->SetFirstVisPageInvalid();
939 
940     getRootFrame()->CheckViewLayout( nullptr, nullptr );
941 }
942 
lcl_PrepFlyInCntRegister(SwContentFrame * pFrame)943 static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
944 {
945     pFrame->Prepare( PrepareHint::Register );
946     if( !pFrame->GetDrawObjs() )
947         return;
948 
949     for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
950     {
951         // #i28701#
952         if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
953         {
954             SwContentFrame *pCnt = pFly->ContainsContent();
955             while ( pCnt )
956             {
957                 lcl_PrepFlyInCntRegister( pCnt );
958                 pCnt = pCnt->GetNextContentFrame();
959             }
960         }
961     }
962 }
963 
PrepareRegisterChg()964 void SwPageFrame::PrepareRegisterChg()
965 {
966     SwContentFrame *pFrame = FindFirstBodyContent();
967     while( pFrame )
968     {
969         lcl_PrepFlyInCntRegister( pFrame );
970         pFrame = pFrame->GetNextContentFrame();
971         if( !IsAnLower( pFrame ) )
972             break;
973     }
974     if( !GetSortedObjs() )
975         return;
976 
977     for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
978     {
979         // #i28701#
980         if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
981         {
982             pFrame = pFly->ContainsContent();
983             while ( pFrame )
984             {
985                 ::lcl_PrepFlyInCntRegister( pFrame );
986                 pFrame = pFrame->GetNextContentFrame();
987             }
988         }
989     }
990 }
991 
992 namespace sw {
993 
994 /// check if there's content on the page that requires it to exist
IsPageFrameEmpty(SwPageFrame const & rPage)995 bool IsPageFrameEmpty(SwPageFrame const& rPage)
996 {
997     bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
998     if (bExistEssentialObjs)
999     {
1000         // Only because the page has Flys does not mean that it is needed. If all Flys are
1001         // attached to generic content it is also superfluous (checking DocBody should be enough)
1002         // OD 19.06.2003 - consider that drawing objects in
1003         // header/footer are supported now.
1004         bool bOnlySuperfluousObjs = true;
1005         SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
1006         for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
1007         {
1008             // #i28701#
1009             SwAnchoredObject* pAnchoredObj = rObjs[i];
1010             // do not consider hidden objects
1011             if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
1012                                 pAnchoredObj->GetDrawObj()->GetLayer() ) &&
1013                  !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
1014             {
1015                 bOnlySuperfluousObjs = false;
1016             }
1017         }
1018         bExistEssentialObjs = !bOnlySuperfluousObjs;
1019     }
1020 
1021     // optimization: check first if essential objects exist.
1022     const SwLayoutFrame* pBody = nullptr;
1023     if ( bExistEssentialObjs ||
1024          rPage.FindFootnoteCont() ||
1025          (nullptr != (pBody = rPage.FindBodyCont()) &&
1026             ( pBody->ContainsContent() ||
1027                 // #i47580#
1028                 // Do not delete page if there's an empty tabframe
1029                 // left. I think it might be correct to use ContainsAny()
1030                 // instead of ContainsContent() to cover the empty-table-case,
1031                 // but I'm not fully sure, since ContainsAny() also returns
1032                 // SectionFrames. Therefore I prefer to do it the safe way:
1033               ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
1034     {
1035         return false;
1036     }
1037     else
1038     {
1039         return true;
1040     }
1041 }
1042 
1043 } // namespace sw
1044 
1045 //FIXME: provide missing documentation
1046 /** Check all pages (starting from the given one) if they use the appropriate frame format.
1047  *
1048  * If "wrong" pages are found, try to fix this as simple as possible.
1049  *
1050  * Also delete pages that don't have content on them.
1051  *
1052  * @param pStart        the page from where to start searching
1053  * @param bNotifyFields
1054  * @param ppPrev
1055  */
CheckPageDescs(SwPageFrame * pStart,bool bNotifyFields,SwPageFrame ** ppPrev)1056 void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
1057 {
1058     SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
1059     assert(pStart && "no starting page.");
1060 
1061     SwViewShell *pSh   = pStart->getRootFrame()->GetCurrShell();
1062     SwViewShellImp *pImp  = pSh ? pSh->Imp() : nullptr;
1063 
1064     if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1065     {
1066         pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
1067         SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
1068                   << pStart->GetPhyPageNum() << ")" );
1069         return;
1070     }
1071 
1072     // For the update of page numbering fields, nDocPos provides
1073     // the page position from where invalidation should start.
1074     SwTwips nDocPos  = LONG_MAX;
1075 
1076     SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
1077     SwDoc* pDoc      = pStart->GetFormat()->GetDoc();
1078     const bool bFootnotes = !pDoc->GetFootnoteIdxs().empty();
1079 
1080     SwPageFrame *pPage = pStart;
1081     if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
1082         pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1083     while ( pPage )
1084     {
1085         SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1086         SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
1087 
1088         SwPageDesc *pDesc = pPage->FindPageDesc();
1089         /// page is intentionally empty page
1090         bool bIsEmpty = pPage->IsEmptyPage();
1091         // false for intentionally empty pages, they need additional check
1092         bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
1093         bool bIsOdd = pPage->OnRightPage();
1094         bool bWantOdd = pPage->WannaRightPage();
1095         bool bFirst = pPage->OnFirstPage();
1096         SwFrameFormat *pFormatWish = bWantOdd
1097             ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);
1098 
1099         if ( bIsOdd != bWantOdd ||
1100              pDesc != pPage->GetPageDesc() ||        // wrong Desc
1101              ( pFormatWish != pPage->GetFormat() &&       // wrong format and
1102                ( !bIsEmpty || pFormatWish ) // not blank /empty
1103              )
1104            )
1105         {
1106             // Updating a page might take a while, so check the WaitCursor
1107             if( pImp )
1108                 pImp->CheckWaitCursor();
1109 
1110             // invalidate the field, starting from here
1111             if ( nDocPos == LONG_MAX )
1112                 nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();
1113 
1114             // Cases:
1115             //  1. Empty page should be "normal" page -> remove empty page and take next one
1116             //  2. Empty page should have different descriptor -> change
1117             //  3. Normal page should be empty -> insert empty page if previous page
1118             //     is not empty, otherwise see (6).
1119             //  4. Normal page should have different descriptor -> change
1120             //  5. Normal page should have different format -> change
1121             //  6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc
1122 
1123             if ( bIsEmpty && ( pFormatWish ||          //1.
1124                  ( !bWantOdd && !pPrevPage ) ) )
1125             {
1126                 // Check all cases for the next page, so we don't oscillate empty pages
1127                 // Skip case 1 and 2, as we require a non-empty next page to save the empty page
1128                 // Case 3 is the one we actually want to predict and skip
1129                 // We can skip the empty check of case 3, as we just work on an existing next page
1130                 bool bNextWantOdd;
1131                 SwPageDesc *pNextDesc;
1132                 if ( pNextPage && !pNextPage->IsEmptyPage() &&    //3.
1133                      pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
1134                      pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) )   //4.
1135                 {
1136                     bool bNextFirst = pNextPage->OnFirstPage();
1137                     SwFrameFormat *pNextFormatWish = bNextWantOdd ?   //5.
1138                         pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
1139                     if ( !pNextFormatWish )    // 6.
1140                         pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
1141                     if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
1142                     {
1143                         SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1144                                   << " c: 1+3 - skip next page of p: " << pPage );
1145                         if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
1146                             pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
1147                         // We can skip the next page, as all checks were already done!
1148                         pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
1149                         continue;
1150                     }
1151                 }
1152 
1153                 pPage->Cut();
1154                 bool bUpdatePrev = false;
1155                 if (ppPrev && *ppPrev == pPage)
1156                     bUpdatePrev = true;
1157                 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1158                           << " c: 1 - destroy p: " << pPage );
1159                 SwFrame::DestroyFrame(pPage);
1160                 if ( pStart == pPage )
1161                     pStart = pNextPage;
1162                 pPage = pNextPage;
1163                 if (bUpdatePrev)
1164                     *ppPrev = pNextPage;
1165                 continue;
1166             }
1167             else if ( bIsEmpty && !pFormatWish &&  //2.
1168                       pDesc != pPage->GetPageDesc() )
1169             {
1170                 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1171                           << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
1172                 pPage->SetPageDesc( pDesc, nullptr );
1173             }
1174             else if ( !bIsEmpty &&      //3.
1175                       bIsOdd != bWantOdd &&
1176                       ( ( !pPrevPage && !bWantOdd ) ||
1177                         ( pPrevPage && !pPrevPage->IsEmptyPage() )
1178                       )
1179                     )
1180             {
1181                 if ( pPrevPage )
1182                     pDesc = pPrevPage->GetPageDesc();
1183                 SwPageFrame *pTmp = new SwPageFrame( pDoc->GetEmptyPageFormat(), pRoot, pDesc );
1184                 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1185                           << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
1186                 pTmp->Paste( pRoot, pPage );
1187                 pTmp->PreparePage( false );
1188                 pPage = pTmp;
1189                 isPageFrameEmpty = false; // don't delete it right away!
1190             }
1191             else if ( pPage->GetPageDesc() != pDesc )           //4.
1192             {
1193                 SwPageDesc *pOld = pPage->GetPageDesc();
1194                 pPage->SetPageDesc( pDesc, pFormatWish );
1195                 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1196                           << " c: 4 - set desc + format p: " << pPage
1197                           << " d: " << pDesc << " f: " << pFormatWish );
1198                 if ( bFootnotes )
1199                 {
1200                     // If specific values of the FootnoteInfo are changed, something has to happen.
1201                     // We try to limit the damage...
1202                     // If the page has no FootnoteCont it might be problematic.
1203                     // Let's hope that invalidation is enough.
1204                     SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
1205                     if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
1206                         pCont->InvalidateAll_();
1207                 }
1208             }
1209             else if ( pFormatWish && pPage->GetFormat() != pFormatWish )         //5.
1210             {
1211                 pPage->SetFrameFormat( pFormatWish );
1212                 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1213                           << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
1214             }
1215             else if ( !pFormatWish )                                       //6.
1216             {
1217                 // get format with inverted logic
1218                 pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
1219                 if ( pFormatWish && pPage->GetFormat() != pFormatWish )
1220                 {
1221                     pPage->SetFrameFormat( pFormatWish );
1222                     SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1223                               << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
1224                 }
1225             }
1226 #if OSL_DEBUG_LEVEL > 0
1227             else
1228             {
1229                 OSL_FAIL( "CheckPageDescs, missing solution" );
1230             }
1231 #endif
1232         }
1233         assert(!bIsEmpty || !isPageFrameEmpty);
1234         const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
1235         if (bWantRemovePage && !pPage->IsDeleteForbidden())
1236         {
1237             // It also might be that an empty page is not needed at all.
1238             // However, the algorithm above cannot determine that. It is not needed if the following
1239             // page can live without it. Do obtain that information, we need to dig deeper...
1240             SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
1241             if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
1242             {
1243                 // The following page can find a FrameFormat or has no successor -> empty page not needed
1244                 SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
1245                 if (isPageFrameEmpty && pPage->GetPrev())
1246                 {   // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
1247                     pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
1248                 }
1249                 pPage->Cut();
1250                 bool bUpdatePrev = false;
1251                 if (ppPrev && *ppPrev == pPage)
1252                     bUpdatePrev = true;
1253                 SwFrame::DestroyFrame(pPage);
1254                 SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
1255                 if ( pStart == pPage )
1256                     pStart = pTmp;
1257                 pPage = pTmp;
1258                 if (bUpdatePrev)
1259                     *ppPrev = pTmp;
1260                 continue;
1261             }
1262         }
1263         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1264     }
1265 
1266     pRoot->SetAssertFlyPages();
1267     SwRootFrame::AssertPageFlys( pStart );
1268 
1269     if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
1270     {
1271         SwDocPosUpdate aMsgHint( nDocPos );
1272         pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1273     }
1274 
1275 #if OSL_DEBUG_LEVEL > 0
1276     //1. check if two empty pages are behind one another
1277     bool bEmpty = false;
1278     SwPageFrame *pPg = pStart;
1279     while ( pPg )
1280     {
1281         if ( pPg->IsEmptyPage() )
1282         {
1283             if ( bEmpty )
1284             {
1285                 OSL_FAIL( "double empty pages." );
1286                 break;  // once is enough
1287             }
1288             bEmpty = true;
1289         }
1290         else
1291             bEmpty = false;
1292 
1293         pPg = static_cast<SwPageFrame*>(pPg->GetNext());
1294     }
1295 #endif
1296     SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
1297 }
1298 
1299 namespace
1300 {
isDeleteForbidden(const SwPageFrame * pDel)1301     bool isDeleteForbidden(const SwPageFrame *pDel)
1302     {
1303         if (pDel->IsDeleteForbidden())
1304             return true;
1305         const SwLayoutFrame* pBody = pDel->FindBodyCont();
1306         const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
1307         return pBodyContent && pBodyContent->IsDeleteForbidden();
1308     }
1309 
doInsertPage(SwRootFrame * pRoot,SwPageFrame ** pRefSibling,SwFrameFormat * pFormat,SwPageDesc * pDesc,bool bFootnote,SwPageFrame ** pRefPage)1310     bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
1311                        SwFrameFormat *pFormat, SwPageDesc *pDesc,
1312                        bool bFootnote, SwPageFrame **pRefPage )
1313     {
1314         SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
1315         SwPageFrame *pSibling = *pRefSibling;
1316         if ( pRefPage )
1317         {
1318             *pRefPage = pPage;
1319             SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
1320                                       << " d: " << pDesc << " f: " << pFormat );
1321         }
1322         else
1323             SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
1324                                       << pPage << " d: " << pDesc );
1325         pPage->Paste( pRoot, pSibling );
1326 
1327         SwViewShell* pViewShell = pRoot->GetCurrShell();
1328         if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
1329         {
1330             // Hide-whitespace mode does not shrink the last page, so resize the page that used to
1331             // be the last one.
1332             if (SwFrame* pPrevPage = pPage->GetPrev())
1333             {
1334                 pPrevPage->InvalidateSize();
1335             }
1336         }
1337 
1338         pPage->PreparePage( bFootnote );
1339         // If the sibling has no body text, destroy it as long as it is no footnote page.
1340         if ( pSibling && !pSibling->IsFootnotePage() &&
1341              !pSibling->FindFirstBodyContent() &&
1342              (!pRefPage || !isDeleteForbidden(pSibling)) )
1343         {
1344             pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
1345             return false;
1346         }
1347         return true;
1348     }
1349 }
1350 
InsertPage(SwPageFrame * pPrevPage,bool bFootnote)1351 SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
1352 {
1353     SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
1354     SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
1355     SwPageDesc *pDesc = nullptr;
1356 
1357     // insert right (odd) or left (even) page?
1358     bool bNextRightPage = !pPrevPage->OnRightPage();
1359     bool bWishedRightPage = bNextRightPage;
1360 
1361     // Which PageDesc is relevant?
1362     // For ContentFrame take the one from format if provided,
1363     // otherwise from the Follow of the PrevPage
1364     if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
1365     {
1366         SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
1367         pDesc = rDesc.GetPageDesc();
1368         if ( rDesc.GetNumOffset() )
1369         {
1370             ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
1371             bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
1372             // use the opportunity to set the flag at root
1373             pRoot->SetVirtPageNum( true );
1374         }
1375     }
1376     if ( !pDesc )
1377         pDesc = pPrevPage->GetPageDesc()->GetFollow();
1378 
1379     assert(pDesc && "Missing PageDesc");
1380     if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
1381         bWishedRightPage = !bWishedRightPage;
1382     bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();
1383 
1384     SwDoc *pDoc = pPrevPage->GetFormat()->GetDoc();
1385     bool bCheckPages = false;
1386     // If there is no FrameFormat for this page, create an empty page.
1387     if (bWishedRightPage != bNextRightPage)
1388     {
1389         if( doInsertPage( pRoot, &pSibling, pDoc->GetEmptyPageFormat(),
1390                           pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
1391             bCheckPages = true;
1392     }
1393     SwFrameFormat *const pFormat( bWishedRightPage
1394             ? pDesc->GetRightFormat(bWishedFirst)
1395             : pDesc->GetLeftFormat(bWishedFirst) );
1396     assert(pFormat);
1397     SwPageFrame *pPage = nullptr;
1398     if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
1399         bCheckPages = true;
1400 
1401     if ( pSibling )
1402     {
1403         if ( bCheckPages )
1404         {
1405             CheckPageDescs( pSibling, false );
1406             SwViewShell *pSh = getRootFrame()->GetCurrShell();
1407             SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
1408             if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1409             {
1410                 const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
1411                 if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
1412                 {
1413                     pImp->GetLayAction().SetCheckPageNumDirect(
1414                                                     pSibling->GetPhyPageNum() );
1415                     SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
1416                               << pSibling->GetPhyPageNum() );
1417                 }
1418                 return pPage;
1419             }
1420         }
1421         else
1422             SwRootFrame::AssertPageFlys( pSibling );
1423     }
1424 
1425     // For the update of page numbering fields, nDocPos provides
1426     // the page position from where invalidation should start.
1427     SwViewShell *pSh = getRootFrame()->GetCurrShell();
1428     if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
1429     {
1430         SwDocPosUpdate aMsgHint( pPrevPage->getFrameArea().Top() );
1431         pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1432     }
1433     return pPage;
1434 }
1435 
SidebarPosition() const1436 sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
1437 {
1438     SwViewShell *pSh = getRootFrame()->GetCurrShell();
1439     if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
1440     {
1441         return sw::sidebarwindows::SidebarPosition::RIGHT;
1442     }
1443     else
1444     {
1445         const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
1446         const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
1447         const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());
1448 
1449         return bRightSidebar
1450                ? sw::sidebarwindows::SidebarPosition::RIGHT
1451                : sw::sidebarwindows::SidebarPosition::LEFT;
1452     }
1453 }
1454 
GrowFrame(SwTwips nDist,bool bTst,bool)1455 SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
1456 {
1457     if ( !bTst )
1458     {
1459         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1460         aFrm.AddHeight(nDist );
1461     }
1462 
1463     return nDist;
1464 }
1465 
ShrinkFrame(SwTwips nDist,bool bTst,bool)1466 SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
1467 {
1468     OSL_ENSURE( nDist >= 0, "nDist < 0." );
1469     OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );
1470 
1471     if ( !bTst )
1472     {
1473         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1474         aFrm.AddHeight( -nDist );
1475     }
1476 
1477     return nDist;
1478 }
1479 
RemovePage(SwPageFrame ** pDelRef,SwRemoveResult eResult)1480 void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
1481 {
1482     SwPageFrame *pDel = *pDelRef;
1483     (*pDelRef) = static_cast<SwPageFrame*>(
1484         eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
1485     if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
1486         RemoveFootnotes( pDel, true );
1487     pDel->Cut();
1488     SwFrame::DestroyFrame( pDel );
1489 }
1490 
1491 /// remove pages that are not needed at all
RemoveSuperfluous()1492 void SwRootFrame::RemoveSuperfluous()
1493 {
1494     // A page is empty if the body text area has no ContentFrame, but not if there
1495     // is at least one Fly or one footnote attached to the page. Two runs are
1496     // needed: one for endnote pages and one for the pages of the body text.
1497 
1498     if ( !IsSuperfluous() )
1499         return;
1500     mbCheckSuperfluous = false;
1501 
1502     SwPageFrame *pPage = GetLastPage();
1503     tools::Long nDocPos = LONG_MAX;
1504 
1505     // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
1506     do
1507     {
1508         if (!sw::IsPageFrameEmpty(*pPage))
1509         {
1510             if ( pPage->IsFootnotePage() )
1511             {
1512                 while ( pPage->IsFootnotePage() )
1513                 {
1514                     pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1515                     OSL_ENSURE( pPage, "only endnote pages remain." );
1516                 }
1517                 continue;
1518             }
1519             else
1520                 pPage = nullptr;
1521         }
1522 
1523         if ( pPage )
1524         {
1525             SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
1526             RemovePage( &pPage, SwRemoveResult::Prev );
1527             nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
1528         }
1529     } while ( pPage );
1530 
1531     SwViewShell *pSh = getRootFrame()->GetCurrShell();
1532     if ( nDocPos != LONG_MAX &&
1533          (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
1534     {
1535         SwDocPosUpdate aMsgHint( nDocPos );
1536         GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1537     }
1538 }
1539 
1540 /// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
AssertFlyPages()1541 void SwRootFrame::AssertFlyPages()
1542 {
1543     if ( !IsAssertFlyPages() )
1544         return;
1545     mbAssertFlyPages = false;
1546 
1547     SwDoc *pDoc = GetFormat()->GetDoc();
1548     const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
1549 
1550     // what page targets the "last" Fly?
1551     // note the needed pages in a set
1552     sal_uInt16 nMaxPg(0);
1553     o3tl::sorted_vector< sal_uInt16 > neededPages;
1554     neededPages.reserve(pTable->size());
1555 
1556     for ( size_t i = 0; i < pTable->size(); ++i )
1557     {
1558         const SwFormatAnchor &rAnch = (*pTable)[i]->GetAnchor();
1559         if(!rAnch.GetContentAnchor())
1560         {
1561             const sal_uInt16 nPageNum(rAnch.GetPageNum());
1562 
1563             // calc MaxPage (as before)
1564             nMaxPg = std::max(nMaxPg, nPageNum);
1565 
1566             // note as needed page
1567             neededPages.insert(nPageNum);
1568         }
1569     }
1570 
1571     // How many pages exist at the moment?
1572     // And are there EmptyPages that are needed?
1573     SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
1574     SwPageFrame* pPrevPage(nullptr);
1575     SwPageFrame* pFirstRevivedEmptyPage(nullptr);
1576 
1577     while(pPage) // moved two while-conditions to break-statements (see below)
1578     {
1579         const sal_uInt16 nPageNum(pPage->GetPhyPageNum());
1580 
1581         if(pPage->IsEmptyPage() &&
1582             nullptr != pPrevPage &&
1583             neededPages.find(nPageNum) != neededPages.end())
1584         {
1585             // This is an empty page, but it *is* needed since a SwFrame
1586             // is anchored at it directly. Initially these SwFrames are
1587             // not fully initialized. Need to change the format of this SwFrame
1588             // and let the ::Notify mechanism newly evaluate
1589             // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
1590             // adapted from ::InsertPage (used below), this needs previous page
1591             bool bWishedRightPage(!pPrevPage->OnRightPage());
1592             SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
1593             assert(pDesc && "Missing PageDesc");
1594 
1595             if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
1596             {
1597                 bWishedRightPage = !bWishedRightPage;
1598             }
1599 
1600             bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
1601             SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));
1602 
1603             // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
1604             // m_bEmptyPage, too
1605             pPage->SetFrameFormat(pFormat);
1606 
1607             if(nullptr == pFirstRevivedEmptyPage)
1608             {
1609                 // remember first (lowest) SwPageFrame which needed correction
1610                 pFirstRevivedEmptyPage = pPage;
1611             }
1612         }
1613 
1614         // original while-condition II
1615         if(nullptr == pPage->GetNext())
1616         {
1617             break;
1618         }
1619 
1620         // original while-condition III
1621         if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
1622         {
1623             break;
1624         }
1625 
1626         pPrevPage = pPage;
1627         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1628     }
1629 
1630     if ( nMaxPg > pPage->GetPhyPageNum() )
1631     {
1632         for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
1633             pPage = InsertPage( pPage, false );
1634 
1635         // If the endnote pages are now corrupt, destroy them.
1636         if ( !pDoc->GetFootnoteIdxs().empty() )
1637         {
1638             pPage = static_cast<SwPageFrame*>(Lower());
1639             while ( pPage && !pPage->IsFootnotePage() )
1640                 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1641 
1642             if ( pPage )
1643             {
1644                 SwPageDesc *pTmpDesc = pPage->FindPageDesc();
1645                 bool isRightPage = pPage->OnRightPage();
1646                 if ( pPage->GetFormat() !=
1647                      (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
1648                     RemoveFootnotes( pPage, false, true );
1649             }
1650         }
1651     }
1652 
1653     // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
1654     // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
1655     // which did think until now that these Page(s) are empty.
1656     // After trying to correct myself I found SwRootFrame::AssertPageFlys
1657     // directly below that already does that, so use it.
1658     if(nullptr != pFirstRevivedEmptyPage)
1659     {
1660         AssertPageFlys(pFirstRevivedEmptyPage);
1661     }
1662 
1663     //Remove masters that haven't been replaced yet from the list.
1664     RemoveMasterObjs( mpDrawPage );
1665 
1666 #if OSL_DEBUG_LEVEL > 0
1667     pPage = static_cast<SwPageFrame*>(Lower());
1668     while ( pPage && pPage->GetNext() &&
1669             !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
1670     {
1671         SAL_INFO( "sw.pageframe",  "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1672                    << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1673                    << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1674         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1675     }
1676     SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1677               << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1678               << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1679 #endif
1680 }
1681 
1682 /// Ensure that after the given page all page-bound objects are located on the correct page
AssertPageFlys(SwPageFrame * pPage)1683 void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
1684 {
1685     SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
1686     while ( pPage )
1687     {
1688         if (pPage->GetSortedObjs())
1689         {
1690             size_t i = 0;
1691             while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
1692             {
1693                 // #i28701#
1694                 SwFrameFormat& rFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
1695                 const SwFormatAnchor &rAnch = rFormat.GetAnchor();
1696                 const sal_uInt16 nPg = rAnch.GetPageNum();
1697                 if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
1698                      nPg != pPage->GetPhyPageNum() )
1699                 {
1700                     SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
1701                     // If on the wrong page, check if previous page is empty
1702                     if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
1703                         static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
1704                     {
1705                         // It can move by itself. Just send a modify to its anchor attribute.
1706 #if OSL_DEBUG_LEVEL > 1
1707                         const size_t nCnt = pPage->GetSortedObjs()->size();
1708                         rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1709                         OSL_ENSURE( !pPage->GetSortedObjs() ||
1710                                 nCnt != pPage->GetSortedObjs()->size(),
1711                                 "Object couldn't be reattached!" );
1712 #else
1713                         rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1714 #endif
1715                         // Do not increment index, in this case
1716                         continue;
1717                     }
1718                 }
1719                 ++i;
1720             }
1721         }
1722         pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1723     }
1724     SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
1725 }
1726 
ChgSize(const Size & aNewSize)1727 Size SwRootFrame::ChgSize( const Size& aNewSize )
1728 {
1729     {
1730         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1731         aFrm.SSize(aNewSize);
1732     }
1733 
1734     InvalidatePrt_();
1735     mbFixSize = false;
1736     return getFrameArea().SSize();
1737 }
1738 
MakeAll(vcl::RenderContext *)1739 void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
1740 {
1741     if ( !isFrameAreaPositionValid() )
1742     {
1743         setFrameAreaPositionValid(true);
1744         SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1745         aFrm.Pos().setX(DOCUMENTBORDER);
1746         aFrm.Pos().setY(DOCUMENTBORDER);
1747     }
1748 
1749     if ( !isFramePrintAreaValid() )
1750     {
1751         setFramePrintAreaValid(true);
1752         SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1753         aPrt.Pos().setX(0);
1754         aPrt.Pos().setY(0);
1755         aPrt.SSize( getFrameArea().SSize() );
1756     }
1757 
1758     if ( !isFrameAreaSizeValid() )
1759     {
1760         // SSize is set by the pages (Cut/Paste).
1761         setFrameAreaSizeValid(true);
1762     }
1763 }
1764 
ImplInvalidateBrowseWidth()1765 void SwRootFrame::ImplInvalidateBrowseWidth()
1766 {
1767     mbBrowseWidthValid = false;
1768     SwFrame *pPg = Lower();
1769     while ( pPg )
1770     {
1771         pPg->InvalidateSize();
1772         pPg = pPg->GetNext();
1773     }
1774 }
1775 
ImplCalcBrowseWidth()1776 void SwRootFrame::ImplCalcBrowseWidth()
1777 {
1778     OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
1779             "CalcBrowseWidth and not in BrowseView" );
1780 
1781     // The (minimal) with is determined from borders, tables and paint objects.
1782     // It is calculated based on the attributes. Thus, it is not relevant how wide they are
1783     // currently but only how wide they want to be.
1784     // Frames and paint objects inside other objects (frames, tables) do not count.
1785     // Borders and columns are not taken into account.
1786 
1787     SwFrame *pFrame = ContainsContent();
1788     while ( pFrame && !pFrame->IsInDocBody() )
1789         pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
1790     if ( !pFrame )
1791         return;
1792 
1793     mbBrowseWidthValid = true;
1794     SwViewShell *pSh = getRootFrame()->GetCurrShell();
1795     mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;
1796 
1797     do
1798     {
1799         if ( pFrame->IsInTab() )
1800             pFrame = pFrame->FindTabFrame();
1801 
1802         if ( pFrame->IsTabFrame() &&
1803              !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
1804         {
1805             SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
1806             const SwBorderAttrs &rAttrs = *aAccess.Get();
1807             const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
1808             tools::Long nWidth = rAttrs.GetSize().Width();
1809             if ( nWidth < int(USHRT_MAX)-2000 && //-2k, because USHRT_MAX gets missing while trying to resize!  (and cast to int to avoid -Wsign-compare due to broken USHRT_MAX on Android)
1810                  text::HoriOrientation::FULL != rHori.GetHoriOrient() )
1811             {
1812                 const SwHTMLTableLayout *pLayoutInfo =
1813                     static_cast<const SwTabFrame *>(pFrame)->GetTable()
1814                                             ->GetHTMLTableLayout();
1815                 if ( pLayoutInfo )
1816                     nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );
1817 
1818                 switch ( rHori.GetHoriOrient() )
1819                 {
1820                     case text::HoriOrientation::NONE:
1821                         // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
1822                         nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
1823                         break;
1824                     case text::HoriOrientation::LEFT_AND_WIDTH:
1825                         nWidth += rAttrs.CalcLeft( pFrame );
1826                         break;
1827                     default:
1828                         break;
1829                 }
1830                 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1831             }
1832         }
1833         else if ( pFrame->GetDrawObjs() )
1834         {
1835             for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
1836             {
1837                 // #i28701#
1838                 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
1839                 const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
1840                 const bool bFly = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) !=  nullptr;
1841                 if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
1842                     || rFormat.GetFrameSize().GetWidthPercent())
1843                 {
1844                     continue;
1845                 }
1846 
1847                 tools::Long nWidth = 0;
1848                 switch ( rFormat.GetAnchor().GetAnchorId() )
1849                 {
1850                     case RndStdIds::FLY_AS_CHAR:
1851                         nWidth = bFly ? rFormat.GetFrameSize().GetWidth() :
1852                                         pAnchoredObj->GetObjRect().Width();
1853                         break;
1854                     case RndStdIds::FLY_AT_PARA:
1855                         {
1856                             // #i33170#
1857                             // Reactivated old code because
1858                             // nWidth = pAnchoredObj->GetObjRect().Right()
1859                             // gives wrong results for objects that are still
1860                             // at position FAR_AWAY.
1861                             if ( bFly )
1862                             {
1863                                 nWidth = rFormat.GetFrameSize().GetWidth();
1864                                 const SwFormatHoriOrient &rHori = rFormat.GetHoriOrient();
1865                                 switch ( rHori.GetHoriOrient() )
1866                                 {
1867                                     case text::HoriOrientation::NONE:
1868                                         nWidth += rHori.GetPos();
1869                                         break;
1870                                     case text::HoriOrientation::INSIDE:
1871                                     case text::HoriOrientation::LEFT:
1872                                         if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
1873                                             nWidth += pFrame->getFramePrintArea().Left();
1874                                         break;
1875                                     default:
1876                                         break;
1877                                 }
1878                             }
1879                             else
1880                                 // Paint objects to not have attributes and
1881                                 // are defined by their current size
1882                                 nWidth = pAnchoredObj->GetObjRect().Right() -
1883                                          pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
1884                         }
1885                         break;
1886                     default:    /* do nothing */;
1887                 }
1888                 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1889             }
1890         }
1891         pFrame = pFrame->FindNextCnt();
1892     } while ( pFrame );
1893 }
1894 
StartAllAction()1895 void SwRootFrame::StartAllAction()
1896 {
1897     if ( GetCurrShell() )
1898         for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1899         {
1900             if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1901                 pCursorShell->StartAction();
1902             else
1903                 rSh.StartAction();
1904         }
1905 }
1906 
EndAllAction(bool bVirDev)1907 void SwRootFrame::EndAllAction( bool bVirDev )
1908 {
1909     if ( !GetCurrShell() )
1910         return;
1911 
1912     for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1913     {
1914         const bool bOldEndActionByVirDev = rSh.IsEndActionByVirDev();
1915         rSh.SetEndActionByVirDev( bVirDev );
1916         if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1917         {
1918             pCursorShell->EndAction();
1919             pCursorShell->CallChgLnk();
1920             if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
1921                 pFEShell->SetChainMarker();
1922         }
1923         else
1924             rSh.EndAction();
1925         rSh.SetEndActionByVirDev( bOldEndActionByVirDev );
1926     }
1927 }
1928 
UnoRemoveAllActions()1929 void SwRootFrame::UnoRemoveAllActions()
1930 {
1931     if ( !GetCurrShell() )
1932         return;
1933 
1934     for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1935     {
1936         // #i84729#
1937         // No end action, if <SwViewShell> instance is currently in its end action.
1938         // Recursives calls to <::EndAction()> are not allowed.
1939         if ( !rSh.IsInEndAction() )
1940         {
1941             OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
1942             bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) !=  nullptr;
1943             bool bFE = dynamic_cast<const SwFEShell*>( &rSh) !=  nullptr;
1944             sal_uInt16 nRestore = 0;
1945             while( rSh.ActionCount() )
1946             {
1947                 if( bCursor )
1948                 {
1949                     static_cast<SwCursorShell*>(&rSh)->EndAction();
1950                     static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
1951                     if ( bFE )
1952                         static_cast<SwFEShell*>(&rSh)->SetChainMarker();
1953                 }
1954                 else
1955                     rSh.EndAction();
1956                 nRestore++;
1957             }
1958             rSh.SetRestoreActions(nRestore);
1959         }
1960         rSh.LockView(true);
1961     }
1962 }
1963 
UnoRestoreAllActions()1964 void SwRootFrame::UnoRestoreAllActions()
1965 {
1966     if ( !GetCurrShell() )
1967         return;
1968 
1969     for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1970     {
1971         sal_uInt16 nActions = rSh.GetRestoreActions();
1972         while( nActions-- )
1973         {
1974             if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1975                 pCursorShell->StartAction();
1976             else
1977                 rSh.StartAction();
1978         }
1979         rSh.SetRestoreActions(0);
1980         rSh.LockView(false);
1981     }
1982 }
1983 
1984 // Helper functions for SwRootFrame::CheckViewLayout
1985 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );
1986 
lcl_MoveAllLowerObjs(SwFrame * pFrame,const Point & rOffset)1987 static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
1988 {
1989     const bool bPage = pFrame->IsPageFrame();
1990     const SwSortedObjs* pSortedObj = bPage
1991                         ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
1992                         : pFrame->GetDrawObjs();
1993     if (pSortedObj == nullptr)
1994         return;
1995 
1996     // note: pSortedObj elements may be removed and inserted from
1997     // MoveObjectIfActive(), invalidating iterators
1998     // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
1999     for (size_t i = 0; i < pSortedObj->size(); ++i)
2000     {
2001         SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
2002         const SwFrameFormat& rObjFormat = pAnchoredObj->GetFrameFormat();
2003         const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
2004 
2005         // all except from the as character anchored objects are moved
2006         // when processing the page frame:
2007         if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
2008             continue;
2009 
2010         SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );
2011 
2012         if ( auto pFlyFrame = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
2013         {
2014             lcl_MoveAllLowers( pFlyFrame, rOffset );
2015             // tdf#138785 update position specific to as-char flys
2016             if (pFlyFrame->IsFlyInContentFrame())
2017             {
2018                 static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
2019             }
2020             pFlyFrame->NotifyDrawObj();
2021             // --> let the active embedded object be moved
2022             SwFrame* pLower = pFlyFrame->Lower();
2023             if ( pLower && pLower->IsNoTextFrame() )
2024             {
2025                 SwRootFrame* pRoot = pLower->getRootFrame();
2026                 SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
2027                 if ( pSh )
2028                 {
2029                     SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
2030                     SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
2031                     if ( pNode )
2032                     {
2033                         svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
2034                         if ( xObj.is() )
2035                         {
2036                             for(SwViewShell& rSh : pSh->GetRingContainer())
2037                             {
2038                                 SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
2039                                 if ( pFEShell )
2040                                     pFEShell->MoveObjectIfActive( xObj, rOffset );
2041                             }
2042                         }
2043                     }
2044                 }
2045             }
2046         }
2047         else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
2048         {
2049             // don't touch objects that are not yet positioned:
2050             if ( pAnchoredDrawObj->NotYetPositioned() )
2051                 continue;
2052 
2053             const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
2054             const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
2055             pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
2056             pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );
2057 
2058             // clear contour cache
2059             if ( pAnchoredDrawObj->GetFrameFormat().GetSurround().IsContour() )
2060                 ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
2061         }
2062         // #i92511#
2063         // cache for object rectangle inclusive spaces has to be invalidated.
2064         pAnchoredObj->InvalidateObjRectWithSpaces();
2065     }
2066 }
2067 
lcl_MoveAllLowers(SwFrame * pFrame,const Point & rOffset)2068 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
2069 {
2070     const SwRect aFrame( pFrame->getFrameArea() );
2071 
2072     // first move the current frame
2073     // RotateFlyFrame3: moved to transform_translate instead of
2074     // direct modification to allow the SwFrame evtl. needed own reactions
2075     pFrame->transform_translate(rOffset);
2076 
2077     // Don't forget accessibility:
2078     if( pFrame->IsAccessibleFrame() )
2079     {
2080         SwRootFrame *pRootFrame = pFrame->getRootFrame();
2081         if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
2082             pRootFrame->GetCurrShell() )
2083         {
2084             pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
2085         }
2086     }
2087 
2088     // the move any objects
2089     lcl_MoveAllLowerObjs( pFrame, rOffset );
2090 
2091     // finally, for layout frames we have to call this function recursively:
2092     if ( dynamic_cast< const SwLayoutFrame *>( pFrame ) !=  nullptr )
2093     {
2094         SwFrame* pLowerFrame = pFrame->GetLower();
2095         while ( pLowerFrame )
2096         {
2097             lcl_MoveAllLowers( pLowerFrame, rOffset );
2098             pLowerFrame = pLowerFrame->GetNext();
2099         }
2100     }
2101 }
2102 
2103 // Calculate how the pages have to be positioned
CheckViewLayout(const SwViewOption * pViewOpt,const SwRect * pVisArea)2104 void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
2105 {
2106     SwViewShell* pSh = GetCurrShell();
2107     vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
2108     // #i91432#
2109     // No calculation of page positions, if only an empty page is present.
2110     // This situation occurs when <SwRootFrame> instance is in construction
2111     // and the document contains only left pages.
2112     if ( Lower()->GetNext() == nullptr &&
2113          static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
2114     {
2115         return;
2116     }
2117 
2118     if ( !pVisArea )
2119     {
2120         // no early return for bNewPage
2121         if ( mnViewWidth < 0 )
2122             mnViewWidth = 0;
2123     }
2124     else
2125     {
2126         assert(pViewOpt && "CheckViewLayout required ViewOptions");
2127 
2128         const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
2129         const bool bBookMode = pViewOpt->IsViewLayoutBookMode();
2130 
2131         if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
2132             return;
2133 
2134         mnColumns = nColumns;
2135         mbBookMode = bBookMode;
2136         mnViewWidth = pVisArea->Width();
2137         mbSidebarChanged = false;
2138     }
2139 
2140     if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
2141     {
2142         mnColumns = 1;
2143         mbBookMode = false;
2144     }
2145 
2146     Calc(pRenderContext);
2147 
2148     const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
2149     SetCallbackActionEnabled( false );
2150 
2151     maPageRects.clear();
2152 
2153     const tools::Long nBorder = getFrameArea().Pos().getX();
2154     const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
2155     const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
2156                                            : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
2157                                                   : SwViewOption::defGapBetweenPages);
2158 
2159     // check how many pages fit into the first page layout row:
2160     SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());
2161 
2162     // will contain the number of pages per row. 0 means that
2163     // the page does not fit.
2164     tools::Long nWidthRemain = nVisWidth;
2165 
2166     // after one row has been processed, these variables contain
2167     // the width of the row and the maximum of the page heights
2168     tools::Long nCurrentRowHeight = 0;
2169     tools::Long nCurrentRowWidth = 0;
2170 
2171     // these variables are used to finally set the size of the
2172     // root frame
2173     tools::Long nSumRowHeight = 0;
2174     SwTwips nMinPageLeft = TWIPS_MAX;
2175     SwTwips nMaxPageRight = 0;
2176     SwPageFrame* pStartOfRow = pPageFrame;
2177     sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
2178     bool bFirstRow = true;
2179 
2180     bool bPageChanged = false;
2181     const bool bRTL = !IsLeftToRightViewLayout();
2182     const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );
2183 
2184     while ( pPageFrame )
2185     {
2186         // we consider the current page to be "start of row" if
2187         // 1. it is the first page in the current row or
2188         // 2. it is the second page in the row and the first page is an empty page in non-book view:
2189         const bool bStartOfRow = pPageFrame == pStartOfRow ||
2190                                              ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );
2191 
2192         const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;
2193 
2194         // no half doc border space for first page in each row and
2195         tools::Long nPageWidth = 0;
2196         tools::Long nPageHeight = 0;
2197 
2198         if ( mbBookMode )
2199         {
2200             const SwFrame& rFormatPage = pPageFrame->GetFormatPage();
2201 
2202             nPageWidth  = rFormatPage.getFrameArea().Width()  + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
2203             nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
2204         }
2205         else
2206         {
2207             if ( !pPageFrame->IsEmptyPage() )
2208             {
2209                 nPageWidth  = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
2210                 nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
2211             }
2212         }
2213 
2214         if ( !bEmptyPage )
2215             ++nNumberOfPagesInRow;
2216 
2217         // finish current row if
2218         // 1. in dynamic mode the current page does not fit anymore or
2219         // 2. the current page exceeds the maximum number of columns
2220         bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
2221                             (0 != mnColumns && mnColumns < nNumberOfPagesInRow);
2222 
2223         // make sure that at least one page goes to the current row:
2224         if ( !bRowFinished || bStartOfRow )
2225         {
2226             // current page is allowed to be in current row
2227             nWidthRemain = nWidthRemain - nPageWidth;
2228 
2229             nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
2230             nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );
2231 
2232             pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());
2233 
2234             if ( !pPageFrame )
2235                 bRowFinished = true;
2236         }
2237 
2238         if ( bRowFinished )
2239         {
2240             // pPageFrame now points to the first page in the new row or null
2241             // pStartOfRow points to the first page in the current row
2242 
2243             // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
2244             if ( !pPageFrame && nWidthRemain > 0 )
2245             {
2246                 // find last page in current row:
2247                 const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
2248                 while( pLastPageInCurrentRow->GetNext() )
2249                     pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());
2250 
2251                 if ( pLastPageInCurrentRow->IsEmptyPage() )
2252                     pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());
2253 
2254                 // check how many times the last page would still fit into the remaining space:
2255                 sal_uInt16 nNumberOfVirtualPages = 0;
2256                 const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
2257                 SwTwips nRemain = nWidthRemain;
2258                 SwTwips nVirtualPagesWidth = 0;
2259                 SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;
2260 
2261                 while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
2262                 {
2263                     SwTwips nLastPageWidthWithGap = nLastPageWidth;
2264                     if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
2265                         nLastPageWidthWithGap += nGapBetweenPages;
2266 
2267                     if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
2268                     {
2269                         ++nNumberOfVirtualPages;
2270                         nVirtualPagesWidth += nLastPageWidthWithGap;
2271                     }
2272                     nRemain = nRemain - nLastPageWidthWithGap;
2273                 }
2274 
2275                 nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
2276             }
2277 
2278             // first page in book mode is always special:
2279             if ( bFirstRow && mbBookMode )
2280             {
2281                 // #i88036#
2282                 nCurrentRowWidth +=
2283                     pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2284             }
2285 
2286             // center page if possible
2287             tools::Long nSizeDiff = 0;
2288             if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
2289                 nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;
2290 
2291             // adjust positions of pages in current row
2292             tools::Long nX = nSizeDiff;
2293 
2294             const tools::Long nRowStart = nBorder + nSizeDiff;
2295             const tools::Long nRowEnd   = nRowStart + nCurrentRowWidth;
2296 
2297             if ( bFirstRow && mbBookMode )
2298             {
2299                 // #i88036#
2300                 nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2301             }
2302 
2303             SwPageFrame* pEndOfRow = pPageFrame;
2304             SwPageFrame* pPageToAdjust = pStartOfRow;
2305 
2306             do
2307             {
2308                 const SwPageFrame* pFormatPage = pPageToAdjust;
2309                 if ( mbBookMode )
2310                     pFormatPage = &pPageToAdjust->GetFormatPage();
2311 
2312                 const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
2313                 const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
2314                 const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
2315                 const SwTwips nLeftPageAddOffset = bLeftSidebar ?
2316                                                    nSidebarWidth :
2317                                                    0;
2318 
2319                 Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
2320                 Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );
2321 
2322                 // RTL view layout: Calculate mirrored page position
2323                 if ( bRTL )
2324                 {
2325                     const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
2326                     aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
2327                     aNewPagePosWithLeftOffset = aNewPagePos;
2328                     aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
2329                 }
2330 
2331                 if ( aNewPagePosWithLeftOffset != aOldPagePos )
2332                 {
2333                     lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
2334                     pPageToAdjust->SetCompletePaint();
2335                     bPageChanged = true;
2336                 }
2337 
2338                 // calculate area covered by the current page and store to
2339                 // maPageRects. This is used e.g., for cursor setting
2340                 const bool bFirstColumn = pPageToAdjust == pStartOfRow;
2341                 const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
2342                 const bool bLastRow = !pEndOfRow;
2343 
2344                 nMinPageLeft  = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
2345                 nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));
2346 
2347                 // border of nGapBetweenPages around the current page:
2348                 SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
2349                                              aNewPagePos.getY(),
2350                                              pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
2351                                              nCurrentRowHeight );
2352 
2353                 static const tools::Long nOuterClickDiff = 1000000;
2354 
2355                 // adjust borders for these special cases:
2356                 if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
2357                     aPageRectWithBorders.SubLeft( nOuterClickDiff );
2358                 if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
2359                     aPageRectWithBorders.AddRight( nOuterClickDiff );
2360                 if ( bFirstRow )
2361                     aPageRectWithBorders.SubTop( nOuterClickDiff );
2362                 if ( bLastRow )
2363                     aPageRectWithBorders.AddBottom( nOuterClickDiff );
2364 
2365                 maPageRects.push_back( aPageRectWithBorders );
2366 
2367                 nX = nX + nCurrentPageWidth;
2368                 pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());
2369 
2370                 // distance to next page
2371                 if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
2372                 {
2373                     // in book view, we add the x gap before left (even) pages:
2374                     if ( mbBookMode )
2375                     {
2376                         if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
2377                             nX = nX + nGapBetweenPages;
2378                     }
2379                     else
2380                     {
2381                         // in non-book view, don't add x gap before
2382                         // 1. the last empty page in a row
2383                         // 2. after an empty page
2384                         const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
2385                                                  ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );
2386 
2387                         if  ( !bDontAddGap )
2388                             nX = nX + nGapBetweenPages;
2389                     }
2390                 }
2391             }
2392             while (pPageToAdjust && pPageToAdjust != pEndOfRow);
2393 
2394             // adjust values for root frame size
2395             nSumRowHeight = nSumRowHeight + nCurrentRowHeight;
2396 
2397             // start new row:
2398             nCurrentRowHeight = 0;
2399             nCurrentRowWidth = 0;
2400             pStartOfRow = pEndOfRow;
2401             nWidthRemain = nVisWidth;
2402             nNumberOfPagesInRow = 0;
2403             bFirstRow = false;
2404         } // end row finished
2405     } // end while
2406 
2407     // set size of root frame:
2408     const Size aOldSize( getFrameArea().SSize() );
2409     const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );
2410 
2411     if ( bPageChanged || aNewSize != aOldSize )
2412     {
2413         ChgSize( aNewSize );
2414         ::AdjustSizeChgNotify( this );
2415         Calc(pRenderContext);
2416 
2417         if ( pSh && pSh->GetDoc()->GetDocShell() )
2418         {
2419             pSh->SetFirstVisPageInvalid();
2420             if (bOldCallbackActionEnabled)
2421             {
2422                 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
2423                 pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
2424             }
2425         }
2426     }
2427 
2428     maPagesArea.Pos( getFrameArea().Pos() );
2429     maPagesArea.SSize( aNewSize );
2430     if ( TWIPS_MAX != nMinPageLeft )
2431         maPagesArea.Left_( nMinPageLeft );
2432 
2433     SetCallbackActionEnabled( bOldCallbackActionEnabled );
2434 }
2435 
IsLeftToRightViewLayout() const2436 bool SwRootFrame::IsLeftToRightViewLayout() const
2437 {
2438     // Layout direction determined by layout direction of the first page.
2439     // #i88036#
2440     // Only ask a non-empty page frame for its layout direction
2441     assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
2442     const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
2443     return !rPage.IsRightToLeft() && !rPage.IsVertical();
2444 }
2445 
GetFormatPage() const2446 const SwPageFrame& SwPageFrame::GetFormatPage() const
2447 {
2448     const SwPageFrame* pRet = this;
2449     if ( IsEmptyPage() )
2450     {
2451         pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
2452         // #i88035#
2453         // Typically a right empty page frame has a next non-empty page frame and
2454         // a left empty page frame has a previous non-empty page frame.
2455         // But under certain circumstances this assumption is not true -
2456         // e.g. during insertion of a left page at the end of the document right
2457         // after a left page in an intermediate state a right empty page does not
2458         // have a next page frame.
2459         if ( pRet == nullptr )
2460         {
2461             if ( OnRightPage() )
2462             {
2463                 pRet = static_cast<const SwPageFrame*>( GetPrev() );
2464             }
2465             else
2466             {
2467                 pRet = static_cast<const SwPageFrame*>( GetNext() );
2468             }
2469         }
2470         assert(pRet &&
2471                 "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
2472     }
2473     return *pRet;
2474 }
2475 
IsOverHeaderFooterArea(const Point & rPt,FrameControlType & rControl) const2476 bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
2477 {
2478     tools::Long nUpperLimit = 0;
2479     tools::Long nLowerLimit = 0;
2480     const SwFrame* pFrame = Lower();
2481     while ( pFrame )
2482     {
2483         if ( pFrame->IsBodyFrame() )
2484         {
2485             nUpperLimit = pFrame->getFrameArea().Top();
2486             nLowerLimit = pFrame->getFrameArea().Bottom();
2487         }
2488         else if ( pFrame->IsFootnoteContFrame() )
2489             nLowerLimit = pFrame->getFrameArea().Bottom();
2490 
2491         pFrame = pFrame->GetNext();
2492     }
2493 
2494     SwRect aHeaderArea( getFrameArea().TopLeft(),
2495            Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );
2496 
2497     SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
2498     const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
2499     if ( aHeaderArea.IsInside( rPt ) )
2500     {
2501         if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
2502         {
2503             rControl = FrameControlType::Header;
2504             return true;
2505         }
2506     }
2507     else
2508     {
2509         SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
2510                 Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );
2511 
2512         if ( aFooterArea.IsInside( rPt ) &&
2513              (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
2514         {
2515             rControl = FrameControlType::Footer;
2516             return true;
2517         }
2518     }
2519 
2520     return false;
2521 }
2522 
CheckPageHeightValidForHideWhitespace(SwTwips nDiff)2523 bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
2524 {
2525     SwViewShell* pShell = getRootFrame()->GetCurrShell();
2526     if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
2527     {
2528         // When whitespace is hidden, the page frame has two heights: the
2529         // nominal (defined by the frame format), and the actual (which is
2530         // at most the nominal height, but can be smaller in case there is
2531         // no content for the whole page).
2532         // The layout size is the actual one, but we want to move the
2533         // content frame to a new page only in case it doesn't fit the
2534         // nominal size.
2535         if (nDiff < 0)
2536         {
2537             // Content frame doesn't fit the actual size, check if it fits the nominal one.
2538             const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
2539             const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
2540             tools::Long nWhitespace = rPageSize.getHeight() - getFrameArea().Height();
2541             if (nWhitespace > -nDiff)
2542             {
2543                 // It does: don't move it and invalidate our page frame so
2544                 // that it gets a larger height.
2545                 return false;
2546             }
2547         }
2548     }
2549 
2550     return true;
2551 }
2552 
GetHeaderFrame() const2553 const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
2554 {
2555     const SwFrame* pLowerFrame = Lower();
2556     while (pLowerFrame)
2557     {
2558         if (pLowerFrame->IsHeaderFrame())
2559             return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
2560         pLowerFrame = pLowerFrame->GetNext();
2561     }
2562     return nullptr;
2563 }
2564 
GetFooterFrame() const2565 const SwFooterFrame* SwPageFrame::GetFooterFrame() const
2566 {
2567     const SwFrame* pLowerFrame = Lower();
2568     while (pLowerFrame)
2569     {
2570         if (pLowerFrame->IsFooterFrame())
2571             return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
2572         pLowerFrame = pLowerFrame->GetNext();
2573     }
2574     return nullptr;
2575 }
2576 
GetGridItem(SwPageFrame const * const pPage)2577 SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
2578 {
2579     if (pPage && pPage->HasGrid())
2580     {
2581         SwTextGridItem const& rGridItem(
2582                 pPage->GetPageDesc()->GetMaster().GetTextGrid());
2583         if (GRID_NONE != rGridItem.GetGridType())
2584         {
2585             return &rGridItem;
2586         }
2587     }
2588     return nullptr;
2589 }
2590 
GetGridWidth(SwTextGridItem const & rG,SwDoc const & rDoc)2591 sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
2592 {
2593     return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
2594 }
2595 
2596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2597