    /* -*- 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 <sal/config.h>

    #include <o3tl/safeint.hxx>
    #include <svx/svdmodel.hxx>
    #include <svx/svdpage.hxx>
    #include <drawdoc.hxx>
    #include <fmtpdsc.hxx>
    #include <swtable.hxx>
    #include <rootfrm.hxx>
    #include <pagefrm.hxx>
    #include <dflyobj.hxx>
    #include <frmtool.hxx>
    #include "virtoutp.hxx"
    #include <blink.hxx>
    #include <sectfrm.hxx>
    #include <notxtfrm.hxx>
    #include <pagedesc.hxx>
    #include <viewimp.hxx>
    #include <hints.hxx>
    #include <viewopt.hxx>
    #include <set>
    #include <IDocumentSettingAccess.hxx>
    #include <IDocumentFieldsAccess.hxx>
    #include <DocumentLayoutManager.hxx>
    #include <ndindex.hxx>

    SwLayVout     *SwRootFrame::s_pVout = nullptr;
    bool           SwRootFrame::s_isInPaint = false;
    bool           SwRootFrame::s_isNoVirDev = false;

    SwCache *SwFrame::mpCache = nullptr;

    long FirstMinusSecond( long nFirst, long nSecond )
        { return nFirst - nSecond; }
    long SecondMinusFirst( long nFirst, long nSecond )
        { return nSecond - nFirst; }
    long SwIncrement( long nA, long nAdd )
        { return nA + nAdd; }
    long SwDecrement( long nA, long nSub )
        { return nA - nSub; }

    static SwRectFnCollection aHorizontal = {
        /*.fnGetTop =*/&SwRect::Top_,
        /*.fnGetBottom =*/&SwRect::Bottom_,
        /*.fnGetLeft =*/&SwRect::Left_,
        /*.fnGetRight =*/&SwRect::Right_,
        /*.fnGetWidth =*/&SwRect::Width_,
        /*.fnGetHeight =*/&SwRect::Height_,
        /*.fnGetPos =*/&SwRect::TopLeft,
        /*.fnGetSize =*/&SwRect::Size_,

        /*.fnSetTop =*/&SwRect::Top_,
        /*.fnSetBottom =*/&SwRect::Bottom_,
        /*.fnSetLeft =*/&SwRect::Left_,
        /*.fnSetRight =*/&SwRect::Right_,
        /*.fnSetWidth =*/&SwRect::Width_,
        /*.fnSetHeight =*/&SwRect::Height_,

        /*.fnSubTop =*/&SwRect::SubTop,
        /*.fnAddBottom =*/&SwRect::AddBottom,
        /*.fnSubLeft =*/&SwRect::SubLeft,
        /*.fnAddRight =*/&SwRect::AddRight,
        /*.fnAddWidth =*/&SwRect::AddWidth,
        /*.fnAddHeight =*/&SwRect::AddHeight,

        /*.fnSetPosX =*/&SwRect::SetPosX,
        /*.fnSetPosY =*/&SwRect::SetPosY,

        /*.fnGetTopMargin =*/&SwFrame::GetTopMargin,
        /*.fnGetBottomMargin =*/&SwFrame::GetBottomMargin,
        /*.fnGetLeftMargin =*/&SwFrame::GetLeftMargin,
        /*.fnGetRightMargin =*/&SwFrame::GetRightMargin,
        /*.fnSetXMargins =*/&SwFrame::SetLeftRightMargins,
        /*.fnSetYMargins =*/&SwFrame::SetTopBottomMargins,
        /*.fnGetPrtTop =*/&SwFrame::GetPrtTop,
        /*.fnGetPrtBottom =*/&SwFrame::GetPrtBottom,
        /*.fnGetPrtLeft =*/&SwFrame::GetPrtLeft,
        /*.fnGetPrtRight =*/&SwFrame::GetPrtRight,
        /*.fnTopDist =*/&SwRect::GetTopDistance,
        /*.fnBottomDist =*/&SwRect::GetBottomDistance,
        /*.fnLeftDist =*/&SwRect::GetLeftDistance,
        /*.fnRightDist =*/&SwRect::GetRightDistance,
        /*.fnSetLimit =*/&SwFrame::SetMaxBottom,
        /*.fnOverStep =*/&SwRect::OverStepBottom,

        /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
        /*.fnMakePos =*/&SwFrame::MakeBelowPos,
        /*.fnXDiff =*/&FirstMinusSecond,
        /*.fnYDiff =*/&FirstMinusSecond,
        /*.fnXInc =*/&SwIncrement,
        /*.fnYInc =*/&o3tl::saturating_add<long>,

        /*.fnSetLeftAndWidth =*/&SwRect::SetLeftAndWidth,
        /*.fnSetTopAndHeight =*/&SwRect::SetTopAndHeight
    };

    static SwRectFnCollection aVertical = {
        /*.fnGetTop =*/&SwRect::Right_,
        /*.fnGetBottom =*/&SwRect::Left_,
        /*.fnGetLeft =*/&SwRect::Top_,
        /*.fnGetRight =*/&SwRect::Bottom_,
        /*.fnGetWidth =*/&SwRect::Height_,
        /*.fnGetHeight =*/&SwRect::Width_,
        /*.fnGetPos =*/&SwRect::TopRight,
        /*.fnGetSize =*/&SwRect::SwappedSize,

        /*.fnSetTop =*/&SwRect::Right_,
        /*.fnSetBottom =*/&SwRect::Left_,
        /*.fnSetLeft =*/&SwRect::Top_,
        /*.fnSetRight =*/&SwRect::Bottom_,
        /*.fnSetWidth =*/&SwRect::Height_,
        /*.fnSetHeight =*/&SwRect::Width_,

        /*.fnSubTop =*/&SwRect::AddRight,
        /*.fnAddBottom =*/&SwRect::SubLeft,
        /*.fnSubLeft =*/&SwRect::SubTop,
        /*.fnAddRight =*/&SwRect::AddBottom,
        /*.fnAddWidth =*/&SwRect::AddHeight,
        /*.fnAddHeight =*/&SwRect::AddWidth,

        /*.fnSetPosX =*/&SwRect::SetPosY,
        /*.fnSetPosY =*/&SwRect::SetPosX,

        /*.fnGetTopMargin =*/&SwFrame::GetRightMargin,
        /*.fnGetBottomMargin =*/&SwFrame::GetLeftMargin,
        /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
        /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
        /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
        /*.fnSetYMargins =*/&SwFrame::SetRightLeftMargins,
        /*.fnGetPrtTop =*/&SwFrame::GetPrtRight,
        /*.fnGetPrtBottom =*/&SwFrame::GetPrtLeft,
        /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
        /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
        /*.fnTopDist =*/&SwRect::GetRightDistance,
        /*.fnBottomDist =*/&SwRect::GetLeftDistance,
        /*.fnLeftDist =*/&SwRect::GetTopDistance,
        /*.fnRightDist =*/&SwRect::GetBottomDistance,
        /*.fnSetLimit =*/&SwFrame::SetMinLeft,
        /*.fnOverStep =*/&SwRect::OverStepLeft,

        /*.fnSetPos =*/&SwRect::SetUpperRightCorner,
        /*.fnMakePos =*/&SwFrame::MakeLeftPos,
        /*.fnXDiff =*/&FirstMinusSecond,
        /*.fnYDiff =*/&SecondMinusFirst,
        /*.fnXInc =*/&SwIncrement,
        /*.fnYInc =*/&SwDecrement,

        /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
        /*.fnSetTopAndHeight =*/&SwRect::SetRightAndWidth
    };

    static SwRectFnCollection aVerticalLeftToRight = {
        /*.fnGetTop =*/&SwRect::Left_,
        /*.fnGetBottom =*/&SwRect::Right_,
        /*.fnGetLeft =*/&SwRect::Top_,
        /*.fnGetRight =*/&SwRect::Bottom_,
        /*.fnGetWidth =*/&SwRect::Height_,
        /*.fnGetHeight =*/&SwRect::Width_,
        /*.fnGetPos =*/&SwRect::TopLeft,
        /*.fnGetSize =*/&SwRect::SwappedSize,

        /*.fnSetTop =*/&SwRect::Left_,
        /*.fnSetBottom =*/&SwRect::Right_,
        /*.fnSetLeft =*/&SwRect::Top_,
        /*.fnSetRight =*/&SwRect::Bottom_,
        /*.fnSetWidth =*/&SwRect::Height_,
        /*.fnSetHeight =*/&SwRect::Width_,

        /*.fnSubTop =*/&SwRect::SubLeft,
        /*.fnAddBottom =*/&SwRect::AddRight,
        /*.fnSubLeft =*/&SwRect::SubTop,
        /*.fnAddRight =*/&SwRect::AddBottom,
        /*.fnAddWidth =*/&SwRect::AddHeight,
        /*.fnAddHeight =*/&SwRect::AddWidth,

        /*.fnSetPosX =*/&SwRect::SetPosY,
        /*.fnSetPosY =*/&SwRect::SetPosX,

        /*.fnGetTopMargin =*/&SwFrame::GetLeftMargin,
        /*.fnGetBottomMargin =*/&SwFrame::GetRightMargin,
        /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
        /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
        /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
        /*.fnSetYMargins =*/&SwFrame::SetLeftRightMargins,
        /*.fnGetPrtTop =*/&SwFrame::GetPrtLeft,
        /*.fnGetPrtBottom =*/&SwFrame::GetPrtRight,
        /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
        /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
        /*.fnTopDist =*/&SwRect::GetLeftDistance,
        /*.fnBottomDist =*/&SwRect::GetRightDistance,
        /*.fnLeftDist =*/&SwRect::GetTopDistance,
        /*.fnRightDist =*/&SwRect::GetBottomDistance,
        /*.fnSetLimit =*/&SwFrame::SetMaxRight,
        /*.fnOverStep =*/&SwRect::OverStepRight,

        /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
        /*.fnMakePos =*/&SwFrame::MakeRightPos,
        /*.fnXDiff =*/&FirstMinusSecond,
        /*.fnYDiff =*/&FirstMinusSecond,
        /*.fnXInc =*/&SwIncrement,
        /*.fnYInc =*/&SwIncrement,

        /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
        /*.fnSetTopAndHeight =*/&SwRect::SetLeftAndWidth
    };

    SwRectFn fnRectHori = &aHorizontal;
    SwRectFn fnRectVert = &aVertical;
    SwRectFn fnRectVertL2R = &aVerticalLeftToRight;

    // #i65250#
    sal_uInt32 SwFrame::mnLastFrameId=0;

    void FrameInit()
    {
        SwRootFrame::s_pVout = new SwLayVout();
        SwCache *pNew = new SwCache( 100
    #ifdef DBG_UTIL
        , "static SwBorderAttrs::pCache"
    #endif
        );
        SwFrame::SetCache( pNew );
    }

    void FrameFinit()
    {
    #if OSL_DEBUG_LEVEL > 0
        // The cache may only contain null pointers at this time.
        for( size_t n = SwFrame::GetCachePtr()->size(); n; )
            if( (*SwFrame::GetCachePtr())[ --n ] )
            {
                SwCacheObj* pObj = (*SwFrame::GetCachePtr())[ n ];
                OSL_ENSURE( !pObj, "Who didn't deregister?");
            }
    #endif
        delete SwRootFrame::s_pVout;
        delete SwFrame::GetCachePtr();
    }

    // RootFrame::Everything that belongs to CurrShell

    CurrShell::CurrShell( SwViewShell *pNew )
    {
        OSL_ENSURE( pNew, "insert 0-Shell?" );
        pRoot = pNew->GetLayout();
        if ( pRoot )
        {
            pPrev = pRoot->mpCurrShell;
            pRoot->mpCurrShell = pNew;
            pRoot->mpCurrShells->insert( this );
        }
        else
            pPrev = nullptr;
    }

    CurrShell::~CurrShell()
    {
        if ( pRoot )
        {
            pRoot->mpCurrShells->erase( this );
            if ( pPrev )
                pRoot->mpCurrShell = pPrev;
            if ( pRoot->mpCurrShells->empty() && pRoot->mpWaitingCurrShell )
            {
                pRoot->mpCurrShell = pRoot->mpWaitingCurrShell;
                pRoot->mpWaitingCurrShell = nullptr;
            }
        }
    }

    void SetShell( SwViewShell *pSh )
    {
        SwRootFrame *pRoot = pSh->GetLayout();
        if ( pRoot->mpCurrShells->empty() )
            pRoot->mpCurrShell = pSh;
        else
            pRoot->mpWaitingCurrShell = pSh;
    }

    void SwRootFrame::DeRegisterShell( SwViewShell *pSh )
    {
        // Activate some shell if possible
        if ( mpCurrShell == pSh )
        {
            mpCurrShell = nullptr;
            for(SwViewShell& rShell : pSh->GetRingContainer())
            {
                if(&rShell != pSh)
                {
                    mpCurrShell = &rShell;
                    break;
                }
            }
        }

        // Doesn't matter anymore
        if ( mpWaitingCurrShell == pSh )
            mpWaitingCurrShell = nullptr;

        // Remove references
        for ( SwCurrShells::iterator it = mpCurrShells->begin(); it != mpCurrShells->end(); ++it )
        {
            CurrShell *pC = *it;
            if (pC->pPrev == pSh)
                pC->pPrev = nullptr;
        }
    }

    void InitCurrShells( SwRootFrame *pRoot )
    {
        pRoot->mpCurrShells.reset( new SwCurrShells );
    }

    /*
    |*      The RootFrame requests an own FrameFormat from the document, which it is
    |*      going to delete again in the dtor. The own FrameFormat is derived from
    |*      the passed FrameFormat.
    |*/
    SwRootFrame::SwRootFrame( SwFrameFormat *pFormat, SwViewShell * pSh ) :
        SwLayoutFrame( pFormat->GetDoc()->MakeFrameFormat(
            "Root", pFormat ), nullptr ),
        maPagesArea(),
        mnViewWidth( -1 ),
        mnColumns( 0 ),
        mbBookMode( false ),
        mbSidebarChanged( false ),
        mbNeedGrammarCheck( false ),
        mbCheckSuperfluous( false ),
        mbIdleFormat( true ),
        mbBrowseWidthValid( false ),
        mbTurboAllowed( true ),
        mbAssertFlyPages( true ),
        mbIsVirtPageNum( false ),
        mbIsNewLayout( true ),
        mbCallbackActionEnabled ( false ),
        mbLayoutFreezed ( false ),
        mbHideRedlines( false ),
        mnBrowseWidth(MIN_BROWSE_WIDTH),
        mpTurbo( nullptr ),
        mpLastPage( nullptr ),
        mpCurrShell( pSh ),
        mpWaitingCurrShell( nullptr ),
        mpDrawPage( nullptr ),
        mnPhyPageNums( 0 ),
        mnAccessibleShells( 0 )
    {
        mnFrameType = SwFrameType::Root;
        setRootFrame( this );
    }

    void SwRootFrame::Init( SwFrameFormat* pFormat )
    {
        InitCurrShells( this );

        IDocumentTimerAccess& rTimerAccess = pFormat->getIDocumentTimerAccess();
        IDocumentLayoutAccess& rLayoutAccess = pFormat->getIDocumentLayoutAccess();
        IDocumentFieldsAccess& rFieldsAccess = pFormat->getIDocumentFieldsAccess();
        const IDocumentSettingAccess& rSettingAccess = pFormat->getIDocumentSettingAccess();
        rTimerAccess.StopIdling();
        // For creating the Flys by MakeFrames()
        rLayoutAccess.SetCurrentViewShell( GetCurrShell() );
        mbCallbackActionEnabled = false; // needs to be set to true before leaving!

        SwDrawModel* pMd = pFormat->getIDocumentDrawModelAccess().GetDrawModel();
        if ( pMd )
        {
            // Disable "multiple layout"
            mpDrawPage = pMd->GetPage(0);

            mpDrawPage->SetSize( getFrameArea().SSize() );
        }

        // Initialize the layout: create pages, link content with Content etc.
        // First, initialize some stuff, then get hold of the first
        // node (which will be needed for the PageDesc).

        SwDoc* pDoc = pFormat->GetDoc();
        SwNodeIndex aIndex( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode() );
        SwContentNode *pNode = pDoc->GetNodes().GoNextSection( &aIndex, true, false );
        // #123067# pNode = 0 can really happen
        SwTableNode *pTableNd= pNode ? pNode->FindTableNode() : nullptr;

        // Get hold of PageDesc (either via FrameFormat of the first node or the initial one).
        SwPageDesc *pDesc = nullptr;
        ::boost::optional<sal_uInt16> oPgNum;

        if ( pTableNd )
        {
            const SwFormatPageDesc &rDesc = pTableNd->GetTable().GetFrameFormat()->GetPageDesc();
            pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
            //#19104# respect the page number offset!!
            oPgNum = rDesc.GetNumOffset();
            if (oPgNum)
                mbIsVirtPageNum = true;
        }
        else if ( pNode )
        {
            const SwFormatPageDesc &rDesc = pNode->GetSwAttrSet().GetPageDesc();
            pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
            //#19104# respect the page number offset!!
            oPgNum = rDesc.GetNumOffset();
            if (oPgNum)
                mbIsVirtPageNum = true;
        }
        else
            mbIsVirtPageNum = false;
        if ( !pDesc )
            pDesc = &pDoc->GetPageDesc( 0 );

        const bool bOdd = !oPgNum || 0 != ( oPgNum.get() % 2 );
        const bool bFirst = true;
        // Even page numbers are supposed to be printed as left pages.  So if a
        // page number has been explicitly set for this first page, then we must
        // insert a blank page before it to make it a left page.
        const bool bInsertEmpty = !bOdd;

        // Create a page and put it in the layout
        SwPageFrame *pPage = ::InsertNewPage( *pDesc, this, bOdd, bFirst, bInsertEmpty, false, nullptr );

        // Find the first page in the Bodytext section.
        SwLayoutFrame *pLay = pPage->FindBodyCont();
        while( pLay->Lower() )
            pLay = static_cast<SwLayoutFrame*>(pLay->Lower());

        SwNodeIndex aTmp( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode(), 1 );
        ::InsertCnt_( pLay, pDoc, aTmp.GetIndex(), true );
        //Remove masters that haven't been replaced yet from the list.
        RemoveMasterObjs( mpDrawPage );
        if( rSettingAccess.get(DocumentSettingId::GLOBAL_DOCUMENT) )
            rFieldsAccess.UpdateRefFields();
        //b6433357: Update page fields after loading
        if ( !mpCurrShell || !mpCurrShell->Imp()->IsUpdateExpFields() )
        {
            SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
            rFieldsAccess.UpdatePageFields( &aMsgHint );
        }

        rTimerAccess.StartIdling();
        mbCallbackActionEnabled = true;

        SwViewShell *pViewSh  = GetCurrShell();
        if (pViewSh)
            mbNeedGrammarCheck = pViewSh->GetViewOptions()->IsOnlineSpell();
    }

    void SwRootFrame::DestroyImpl()
    {
        mbTurboAllowed = false;
        mpTurbo = nullptr;

        if(pBlink)
            pBlink->FrameDelete( this );
        SwFrameFormat *pRegisteredInNonConst = static_cast<SwFrameFormat*>(GetDep());
        if ( pRegisteredInNonConst )
        {
            SwDoc *pDoc = pRegisteredInNonConst->GetDoc();
            pDoc->DelFrameFormat( pRegisteredInNonConst );
            // do this before calling RemoveFootnotes() because footnotes
            // can contain anchored objects
            pDoc->GetDocumentLayoutManager().ClearSwLayouterEntries();
        }

        mpDestroy.reset();

        // Remove references
        for ( SwCurrShells::iterator it = mpCurrShells->begin(); it != mpCurrShells->end(); ++it )
            (*it)->pRoot = nullptr;

        mpCurrShells.reset();

        // Some accessible shells are left => problems on second SwFrame::Destroy call
        assert(0 == mnAccessibleShells);

        // fdo#39510 crash on document close with footnotes
        // Object ownership in writer and esp. in layout are a mess: Before the
        // document/layout split SwDoc and SwRootFrame were essentially one object
        // and magically/uncleanly worked around their common destruction by call
        // to SwDoc::IsInDtor() -- even from the layout. As of now destruction of
        // the layout proceeds forward through the frames. Since SwTextFootnote::DelFrames
        // also searches backwards to find the master of footnotes, they must be
        // considered to be owned by the SwRootFrame and also be destroyed here,
        // before tearing down the (now footnote free) rest of the layout.
        RemoveFootnotes(nullptr, false, true);

        SwLayoutFrame::DestroyImpl();
    }

    SwRootFrame::~SwRootFrame()
    {
    }

    void SwRootFrame::RemoveMasterObjs( SdrPage *pPg )
    {
        // Remove all master objects from the Page. But don't delete!
        for( size_t i = pPg ? pPg->GetObjCount() : 0; i; )
        {
            SdrObject* pObj = pPg->GetObj( --i );
            if( dynamic_cast< const SwFlyDrawObj *>( pObj ) !=  nullptr )
                pPg->RemoveObject( i );
        }
    }

    void SwRootFrame::AllCheckPageDescs() const
    {
        if ( !IsLayoutFreezed() )
            CheckPageDescs( const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower())) );
    }

    void SwRootFrame::AllInvalidateAutoCompleteWords() const
    {
        SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
        while ( pPage )
        {
            pPage->InvalidateAutoCompleteWords();
            pPage = static_cast<SwPageFrame*>(pPage->GetNext());
        }
    }

    void SwRootFrame::AllAddPaintRect() const
    {
        GetCurrShell()->AddPaintRect( getFrameArea() );
    }

    void SwRootFrame::AllRemoveFootnotes()
    {
        RemoveFootnotes();
    }

    void SwRootFrame::AllInvalidateSmartTagsOrSpelling(bool bSmartTags) const
    {
        SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
        while ( pPage )
        {
            if ( bSmartTags )
                pPage->InvalidateSmartTags();

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

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

↑ V1004 The 'pPg' pointer was used unsafely after it was verified against nullptr. Check lines: 515, 517.