/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <config_wasm_strip.h>

#include <comphelper/lok.hxx>
#include <ndole.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <svl/itemiter.hxx>
#include <fmtfsize.hxx>
#include <fmthdft.hxx>
#include <fmtclds.hxx>
#include <fmtpdsc.hxx>
#include <fmtornt.hxx>
#include <fmtsrnd.hxx>
#include <ftninfo.hxx>
#include <frmtool.hxx>
#include <tgrditem.hxx>
#include <viewopt.hxx>
#include <docsh.hxx>
#include <wrtsh.hxx>
#include <view.hxx>
#include <edtwin.hxx>
#include <frameformats.hxx>

#include <viewimp.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <dcontact.hxx>
#include <hints.hxx>
#include <FrameControlsManager.hxx>

#include <ftnidx.hxx>
#include <bodyfrm.hxx>
#include <ftnfrm.hxx>
#include <tabfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <sectfrm.hxx>
#include <layact.hxx>
#include <flyfrms.hxx>
#include <htmltbl.hxx>
#include <pagedesc.hxx>
#include <editeng/frmdiritem.hxx>
#include <sortedobjs.hxx>
#include <calbck.hxx>
#include <txtfly.hxx>
#include <frmatr.hxx>

using namespace ::com::sun::star;

SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
    SwLayoutFrame( pFormat, pSib )
{
    mnFrameType = SwFrameType::Body;
}

void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
{
    // Formatting of the body is too simple, thus, it gets its own format method.
    // Borders etc. are not taken into account here.
    // Width is taken from the PrtArea of the Upper. Height is the height of the
    // PrtArea of the Upper minus any neighbors (for robustness).
    // The PrtArea has always the size of the frame.

    if ( !isFrameAreaSizeValid() )
    {
        SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
        SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
        const SwFrame *pFrame = GetUpper()->Lower();
        while (pFrame)
        {
            if ( pFrame != this )
            {
                if( pFrame->IsVertical() )
                    nWidth -= pFrame->getFrameArea().Width();
                else
                    nHeight -= pFrame->getFrameArea().Height();
            }
            pFrame = pFrame->GetNext();
        }

        if ( nHeight < 0 )
        {
            nHeight = 0;
        }

        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Height( nHeight );

        if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
        {
            aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
        }

        aFrm.Width( nWidth );
    }

    bool bNoGrid = true;
    if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
    {
        SwTextGridItem const*const pGrid(
                GetGridItem(static_cast<SwPageFrame*>(GetUpper())));

        bool bCenterGrid = !GetFormat()->getIDocumentSettingAccess().get(
            DocumentSettingId::MS_WORD_COMP_GRID_METRICS);
        if (pGrid && bCenterGrid)
        {
            bNoGrid = false;
            tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
            SwRectFnSet aRectFnSet(*this);
            tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
            tools::Long nBorder = 0;
            if( SwTextGrid::LinesAndChars == pGrid->GetGridType() )
            {
                //for textgrid refactor
                SwDoc& rDoc = GetFormat()->GetDoc();
                nBorder = nSize % (GetGridWidth(*pGrid, rDoc));
                nSize -= nBorder;
                nBorder /= 2;
            }

            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetPosX( aPrt, nBorder );
            aRectFnSet.SetWidth( aPrt, nSize );

            // Height of body frame:
            nBorder = aRectFnSet.GetHeight(getFrameArea());

            // Number of possible lines in area of body frame:
            tools::Long nNumberOfLines = nBorder / nSum;
            if( nNumberOfLines > pGrid->GetLines() )
                nNumberOfLines = pGrid->GetLines();

            // Space required for nNumberOfLines lines:
            nSize = nNumberOfLines * nSum;
            nBorder -= nSize;
            nBorder /= 2;

            // #i21774# Footnotes and centering the grid does not work together:
            const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc().
                                        GetFootnoteIdxs().empty();

            aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
            aRectFnSet.SetHeight( aPrt, nSize );
        }
    }

    if( bNoGrid )
    {
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
        aPrt.Pos().setX(0);
        aPrt.Pos().setY(0);
        aPrt.Height( getFrameArea().Height() );
        aPrt.Width( getFrameArea().Width() );
    }

    setFrameAreaSizeValid(true);
    setFramePrintAreaValid(true);
}

void SwBodyFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("body"));
    dumpAsXmlAttributes(writer);

    SwLayoutFrame::dumpAsXml(writer);

    (void)xmlTextWriterEndElement(writer);
}

SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
    SwFootnoteBossFrame( pFormat, pSib ),
    m_pDesc( pPgDsc ),
    m_nPhyPageNum( 0 )
{
    SetDerivedVert( false );
    SetDerivedR2L( false );
    if( m_pDesc )
    {
        m_bHasGrid = true;
        SwTextGridItem const*const pGrid(GetGridItem(this));
        if( !pGrid )
            m_bHasGrid = false;
    }
    else
        m_bHasGrid = false;
    SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
                     pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
    mnFrameType = SwFrameType::Page;
    m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
    m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;

    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
    vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;

    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);

        if ( bBrowseMode )
        {
            aFrm.Height( 0 );
            tools::Long nWidth = pSh->VisArea().Width();

            if ( !nWidth )
            {
                nWidth = 5000;     // changes anyway
            }

            aFrm.Width ( nWidth );
        }
        else
        {
            aFrm.SSize( pFormat->GetFrameSize().GetSize() );
        }
    }

    // create and insert body area if it is not a blank page
    SwDoc& rDoc(pFormat->GetDoc());
    m_bEmptyPage = (pFormat == rDoc.GetEmptyPageFormat());

    if(m_bEmptyPage)
    {
        return;
    }

    Calc(pRenderContext); // so that the PrtArea is correct
    SwBodyFrame *pBodyFrame = new SwBodyFrame( rDoc.GetDfltFrameFormat(), this );
    pBodyFrame->ChgSize( getFramePrintArea().SSize() );
    pBodyFrame->Paste( this );
    pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
    pBodyFrame->InvalidatePos();

    if ( bBrowseMode )
        InvalidateSize_();

    // insert header/footer,, but only if active.
    if ( pFormat->GetHeader().IsActive() )
        PrepareHeader();
    if ( pFormat->GetFooter().IsActive() )
        PrepareFooter();

    const SwFormatCol &rCol = pFormat->GetCol();
    if ( rCol.GetNumCols() > 1 )
    {
        const SwFormatCol aOld; //ChgColumns() needs an old value
        pBodyFrame->ChgColumns( aOld, rCol );
    }

}

void SwPageFrame::DestroyImpl()
{
    // Cleanup the header-footer controls in all SwEditWins
    SwViewShell* pSh = getRootFrame()->GetCurrShell();
    if (pSh)
    {
        for (SwViewShell& rSh : pSh->GetRingContainer())
        {
            SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( &rSh );
            if ( pWrtSh )
            {
                SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
                rEditWin.GetFrameControlsManager( ).RemoveControls( this );
            }
        }
    }

    // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
    if (m_pSortedObjs)
    {
        // Objects can be anchored at pages that are before their anchors (why ever...).
        // In such cases, we would access already freed memory.
        for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
        {
            pAnchoredObj->SetPageFrame( nullptr );
        }
        m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
    }

    // prevent access to destroyed pages
    if( GetFormat() && !GetFormat()->GetDoc().IsInDtor() )
    {
        if ( pSh )
        {
            SwViewShellImp *pImp = pSh->Imp();
            pImp->SetFirstVisPageInvalid();
            if ( pImp->IsAction() )
                pImp->GetLayAction().SetAgain(true);
            // #i9719# - retouche area of page
            // including border and shadow area.
            const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
            SwRect aRetoucheRect;
            SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
            pSh->AddPaintRect( aRetoucheRect );
        }
    }

    SwFootnoteBossFrame::DestroyImpl();
}

SwPageFrame::~SwPageFrame()
{
}

void SwPageFrame::CheckGrid( bool bInvalidate )
{
    bool bOld = m_bHasGrid;
    m_bHasGrid = true;
    SwTextGridItem const*const pGrid(GetGridItem(this));
    m_bHasGrid = nullptr != pGrid;
    if( !(bInvalidate || bOld != m_bHasGrid) )
        return;

    SwLayoutFrame* pBody = FindBodyCont();
    if( pBody )
    {
        pBody->InvalidatePrt();
        SwContentFrame* pFrame = pBody->ContainsContent();
        while( pBody->IsAnLower( pFrame ) )
        {
            static_cast<SwTextFrame*>(pFrame)->Prepare();
            pFrame = pFrame->GetNextContentFrame();
        }
    }
    SetCompletePaint();
}

void SwPageFrame::CheckDirection( bool bVert )
{
    SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
    if( bVert )
    {
        if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
        {
            mbVertLR = false;
            mbVertical = false;
        }
        else
        {
            const SwViewShell *pSh = getRootFrame()->GetCurrShell();
            if( pSh && pSh->GetViewOptions()->getBrowseMode() )
            {
                mbVertLR = false;
                mbVertical = false;
            }
            else
            {
                mbVertical = true;

                if(SvxFrameDirection::Vertical_RL_TB == nDir)
                    mbVertLR = false;
                else if(SvxFrameDirection::Vertical_LR_TB==nDir)
                    mbVertLR = true;
            }
        }

        mbInvalidVert = false;
    }
    else
    {
        if( SvxFrameDirection::Horizontal_RL_TB == nDir )
            mbRightToLeft = true;
        else
            mbRightToLeft = false;
        mbInvalidR2L = false;
    }
}

/// create specific Flys for this page and format generic content
static void lcl_FormatLay( SwLayoutFrame *pLay )
{
    vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
    // format all LayoutFrames - no tables, Flys etc.

    SwFrame *pTmp = pLay->Lower();
    // first the low-level ones
    while ( pTmp )
    {
        const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
                           | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FootnoteContainer
                           | SwFrameType::Footnote | SwFrameType::Body;
        if ( pTmp->GetType() & nTypes )
            ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
        pTmp = pTmp->GetNext();
    }
    pLay->Calc(pRenderContext);
}

/// Create Flys or register draw objects
static void lcl_MakeObjs(const sw::FrameFormats<sw::SpzFrameFormat*>& rSpzs, SwPageFrame* pPage)
{
    // formats are in the special table of the document
    const sal_uInt16 nPhyPageNum = pPage->GetPhyPageNum();
    size_t i = 0;
    const size_t nSpzsCnt = rSpzs.size();
    while (i < nSpzsCnt)
    {
        auto pSpz = rSpzs[i];
        const SwFormatAnchor &rAnch = pSpz->GetAnchor();
        if ( rAnch.GetPageNum() == nPhyPageNum )
        {
            if( rAnch.GetAnchorNode() )
            {
                if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
                {
                    SwFormatAnchor aAnch( rAnch );
                    aAnch.SetAnchor( nullptr );
                    pSpz->SetFormatAttr( aAnch );
                }
                else
                {
                    ++i;
                    continue;
                }
            }

            // is it a border or a SdrObject?
            bool bSdrObj = RES_DRAWFRMFMT == pSpz->Which();
            SdrObject *pSdrObj = nullptr;
            if ( bSdrObj  && nullptr == (pSdrObj = pSpz->FindSdrObject()) )
            {
                OSL_FAIL( "DrawObject not found." );
                pSpz->GetDoc().DelFrameFormat( pSpz );
                continue;
            }
            // The object might be anchored to another page, e.g. when inserting
            // a new page due to a page descriptor change. In such cases, the
            // object needs to be moved.
            // In some cases the object is already anchored to the correct page.
            // This will be handled here and does not need to be coded extra.
            SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
            if ( bSdrObj )
            {
                // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
                if (SwDrawContact *pContact =
                            static_cast<SwDrawContact*>(::GetUserCall(pSdrObj)))
                {
                    if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
                    {
                        pDrawVirtObj->RemoveFromWriterLayout();
                        pDrawVirtObj->RemoveFromDrawingPage();
                        pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
                    }
                    else
                    {
                        if ( pContact->GetAnchorFrame() )
                            pContact->DisconnectFromLayout( false );
                        pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
                    }
                }
            }
            else
            {
                SwIterator<SwFlyFrame,SwFormat> aIter( *pSpz );
                SwFlyFrame *pFly = aIter.First();
                if ( pFly)
                {
                    if( pFly->GetAnchorFrame() )
                        pFly->AnchorFrame()->RemoveFly( pFly );
                }
                else
                    pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pSpz), pPg, pPg );
                pPg->AppendFly( pFly );
                ::RegistFlys( pPg, pFly );
            }
        }
        ++i;
    }
}

void SwPageFrame::PreparePage( bool bFootnote )
{
    SetFootnotePage( bFootnote );

    // #i82258#
    // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
    // the side effect, that the content of page header and footer are formatted.
    // For this formatting it is needed that the anchored objects are registered
    // at the <SwPageFrame> instance.
    // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
    ::RegistFlys( this, this );

    if ( Lower() )
    {
                ::lcl_FormatLay( this );
    }

    // Flys and draw objects that are still attached to the document.
    // Footnote pages do not have page-bound Flys!
    // There might be Flys or draw objects that want to be placed on
    // empty pages, however, the empty pages ignore that and the following
    // pages take care of them.
    if ( !bFootnote && !IsEmptyPage() )
    {
        SwDoc& rDoc = GetFormat()->GetDoc();

        if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
            lcl_MakeObjs( *rDoc.GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
        lcl_MakeObjs( *rDoc.GetSpzFrameFormats(), this );
    }
}

void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::SwPageFootnote)
    {
        // currently the savest way:
        static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
        SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
        if(!GetMaxFootnoteHeight())
            SetMaxFootnoteHeight(LONG_MAX);
        SetColMaxFootnoteHeight();
        // here, the page might be destroyed:
        static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
    }
    else if (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
    {
        // a page frame exists, so use this one
        static_cast<const sw::AutoFormatUsedHint&>(rHint).SetUsed();
        return;
    }
    else if (rHint.GetId() == SfxHintId::SwLegacyModify || rHint.GetId() == SfxHintId::SwFormatChange
            || rHint.GetId() == SfxHintId::SwAttrSetChange)
    {
        if(auto pSh = getRootFrame()->GetCurrShell())
            pSh->SetFirstVisPageInvalid();

        SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
        if (rHint.GetId() == SfxHintId::SwLegacyModify)
        {
            auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
            UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
        }
        else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
        {
            auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
            if(pChangeHint->m_pNew)
            {
                const SwAttrSetChg& rOldSetChg = *pChangeHint->m_pOld;
                const SwAttrSetChg& rNewSetChg = *pChangeHint->m_pNew;
                SfxItemIter aOIter(*rOldSetChg.GetChgSet());
                SfxItemIter aNIter(*rNewSetChg.GetChgSet());
                const SfxPoolItem* pOItem = aOIter.GetCurItem();
                const SfxPoolItem* pNItem = aNIter.GetCurItem();
                SwAttrSetChg aOldSet(rOldSetChg);
                SwAttrSetChg aNewSet(rNewSetChg);
                do
                {
                    UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
                    pOItem = aOIter.NextItem();
                    pNItem = aNIter.NextItem();
                } while(pNItem);
                if(aOldSet.Count() || aNewSet.Count())
                    SwLayoutFrame::SwClientNotify(rModify, sw::AttrSetChangeHint(&aOldSet, &aNewSet));
            }
        }
        else // rHint.GetId() == SfxHintId::SwFormatChange
        {
            auto pChangeHint = static_cast<const SwFormatChangeHint*>(&rHint);
            UpdateAttrForFormatChange(pChangeHint->m_pOldFormat, pChangeHint->m_pNewFormat, eInvFlags);
        }

        if (eInvFlags == SwPageFrameInvFlags::NONE)
            return;

        InvalidatePage( this );
        if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
            InvalidatePrt_();
        if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
            SetCompletePaint();
        if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
            GetNext()->InvalidatePos();
        if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
            PrepareHeader();
        if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
            PrepareFooter();
        if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
            CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
    } else
        SwFrame::SwClientNotify(rModify, rHint);
}

void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
                             SwPageFrameInvFlags &rInvFlags,
                             SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
{
    bool bClear = true;
    const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
    switch( nWhich )
    {
        case RES_FRM_SIZE:
        {
            const SwRect aOldPageFrameRect( getFrameArea() );
            SwViewShell *pSh = getRootFrame()->GetCurrShell();
            if( pSh && pSh->GetViewOptions()->getBrowseMode() )
            {
                setFrameAreaSizeValid(false);
                // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
                // Calculation of the page is not necessary, because its size is
                // invalidated here and further invalidation is done in the
                // calling method <SwPageFrame::Modify(..)> and probably by calling
                // <SwLayoutFrame::SwClientNotify(..)> at the end.
                // It can also causes inconsistences, because the lowers are
                // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
                // a next page is called. This is performed on the switch to the
                // online layout.
                //MakeAll();
            }
            else if (pNew)
            {
                const SwFormatFrameSize &rSz = static_cast<const SwFormatFrameSize&>(*pNew);

                {
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
                    aFrm.Width ( std::max( rSz.GetWidth(),  tools::Long(MINLAY) ) );
                }

                if ( GetUpper() )
                {
                    static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
                }
            }
            // cleanup Window
            if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
            {
                // #i9719# - consider border and shadow of
                // page frame for determine 'old' rectangle - it's used for invalidating.
                const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
                SwRect aOldRectWithBorderAndShadow;
                SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
                    IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
                pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
            }
            rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
            if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
                rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
        }
        break;

        case RES_COL:
            assert(pOld && pNew); //COL Missing Format
            if (pOld && pNew)
            {
                SwLayoutFrame *pB = FindBodyCont();
                assert(pB); //page without body
                pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
                rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
            }
        break;

        case RES_HEADER:
            rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
            break;

        case RES_FOOTER:
            rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
            break;
        case RES_TEXTGRID:
            rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
            break;
        case RES_FRAMEDIR :
            CheckDirChange();
            break;

        default:
            bClear = false;
    }
    if ( !bClear )
        return;

    if ( pOldSet || pNewSet )
    {
        if ( pOldSet )
            pOldSet->ClearItem( nWhich );
        if ( pNewSet )
            pNewSet->ClearItem( nWhich );
    }
    else
    {
        SwModify aMod;
        SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
    }
}

void SwPageFrame::UpdateAttrForFormatChange( SwFormat* pOldFormat, SwFormat* pNewFormat,
                             SwPageFrameInvFlags &rInvFlags )
{
    // state of m_bEmptyPage needs to be determined newly
    const bool bNewState(GetFormat() == GetFormat()->GetDoc().GetEmptyPageFormat());

    if(m_bEmptyPage != bNewState)
    {
        // copy new state
        m_bEmptyPage = bNewState;

        if(nullptr == GetLower())
        {
            // if we were an empty page before there is not yet a BodyArea in the
            // form of a SwBodyFrame, see constructor
            SwViewShell* pSh(getRootFrame()->GetCurrShell());
            vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
            Calc(pRenderContext); // so that the PrtArea is correct
            SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
            pBodyFrame->ChgSize(getFramePrintArea().SSize());
            pBodyFrame->Paste(this);
            pBodyFrame->InvalidatePos();
        }
    }

    // If the frame format is changed, several things might also change:
    // 1. columns:
    assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
    const SwFormatCol &rOldCol = pOldFormat->GetCol();
    const SwFormatCol &rNewCol = pNewFormat->GetCol();
    if( rOldCol != rNewCol )
    {
        SwLayoutFrame *pB = FindBodyCont();
        assert(pB && "Page without Body.");
        pB->ChgColumns( rOldCol, rNewCol );
        rInvFlags |= SwPageFrameInvFlags::CheckGrid;
    }

    // 2. header and footer:
    const SwFormatHeader &rOldH = pOldFormat->GetHeader();
    const SwFormatHeader &rNewH = pNewFormat->GetHeader();
    if( rOldH != rNewH )
        rInvFlags |= SwPageFrameInvFlags::PrepareHeader;

    const SwFormatFooter &rOldF = pOldFormat->GetFooter();
    const SwFormatFooter &rNewF = pNewFormat->GetFooter();
    if( rOldF != rNewF )
        rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
    CheckDirChange();

    const SwRect aOldPageFrameRect( getFrameArea() );
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if( pSh && pSh->GetViewOptions()->getBrowseMode() )
    {
        setFrameAreaSizeValid(false);
        // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
        // Calculation of the page is not necessary, because its size is
        // invalidated here and further invalidation is done in the
        // calling method <SwPageFrame::Modify(..)> and probably by calling
        // <SwLayoutFrame::SwClientNotify(..)> at the end.
        // It can also causes inconsistences, because the lowers are
        // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
        // a next page is called. This is performed on the switch to the
        // online layout.
        //MakeAll();
    }
    else if (pNewFormat)
    {
        const SwFormatFrameSize &rSz = pNewFormat->GetFrameSize();

        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
            aFrm.Width ( std::max( rSz.GetWidth(),  tools::Long(MINLAY) ) );
        }

        if ( GetUpper() )
        {
            static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
        }
    }
    // cleanup Window
    if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
    {
        // #i9719# - consider border and shadow of
        // page frame for determine 'old' rectangle - it's used for invalidating.
        const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
        SwRect aOldRectWithBorderAndShadow;
        SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
            IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
        pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
    }
    rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
    if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
        rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;

    SwModify aMod;
    SwLayoutFrame::SwClientNotify(aMod, SwFormatChangeHint(pOldFormat, pNewFormat));
}

void  SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
{
    m_pDesc = pNew;
    if ( pFormat )
        SetFrameFormat( pFormat );
}

/* determine the right PageDesc:
 *  0.  from the document for footnote and endnote pages
 *  1.  from the first BodyContent below a page
 *  2.  from PageDesc of the predecessor page
 *  3.  from PageDesc of the previous page if blank page
 *  3.1 from PageDesc of the next page if no predecessor exists
 *  4.  default PageDesc
 *  5.  In BrowseMode use the first paragraph or default PageDesc.
 */
SwPageDesc *SwPageFrame::FindPageDesc()
{
    // 0.
    if ( IsFootnotePage() )
    {
        SwDoc& rDoc = GetFormat()->GetDoc();
        if ( IsEndNotePage() )
            return rDoc.GetEndNoteInfo().GetPageDesc( rDoc );
        else
            return rDoc.GetFootnoteInfo().GetPageDesc( rDoc );
    }

    SwPageDesc *pRet = nullptr;

    //5.
    const SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if( pSh && pSh->GetViewOptions()->getBrowseMode() )
    {
        SwContentFrame *pFrame = GetUpper()->ContainsContent();
        while (pFrame && !pFrame->IsInDocBody())
            pFrame = pFrame->GetNextContentFrame();
        if (pFrame)
        {
            SwFrame *pFlow = pFrame;
            if ( pFlow->IsInTab() )
                pFlow = pFlow->FindTabFrame();
            pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
        }
        if ( !pRet )
            pRet = &GetFormat()->GetDoc().GetPageDesc( 0 );
        return pRet;
    }

    SwContentFrame* pFirstContent = FindFirstBodyContent();
    while (pFirstContent && pFirstContent->IsInSct()
            && pFirstContent->FindSctFrame()->IsHiddenNow())
    {
        pFirstContent = pFirstContent->GetNextContentFrame();
    }
    SwFrame* pFlow = pFirstContent;
    if ( pFlow && pFlow->IsInTab() )
        pFlow = pFlow->FindTabFrame();

    //1.
    if ( pFlow )
    {
        SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
        if ( !pTmp->IsFollow() )
            pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
    }

    //3. and 3.1
    if ( !pRet && IsEmptyPage() )
            // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
            // the same page description as its prev, just like after construction
            // of the empty page.
        pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
               GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;

    //2.
    if ( !pRet )
        pRet = GetPrev() ?
                    static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;

    //4.
    if ( !pRet )
        pRet = &GetFormat()->GetDoc().GetPageDesc( 0 );

    OSL_ENSURE( pRet, "could not find page descriptor." );
    return pRet;
}

// Notify if the RootFrame changes its size
void AdjustSizeChgNotify( SwRootFrame *pRoot )
{
    const bool bOld = pRoot->IsSuperfluous();
    pRoot->mbCheckSuperfluous = false;
    if ( pRoot->GetCurrShell() )
    {
        for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
        {
            if( pRoot == rSh.GetLayout() )
            {
                rSh.SizeChgNotify();
                if ( rSh.Imp() )
                    rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
            }
        }
    }
    pRoot->mbCheckSuperfluous = bOld;
}

inline void SetLastPage( SwPageFrame *pPage )
{
    static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
}

void SwPageFrame::Cut()
{
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if ( !IsEmptyPage() )
    {
        if ( GetNext() )
            GetNext()->InvalidatePos();

        // move Flys whose anchor is on a different page (draw objects are not relevant here)
        if ( GetSortedObjs() )
        {
            size_t i = 0;
            while ( GetSortedObjs() && i < GetSortedObjs()->size() )
            {
                // #i28701#
                SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];

                if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
                {
                    SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
                                pFly->AnchorFrame()->FindPageFrame() : nullptr;
                    if ( pAnchPage && (pAnchPage != this) )
                    {
                        MoveFly( pFly, pAnchPage );
                        pFly->InvalidateSize();
                        pFly->InvalidatePos_();
                        // Do not increment index, in this case
                        continue;
                    }
                }
                ++i;
            }
        }
        // cleanup Window
        if ( pSh && pSh->GetWin() )
            pSh->InvalidateWindows( getFrameArea() );
    }

    // decrease the root's page number
    static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
    SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
    if ( pPg )
    {
        while ( pPg )
        {
            --pPg->m_nPhyPageNum;
            pPg = static_cast<SwPageFrame*>(pPg->GetNext());
        }
    }
    else
        ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );

    SwFrame* pRootFrame = GetUpper();

    // cut all connections
    RemoveFromLayout();

    if ( pRootFrame )
        static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
}

void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
{
    OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
    assert(pParent && "No parent for Paste().");
    OSL_ENSURE( pParent != this, "I'm my own parent." );
    OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
    OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
            "I am still registered somewhere." );

    // insert into tree structure
    InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );

    // increase the root's page number
    static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
    if( GetPrev() )
        SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
    else
        SetPhyPageNum( 1 );
    SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
    if ( pPg )
    {
        while ( pPg )
        {
            ++pPg->m_nPhyPageNum;
            pPg->InvalidatePos_();
            pPg->InvalidateLayout();
            pPg = static_cast<SwPageFrame*>(pPg->GetNext());
        }
    }
    else
        ::SetLastPage( this );

    if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
        InvalidateSize_();

    InvalidatePos();

    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if ( pSh )
        pSh->SetFirstVisPageInvalid();

    getRootFrame()->CheckViewLayout( nullptr, nullptr );
}

static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
{
    pFrame->Prepare( PrepareHint::Register );
    if( !pFrame->GetDrawObjs() )
        return;

    for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
    {
        // #i28701#
        if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
        {
            SwContentFrame *pCnt = pFly->ContainsContent();
            while ( pCnt )
            {
                lcl_PrepFlyInCntRegister( pCnt );
                pCnt = pCnt->GetNextContentFrame();
            }
        }
    }
}

void SwPageFrame::PrepareRegisterChg()
{
    SwContentFrame *pFrame = FindFirstBodyContent();
    while( pFrame )
    {
        lcl_PrepFlyInCntRegister( pFrame );
        pFrame = pFrame->GetNextContentFrame();
        if( !IsAnLower( pFrame ) )
            break;
    }
    if( !GetSortedObjs() )
        return;

    for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
    {
        // #i28701#
        if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
        {
            pFrame = pFly->ContainsContent();
            while ( pFrame )
            {
                ::lcl_PrepFlyInCntRegister( pFrame );
                pFrame = pFrame->GetNextContentFrame();
            }
        }
    }
}

namespace sw {

/// check if there's content on the page that requires it to exist
bool IsPageFrameEmpty(SwPageFrame const& rPage)
{
    bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
    if (bExistEssentialObjs)
    {
        // Only because the page has Flys does not mean that it is needed. If all Flys are
        // attached to generic content it is also superfluous (checking DocBody should be enough)
        // OD 19.06.2003 - consider that drawing objects in
        // header/footer are supported now.
        bool bOnlySuperfluousObjs = true;
        SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
        for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
        {
            // #i28701#
            SwAnchoredObject* pAnchoredObj = rObjs[i];
            // do not consider hidden objects
            if ( rPage.GetFormat()->GetDoc().getIDocumentDrawModelAccess().IsVisibleLayerId(
                                pAnchoredObj->GetDrawObj()->GetLayer() ) &&
                 !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
            {
                bOnlySuperfluousObjs = false;
            }
        }
        bExistEssentialObjs = !bOnlySuperfluousObjs;
    }

    // optimization: check first if essential objects exist.
    if (bExistEssentialObjs)
        return false;
    else if (rPage.FindFootnoteCont())
        return false;
    else
    {
        if (const SwLayoutFrame* pBody = rPage.FindBodyCont())
        {
            const SwFrame* pLower = pBody->Lower();
            if ( pBody->ContainsContent() ||
              // check for section frames that are being formatted on the stack
              rPage.ContainsDeleteForbiddenLayFrame() ||
                // #i47580#
                // Do not delete page if there's an empty tabframe
                // left. I think it might be correct to use ContainsAny()
                // instead of ContainsContent() to cover the empty-table-case,
                // but I'm not fully sure, since ContainsAny() also returns
                // SectionFrames. Therefore I prefer to do it the safe way:
              ( pLower && pLower->IsTabFrame() ) )
                return false;
        }
    }

    return true;
}

} // namespace sw

//FIXME: provide missing documentation
/** Check all pages (starting from the given one) if they use the appropriate frame format.
 *
 * If "wrong" pages are found, try to fix this as simple as possible.
 *
 * Also delete pages that don't have content on them.
 *
 * @param pStart        the page from where to start searching
 * @param bNotifyFields
 * @param ppPrev
 */
void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
{
    SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
    assert(pStart && "no starting page.");

    SwViewShell *pSh   = pStart->getRootFrame()->GetCurrShell();
    SwViewShellImp *pImp  = pSh ? pSh->Imp() : nullptr;

    if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
    {
        pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
        SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
                  << pStart->GetPhyPageNum() << ")" );
        return;
    }

    // For the update of page numbering fields, nDocPos provides
    // the page position from where invalidation should start.
    SwTwips nDocPos  = LONG_MAX;

    SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
    SwDoc& rDoc      = pStart->GetFormat()->GetDoc();
    const bool bFootnotes = !rDoc.GetFootnoteIdxs().empty();

    SwPageFrame *pPage = pStart;
    if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
        pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
    while ( pPage )
    {
        SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
        SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());

        SwPageDesc *pDesc = pPage->FindPageDesc();
        /// page is intentionally empty page
        bool bIsEmpty = pPage->IsEmptyPage();
        // false for intentionally empty pages, they need additional check
        bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
        bool bIsOdd = pPage->OnRightPage();
        bool bWantOdd = pPage->WannaRightPage();
        bool bFirst = pPage->OnFirstPage();
        SwFrameFormat *pFormatWish = bWantOdd
            ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);

        if ( bIsOdd != bWantOdd ||
             pDesc != pPage->GetPageDesc() ||        // wrong Desc
             ( pFormatWish != pPage->GetFormat() &&       // wrong format and
               ( !bIsEmpty || pFormatWish ) // not blank /empty
             )
           )
        {
            // Updating a page might take a while, so check the WaitCursor
            if( pImp )
                pImp->CheckWaitCursor();

            // invalidate the field, starting from here
            if ( nDocPos == LONG_MAX )
                nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();

            // Cases:
            //  1. Empty page should be "normal" page -> remove empty page and take next one
            //  2. Empty page should have different descriptor -> change
            //  3. Normal page should be empty -> insert empty page if previous page
            //     is not empty, otherwise see (6).
            //  4. Normal page should have different descriptor -> change
            //  5. Normal page should have different format -> change
            //  6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc

            if ( bIsEmpty && ( pFormatWish ||          //1.
                 ( !bWantOdd && !pPrevPage ) ) )
            {
                // Check all cases for the next page, so we don't oscillate empty pages
                // Skip case 1 and 2, as we require a non-empty next page to save the empty page
                // Case 3 is the one we actually want to predict and skip
                // We can skip the empty check of case 3, as we just work on an existing next page
                bool bNextWantOdd;
                SwPageDesc *pNextDesc;
                if ( pNextPage && !pNextPage->IsEmptyPage() &&    //3.
                     pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
                     pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) )   //4.
                {
                    bool bNextFirst = pNextPage->OnFirstPage();
                    SwFrameFormat *pNextFormatWish = bNextWantOdd ?   //5.
                        pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
                    if ( !pNextFormatWish )    // 6.
                        pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
                    if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
                    {
                        SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                                  << " c: 1+3 - skip next page of p: " << pPage );
                        if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
                            pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
                        // We can skip the next page, as all checks were already done!
                        pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
                        continue;
                    }
                }

                pPage->Cut();
                bool bUpdatePrev = false;
                if (ppPrev && *ppPrev == pPage)
                    bUpdatePrev = true;
                SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                          << " c: 1 - destroy p: " << pPage );
                SwFrame::DestroyFrame(pPage);
                if ( pStart == pPage )
                    pStart = pNextPage;
                pPage = pNextPage;
                if (bUpdatePrev)
                    *ppPrev = pNextPage;
                continue;
            }
            else if ( bIsEmpty && !pFormatWish &&  //2.
                      pDesc != pPage->GetPageDesc() )
            {
                SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                          << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
                pPage->SetPageDesc( pDesc, nullptr );
            }
            else if ( !bIsEmpty &&      //3.
                      bIsOdd != bWantOdd &&
                      ( ( !pPrevPage && !bWantOdd ) ||
                        ( pPrevPage && !pPrevPage->IsEmptyPage() )
                      )
                    )
            {
                if ( pPrevPage )
                    pDesc = pPrevPage->GetPageDesc();
                SwPageFrame *pTmp = new SwPageFrame( rDoc.GetEmptyPageFormat(), pRoot, pDesc );
                SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                          << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
                pTmp->Paste( pRoot, pPage );
                pTmp->PreparePage( false );
                pPage = pTmp;
                isPageFrameEmpty = false; // don't delete it right away!
            }
            else if ( pPage->GetPageDesc() != pDesc )           //4.
            {
                SwPageDesc *pOld = pPage->GetPageDesc();
                pPage->SetPageDesc( pDesc, pFormatWish );
                SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                          << " c: 4 - set desc + format p: " << pPage
                          << " d: " << pDesc << " f: " << pFormatWish );
                if ( bFootnotes )
                {
                    // If specific values of the FootnoteInfo are changed, something has to happen.
                    // We try to limit the damage...
                    // If the page has no FootnoteCont it might be problematic.
                    // Let's hope that invalidation is enough.
                    SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
                    if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
                        pCont->InvalidateAll_();
                }
            }
            else if ( pFormatWish && pPage->GetFormat() != pFormatWish )         //5.
            {
                pPage->SetFrameFormat( pFormatWish );
                SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                          << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
            }
            else if ( !pFormatWish )                                       //6.
            {
                // get format with inverted logic
                pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
                if ( pFormatWish && pPage->GetFormat() != pFormatWish )
                {
                    pPage->SetFrameFormat( pFormatWish );
                    SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
                              << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
                }
            }
#if OSL_DEBUG_LEVEL > 0
            else
            {
                OSL_FAIL( "CheckPageDescs, missing solution" );
            }
#endif
        }
        assert(!bIsEmpty || !isPageFrameEmpty);
        const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
        if (bWantRemovePage && !pPage->IsDeleteForbidden())
        {
            // It also might be that an empty page is not needed at all.
            // However, the algorithm above cannot determine that. It is not needed if the following
            // page can live without it. Do obtain that information, we need to dig deeper...
            SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
            if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
            {
                // The following page can find a FrameFormat or has no successor -> empty page not needed
                SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
                if (isPageFrameEmpty && pPage->GetPrev())
                {   // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
                    pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
                }
                pPage->Cut();
                bool bUpdatePrev = false;
                if (ppPrev && *ppPrev == pPage)
                    bUpdatePrev = true;
                SwFrame::DestroyFrame(pPage);
                SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
                if ( pStart == pPage )
                    pStart = pTmp;
                pPage = pTmp;
                if (bUpdatePrev)
                    *ppPrev = pTmp;
                continue;
            }
        }
        pPage = static_cast<SwPageFrame*>(pPage->GetNext());
    }

    pRoot->SetAssertFlyPages();
    SwRootFrame::AssertPageFlys( pStart );

    if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
    {
        rDoc.getIDocumentFieldsAccess().UpdatePageFields(nDocPos);
    }

#if OSL_DEBUG_LEVEL > 0
    //1. check if two empty pages are behind one another
    bool bEmpty = false;
    SwPageFrame *pPg = pStart;
    while ( pPg )
    {
        if ( pPg->IsEmptyPage() )
        {
            if ( bEmpty )
            {
                OSL_FAIL( "double empty pages." );
                break;  // once is enough
            }
            bEmpty = true;
        }
        else
            bEmpty = false;

        pPg = static_cast<SwPageFrame*>(pPg->GetNext());
    }
#endif
    SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
}

namespace
{
    bool isDeleteForbidden(const SwPageFrame *pDel)
    {
        if (pDel->IsDeleteForbidden())
            return true;
        const SwLayoutFrame* pBody = pDel->FindBodyCont();
        const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
        return pBodyContent && pBodyContent->IsDeleteForbidden();
    }

    bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
                       SwFrameFormat *pFormat, SwPageDesc *pDesc,
                       bool bFootnote, SwPageFrame **pRefPage )
    {
        SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
        SwPageFrame *pSibling = *pRefSibling;
        if ( pRefPage )
        {
            *pRefPage = pPage;
            SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
                                      << " d: " << pDesc << " f: " << pFormat );
        }
        else
            SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
                                      << pPage << " d: " << pDesc );
        pPage->Paste( pRoot, pSibling );

        SwViewShell* pViewShell = pRoot->GetCurrShell();
        if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
        {
            // Hide-whitespace mode does not shrink the last page, so resize the page that used to
            // be the last one.
            if (SwFrame* pPrevPage = pPage->GetPrev())
            {
                pPrevPage->InvalidateSize();
            }
        }

        pPage->PreparePage( bFootnote );
        // If the sibling has no body text, destroy it as long as it is no footnote page.
        if (!pSibling)
            return true;
        if (pSibling->IsFootnotePage())
            return true;
        if (pSibling->FindFirstBodyContent())
            return true;

        if (!pRefPage || !isDeleteForbidden(pSibling))
        {
            pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
            return false;
        }

        return true;
    }
}

SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
{
    SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
    SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
    SwPageDesc *pDesc = nullptr;

    // insert right (odd) or left (even) page?
    bool bNextRightPage = !pPrevPage->OnRightPage();
    bool bWishedRightPage = bNextRightPage;

    // Which PageDesc is relevant?
    // For ContentFrame take the one from format if provided,
    // otherwise from the Follow of the PrevPage
    if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
    {
        SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
        pDesc = rDesc.GetPageDesc();
        if ( rDesc.GetNumOffset() )
        {
            ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
            bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
            // use the opportunity to set the flag at root
            pRoot->SetVirtPageNum( true );
        }
    }
    if ( !pDesc )
        pDesc = pPrevPage->GetPageDesc()->GetFollow();

    assert(pDesc && "Missing PageDesc");
    if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
        bWishedRightPage = !bWishedRightPage;
    bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();

    SwDoc& rDoc = pPrevPage->GetFormat()->GetDoc();
    bool bCheckPages = false;
    // If there is no FrameFormat for this page, create an empty page.
    if (bWishedRightPage != bNextRightPage)
    {
        if( doInsertPage( pRoot, &pSibling, rDoc.GetEmptyPageFormat(),
                          pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
            bCheckPages = true;
    }
    SwFrameFormat *const pFormat( bWishedRightPage
            ? pDesc->GetRightFormat(bWishedFirst)
            : pDesc->GetLeftFormat(bWishedFirst) );
    assert(pFormat);
    SwPageFrame *pPage = nullptr;
    if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
        bCheckPages = true;

    if ( pSibling )
    {
        if ( bCheckPages )
        {
            CheckPageDescs( pSibling, false );
            SwViewShell *pSh = getRootFrame()->GetCurrShell();
            SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
            if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
            {
                const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
                if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
                {
                    pImp->GetLayAction().SetCheckPageNumDirect(
                                                    pSibling->GetPhyPageNum() );
                    SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
                              << pSibling->GetPhyPageNum() );
                }
                return pPage;
            }
        }
        else
            SwRootFrame::AssertPageFlys( pSibling );
    }

    // For the update of page numbering fields, nDocPos provides
    // the page position from where invalidation should start.
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
    {
        rDoc.getIDocumentFieldsAccess().UpdatePageFields(pPrevPage->getFrameArea().Top());
    }
    return pPage;
}

sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
{
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
    {
        return sw::sidebarwindows::SidebarPosition::RIGHT;
    }
    else
    {
        const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
        const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
        const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());

        return bRightSidebar
               ? sw::sidebarwindows::SidebarPosition::RIGHT
               : sw::sidebarwindows::SidebarPosition::LEFT;
    }
}

SwTwips SwRootFrame::GrowFrame(SwTwips nDist, SwResizeLimitReason& reason, bool bTst, bool)
{
    if ( !bTst )
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.AddHeight(nDist );
    }
    reason = SwResizeLimitReason::Unspecified;
    return nDist;
}

SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
{
    OSL_ENSURE( nDist >= 0, "nDist < 0." );
    OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );

    if ( !bTst )
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.AddHeight( -nDist );
    }

    return nDist;
}

void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
{
    SwPageFrame *pDel = *pDelRef;
    (*pDelRef) = static_cast<SwPageFrame*>(
        eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
    if ( !GetFormat()->GetDoc().GetFootnoteIdxs().empty() )
        RemoveFootnotes( pDel, true );
    pDel->Cut();
    SwFrame::DestroyFrame( pDel );
}

/// remove pages that are not needed at all
void SwRootFrame::RemoveSuperfluous()
{
    // A page is empty if the body text area has no ContentFrame, but not if there
    // is at least one Fly or one footnote attached to the page. Two runs are
    // needed: one for endnote pages and one for the pages of the body text.

    if ( !IsSuperfluous() )
        return;
    mbCheckSuperfluous = false;

    SwPageFrame *pPage = GetLastPage();
    tools::Long nDocPos = LONG_MAX;

    // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
    do
    {
        if (!sw::IsPageFrameEmpty(*pPage))
        {
            if ( pPage->IsFootnotePage() )
            {
                while ( pPage->IsFootnotePage() )
                {
                    pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
                    assert(pPage && "only endnote pages remain.");
                }
                continue;
            }
            else
                pPage = nullptr;
        }

        if ( pPage )
        {
            SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
            RemovePage( &pPage, SwRemoveResult::Prev );
            nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
        }
    } while ( pPage );

    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    if ( nDocPos != LONG_MAX &&
         (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
    {
        GetFormat()->GetDoc().getIDocumentFieldsAccess().UpdatePageFields(nDocPos);
    }
}

/// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
void SwRootFrame::AssertFlyPages()
{
    if ( !IsAssertFlyPages() )
        return;
    mbAssertFlyPages = false;

    SwDoc& rDoc = GetFormat()->GetDoc();
    const sw::SpzFrameFormats* pSpzs = rDoc.GetSpzFrameFormats();

    // what page targets the "last" Fly?
    // note the needed pages in a set
    sal_uInt16 nMaxPg(0);
    o3tl::sorted_vector< sal_uInt16 > neededPages;
    neededPages.reserve(pSpzs->size());

    for(auto pSpz: *pSpzs )
    {
        const SwFormatAnchor &rAnch = pSpz->GetAnchor();
        if(!rAnch.GetAnchorNode())
        {
            const sal_uInt16 nPageNum(rAnch.GetPageNum());

            // calc MaxPage (as before)
            nMaxPg = std::max(nMaxPg, nPageNum);

            // note as needed page
            neededPages.insert(nPageNum);
        }
    }

    // How many pages exist at the moment?
    // And are there EmptyPages that are needed?
    SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
    SwPageFrame* pPrevPage(nullptr);
    SwPageFrame* pFirstRevivedEmptyPage(nullptr);

    while(pPage) // moved two while-conditions to break-statements (see below)
    {
        const sal_uInt16 nPageNum(pPage->GetPhyPageNum());

        if(pPage->IsEmptyPage() &&
            nullptr != pPrevPage &&
            neededPages.find(nPageNum) != neededPages.end())
        {
            // This is an empty page, but it *is* needed since a SwFrame
            // is anchored at it directly. Initially these SwFrames are
            // not fully initialized. Need to change the format of this SwFrame
            // and let the ::Notify mechanism newly evaluate
            // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
            // adapted from ::InsertPage (used below), this needs previous page
            bool bWishedRightPage(!pPrevPage->OnRightPage());
            SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
            assert(pDesc && "Missing PageDesc");

            if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
            {
                bWishedRightPage = !bWishedRightPage;
            }

            bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
            SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));

            // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
            // m_bEmptyPage, too
            pPage->SetFrameFormat(pFormat);

            if(nullptr == pFirstRevivedEmptyPage)
            {
                // remember first (lowest) SwPageFrame which needed correction
                pFirstRevivedEmptyPage = pPage;
            }
        }

        // original while-condition II
        if(nullptr == pPage->GetNext())
        {
            break;
        }

        // original while-condition III
        if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
        {
            break;
        }

        pPrevPage = pPage;
        pPage = static_cast<SwPageFrame*>(pPage->GetNext());
    }

    assert(pPage);

    if ( nMaxPg > pPage->GetPhyPageNum() )
    {
        for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
            pPage = InsertPage( pPage, false );

        // If the endnote pages are now corrupt, destroy them.
        if ( !rDoc.GetFootnoteIdxs().empty() )
        {
            pPage = static_cast<SwPageFrame*>(Lower());
            while ( pPage && !pPage->IsFootnotePage() )
                pPage = static_cast<SwPageFrame*>(pPage->GetNext());

            if ( pPage )
            {
                SwPageDesc *pTmpDesc = pPage->FindPageDesc();
                bool isRightPage = pPage->OnRightPage();
                if ( pPage->GetFormat() !=
                     (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
                    RemoveFootnotes( pPage, false, true );
            }
        }
    }

    // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
    // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
    // which did think until now that these Page(s) are empty.
    // After trying to correct myself I found SwRootFrame::AssertPageFlys
    // directly below that already does that, so use it.
    if(nullptr != pFirstRevivedEmptyPage)
    {
        AssertPageFlys(pFirstRevivedEmptyPage);
    }

    //Remove masters that haven't been replaced yet from the list.
    RemoveMasterObjs( mpDrawPage );

#if OSL_DEBUG_LEVEL > 0
    pPage = static_cast<SwPageFrame*>(Lower());
    while ( pPage->GetNext() &&
            !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
    {
        SAL_INFO( "sw.pageframe",  "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
                   << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
                   << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
        pPage = static_cast<SwPageFrame*>(pPage->GetNext());
    }
    SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
              << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
              << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
#endif
}

/// Ensure that after the given page all page-bound objects are located on the correct page
void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
{
    SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
    while ( pPage )
    {
        if (pPage->GetSortedObjs())
        {
            size_t i = 0;
            while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
            {
                // #i28701#
                SwFrameFormat* pFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
                const SwFormatAnchor &rAnch = pFormat->GetAnchor();
                const sal_uInt16 nPg = rAnch.GetPageNum();
                if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
                     nPg != pPage->GetPhyPageNum() )
                {
                    SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
                    // If on the wrong page, check if previous page is empty
                    if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
                        static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
                    {
                        // It can move by itself. Just send a modify to its anchor attribute.
#if OSL_DEBUG_LEVEL > 1
                        const size_t nCnt = pPage->GetSortedObjs()->size();
                        pFormat->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
                        OSL_ENSURE( !pPage->GetSortedObjs() ||
                                nCnt != pPage->GetSortedObjs()->size(),
                                "Object couldn't be reattached!" );
#else
                        pFormat->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
#endif
                        // Do not increment index, in this case
                        continue;
                    }
                }
                ++i;
            }
        }
        pPage = static_cast<SwPageFrame*>(pPage->GetNext());
    }
    SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
}

Size SwRootFrame::ChgSize( const Size& aNewSize )
{
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.SSize(aNewSize);
    }

    InvalidatePrt_();
    mbFixSize = false;
    return getFrameArea().SSize();
}

void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
{
    if ( !isFrameAreaPositionValid() )
    {
        setFrameAreaPositionValid(true);
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Pos().setX(DOCUMENTBORDER);
        aFrm.Pos().setY(DOCUMENTBORDER);
    }

    if ( !isFramePrintAreaValid() )
    {
        setFramePrintAreaValid(true);
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
        aPrt.Pos().setX(0);
        aPrt.Pos().setY(0);
        aPrt.SSize( getFrameArea().SSize() );
    }

    if ( !isFrameAreaSizeValid() )
    {
        // SSize is set by the pages (Cut/Paste).
        setFrameAreaSizeValid(true);
    }
}

void SwRootFrame::ImplInvalidateBrowseWidth()
{
    mbBrowseWidthValid = false;
    SwFrame *pPg = Lower();
    while ( pPg )
    {
        pPg->InvalidateSize();
        pPg = pPg->GetNext();
    }
}

void SwRootFrame::ImplCalcBrowseWidth()
{
    OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
            "CalcBrowseWidth and not in BrowseView" );

    // The (minimal) with is determined from borders, tables and paint objects.
    // It is calculated based on the attributes. Thus, it is not relevant how wide they are
    // currently but only how wide they want to be.
    // Frames and paint objects inside other objects (frames, tables) do not count.
    // Borders and columns are not taken into account.

    SwFrame *pFrame = ContainsContent();
    while ( pFrame && !pFrame->IsInDocBody() )
        pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
    if ( !pFrame )
        return;

    mbBrowseWidthValid = true;
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;

    do
    {
        if ( pFrame->IsInTab() )
            pFrame = pFrame->FindTabFrame();

        if ( pFrame->IsTabFrame() &&
             !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
        {
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
            const SwBorderAttrs &rAttrs = *aAccess.Get();
            const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
            tools::Long nWidth = rAttrs.GetSize().Width();
            if ( nWidth < std::numeric_limits<tools::Long>::max()-2000 && //-2k, because it changes while trying to resize!
                 text::HoriOrientation::FULL != rHori.GetHoriOrient() )
            {
                const SwHTMLTableLayout *pLayoutInfo =
                    static_cast<const SwTabFrame *>(pFrame)->GetTable()
                                            ->GetHTMLTableLayout();
                if ( pLayoutInfo )
                    nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );

                switch ( rHori.GetHoriOrient() )
                {
                    case text::HoriOrientation::NONE:
                        // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
                        nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
                        break;
                    case text::HoriOrientation::LEFT_AND_WIDTH:
                        nWidth += rAttrs.CalcLeft( pFrame );
                        break;
                    default:
                        break;
                }
                mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
            }
        }
        else if ( pFrame->GetDrawObjs() )
        {
            for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
            {
                // #i28701#
                SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
                const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
                const bool bFly = pAnchoredObj->DynCastFlyFrame() !=  nullptr;
                if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
                    || pFormat->GetFrameSize().GetWidthPercent())
                {
                    continue;
                }

                tools::Long nWidth = 0;
                switch ( pFormat->GetAnchor().GetAnchorId() )
                {
                    case RndStdIds::FLY_AS_CHAR:
                        nWidth = bFly ? pFormat->GetFrameSize().GetWidth() :
                                        pAnchoredObj->GetObjRect().Width();
                        break;
                    case RndStdIds::FLY_AT_PARA:
                        {
                            // #i33170#
                            // Reactivated old code because
                            // nWidth = pAnchoredObj->GetObjRect().Right()
                            // gives wrong results for objects that are still
                            // at position FAR_AWAY.
                            if ( bFly )
                            {
                                nWidth = pFormat->GetFrameSize().GetWidth();
                                const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
                                switch ( rHori.GetHoriOrient() )
                                {
                                    case text::HoriOrientation::NONE:
                                        nWidth += rHori.GetPos();
                                        break;
                                    case text::HoriOrientation::INSIDE:
                                    case text::HoriOrientation::LEFT:
                                        if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
                                            nWidth += pFrame->getFramePrintArea().Left();
                                        break;
                                    default:
                                        break;
                                }
                            }
                            else
                                // Paint objects to not have attributes and
                                // are defined by their current size
                                nWidth = pAnchoredObj->GetObjRect().Right() -
                                         pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
                        }
                        break;
                    default:    /* do nothing */;
                }
                mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
            }
        }
        pFrame = pFrame->FindNextCnt();
    } while ( pFrame );
}

void SwRootFrame::StartAllAction()
{
    if ( GetCurrShell() )
        for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
        {
            if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
                pCursorShell->StartAction();
            else
                rSh.StartAction();
        }
}

void SwRootFrame::EndAllAction()
{
    if ( !GetCurrShell() )
        return;

    for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
    {
        if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
        {
            pCursorShell->EndAction();
            pCursorShell->CallChgLnk();
            if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
                pFEShell->SetChainMarker();
        }
        else
            rSh.EndAction();
    }
}

void SwRootFrame::UnoRemoveAllActions()
{
    if ( !GetCurrShell() )
        return;

    for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
    {
        // #i84729#
        // No end action, if <SwViewShell> instance is currently in its end action.
        // Recursive calls to <::EndAction()> are not allowed.
        if ( !rSh.IsInEndAction() )
        {
            OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
            bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) !=  nullptr;
            bool bFE = dynamic_cast<const SwFEShell*>( &rSh) !=  nullptr;
            sal_uInt16 nRestore = 0;
            while( rSh.ActionCount() )
            {
                if( bCursor )
                {
                    static_cast<SwCursorShell*>(&rSh)->EndAction();
                    static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
                    if ( bFE )
                        static_cast<SwFEShell*>(&rSh)->SetChainMarker();
                }
                else
                    rSh.EndAction();
                nRestore++;
            }
            rSh.SetRestoreActions(nRestore);
        }
        rSh.LockView(true);
    }
}

void SwRootFrame::UnoRestoreAllActions()
{
    if ( !GetCurrShell() )
        return;

    for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
    {
        sal_uInt16 nActions = rSh.GetRestoreActions();
        while( nActions-- )
        {
            if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
                pCursorShell->StartAction();
            else
                rSh.StartAction();
        }
        rSh.SetRestoreActions(0);
        rSh.LockView(false);
    }
}

// Helper functions for SwRootFrame::CheckViewLayout
static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );

static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
{
    const bool bPage = pFrame->IsPageFrame();
    const SwSortedObjs* pSortedObj = bPage
                        ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
                        : pFrame->GetDrawObjs();
    if (pSortedObj == nullptr)
        return;

    // note: pSortedObj elements may be removed and inserted from
    // MoveObjectIfActive(), invalidating iterators
    // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
    for (size_t i = 0; i < pSortedObj->size(); ++i)
    {
        SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
        const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
        const SwFormatAnchor& rAnchor = pObjFormat->GetAnchor();

        // all except from the as character anchored objects are moved
        // when processing the page frame:
        if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
            continue;

        SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );

        if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
        {
            lcl_MoveAllLowers( pFlyFrame, rOffset );
            // tdf#138785 update position specific to as-char flys
            if (pFlyFrame->IsFlyInContentFrame())
            {
                static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
            }
            pFlyFrame->NotifyDrawObj();
            // --> let the active embedded object be moved
            SwFrame* pLower = pFlyFrame->Lower();
            if ( pLower && pLower->IsNoTextFrame() )
            {
                SwRootFrame* pRoot = pLower->getRootFrame();
                SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
                if ( pSh )
                {
                    SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
                    SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
                    if ( pNode )
                    {
                        svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
                        if ( xObj.is() )
                        {
                            for(SwViewShell& rSh : pSh->GetRingContainer())
                            {
                                SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
                                if ( pFEShell )
                                    pFEShell->MoveObjectIfActive( xObj, rOffset );
                            }
                        }
                    }
                }
            }
        }
        else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
        {
            // don't touch objects that are not yet positioned:
            if ( pAnchoredDrawObj->NotYetPositioned() )
                continue;

            const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
            const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
            pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
            pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );

            // clear contour cache
            if ( pAnchoredDrawObj->GetFrameFormat()->GetSurround().IsContour() )
                ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
        }
        // #i92511#
        // cache for object rectangle inclusive spaces has to be invalidated.
        pAnchoredObj->InvalidateObjRectWithSpaces();
    }
}

static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
{
    // first move the current frame
    // RotateFlyFrame3: moved to transform_translate instead of
    // direct modification to allow the SwFrame evtl. needed own reactions
    pFrame->transform_translate(rOffset);

    // Don't forget accessibility:
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if( pFrame->IsAccessibleFrame() )
    {
        SwRootFrame *pRootFrame = pFrame->getRootFrame();
        if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
            pRootFrame->GetCurrShell() )
        {
            const SwRect aFrame( pFrame->getFrameArea() );

            pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
        }
    }
#endif

    // the move any objects
    lcl_MoveAllLowerObjs( pFrame, rOffset );

    // finally, for layout frames we have to call this function recursively:
    if (pFrame->IsLayoutFrame())
    {
        SwFrame* pLowerFrame = pFrame->GetLower();
        while ( pLowerFrame )
        {
            lcl_MoveAllLowers( pLowerFrame, rOffset );
            pLowerFrame = pLowerFrame->GetNext();
        }
    }
}

// Calculate how the pages have to be positioned
void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
{
    SwViewShell* pSh = GetCurrShell();
    vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
    // #i91432#
    // No calculation of page positions, if only an empty page is present.
    // This situation occurs when <SwRootFrame> instance is in construction
    // and the document contains only left pages.
    if ( Lower()->GetNext() == nullptr &&
         static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
    {
        return;
    }

    if ( !pVisArea )
    {
        // no early return for bNewPage
        if ( mnViewWidth < 0 )
            mnViewWidth = 0;
    }
    else
    {
        assert(pViewOpt && "CheckViewLayout required ViewOptions");

        const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
        const bool bBookMode = pViewOpt->IsViewLayoutBookMode();

        if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
            return;

        mnColumns = nColumns;
        mbBookMode = bBookMode;
        mnViewWidth = pVisArea->Width();
        mbSidebarChanged = false;
    }

    if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
    {
        mnColumns = 1;
        mbBookMode = false;
    }

    Calc(pRenderContext);

    const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
    SetCallbackActionEnabled( false );

    maPageRects.clear();

    const tools::Long nBorder = getFrameArea().Pos().getX();
    const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
    const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
                                           : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
                                                  : SwViewOption::defGapBetweenPages);

    // check how many pages fit into the first page layout row:
    SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());

    // will contain the number of pages per row. 0 means that
    // the page does not fit.
    tools::Long nWidthRemain = nVisWidth;

    // after one row has been processed, these variables contain
    // the width of the row and the maximum of the page heights
    tools::Long nCurrentRowHeight = 0;
    tools::Long nCurrentRowWidth = 0;

    // these variables are used to finally set the size of the
    // root frame
    tools::Long nSumRowHeight = 0;
    SwTwips nMinPageLeft = TWIPS_MAX;
    SwTwips nMaxPageRight = 0;
    SwPageFrame* pStartOfRow = pPageFrame;
    sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
    bool bFirstRow = true;

    bool bPageChanged = false;
    const bool bRTL = !IsLeftToRightViewLayout();
    const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );

    while ( pPageFrame )
    {
        // we consider the current page to be "start of row" if
        // 1. it is the first page in the current row or
        // 2. it is the second page in the row and the first page is an empty page in non-book view:
        const bool bStartOfRow = pPageFrame == pStartOfRow ||
                                             ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );

        const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;

        // no half doc border space for first page in each row and
        tools::Long nPageWidth = 0;
        tools::Long nPageHeight = 0;

        if ( mbBookMode )
        {
            const SwFrame& rFormatPage = pPageFrame->GetFormatPage();

            nPageWidth  = rFormatPage.getFrameArea().Width()  + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
            nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
        }
        else
        {
            if ( !pPageFrame->IsEmptyPage() )
            {
                nPageWidth  = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
                nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
            }
        }

        if ( !bEmptyPage )
            ++nNumberOfPagesInRow;

        // finish current row if
        // 1. in dynamic mode the current page does not fit anymore or
        // 2. the current page exceeds the maximum number of columns
        bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
                            (0 != mnColumns && mnColumns < nNumberOfPagesInRow);

        // make sure that at least one page goes to the current row:
        if ( !bRowFinished || bStartOfRow )
        {
            // current page is allowed to be in current row
            nWidthRemain = nWidthRemain - nPageWidth;

            nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
            nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );

            pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());

            if ( !pPageFrame )
                bRowFinished = true;
        }

        if ( bRowFinished )
        {
            // pPageFrame now points to the first page in the new row or null
            // pStartOfRow points to the first page in the current row

            // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
            if ( !pPageFrame && nWidthRemain > 0 )
            {
                // find last page in current row:
                const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
                while( pLastPageInCurrentRow->GetNext() )
                    pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());

                if ( pLastPageInCurrentRow->IsEmptyPage() )
                    pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());

                // check how many times the last page would still fit into the remaining space:
                sal_uInt16 nNumberOfVirtualPages = 0;
                const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
                SwTwips nRemain = nWidthRemain;
                SwTwips nVirtualPagesWidth = 0;
                SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;

                while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
                {
                    SwTwips nLastPageWidthWithGap = nLastPageWidth;
                    if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
                        nLastPageWidthWithGap += nGapBetweenPages;

                    if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
                    {
                        ++nNumberOfVirtualPages;
                        nVirtualPagesWidth += nLastPageWidthWithGap;
                    }
                    nRemain = nRemain - nLastPageWidthWithGap;
                }

                nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
            }

            // first page in book mode is always special:
            if ( bFirstRow && mbBookMode )
            {
                // #i88036#
                nCurrentRowWidth +=
                    pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
            }

            // center page if possible
            tools::Long nSizeDiff = 0;
            if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
                nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;

            // adjust positions of pages in current row
            tools::Long nX = nSizeDiff;

            const tools::Long nRowStart = nBorder + nSizeDiff;
            const tools::Long nRowEnd   = nRowStart + nCurrentRowWidth;

            if ( bFirstRow && mbBookMode )
            {
                // #i88036#
                nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
            }

            SwPageFrame* pEndOfRow = pPageFrame;
            SwPageFrame* pPageToAdjust = pStartOfRow;

            do
            {
                const SwPageFrame* pFormatPage = pPageToAdjust;
                if ( mbBookMode )
                    pFormatPage = &pPageToAdjust->GetFormatPage();

                const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
                const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
                const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
                const SwTwips nLeftPageAddOffset = bLeftSidebar ?
                                                   nSidebarWidth :
                                                   0;

                Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
                Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );

                // RTL view layout: Calculate mirrored page position
                if ( bRTL )
                {
                    const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
                    aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
                    aNewPagePosWithLeftOffset = aNewPagePos;
                    aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
                }

                if ( aNewPagePosWithLeftOffset != aOldPagePos )
                {
                    lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
                    pPageToAdjust->SetCompletePaint();
                    bPageChanged = true;
                }

                // calculate area covered by the current page and store to
                // maPageRects. This is used e.g., for cursor setting
                const bool bFirstColumn = pPageToAdjust == pStartOfRow;
                const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
                const bool bLastRow = !pEndOfRow;

                nMinPageLeft  = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
                nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));

                // border of nGapBetweenPages around the current page:
                SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
                                             aNewPagePos.getY(),
                                             pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
                                             nCurrentRowHeight );

                static const tools::Long nOuterClickDiff = 1000000;

                // adjust borders for these special cases:
                if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
                    aPageRectWithBorders.SubLeft( nOuterClickDiff );
                if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
                    aPageRectWithBorders.AddRight( nOuterClickDiff );
                if ( bFirstRow )
                    aPageRectWithBorders.SubTop( nOuterClickDiff );
                if ( bLastRow )
                    aPageRectWithBorders.AddBottom( nOuterClickDiff );

                maPageRects.push_back( aPageRectWithBorders );

                nX = nX + nCurrentPageWidth;
                pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());

                // distance to next page
                if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
                {
                    // in book view, we add the x gap before left (even) pages:
                    if ( mbBookMode )
                    {
                        if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
                            nX = nX + nGapBetweenPages;
                    }
                    else
                    {
                        // in non-book view, don't add x gap before
                        // 1. the last empty page in a row
                        // 2. after an empty page
                        const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
                                                 ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );

                        if  ( !bDontAddGap )
                            nX = nX + nGapBetweenPages;
                    }
                }
            }
            while (pPageToAdjust && pPageToAdjust != pEndOfRow);

            // adjust values for root frame size
            nSumRowHeight = nSumRowHeight + nCurrentRowHeight;

            // start new row:
            nCurrentRowHeight = 0;
            nCurrentRowWidth = 0;
            pStartOfRow = pEndOfRow;
            nWidthRemain = nVisWidth;
            nNumberOfPagesInRow = 0;
            bFirstRow = false;
        } // end row finished
    } // end while

    // set size of root frame:
    const Size aOldSize( getFrameArea().SSize() );
    const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );

    if ( bPageChanged || aNewSize != aOldSize )
    {
        ChgSize( aNewSize );
        ::AdjustSizeChgNotify( this );
        Calc(pRenderContext);

        if ( pSh && pSh->GetDoc()->GetDocShell() )
        {
            pSh->SetFirstVisPageInvalid();
            if (bOldCallbackActionEnabled)
            {
                pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
                pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
            }
        }
    }

    maPagesArea.Pos( getFrameArea().Pos() );
    maPagesArea.SSize( aNewSize );
    if ( TWIPS_MAX != nMinPageLeft )
        maPagesArea.Left_( nMinPageLeft );

    SetCallbackActionEnabled( bOldCallbackActionEnabled );
}

bool SwRootFrame::IsLeftToRightViewLayout() const
{
    // Layout direction determined by layout direction of the first page.
    // #i88036#
    // Only ask a non-empty page frame for its layout direction
    assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
    const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
    return !rPage.IsRightToLeft() && !rPage.IsVertical();
}

const SwPageFrame& SwPageFrame::GetFormatPage() const
{
    const SwPageFrame* pRet = this;
    if ( IsEmptyPage() )
    {
        pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
        // #i88035#
        // Typically a right empty page frame has a next non-empty page frame and
        // a left empty page frame has a previous non-empty page frame.
        // But under certain circumstances this assumption is not true -
        // e.g. during insertion of a left page at the end of the document right
        // after a left page in an intermediate state a right empty page does not
        // have a next page frame.
        if ( pRet == nullptr )
        {
            if ( OnRightPage() )
            {
                pRet = static_cast<const SwPageFrame*>( GetPrev() );
            }
            else
            {
                pRet = static_cast<const SwPageFrame*>( GetNext() );
            }
        }
        assert(pRet &&
                "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
    }
    return *pRet;
}

bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
{
    tools::Long nUpperLimit = 0;
    tools::Long nLowerLimit = 0;
    const SwFrame* pFrame = Lower();
    while ( pFrame )
    {
        if ( pFrame->IsBodyFrame() )
        {
            nUpperLimit = pFrame->getFrameArea().Top();
            nLowerLimit = pFrame->getFrameArea().Bottom();
        }
        else if ( pFrame->IsFootnoteContFrame() )
            nLowerLimit = pFrame->getFrameArea().Bottom();

        pFrame = pFrame->GetNext();
    }

    SwRect aHeaderArea( getFrameArea().TopLeft(),
           Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );

    SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
    const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
    if ( aHeaderArea.Contains( rPt ) )
    {
        if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
        {
            rControl = FrameControlType::Header;
            return true;
        }
    }
    else
    {
        SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
                Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );

        if ( aFooterArea.Contains( rPt ) &&
             (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
        {
            rControl = FrameControlType::Footer;
            return true;
        }
    }

    return false;
}

bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
{
    SwViewShell* pShell = getRootFrame()->GetCurrShell();
    if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
    {
        // When whitespace is hidden, the page frame has two heights: the
        // nominal (defined by the frame format), and the actual (which is
        // at most the nominal height, but can be smaller in case there is
        // no content for the whole page).
        // The layout size is the actual one, but we want to move the
        // content frame to a new page only in case it doesn't fit the
        // nominal size.
        if (nDiff < 0)
        {
            // Content frame doesn't fit the actual size, check if it fits the nominal one.
            const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
            const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
            // Count what would be the max size for the body frame, ignoring top/bottom margin.
            const SvxULSpaceItem& rULSpace = pPageFormat->GetULSpace();
            SwTwips nNominalHeight = rPageSize.getHeight() - rULSpace.GetUpper() - rULSpace.GetLower();
            tools::Long nWhitespace = nNominalHeight - getFrameArea().Height();
            if (nWhitespace > -nDiff)
            {
                // It does: don't move it and invalidate our page frame so
                // that it gets a larger height.
                return false;
            }
        }
    }

    return true;
}

const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
{
    const SwFrame* pLowerFrame = Lower();
    while (pLowerFrame)
    {
        if (pLowerFrame->IsHeaderFrame())
            return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
        pLowerFrame = pLowerFrame->GetNext();
    }
    return nullptr;
}

const SwFooterFrame* SwPageFrame::GetFooterFrame() const
{
    const SwFrame* pLowerFrame = Lower();
    while (pLowerFrame)
    {
        if (pLowerFrame->IsFooterFrame())
            return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
        pLowerFrame = pLowerFrame->GetNext();
    }
    return nullptr;
}

void SwPageFrame::UpdateVirtPageNumInfo(sw::VirtPageNumHint& rHint, const SwFrame* pFrame) const
{
    assert(!rHint.IsFound());
    if(this == rHint.GetOrigPage() && !pFrame->GetPrev())
    {
        // Should be the one (can temporarily be different, should we be concerned about this possibility?)
        rHint.SetFound();
        rHint.SetInfo(this, pFrame);
    }
    if(GetPhyPageNum() < rHint.GetOrigPage()->GetPhyPageNum() &&
         (!rHint.GetPage() || GetPhyPageNum() > rHint.GetPage()->GetPhyPageNum()))
    {
        // This could be the one.
        rHint.SetInfo(this, pFrame);
    }
}

void SwPageFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("page"));
    dumpAsXmlAttributes(writer);

    (void)xmlTextWriterStartElement(writer, BAD_CAST("page_status"));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyLayout"), BAD_CAST(OString::boolean(!IsInvalidFlyLayout()).getStr()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyContent"), BAD_CAST(OString::boolean(!IsInvalidFlyContent()).getStr()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyInCnt"), BAD_CAST(OString::boolean(!IsInvalidFlyInCnt()).getStr()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!IsInvalidLayout()).getStr()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!IsInvalidContent()).getStr()));
    (void)xmlTextWriterEndElement(writer);
    (void)xmlTextWriterStartElement(writer, BAD_CAST("page_info"));
    (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", GetPhyPageNum());
    (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", GetVirtPageNum());
    UIName aFormatName = GetPageDesc()->GetName();
    (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName.toString(), RTL_TEXTENCODING_UTF8).getStr()));
    (void)xmlTextWriterEndElement(writer);
    if (auto const* pObjs = GetSortedObjs())
    {
        (void)xmlTextWriterStartElement(writer, BAD_CAST("sorted_objs"));
        for (SwAnchoredObject const*const pObj : *pObjs)
        {   // just print pointer, full details will be printed on its anchor frame
            // this nonsense is needed because of multiple inheritance
            if (SwFlyFrame const* pFly = pObj->DynCastFlyFrame())
            {
                (void)xmlTextWriterStartElement(writer, BAD_CAST("fly"));
                (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pFly);
            }
            else
            {
                (void)xmlTextWriterStartElement(writer, BAD_CAST(pObj->getElementName()));
                (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pObj);
            }
            (void)xmlTextWriterEndElement(writer);
        }
        (void)xmlTextWriterEndElement(writer);
    }

    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    const SwSortedObjs* pAnchored = GetDrawObjs();
    if ( pAnchored && pAnchored->size() > 0 )
    {
        (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );

        for (SwAnchoredObject* pObject : *pAnchored)
        {
            pObject->dumpAsXml( writer );
        }

        (void)xmlTextWriterEndElement( writer );
    }
    dumpChildrenAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
}

SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
{
    if (pPage && pPage->HasGrid())
    {
        SwTextGridItem const& rGridItem(
                pPage->GetPageDesc()->GetMaster().GetTextGrid());
        if (SwTextGrid::NONE != rGridItem.GetGridType())
        {
            return &rGridItem;
        }
    }
    return nullptr;
}

sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
{
    return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
