/* -*- 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 <svx/svdpage.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <sal/log.hxx>
#include <o3tl/deleter.hxx>
#include <osl/diagnose.h>

#include <drawdoc.hxx>
#include <fmtornt.hxx>
#include <fmthdft.hxx>
#include <fmtfsize.hxx>
#include <fmtsrnd.hxx>
#include <docary.hxx>
#include <lineinfo.hxx>
#include <swmodule.hxx>
#include <pagefrm.hxx>
#include <colfrm.hxx>
#include <fesh.hxx>
#include <viewimp.hxx>
#include <viewopt.hxx>
#include <dflyobj.hxx>
#include <dcontact.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <ftnfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <flyfrms.hxx>
#include <layact.hxx>
#include <pagedesc.hxx>
#include <section.hxx>
#include <sectfrm.hxx>
#include <node2lay.hxx>
#include <ndole.hxx>
#include <hints.hxx>
#include "layhelp.hxx"
#include <laycache.hxx>
#include <rootfrm.hxx>
#include <paratr.hxx>
#include <redline.hxx>
#include <sortedobjs.hxx>
#include <objectformatter.hxx>
#include <calbck.hxx>
#include <ndtxt.hxx>
#include <undobj.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentTimerAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentState.hxx>
#include <frameformats.hxx>
#include <boost/circular_buffer.hpp>
#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
#include <officecfg/Office/Writer.hxx>

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

namespace {
    // FIXME: would likely better be a member of SwRootFrame instead of a global flag
    bool isFlyCreationSuppressed = false;
}
namespace sw {
    FlyCreationSuppressor::FlyCreationSuppressor(bool wasAlreadySuppressedAllowed)
        : m_wasAlreadySuppressed(isFlyCreationSuppressed)
    {
        (void)wasAlreadySuppressedAllowed;
        assert(wasAlreadySuppressedAllowed || !isFlyCreationSuppressed);
        isFlyCreationSuppressed = true;
    }
    FlyCreationSuppressor::~FlyCreationSuppressor()
    {
        isFlyCreationSuppressed = m_wasAlreadySuppressed;
    }
}

bool bObjsDirect = true;
bool bSetCompletePaintOnInvalidate = false;

sal_uInt8 StackHack::s_nCnt = 0;
bool StackHack::s_bLocked = false;

SwFrameNotify::SwFrameNotify( SwFrame *pF ) :
    mpFrame( pF ),
    maFrame( pF->getFrameArea() ),
    maPrt( pF->getFramePrintArea() ),
    mbInvaKeep( false ),
    mbValidSize( pF->isFrameAreaSizeValid() )
{
    if ( pF->IsTextFrame() )
    {
        mnFlyAnchorOfst = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( true );
        mnFlyAnchorOfstNoWrap = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( false );
    }
    else
    {
        mnFlyAnchorOfst = 0;
        mnFlyAnchorOfstNoWrap = 0;
    }

    mbHadFollow = pF->IsContentFrame() && static_cast<SwContentFrame*>(pF)->GetFollow();
}

SwFrameNotify::~SwFrameNotify()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

void SwFrameNotify::ImplDestroy()
{
    SwRectFnSet aRectFnSet(*mpFrame);
    const bool bAbsP = aRectFnSet.PosDiff(maFrame, mpFrame->getFrameArea());
    const bool bChgWidth =
            aRectFnSet.GetWidth(maFrame) != aRectFnSet.GetWidth(mpFrame->getFrameArea());
    const bool bChgHeight =
            aRectFnSet.GetHeight(maFrame)!=aRectFnSet.GetHeight(mpFrame->getFrameArea());
    const bool bChgFlyBasePos = mpFrame->IsTextFrame() &&
       ( ( mnFlyAnchorOfst != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( true ) ) ||
         ( mnFlyAnchorOfstNoWrap != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( false ) ) );

    if ( mpFrame->IsFlowFrame() && !mpFrame->IsInFootnote() )
    {
        SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( mpFrame );

        if ( !pFlow->IsFollow() )
        {
            if ( !mpFrame->GetIndPrev() )
            {
                if ( mbInvaKeep )
                {
                    SwFrame *pPre = pFlow->FindPrevIgnoreHidden();
                    if ( pPre && pPre->IsFlowFrame() )
                    {
                        // 1. pPre wants to keep with me:
                        bool bInvalidPrePos = SwFlowFrame::CastFlowFrame(pPre)->IsKeep(pPre->GetAttrSet()->GetKeep(), pPre->GetBreakItem())
                            && pPre->GetIndPrev();

                        // 2. pPre is a table and the last row wants to keep with me:
                        if ( !bInvalidPrePos && pPre->IsTabFrame() )
                        {
                            SwTabFrame* pPreTab = static_cast<SwTabFrame*>(pPre);
                            if ( pPreTab->GetFormat()->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
                            {
                                SwRowFrame* pLastRow = static_cast<SwRowFrame*>(pPreTab->GetLastLower());
                                if ( pLastRow && pLastRow->ShouldRowKeepWithNext() )
                                    bInvalidPrePos = true;
                            }
                        }

                        if ( bInvalidPrePos )
                            pPre->InvalidatePos();
                    }
                }
            }
            else if ( !pFlow->HasFollow() )
            {
                tools::Long nOldHeight = aRectFnSet.GetHeight(maFrame);
                tools::Long nNewHeight = aRectFnSet.GetHeight(mpFrame->getFrameArea());
                if( (nOldHeight > nNewHeight) || (!nOldHeight && nNewHeight) )
                    pFlow->CheckKeep();
            }
        }
    }

    if ( bAbsP )
    {
        mpFrame->SetCompletePaint();

        SwFrame* pNxt = mpFrame->GetIndNext();
        // #121888# - skip empty section frames
        while ( pNxt &&
                pNxt->IsSctFrame() && !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
        {
            pNxt = pNxt->GetIndNext();
        }

        if ( pNxt )
        {
            pNxt->InvalidatePos();
            if (pNxt->IsTextFrame() && static_cast<SwTextFrame*>(pNxt)->IsUndersized())
            {   // tdf#137523 it could have more space at new pos
                pNxt->InvalidateSize();
                pNxt->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
            }
        }
        else
        {
            // #104100# - correct condition for setting retouche
            // flag for vertical layout.
            if( mpFrame->IsRetoucheFrame() &&
                aRectFnSet.TopDist( maFrame, aRectFnSet.GetTop(mpFrame->getFrameArea()) ) > 0 )
            {
                mpFrame->SetRetouche();
            }

            // A fresh follow frame does not have to be invalidated, because
            // it is already formatted:
            if ( mbHadFollow || !mpFrame->IsContentFrame() || !static_cast<SwContentFrame*>(mpFrame)->GetFollow() )
            {
                if ( !mpFrame->IsTabFrame() || !static_cast<SwTabFrame*>(mpFrame)->GetFollow() )
                    mpFrame->InvalidateNextPos();
            }
        }
    }

    //For each resize of the background graphics is a repaint necessary.
    const bool bPrtWidth =
            aRectFnSet.GetWidth(maPrt) != aRectFnSet.GetWidth(mpFrame->getFramePrintArea());
    const bool bPrtHeight =
            aRectFnSet.GetHeight(maPrt)!=aRectFnSet.GetHeight(mpFrame->getFramePrintArea());
    if ( bPrtWidth || bPrtHeight )
    {
        bool bUseNewFillProperties(false);
        if (mpFrame->supportsFullDrawingLayerFillAttributeSet())
        {
            drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(mpFrame->getSdrAllFillAttributesHelper());
            if(aFillAttributes && aFillAttributes->isUsed())
            {
                bUseNewFillProperties = true;
                // use SetCompletePaint if needed
                if(aFillAttributes->needCompleteRepaint())
                {
                    mpFrame->SetCompletePaint();
                }
            }
        }
        if (!bUseNewFillProperties)
        {
            const SvxGraphicPosition ePos = mpFrame->GetAttrSet()->GetBackground().GetGraphicPos();
            if(GPOS_NONE != ePos && GPOS_TILED != ePos)
                mpFrame->SetCompletePaint();
        }
    }
    else
    {
        // #97597# - consider case that *only* margins between
        // frame and printing area has changed. Then, frame has to be repainted,
        // in order to force paint of the margin areas.
        if ( !bAbsP && (bChgWidth || bChgHeight) )
        {
            mpFrame->SetCompletePaint();
        }
    }

    const bool bPrtP = aRectFnSet.PosDiff( maPrt, mpFrame->getFramePrintArea() );
    if ( bAbsP || bPrtP || bChgWidth || bChgHeight ||
         bPrtWidth || bPrtHeight || bChgFlyBasePos )
    {
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        if( mpFrame->IsAccessibleFrame() )
        {
            SwRootFrame *pRootFrame = mpFrame->getRootFrame();
            if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
                pRootFrame->GetCurrShell() )
            {
                pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame );
            }
        }
#endif

        // Notification of anchored objects
        if ( mpFrame->GetDrawObjs() )
        {
            const SwSortedObjs &rObjs = *mpFrame->GetDrawObjs();
            SwPageFrame* pPageFrame = nullptr;
            for (SwAnchoredObject* pObj : rObjs)
            {
                // OD 2004-03-31 #i26791# - no general distinction between
                // Writer fly frames and drawing objects
                bool bNotify = false;
                bool bNotifySize = false;
                SwContact* pContact = ::GetUserCall( pObj->GetDrawObj() );
                if (!pContact)
                    continue;
                const bool bAnchoredAsChar = pContact->ObjAnchoredAsChar();
                if ( !bAnchoredAsChar )
                {
                    // Notify object, which aren't anchored as-character:

                    // always notify objects, if frame position has changed
                    // or if the object is to-page|to-fly anchored.
                    if ( bAbsP ||
                         pContact->ObjAnchoredAtPage() ||
                         pContact->ObjAnchoredAtFly() )
                    {
                        bNotify = true;

                        // assure that to-fly anchored Writer fly frames are
                        // registered at the correct page frame, if frame
                        // position has changed.
                        if ( bAbsP && pContact->ObjAnchoredAtFly() &&
                             pObj->DynCastFlyFrame() !=  nullptr )
                        {
                            // determine to-fly anchored Writer fly frame
                            SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj);
                            // determine page frame of to-fly anchored
                            // Writer fly frame
                            SwPageFrame* pFlyPageFrame = pFlyFrame->FindPageFrame();
                            // determine page frame, if needed.
                            if ( !pPageFrame )
                            {
                                pPageFrame = mpFrame->FindPageFrame();
                            }
                            if ( pPageFrame != pFlyPageFrame )
                            {
                                OSL_ENSURE( pFlyPageFrame, "~SwFrameNotify: Fly from Nowhere" );
                                if( pFlyPageFrame )
                                    pFlyPageFrame->MoveFly( pFlyFrame, pPageFrame );
                                else
                                    pPageFrame->AppendFlyToPage( pFlyFrame );
                            }
                        }
                    }
                    // otherwise the objects are notified in dependence to
                    // its positioning and alignment
                    else
                    {
                        const SwFormatVertOrient& rVert =
                                        pContact->GetFormat()->GetVertOrient();
                        if ( ( rVert.GetVertOrient() == text::VertOrientation::CENTER ||
                               rVert.GetVertOrient() == text::VertOrientation::BOTTOM ||
                               rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) &&
                             ( bChgHeight || bPrtHeight ) )
                        {
                            bNotify = true;
                        }
                        if ( !bNotify )
                        {
                            const SwFormatHoriOrient& rHori =
                                        pContact->GetFormat()->GetHoriOrient();
                            if ( ( rHori.GetHoriOrient() != text::HoriOrientation::NONE ||
                                   rHori.GetRelationOrient()== text::RelOrientation::PRINT_AREA ||
                                   rHori.GetRelationOrient()== text::RelOrientation::FRAME ) &&
                                 ( bChgWidth || bPrtWidth || bChgFlyBasePos ) )
                            {
                                bNotify = true;
                            }
                        }
                    }
                }
                else if ( bPrtWidth )
                {
                    // Notify as-character anchored objects, if printing area
                    // width has changed.
                    bNotify = true;
                    bNotifySize = true;
                }

                // perform notification via the corresponding invalidations
                if ( bNotify )
                {
                    if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
                    {
                        if ( bNotifySize )
                            pFlyFrame->InvalidateSize_();
                        // #115759# - no invalidation of
                        // position for as-character anchored objects.
                        if ( !bAnchoredAsChar )
                        {
                            pFlyFrame->InvalidatePos_();
                        }
                        pFlyFrame->Invalidate_();
                    }
                    else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) !=  nullptr )
                    {
                        // #115759# - no invalidation of
                        // position for as-character anchored objects.
                        if ( !bAnchoredAsChar )
                        {
                            pObj->InvalidateObjPos();
                        }
                    }
                    else
                    {
                        OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - unknown anchored object type." );
                    }
                }
            }
        }
    }
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() )
    {
        SwRootFrame *pRootFrame = mpFrame->getRootFrame();
        if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
            pRootFrame->GetCurrShell() )
        {
            pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame );
        }
    }
#endif

    // #i9046# Automatic frame width
    SwFlyFrame* pFly = nullptr;
    // #i35879# Do not trust the inf flags. pFrame does not
    // necessarily have to have an upper!
    if ( mpFrame->IsFlyFrame() || nullptr == ( pFly = mpFrame->ImplFindFlyFrame() ))
        return;

    // #i61999#
    // no invalidation of columned Writer fly frames, because automatic
    // width doesn't make sense for such Writer fly frames.
    SwFrame* pLower = pFly->Lower();
    if ( !pLower || pLower->IsColumnFrame() )
        return;

    const SwFormatFrameSize &rFrameSz = pFly->GetFormat()->GetFrameSize();

    // This could be optimized. Basically the fly frame only has to
    // be invalidated, if the first line of pFrame (if pFrame is a content
    // frame, for other frame types it's the print area) has changed its
    // size and pFrame was responsible for the current width of pFly. On
    // the other hand, this is only rarely used and re-calculation of
    // the fly frame does not cause too much trouble. So we keep it this
    // way:
    if ( SwFrameSize::Fixed != rFrameSz.GetWidthSizeType() )
    {
        // #i50668#, #i50998# - invalidation of position
        // of as-character anchored fly frames not needed and can cause
        // layout loops
        if ( dynamic_cast<const SwFlyInContentFrame*>( pFly) ==  nullptr )
        {
            pFly->InvalidatePos();
        }
        pFly->InvalidateSize();
    }
}

SwLayNotify::SwLayNotify( SwLayoutFrame *pLayFrame ) :
    SwFrameNotify( pLayFrame ),
    m_bLowersComplete( false )
{
}

// OD 2004-05-11 #i28701# - local method to invalidate the position of all
// frames inclusive its floating screen objects, which are lowers of the given
// layout frame
static void lcl_InvalidatePosOfLowers( SwLayoutFrame& _rLayoutFrame )
{
    if( _rLayoutFrame.IsFlyFrame() && _rLayoutFrame.GetDrawObjs() )
    {
        _rLayoutFrame.InvalidateObjs( false );
    }

    SwFrame* pLowerFrame = _rLayoutFrame.Lower();
    while ( pLowerFrame )
    {
        pLowerFrame->InvalidatePos();
        if ( pLowerFrame->IsTextFrame() )
        {
            static_cast<SwTextFrame*>(pLowerFrame)->Prepare( PrepareHint::FramePositionChanged );
        }
        else if ( pLowerFrame->IsTabFrame() )
        {
            pLowerFrame->InvalidatePrt();
        }

        pLowerFrame->InvalidateObjs( false );

        pLowerFrame = pLowerFrame->GetNext();
    }
}

void SwLayNotify::ImplDestroy()
{
    SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(mpFrame);
    SwRectFnSet aRectFnSet(*pLay);
    bool bNotify = false;
    if ( pLay->getFramePrintArea().SSize() != maPrt.SSize() )
    {
        if ( !IsLowersComplete() )
        {
            bool bInvaPercent;

            if ( pLay->IsRowFrame() )
            {
                bInvaPercent = true;
                tools::Long nNew = aRectFnSet.GetHeight(pLay->getFramePrintArea());
                if( nNew != aRectFnSet.GetHeight(maPrt) )
                     static_cast<SwRowFrame*>(pLay)->AdjustCells( nNew, true);
                if( aRectFnSet.GetWidth(pLay->getFramePrintArea())
                    != aRectFnSet.GetWidth(maPrt) )
                     static_cast<SwRowFrame*>(pLay)->AdjustCells( 0, false );
            }
            else
            {
                //Proportional adoption of the internal.
                //1. If the formatted is no Fly
                //2. If he contains no columns
                //3. If the Fly has a fixed height and the columns
                //   are next to be.
                //   Hoehe danebenliegen.
                //4. Never at SectionFrames.
                bool bLow;
                if( pLay->IsFlyFrame() )
                {
                    if ( pLay->Lower() )
                    {
                        bLow = !pLay->Lower()->IsColumnFrame() ||
                            aRectFnSet.GetHeight(pLay->Lower()->getFrameArea())
                             != aRectFnSet.GetHeight(pLay->getFramePrintArea());
                    }
                    else
                        bLow = false;
                }
                else if( pLay->IsSctFrame() )
                {
                    if ( pLay->Lower() )
                    {
                        if( pLay->Lower()->IsColumnFrame() && pLay->Lower()->GetNext() )
                            bLow = pLay->Lower()->getFrameArea().Height() != pLay->getFramePrintArea().Height();
                        else
                            bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
                    }
                    else
                        bLow = false;
                }
                else if( pLay->IsFooterFrame() && !pLay->HasFixSize() )
                    bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
                else
                    bLow = true;
                bInvaPercent = bLow;
                if ( bLow )
                {
                    pLay->ChgLowersProp( maPrt.SSize() );
                }
                // If the PrtArea has been extended, it might be possible that the chain of parts
                // can take another frame. As a result, the "possible right one" needs to be
                // invalidated. This only pays off if this or its Uppers are moveable sections.
                // A PrtArea has been extended if width or height are larger than before.
                if ( (pLay->getFramePrintArea().Height() > maPrt.Height() ||
                      pLay->getFramePrintArea().Width()  > maPrt.Width()) &&
                     (pLay->IsMoveable() || pLay->IsFlyFrame()) )
                {
                    SwFrame *pTmpFrame = pLay->Lower();
                    if ( pTmpFrame && pTmpFrame->IsFlowFrame() )
                    {
                        while ( pTmpFrame->GetNext() )
                            pTmpFrame = pTmpFrame->GetNext();
                        pTmpFrame->InvalidateNextPos();
                    }
                }
            }
            bNotify = true;
            //EXPENSIVE!! But how we do it more elegant?
            if( bInvaPercent )
                pLay->InvaPercentLowers( pLay->getFramePrintArea().Height() - maPrt.Height() );
        }
        if ( pLay->IsTabFrame() )
            //So that _only_ the shadow is drawn while resizing.
            static_cast<SwTabFrame*>(pLay)->SetComplete();
        else
        {
            const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
            if( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) ||
                  !(pLay->GetType() & (SwFrameType::Body | SwFrameType::Page)) )
            //Thereby the subordinates are retouched clean.
            //Example problem: Take the Flys with the handles and downsize.
            //Not for body and page, otherwise it flickers when loading HTML.
                pLay->SetCompletePaint();
        }
    }
    //Notify Lower if the position has changed.
    const bool bPrtPos = aRectFnSet.PosDiff( maPrt, pLay->getFramePrintArea() );
    const bool bPos = bPrtPos || aRectFnSet.PosDiff( maFrame, pLay->getFrameArea() );
    const bool bSize = pLay->getFrameArea().SSize() != maFrame.SSize();

    if ( bPos && pLay->Lower() && !IsLowersComplete() )
    {
        pLay->Lower()->InvalidatePos();
        SwFootnoteFrame* pFtnFrame = pLay->Lower()->IsFootnoteFrame() ?
            static_cast<SwFootnoteFrame*>(pLay->Lower()) : nullptr;
        SwFrame* pFtnLower = pFtnFrame ? pFtnFrame->Lower() : nullptr;
        if (pFtnLower)
            pFtnLower->InvalidatePos();
    }

    if ( bPrtPos )
        pLay->SetCompletePaint();

    //Inform the Follower if the SSize has changed.
    if ( bSize )
    {
        if( pLay->GetNext() )
        {
            if ( pLay->GetNext()->IsLayoutFrame() )
                pLay->GetNext()->InvalidatePos_();
            else
                pLay->GetNext()->InvalidatePos();
        }
        else if( pLay->IsSctFrame() )
            pLay->InvalidateNextPos();
    }

    SwFrame* pLower = pLay->Lower();
    if ( !IsLowersComplete() &&
         !(pLay->GetType()&(SwFrameType::Fly|SwFrameType::Section) &&
            pLower && pLower->IsColumnFrame()) &&
         (bPos || bNotify) &&
         !(pLay->GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FootnoteContainer|SwFrameType::Page|SwFrameType::Root)))
    {
        // #i44016# - force unlock of position of lower objects.
        // #i43913# - no unlock of position of objects,
        // if <pLay> is a cell frame, and its table frame resp. its parent table
        // frame is locked.
        // #i47458# - force unlock of position of lower objects,
        // only if position of layout frame has changed.
        bool bUnlockPosOfObjs( bPos );
        if ( bUnlockPosOfObjs && pLay->IsCellFrame() )
        {
            SwTabFrame* pTabFrame( pLay->FindTabFrame() );
            if ( pTabFrame &&
                 ( pTabFrame->IsJoinLocked() ||
                   ( pTabFrame->IsFollow() &&
                     pTabFrame->FindMaster()->IsJoinLocked() ) ) )
            {
                bUnlockPosOfObjs = false;
            }
        }
        // #i49383# - check for footnote frame, if unlock
        // of position of lower objects is allowed.
        else if ( bUnlockPosOfObjs && pLay->IsFootnoteFrame() )
        {
            bUnlockPosOfObjs = static_cast<SwFootnoteFrame*>(pLay)->IsUnlockPosOfLowerObjs();
        }
        // #i51303# - no unlock of object positions for sections
        else if ( bUnlockPosOfObjs && pLay->IsSctFrame() )
        {
            bUnlockPosOfObjs = false;
        }
        pLay->NotifyLowerObjs( bUnlockPosOfObjs );
    }
    if ( bPos && pLay->IsFootnoteFrame() && pLay->Lower() )
    {
        // OD 2004-05-11 #i28701#
        ::lcl_InvalidatePosOfLowers( *pLay );
    }
    if( ( bPos || bSize ) && pLay->IsFlyFrame() && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()
          && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()->IsFlyFrame() )
        static_cast<SwFlyFrame*>(pLay)->AnchorFrame()->InvalidateSize();
}

SwLayNotify::~SwLayNotify()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) :
    SwLayNotify( pFlyFrame ),
    // #115759# - keep correct page frame - the page frame
    // the Writer fly frame is currently registered at.
    m_pOldPage( pFlyFrame->GetPageFrame() ),
    m_aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() )
{
}

void SwFlyNotify::ImplDestroy()
{
    SwFlyFrame *pFly = static_cast<SwFlyFrame*>(mpFrame);
    if ( pFly->IsNotifyBack() )
    {
        SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
        SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
        if ( !pImp || !pImp->IsAction() || !pImp->GetLayAction().IsAgain() )
        {
            //If in the LayAction the IsAgain is set it can be
            //that the old page is destroyed in the meantime!
            ::Notify( pFly, m_pOldPage, m_aFrameAndSpace, &maPrt );
            // #i35640# - additional notify anchor text frame,
            // if Writer fly frame has changed its page
            if ( pFly->GetAnchorFrame()->IsTextFrame() &&
                 pFly->GetPageFrame() != m_pOldPage )
            {
                pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
            }
        }
        pFly->ResetNotifyBack();
    }
    if (pFly->IsForceNotifyNewBackground())
    {
        pFly->NotifyBackground(pFly->FindPageFrame(), pFly->GetObjRectWithSpaces(), PrepareHint::FlyFrameArrive);
        pFly->SetForceNotifyNewBackground(false);
    }

    //Have the size or the position changed,
    //so should the view know this.
    SwRectFnSet aRectFnSet(*pFly);
    const bool bPosChgd = aRectFnSet.PosDiff( maFrame, pFly->getFrameArea() );
    const bool bFrameChgd = pFly->getFrameArea().SSize() != maFrame.SSize();
    const bool bPrtChgd = maPrt != pFly->getFramePrintArea();
    if ( bPosChgd || bFrameChgd || bPrtChgd )
    {
        pFly->NotifyDrawObj();
    }
    if ( bPosChgd && maFrame.Pos().X() != FAR_AWAY )
    {
        // OD 2004-05-10 #i28701# - no direct move of lower Writer fly frames.
        // reason: New positioning and alignment (e.g. to-paragraph anchored,
        // but aligned at page) are introduced.
        // <SwLayNotify::~SwLayNotify()> takes care of invalidation of lower
        // floating screen objects by calling method <SwLayoutFrame::NotifyLowerObjs()>.

        if ( pFly->IsFlyAtContentFrame() )
        {
            SwFrame *pNxt = pFly->AnchorFrame()->FindNext();
            while (pNxt)
            {
                pNxt->InvalidatePos();
                if (!pNxt->IsSctFrame())
                {
                    break;
                }
                // invalidating pos of a section frame doesn't have much
                // effect, so try again with its lower
                pNxt = static_cast<SwSectionFrame*>(pNxt)->Lower();
            }
        }

        // #i26945# - notify anchor.
        // Needed for negative positioned Writer fly frames
        if ( pFly->GetAnchorFrame()->IsTextFrame() )
        {
            pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
        }
    }

    // OD 2004-05-13 #i28701#
    // #i45180# - no adjustment of layout process flags and
    // further notifications/invalidations, if format is called by grow/shrink
    if ( !pFly->ConsiderObjWrapInfluenceOnObjPos() )
        return;
    if (pFly->IsFlyFreeFrame())
    {
        if (static_cast<SwFlyFreeFrame*>(pFly)->IsNoMoveOnCheckClip())
            return;
    }

    // #i54138# - suppress restart of the layout process
    // on changed frame height.
    // Note: It doesn't seem to be necessary and can cause layout loops.
    if ( bPosChgd )
    {
        // indicate a restart of the layout process
        pFly->SetRestartLayoutProcess( true );
    }
    else
    {
        // lock position
        pFly->LockPosition();
    }

    if ( pFly->ConsiderForTextWrap() )
        return;

    // indicate that object has to be considered for text wrap
    pFly->SetConsiderForTextWrap( true );
    // invalidate 'background' in order to allow its 'background'
    // to wrap around it.
    pFly->NotifyBackground( pFly->GetPageFrame(),
                            pFly->GetObjRectWithSpaces(),
                            PrepareHint::FlyFrameArrive );
    // invalidate position of anchor frame in order to force
    // a re-format of the anchor frame, which also causes a
    // re-format of the invalid previous frames of the anchor frame.
    pFly->AnchorFrame()->InvalidatePos();
}

SwFlyNotify::~SwFlyNotify()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

SwContentNotify::SwContentNotify( SwContentFrame *pContentFrame ) :
    SwFrameNotify( pContentFrame ),
    // OD 08.01.2004 #i11859#
    mbChkHeightOfLastLine( false ),
    mnHeightOfLastLine( 0 ),
    // OD 2004-02-26 #i25029#
    mbInvalidatePrevPrtArea( false ),
    mbBordersJoinedWithPrev( false )
{
    // OD 08.01.2004 #i11859#
    if ( !pContentFrame->IsTextFrame() )
        return;

    SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pContentFrame);
    if (!pTextFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING))
    {
        const SvxLineSpacingItem &rSpace = pTextFrame->GetAttrSet()->GetLineSpacing();
        if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
        {
            mbChkHeightOfLastLine = true;
            mnHeightOfLastLine = pTextFrame->GetHeightOfLastLine();
        }
    }
}

void SwContentNotify::ImplDestroy()
{
    SwContentFrame *pCnt = static_cast<SwContentFrame*>(mpFrame);
    if ( bSetCompletePaintOnInvalidate )
        pCnt->SetCompletePaint();

    SwRectFnSet aRectFnSet(*pCnt);
    if ( pCnt->IsInTab() && ( aRectFnSet.PosDiff( pCnt->getFrameArea(), maFrame ) ||
                             pCnt->getFrameArea().SSize() != maFrame.SSize()))
    {
        SwLayoutFrame* pCell = pCnt->GetUpper();
        while( !pCell->IsCellFrame() && pCell->GetUpper() )
            pCell = pCell->GetUpper();
        OSL_ENSURE( pCell->IsCellFrame(), "Where's my cell?" );
        if ( text::VertOrientation::NONE != pCell->GetFormat()->GetVertOrient().GetVertOrient() )
            pCell->InvalidatePrt(); //for the vertical align.
    }

    // OD 2004-02-26 #i25029#
    if ( mbInvalidatePrevPrtArea && mbBordersJoinedWithPrev &&
         pCnt->IsTextFrame() &&
         !pCnt->IsFollow() && !pCnt->GetIndPrev() )
    {
        // determine previous frame
        SwFrame* pPrevFrame = pCnt->FindPrev();
        // skip empty section frames and hidden text frames
        {
            while (pPrevFrame && pPrevFrame->IsHiddenNow())
            {
                pPrevFrame = pPrevFrame->FindPrev();
            }
        }

        // Invalidate printing area of found previous frame
        if ( pPrevFrame )
        {
            if ( pPrevFrame->IsSctFrame() )
            {
                if ( pCnt->IsInSct() )
                {
                    // Note: found previous frame is a section frame and
                    //       <pCnt> is also inside a section.
                    //       Thus due to <mbBordersJoinedWithPrev>,
                    //       <pCnt> had joined its borders/shadow with the
                    //       last content of the found section.
                    // Invalidate printing area of last content in found section.
                    SwFrame* pLstContentOfSctFrame =
                            static_cast<SwSectionFrame*>(pPrevFrame)->FindLastContent();
                    if ( pLstContentOfSctFrame )
                    {
                        pLstContentOfSctFrame->InvalidatePrt();
                    }
                }
            }
            else
            {
                pPrevFrame->InvalidatePrt();
            }
        }
    }

    const bool bFirst = aRectFnSet.GetWidth(maFrame) == 0;

    if ( pCnt->IsNoTextFrame() )
    {
        //Active PlugIn's or OLE-Objects should know something of the change
        //thereby they move their window appropriate.
        SwViewShell *pSh  = pCnt->getRootFrame()->GetCurrShell();
        if ( pSh )
        {
            SwOLENode *const pNd(static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetOLENode());
            if (nullptr != pNd &&
                 (pNd->GetOLEObj().IsOleRef() ||
                  pNd->IsOLESizeInvalid()) )
            {
                const bool bNoTextFramePrtAreaChanged =
                        ( maPrt.SSize().Width() != 0 &&
                          maPrt.SSize().Height() != 0 ) &&
                        maPrt.SSize() != pCnt->getFramePrintArea().SSize();
                OSL_ENSURE( pCnt->IsInFly(), "OLE not in FlyFrame" );
                SwFlyFrame *pFly = pCnt->FindFlyFrame();
                svt::EmbeddedObjectRef& xObj = pNd->GetOLEObj().GetObject();
                SwFEShell *pFESh = nullptr;
                for(SwViewShell& rCurrentShell : pSh->GetRingContainer())
                {   if ( dynamic_cast<const SwCursorShell*>( &rCurrentShell) !=  nullptr )
                    {
                        pFESh = static_cast<SwFEShell*>(&rCurrentShell);
                        // #108369#: Here used to be the condition if (!bFirst).
                        // I think this should mean "do not call CalcAndSetScale"
                        // if the frame is formatted for the first time.
                        // Unfortunately this is not valid anymore since the
                        // SwNoTextFrame already gets a width during CalcLowerPreps.
                        // Nevertheless, the indention of !bFirst seemed to be
                        // to assure that the OLE objects have already been notified
                        // if necessary before calling CalcAndSetScale.
                        // So I replaced !bFirst by !IsOLESizeInvalid. There is
                        // one additional problem specific to the word import:
                        // The layout is calculated _before_ calling PrtOLENotify,
                        // and the OLE objects are not invalidated during import.
                        // Therefore I added the condition !IsUpdateExpField,
                        // have a look at the occurrence of CalcLayout in
                        // uiview/view.cxx.
                        if ( !pNd->IsOLESizeInvalid() &&
                             !pSh->GetDoc()->getIDocumentState().IsUpdateExpField() )
                            pFESh->CalcAndSetScale( xObj,
                                                    &pFly->getFramePrintArea(), &pFly->getFrameArea(),
                                                    bNoTextFramePrtAreaChanged );
                    }
                }

                if ( pFESh && pNd->IsOLESizeInvalid() )
                {
                    pNd->SetOLESizeInvalid( false );
                    pFESh->CalcAndSetScale( xObj ); // create client
                }
            }
            // ditto animated graphics
            if ( getFrameArea().HasArea() && static_cast<SwNoTextFrame*>(pCnt)->HasAnimation() )
            {
                static_cast<SwNoTextFrame*>(pCnt)->StopAnimation();
                pSh->InvalidateWindows( getFrameArea() );
            }
        }
    }

    if ( bFirst )
    {
        pCnt->SetRetouche();    //fix(13870)

        SwDoc& rDoc = pCnt->IsTextFrame()
            ? static_cast<SwTextFrame*>(pCnt)->GetDoc()
            : static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetDoc();
        if ( !rDoc.GetSpzFrameFormats()->empty() &&
             rDoc.DoesContainAtPageObjWithContentAnchor() && !rDoc.getIDocumentState().IsNewDoc() )
        {
            // If certain import filters for foreign file format import
            // AT_PAGE anchored objects, the corresponding page number is
            // typically not known. In this case the content position is
            // stored at which the anchored object is found in the
            // imported document.
            // When this content is formatted it is the time at which
            // the page is known. Thus, this data can be corrected now.

            const SwPageFrame *pPage = nullptr;
            for(sw::SpzFrameFormat* pFormat: *rDoc.GetSpzFrameFormats())
            {
                const SwFormatAnchor &rAnch = pFormat->GetAnchor();
                if ( RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId() ||
                     rAnch.GetAnchorNode() == nullptr )
                {
                    continue;
                }

                if (FrameContainsNode(*pCnt, rAnch.GetAnchorNode()->GetIndex()))
                {
                    OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - to page anchored object with content position." );
                    if ( !pPage )
                    {
                        pPage = pCnt->FindPageFrame();
                    }
                    SwFormatAnchor aAnch( rAnch );
                    aAnch.SetAnchor( nullptr );
                    aAnch.SetPageNum( pPage->GetPhyPageNum() );
                    pFormat->SetFormatAttr( aAnch );
                    if ( RES_DRAWFRMFMT != pFormat->Which() )
                    {
                        pFormat->MakeFrames();
                    }
                }
            }
        }
    }

    // OD 12.01.2004 #i11859# - invalidate printing area of following frame,
    //  if height of last line has changed.
    if ( pCnt->IsTextFrame() && mbChkHeightOfLastLine )
    {
        if ( mnHeightOfLastLine != static_cast<SwTextFrame*>(pCnt)->GetHeightOfLastLine() )
        {
            pCnt->InvalidateNextPrtArea();
        }
    }

    // #i44049#
    if ( pCnt->IsTextFrame() && aRectFnSet.PosDiff( maFrame, pCnt->getFrameArea() ) )
    {
        pCnt->InvalidateObjs();
    }

    // #i43255# - move code to invalidate at-character
    // anchored objects due to a change of its anchor character from
    // method <SwTextFrame::Format(..)>.
    if ( !pCnt->IsTextFrame() )
        return;

    SwTextFrame* pMasterFrame = pCnt->IsFollow()
                           ? static_cast<SwTextFrame*>(pCnt)->FindMaster()
                           : static_cast<SwTextFrame*>(pCnt);
    if ( pMasterFrame && !pMasterFrame->IsFlyLock() &&
         pMasterFrame->GetDrawObjs() )
    {
        SwSortedObjs* pObjs = pMasterFrame->GetDrawObjs();
        for (SwAnchoredObject* pAnchoredObj : *pObjs)
        {
            if ( pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
                    == RndStdIds::FLY_AT_CHAR )
            {
                pAnchoredObj->CheckCharRectAndTopOfLine( !pMasterFrame->IsEmpty() );
            }
        }
    }
}

SwContentNotify::~SwContentNotify()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

// note this *cannot* be static because it's a friend
void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *const pFormat, const SwFormatAnchor & rAnch)
{
            const bool bFlyAtFly = rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY; // LAYER_IMPL
            //Is a frame or a SdrObject described?
            const bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
            // OD 23.06.2003 #108784# - append also drawing objects anchored
            // as character.
            const bool bDrawObjInContent = bSdrObj &&
                                         (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);

            if( !(bFlyAtFly ||
                (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
                (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
                bDrawObjInContent) )
                return;

            SdrObject* pSdrObj = nullptr;
            if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
            {
                OSL_ENSURE( !bSdrObj, "DrawObject not found." );
                pFormat->GetDoc().DelFrameFormat( pFormat );
                return;
            }
            if ( pSdrObj )
            {
                if ( !pSdrObj->getSdrPageFromSdrObject() )
                {
                    pFormat->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)->
                            InsertObject(pSdrObj, pSdrObj->GetOrdNumDirect());
                }

                if (SwDrawContact* pNew = static_cast<SwDrawContact*>(GetUserCall( pSdrObj )))
                {
                    if ( !pNew->GetAnchorFrame() )
                    {
                        SwAnchoredObject& rAnchorObj = *(pNew->GetAnchoredObj(nullptr));
                        pFrame->AppendDrawObj(rAnchorObj);
                        if (rAnchorObj.GetDrawObj()->IsVisible())
                            pNew->MoveObjToVisibleLayer(rAnchorObj.DrawObj());
                    }
                    // OD 19.06.2003 #108784# - add 'virtual' drawing object,
                    // if necessary. But control objects have to be excluded.
                    else if ( !::CheckControlLayer( pSdrObj ) &&
                              pNew->GetAnchorFrame() != pFrame &&
                              !pNew->GetDrawObjectByAnchorFrame( *pFrame ) )
                    {
                        SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame);
                        pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) );

                        pDrawVirtObj->ActionChanged();
                    }
                }
            }
            else
            {
                SwFlyFrame *pFly;
                if( bFlyAtFly )
                    pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
                else
                    pFly = new SwFlyAtContentFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
                pFly->Lock();
                pFrame->AppendFly( pFly );
                pFly->Unlock();
                if ( pPage )
                    ::RegistFlys( pPage, pFly );
            }
}

static bool IsShown(SwNodeOffset const nIndex,
    const SwFormatAnchor & rAnch,
    std::vector<sw::Extent>::const_iterator const*const pIter,
    std::vector<sw::Extent>::const_iterator const*const pEnd,
    SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
    assert(!pIter || *pIter == *pEnd || (*pIter)->pNode->GetIndex() == nIndex);
    SwNode* pAnchorNode = rAnch.GetAnchorNode();
    if (pAnchorNode->GetIndex() != nIndex)
    {
        return false;
    }
    if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
    {
        return pIter == nullptr // not merged
            || pIter != pEnd    // at least one char visible in node
            || !IsSelectFrameAnchoredAtPara(*rAnch.GetContentAnchor(),
                    SwPosition(*pFirstNode, 0),
                    SwPosition(*pLastNode, pLastNode->Len()));
    }
    if (pIter)
    {
        // note: frames are not sorted by anchor position.
        assert(pEnd);
        assert(pFirstNode);
        assert(pLastNode);
        assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY);
        if (*pIter == *pEnd && rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
        {   // tdf#149595 special case - it *could* be shown if first == last
            return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(),
                        SwPosition(*pFirstNode, 0),
                        SwPosition(*pLastNode, pLastNode->Len()));
        }
        for (auto iter = *pIter; iter != *pEnd; ++iter)
        {
            if (iter->nStart == iter->nEnd)
            {
                assert(iter->isHiddenParaMerge);
                continue;
            }
            assert(iter->pNode->GetIndex() == nIndex);
            if (rAnch.GetAnchorContentOffset() < iter->nStart)
            {
                return false;
            }
            if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
            {
                // if there is an extent then obviously the node was not
                // deleted fully...
                // show if start <= pos <= end
                // *or* if first-node/0  *and* not StartOfSection
                // *or* if last-node/Len *and* not EndOfSection

                // first determine the extent to compare to, then
                // construct start/end positions for the deletion *before* the
                // extent and compare once.
                // the interesting corner cases are on the edge of the extent!
                // no need to check for > the last extent because those
                // are never visible.
                if (rAnch.GetAnchorContentOffset() <= iter->nEnd)
                {
                    if (iter->nStart == 0)
                    {
                        return true;
                    }
                    else
                    {
                        SwPosition const start(
                            iter == *pIter
                                ? *pFirstNode // simplification
                                : *iter->pNode,
                            iter == *pIter // first extent?
                                ? iter->pNode == pFirstNode
                                    ? 0 // at start of 1st node
                                    : pFirstNode->Len() // previous node; simplification but should get right result
                                : (iter-1)->nEnd); // previous extent
                        SwPosition const end(*iter->pNode, iter->nStart);
                        return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(), start, end);
                    }
                }
                else if (iter == *pEnd - 1) // special case: after last extent
                {
                    if (iter->nEnd == iter->pNode->Len())
                    {
                        return true; // special case: end of node
                    }
                    else
                    {
                        SwPosition const start(*iter->pNode, iter->nEnd);
                        SwPosition const end(
                            *pLastNode, // simplification
                            iter->pNode == pLastNode
                                ? iter->pNode->Len()
                                : 0);
                        return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(), start, end);
                   }
                }
            }
            else
            {
                assert(rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
                // for AS_CHAR obviously must be <
                if (rAnch.GetAnchorContentOffset() < iter->nEnd)
                {
                    return true;
                }
            }
        }
        return false;
    }
    else
    {
        return true;
    }
}

void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
    std::vector<sw::Extent>::const_iterator const*const pIter,
    std::vector<sw::Extent>::const_iterator const*const pEnd,
    SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
    std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
    for (SwFrameFormat * pFrameFormat : rFlys)
    {
        SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor();
        if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
            || rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
        {
            assert(rAnchor.GetAnchorNode()->GetIndex() == rNode.GetIndex());
            if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode))
            {
                pFrameFormat->DelFrames();
            }
        }
    }
}

void AppendObjsOfNode(sw::FrameFormats<sw::SpzFrameFormat*> const*const pTable, SwNodeOffset const nIndex,
    SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc& rDoc,
    std::vector<sw::Extent>::const_iterator const*const pIter,
    std::vector<sw::Extent>::const_iterator const*const pEnd,
    SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
#if OSL_DEBUG_LEVEL > 0
    std::vector<SwFrameFormat*> checkFormats;
    for(auto pFormat: *pTable)
    {
        const SwFormatAnchor &rAnch = pFormat->GetAnchor();
        if ( rAnch.GetAnchorNode() &&
            IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
        {
            checkFormats.push_back( pFormat );
        }
    }
#else
    (void)pTable;
#endif

    SwNode const& rNode(*rDoc.GetNodes()[nIndex]);
    std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
    for (size_t it = 0; it != rFlys.size(); )
    {
        SwFrameFormat *const pFormat = rFlys[it];
        const SwFormatAnchor &rAnch = pFormat->GetAnchor();
        if ( rAnch.GetAnchorNode() &&
            IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
        {
#if OSL_DEBUG_LEVEL > 0
            std::vector<SwFrameFormat*>::iterator checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat );
            assert( checkPos != checkFormats.end());
            checkFormats.erase( checkPos );
#endif
            AppendObj(pFrame, pPage, pFormat, rAnch);
        }
        ++it;
    }

#if OSL_DEBUG_LEVEL > 0
    assert( checkFormats.empty());
#endif
}


void AppendObjs(const sw::FrameFormats<sw::SpzFrameFormat*> *const pTable, SwNodeOffset const nIndex,
        SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc& rDoc)
{
    if (pFrame->IsTextFrame())
    {
        SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
        if (sw::MergedPara const*const pMerged = pTextFrame->GetMergedPara())
        {
            std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
            std::vector<sw::Extent>::const_iterator iter(iterFirst);
            SwTextNode const* pNode(pMerged->pFirstNode);
            for ( ; ; ++iter)
            {
                if (iter == pMerged->extents.end()
                    || iter->pNode != pNode)
                {
                    AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, rDoc,
                        &iterFirst, &iter, pMerged->pFirstNode, pMerged->pLastNode);
                    SwNodeOffset const until = iter == pMerged->extents.end()
                        ? pMerged->pLastNode->GetIndex() + 1
                        : iter->pNode->GetIndex();
                    for (SwNodeOffset i = pNode->GetIndex() + 1; i < until; ++i)
                    {
                        // let's show at-para flys on nodes that contain start/end of
                        // redline too, even if there's no text there
                        SwNode const*const pTmp(pNode->GetNodes()[i]);
                        if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
                        {
                            AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, rDoc, &iter, &iter, pMerged->pFirstNode, pMerged->pLastNode);
                        }
                    }
                    if (iter == pMerged->extents.end())
                    {
                        break;
                    }
                    pNode = iter->pNode;
                    iterFirst = iter;
                }
            }
        }
        else
        {
            return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, rDoc, nullptr, nullptr, nullptr, nullptr);
        }
    }
    else
    {
        return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, rDoc, nullptr, nullptr, nullptr, nullptr);
    }
}

bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor)
{
    assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA ||
           rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR ||
           rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
    bool ret(true);
    if (auto const pMergedPara = rFrame.GetMergedPara())
    {
        ret = false;
        SwNode* pAnchorNode(rAnchor.GetAnchorNode());
        auto iterFirst(pMergedPara->extents.cbegin());
        if (iterFirst == pMergedPara->extents.end()
            && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
                || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR))
        {
            ret = (pAnchorNode == pMergedPara->pFirstNode
                    && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
                        || rAnchor.GetAnchorContentOffset() == 0))
                || (pAnchorNode == pMergedPara->pLastNode
                    && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
                        || rAnchor.GetAnchorContentOffset() == pMergedPara->pLastNode->Len()));
        }
        auto iter(iterFirst);
        SwTextNode const* pNode(pMergedPara->pFirstNode);
        for ( ; ; ++iter)
        {
            if (iter == pMergedPara->extents.end()
                || iter->pNode != pNode)
            {
                assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
                if (pNode == pAnchorNode)
                {
                    ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter,
                            pMergedPara->pFirstNode, pMergedPara->pLastNode);
                    break;
                }
                if (iter == pMergedPara->extents.end())
                {
                    break;
                }
                pNode = iter->pNode;
                if (pAnchorNode->GetIndex() < pNode->GetIndex())
                {
                    break;
                }
                iterFirst = iter;
            }
        }
    }
    return ret;
}

void AppendAllObjs(const sw::FrameFormats<sw::SpzFrameFormat*>* pTable, const SwFrame* pSib)
{
    //Connecting of all Objects, which are described in the SpzTable with the
    //layout.

    boost::circular_buffer<SwFrameFormat*> vFormatsToConnect(pTable->size());
    for(const auto& pFormat : *pTable)
    {
        const auto& rAnch = pFormat->GetAnchor();
        // Formats can still remain, because we neither use character bound
        // frames nor objects which are anchored to character bounds.
        if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && (rAnch.GetAnchorId() != RndStdIds::FLY_AS_CHAR))
        {
            const SwNode* pAnchorNode = rAnch.GetAnchorNode();
            // formats in header/footer have no dependencies
            if(pAnchorNode && pFormat->GetDoc().IsInHeaderFooter(*pAnchorNode))
                pFormat->MakeFrames();
            else
                vFormatsToConnect.push_back(pFormat);
        }
    }
    const SwRootFrame* pRoot = pSib ? pSib->getRootFrame() : nullptr;
    const SwFrameFormat* pFirstRequeued(nullptr);
    while(!vFormatsToConnect.empty())
    {
        SwFrameFormat* pFormat = vFormatsToConnect.front();
        bool isConnected(false);
        pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
        if(!isConnected)
        {
            pFormat->MakeFrames();
            pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
        }
        // do this *before* push_back! the circular_buffer can be "full"!
        vFormatsToConnect.pop_front();
        if (!isConnected)
        {
            if(pFirstRequeued == pFormat)
                // If nothing happens anymore we can stop.
                break;
            if(!pFirstRequeued)
                pFirstRequeued = pFormat;
            assert(!vFormatsToConnect.full());
            vFormatsToConnect.push_back(pFormat);
        }
        else
        {
            pFirstRequeued = nullptr;
        }
    }
}

namespace sw {

void RecreateStartTextFrames(SwTextNode & rNode)
{
    std::vector<SwTextFrame*> frames;
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
    for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
    {
        if (pFrame->getRootFrame()->HasMergedParas())
        {
            frames.push_back(pFrame);
        }
    }
    auto eMode(sw::FrameMode::Existing);
    for (SwTextFrame * pFrame : frames)
    {
        // SplitNode could have moved the original frame to the start node
        // & created a new one on end, or could have created new frame on
        // start node... grab start node's frame and recreate MergedPara.
        SwTextNode & rFirstNode(pFrame->GetMergedPara()
            ? *pFrame->GetMergedPara()->pFirstNode
            : rNode);
        assert(rFirstNode.GetIndex() <= rNode.GetIndex());
        // clear old one first to avoid DelFrames confusing updates & asserts...
        pFrame->SetMergedPara(nullptr);
        pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
                    *pFrame, rFirstNode, eMode));
        eMode = sw::FrameMode::New; // Existing is not idempotent!
        // note: this may or may not delete frames on the end node
    }
}

} // namespace sw

/** local method to set 'working' position for newly inserted frames

    OD 12.08.2003 #i17969#
*/
static void lcl_SetPos( SwFrame&             _rNewFrame,
                 const SwLayoutFrame& _rLayFrame )
{
    SwRectFnSet aRectFnSet(_rLayFrame);
    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(_rNewFrame);
    aRectFnSet.SetPos( aFrm, aRectFnSet.GetPos(_rLayFrame.getFrameArea()) );

    // move position by one SwTwip in text flow direction in order to get
    // notifications for a new calculated position after its formatting.
    if ( aRectFnSet.IsVert() )
    {
        aFrm.Pos().AdjustX( -1 );
    }
    else
    {
        aFrm.Pos().AdjustY(1 );
    }
}

void InsertCnt_( SwLayoutFrame *pLay, SwDoc& rDoc,
                             SwNodeOffset nIndex, bool bPages, SwNodeOffset nEndIndex,
                             SwFrame *pPrv, sw::FrameMode const eMode )
{
    rDoc.getIDocumentTimerAccess().BlockIdling();
    SwRootFrame* pLayout = pLay->getRootFrame();
    const bool bOldCallbackActionEnabled = pLayout && pLayout->IsCallbackActionEnabled();
    if( bOldCallbackActionEnabled )
        pLayout->SetCallbackActionEnabled( false );

    //In the generation of the Layout bPages=true will be handed over.
    //Then will be new pages generated all x paragraphs already times in advance.
    //On breaks and/or pagedescriptorchanges the corresponding will be generated
    //immediately.
    //The advantage is, that on one hand already a nearly realistic number of
    //pages are created, but above all there are no almost endless long chain
    //of paragraphs, which must be moved expensively until it reaches a tolerable
    //reduced level.
    //We'd like to think that 20 Paragraphs fit on one page.
    //So that it does not become in extreme situations so violent we calculate depending
    //on the node something to it.
    //If in the DocStatistic a usable given pagenumber
    //(Will be cared for while writing), so it will be presumed that this will be
    //number of pages.
    const bool bStartPercent = bPages && !nEndIndex;

    SwPageFrame *pPage = pLay->FindPageFrame();
    sw::SpzFrameFormats* pTable = rDoc.GetSpzFrameFormats();
    SwFrame       *pFrame = nullptr;
    std::unique_ptr<SwActualSection> pActualSection;
    std::unique_ptr<SwLayHelper> pPageMaker;

    //If the layout will be created (bPages == true) we do head on the progress
    //Flys and DrawObjects are not connected immediately, this
    //happens only at the end of the function.
    if ( bPages )
    {
        // Attention: the SwLayHelper class uses references to the content-,
        // page-, layout-frame etc. and may change them!
        pPageMaker.reset(new SwLayHelper( rDoc, pFrame, pPrv, pPage, pLay,
                pActualSection, nIndex, SwNodeOffset(0) == nEndIndex ));
        if( bStartPercent )
        {
            const sal_uLong nPageCount = pPageMaker->CalcPageCount();
            if( nPageCount )
                bObjsDirect = false;
        }
    }

    if( pLay->IsInSct() &&
        ( pLay->IsSctFrame() || pLay->GetUpper() ) ) // Hereby will newbies
            // be intercepted, of which flags could not determined yet,
            // for e.g. while inserting a table
    {
        SwSectionFrame* pSct = pLay->FindSctFrame();
        // If content will be inserted in a footnote, which in a column area,
        // the column area it is not allowed to be broken up.
        // Only if in the inner of the footnote lies an area, is this a candidate
        // for pActualSection.
        // The same applies for areas in tables, if inside the table will be
        // something inserted, it's only allowed to break up areas, which
        // lies in the inside also.
        if( ( !pLay->IsInFootnote() || pSct->IsInFootnote() ) &&
            ( !pLay->IsInTab() || pSct->IsInTab() ) )
        {
            pActualSection.reset(new SwActualSection(nullptr, pSct, pSct->GetSection()->GetFormat()->GetSectionNode()));
            // tdf#132236 for SwUndoDelete: find outer sections whose start
            // nodes aren't contained in the range but whose end nodes are,
            // because section frames may need to be created for them
            SwActualSection * pUpperSection(pActualSection.get());
            while (pUpperSection->GetSectionNode()->EndOfSectionIndex() < nEndIndex)
            {
                SwStartNode *const pStart(pUpperSection->GetSectionNode()->StartOfSectionNode());
                if (!pStart->IsSectionNode())
                {
                    break;
                }
                // note: these don't have a section frame, check it in EndNode case!
                auto const pTmp(new SwActualSection(nullptr, nullptr, static_cast<SwSectionNode*>(pStart)));
                pUpperSection->SetUpper(pTmp);
                pUpperSection = pTmp;
            }
            OSL_ENSURE( !pLay->Lower() || !pLay->Lower()->IsColumnFrame(),
                "InsertCnt_: Wrong Call" );
        }
    }

    //If a section is "open", the pActualSection points to an SwActualSection.
    //If the page breaks, for "open" sections a follow will created.
    //For nested sections (which have, however, not a nested layout),
    //the SwActualSection class has a member, which points to an upper(section).
    //When the "inner" section finishes, the upper will used instead.

    std::vector<SwSectionFrame *> newHiddenSections;

    // Do not consider the end node. The caller (Section/MakeFrames()) has to
    // ensure that the end of this range is positioned before EndIndex!
    for ( ; nEndIndex == SwNodeOffset(0) || nIndex < nEndIndex; ++nIndex)
    {
        assert(pLayout);
        SwNode *pNd = rDoc.GetNodes()[nIndex];
        if ( pNd->IsContentNode() )
        {
            SwContentNode* pNode = static_cast<SwContentNode*>(pNd);
            if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
            {
                if (pNd->IsTextNode()
                    && pNd->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
                {   // must have a frame already
                    assert(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pLayout))->GetMergedPara());
                }
                continue; // skip it
            }
            pFrame = pNode->IsTextNode()
                        ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode)
                        : pNode->MakeFrame(pLay);
            if (pPageMaker && !pLay->IsHiddenNow())
                pPageMaker->CheckInsert( nIndex );

            pFrame->InsertBehind( pLay, pPrv );
            if (!pPrv)
            {
                if (SwSectionFrame *const pSection = pLay->FindSctFrame())
                {
                    if (pSection->ContainsAny() == pFrame)
                    {   // tdf#146258 section PrtArea depends on paragraph upper margin
                        pSection->InvalidatePrt();
                    }
                }
            }
            // #i27138#
            // notify accessibility paragraphs objects about changed
            // CONTENT_FLOWS_FROM/_TO relation.
            // Relation CONTENT_FLOWS_FROM for next paragraph will change
            // and relation CONTENT_FLOWS_TO for previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            if ( pFrame->IsTextFrame() )
            {
                SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
                // no notification, if <SwViewShell> is in construction
                if ( pViewShell && !pViewShell->IsInConstructor() &&
                     pViewShell->GetLayout() &&
                     pViewShell->GetLayout()->IsAnyShellAccessible() &&
                     pFrame->FindPageFrame() != nullptr)
                {
                    auto pNext = pFrame->FindNextCnt( true );
                    auto pPrev = pFrame->FindPrevCnt();
                    pViewShell->InvalidateAccessibleParaFlowRelation(
                        pNext ? pNext->DynCastTextFrame() : nullptr,
                        pPrev ? pPrev->DynCastTextFrame() : nullptr );
                    // #i68958#
                    // The information flags of the text frame are validated
                    // in methods <FindNextCnt(..)> and <FindPrevCnt(..)>.
                    // The information flags have to be invalidated, because
                    // it is possible, that the one of its upper frames
                    // isn't inserted into the layout.
                    pFrame->InvalidateInfFlags();
                }
            }
#endif
            // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
            // for setting position at newly inserted frame
            lcl_SetPos( *pFrame, *pLay );
            pPrv = pFrame;

            if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
            {
                AppendObjs( pTable, nIndex, pFrame, pPage, rDoc );
                // tdf#165351 from SwCellFrame ctor, this may set inf flags
                // before the SwCellFrame has an upper, so reset here
                pFrame->InvalidateInfFlags();
            }
        }
        else if ( pNd->IsTableNode() )
        {   //Should we have encountered a table?
            SwTableNode *pTableNode = static_cast<SwTableNode*>(pNd);
            if (pLayout->IsHideRedlines())
            {
                // in the problematic case, there can be only 1 redline...
                SwPosition const tmp(*pNd);
                SwRangeRedline const*const pRedline(
                    rDoc.getIDocumentRedlineAccess().GetRedline(tmp, nullptr));
                // pathology: redline that starts on a TableNode; cannot
                // be created in UI but by import filters...
                if (pRedline
                    && pRedline->GetType() == RedlineType::Delete
                    && &pRedline->Start()->GetNode() == pNd)
                {
                    SAL_WARN("sw.pageframe", "skipping table frame creation on bizarre redline");
                    while (true)
                    {
                        pTableNode->GetNodes()[nIndex]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
                        if (nIndex == pTableNode->EndOfSectionIndex())
                        {
                            break;
                        }
                        ++nIndex;
                    }
                    continue;
                }
            }
            if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
            {
                assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
                nIndex = pTableNode->EndOfSectionIndex();
                continue; // skip it
            }

            pFrame = pTableNode->MakeFrame( pLay );
            pFrame->InvalidateInfFlags();

            // skip tables deleted with track changes
            if ( !static_cast<SwTabFrame*>(pFrame)->Lower() )
            {
                nIndex = pTableNode->EndOfSectionIndex();
                continue; // skip it
            }

            // #108116# loading may produce table structures that GCLines
            // needs to clean up. To keep table formulas correct, change
            // all table formulas to internal (BOXPTR) representation.
            pTableNode->GetTable().SwitchFormulasToInternalRepresentation();
            pTableNode->GetTable().GCLines();

            if( pPageMaker )
                pPageMaker->CheckInsert( nIndex );

            pFrame->InsertBehind( pLay, pPrv );
            if (pPage) // would null in SwCellFrame ctor
            {   // tdf#134931 call ResetTurbo(); not sure if Paste() would be
                pFrame->InvalidatePage(pPage); // better than InsertBehind()?
            }
            // #i27138#
            // notify accessibility paragraphs objects about changed
            // CONTENT_FLOWS_FROM/_TO relation.
            // Relation CONTENT_FLOWS_FROM for next paragraph will change
            // and relation CONTENT_FLOWS_TO for previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            {
                SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
                // no notification, if <SwViewShell> is in construction
                if ( pViewShell && !pViewShell->IsInConstructor() &&
                     pViewShell->GetLayout() &&
                     pViewShell->GetLayout()->IsAnyShellAccessible() &&
                     pFrame->FindPageFrame() != nullptr)
                {
                    auto pNext = pFrame->FindNextCnt( true );
                    auto pPrev = pFrame->FindPrevCnt();
                    pViewShell->InvalidateAccessibleParaFlowRelation(
                            pNext ? pNext->DynCastTextFrame() : nullptr,
                            pPrev ? pPrev->DynCastTextFrame() : nullptr );
                }
            }
#endif
            if ( bObjsDirect && !pTable->empty() )
                static_cast<SwTabFrame*>(pFrame)->RegistFlys();
            // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
            // for setting position at newly inserted frame
            lcl_SetPos( *pFrame, *pLay );

            pPrv = pFrame;
            //Set the index to the endnode of the table section.
            nIndex = pTableNode->EndOfSectionIndex();

            SwTabFrame* pTmpFrame = static_cast<SwTabFrame*>(pFrame);
            while ( pTmpFrame )
            {
                pTmpFrame->CheckDirChange();
                pTmpFrame = pTmpFrame->IsFollow() ? pTmpFrame->FindMaster() : nullptr;
            }

        }
        else if ( pNd->IsSectionNode() )
        {
            if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
            {
                assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
                continue; // skip it
            }
            SwSectionNode *pNode = static_cast<SwSectionNode*>(pNd);
            {
                if (pActualSection)
                    pActualSection->SetLastPos(pPrv);

                pFrame = pNode->MakeFrame(pLay, pNode->GetSection().CalcHiddenFlag());
                pActualSection.reset( new SwActualSection( pActualSection.release(),
                                                static_cast<SwSectionFrame*>(pFrame), pNode ) );
                if ( pActualSection->GetUpper() )
                {
                    //Insert behind the Upper, the "Follow" of the Upper will be
                    //generated at the EndNode.
                    SwSectionFrame *pTmp = pActualSection->GetUpper()->GetSectionFrame();
                    pFrame->InsertBehind( pTmp->GetUpper(), pTmp );
                    // OD 25.03.2003 #108339# - direct initialization of section
                    // after insertion in the layout
                    static_cast<SwSectionFrame*>(pFrame)->Init();
                }
                else
                {
                    pFrame->InsertBehind( pLay, pPrv );
                    // OD 25.03.2003 #108339# - direct initialization of section
                    // after insertion in the layout
                    static_cast<SwSectionFrame*>(pFrame)->Init();

                    // #i33963#
                    // Do not trust the IsInFootnote flag. If we are currently
                    // building up a table, the upper of pPrv may be a cell
                    // frame, but the cell frame does not have an upper yet.
                    if( pPrv && nullptr != pPrv->ImplFindFootnoteFrame() )
                    {
                        if( pPrv->IsSctFrame() )
                            pPrv = static_cast<SwSectionFrame*>(pPrv)->ContainsContent();
                        if( pPrv && pPrv->IsTextFrame() )
                            static_cast<SwTextFrame*>(pPrv)->Prepare( PrepareHint::QuoVadis, nullptr, false );
                    }
                }

                if (nIndex + 1 == nEndIndex
                        // tdf#136452 may also be needed at end of section
                    || pNode->EndOfSectionIndex() - 1 == nEndIndex)
                {   // tdf#131684 tdf#132236 fix upper of frame moved in
                    // SwUndoDelete; can't be done there unfortunately
                    // because empty section frames are deleted here
                    SwFrame *const pNext(
                        // if there's a parent section, it has been split
                        // into 2 SwSectionFrame already :(
                        (   pFrame->GetNext()
                         && pFrame->GetNext()->IsSctFrame()
                         && pActualSection->GetUpper()
                         && pActualSection->GetUpper()->GetSectionNode() ==
                             static_cast<SwSectionFrame const*>(pFrame->GetNext())->GetSection()->GetFormat()->GetSectionNode())
                        ? static_cast<SwSectionFrame *>(pFrame->GetNext())->ContainsContent()
                        : pFrame->GetNext());
                    if (pNext
                        && pNext->IsTextFrame()
                        && static_cast<SwTextFrame*>(pNext)->GetTextNodeFirst() == rDoc.GetNodes()[nEndIndex]
                        && (pNext->GetUpper() == pFrame->GetUpper()
                            || pFrame->GetNext()->IsSctFrame())) // checked above
                    {
                        pNext->Cut();
                        pNext->InvalidateInfFlags(); // mbInfSct changed
                        // could have columns
                        SwSectionFrame *const pSection(static_cast<SwSectionFrame*>(pFrame));
                        assert(!pSection->Lower() || pSection->Lower()->IsLayoutFrame());
                        SwLayoutFrame *const pParent(pSection->Lower() ? pSection->GetNextLayoutLeaf() : pSection);
                        assert(!pParent->Lower());
                        // paste invalidates, section could have indent...
                        pNext->Paste(pParent, nullptr);
                    }
                }
                // #i27138#
                // notify accessibility paragraphs objects about changed
                // CONTENT_FLOWS_FROM/_TO relation.
                // Relation CONTENT_FLOWS_FROM for next paragraph will change
                // and relation CONTENT_FLOWS_TO for previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                {
                    SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
                    // no notification, if <SwViewShell> is in construction
                    if ( pViewShell && !pViewShell->IsInConstructor() &&
                         pViewShell->GetLayout() &&
                         pViewShell->GetLayout()->IsAnyShellAccessible() &&
                         pFrame->FindPageFrame() != nullptr)
                    {
                        auto pNext = pFrame->FindNextCnt( true );
                        auto pPrev = pFrame->FindPrevCnt();
                        pViewShell->InvalidateAccessibleParaFlowRelation(
                            pNext ? pNext->DynCastTextFrame() : nullptr,
                            pPrev ? pPrev->DynCastTextFrame() : nullptr );
                    }
                }
#endif
                pFrame->CheckDirChange();

                // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
                // for setting position at newly inserted frame
                lcl_SetPos( *pFrame, *pLay );

                // OD 20.11.2002 #105405# - no page, no invalidate.
                if ( pPage )
                {
                    // OD 18.09.2002 #100522#
                    // invalidate page in order to force format and paint of
                    // inserted section frame
                    pFrame->InvalidatePage( pPage );

                    // FME 10.11.2003 #112243#
                    // Invalidate fly content flag:
                    if ( pFrame->IsInFly() )
                        pPage->InvalidateFlyContent();

                    // OD 14.11.2002 #104684# - invalidate page content in order to
                    // force format and paint of section content.
                    pPage->InvalidateContent();
                }

                pLay = static_cast<SwLayoutFrame*>(pFrame);
                if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
                    pLay = pLay->GetNextLayoutLeaf();
                pPrv = nullptr;
            }
        }
        else if ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )
        {
            if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
            {
                assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
                continue; // skip it
            }
            if (pLayout->HasMergedParas() && !pNd->StartOfSectionNode()->IsCreateFrameWhenHidingRedlines())
            {   // tdf#135014 section break in fieldmark (start inside, end outside)
                assert(pNd->StartOfSectionNode()->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
                continue; // skip it
            }
            assert(pActualSection && "Section end without section start?");

            //Close the section, where appropriate activate the surrounding
            //section again.
            if (pActualSection)
            {
                assert(pActualSection->GetSectionNode() == pNd->StartOfSectionNode());
                pActualSection.reset(pActualSection->GetUpper());
            }
            pLay = pLay->FindSctFrame();
            if ( pActualSection )
            {
                //Could be, that the last SectionFrame remains empty.
                //Then now is the time to remove them.
                if ( !pLay->ContainsContent() )
                {
                    SwFrame *pTmpFrame = pLay;
                    pLay = pTmpFrame->GetUpper();
                    pPrv = pTmpFrame->GetPrev();
                    pTmpFrame->RemoveFromLayout();
                    SwFrame::DestroyFrame(pTmpFrame);
                }
                else
                {
                    if (pLay->IsHiddenNow())
                    {
                        newHiddenSections.push_back(static_cast<SwSectionFrame*>(pLay));
                    }
                    pPrv = pLay;
                    pLay = pLay->GetUpper();
                }

                // new section frame
                if (SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame())
                {
                    // Splitting moves the trailing content to the next frame
                    pFrame = pOuterSectionFrame->SplitSect(pActualSection->GetLastPos(), pPrv);

                    // We don't want to leave empty parts back.
                    if (! pOuterSectionFrame->IsColLocked() &&
                        ! pOuterSectionFrame->ContainsContent() )
                    {
                        pOuterSectionFrame->DelEmpty( true );
                        SwFrame::DestroyFrame(pOuterSectionFrame);
                    }
                    else if (pOuterSectionFrame->IsHiddenNow())
                    {
                        newHiddenSections.push_back(pOuterSectionFrame);
                    }
                }
                else
                {
                    pFrame = pActualSection->GetSectionNode()->MakeFrame(
                        pLay, pActualSection->GetSectionNode()->GetSection().IsHiddenFlag());
                    pFrame->InsertBehind( pLay, pPrv );
                    static_cast<SwSectionFrame*>(pFrame)->Init();

                    // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
                    // for setting position at newly inserted frame
                    lcl_SetPos( *pFrame, *pLay );
                }

                pActualSection->SetSectionFrame( static_cast<SwSectionFrame*>(pFrame) );

                pLay = static_cast<SwLayoutFrame*>(pFrame);
                if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
                    pLay = pLay->GetNextLayoutLeaf();
                pPrv = nullptr;
            }
            else
            {
                if (pLay->IsHiddenNow())
                {
                    newHiddenSections.push_back(static_cast<SwSectionFrame*>(pLay));
                }
                //Nothing more with sections, it goes on right behind
                //the SectionFrame.
                pPrv = pLay;
                pLay = pLay->GetUpper();
            }
        }
        else if( pNd->IsStartNode() &&
                 SwFlyStartNode == static_cast<SwStartNode*>(pNd)->GetStartNodeType() )
        {
            if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
            {
                assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
                assert(false); // actually a fly-section can't be deleted?
                continue; // skip it
            }
            if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
            {
                SwFlyFrame* pFly = pLay->FindFlyFrame();
                if( pFly )
                    AppendObjs( pTable, nIndex, pFly, pPage, rDoc );
            }
        }
        else
        {
            assert(!pLayout->HasMergedParas()
                || pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
            // Neither Content nor table nor section, so we are done.
            break;
        }
    }

    if ( pActualSection )
    {
        // Might happen that an empty (Follow-)Section is left over.
        if ( !(pLay = pActualSection->GetSectionFrame())->ContainsContent() )
        {
            pLay->RemoveFromLayout();
            SwFrame::DestroyFrame(pLay);
        }
        pActualSection.reset();
    }

    if ( bPages ) // let the Flys connect to each other
    {
        if ( !isFlyCreationSuppressed )
            AppendAllObjs( pTable, pLayout );
        bObjsDirect = true;
    }

    // do it after AppendAllObjs()
    for (SwSectionFrame * pNew : newHiddenSections)
    {
        for (SwFlowFrame * pSect = pNew; pSect; pSect = pSect->GetPrecede())
        {   // flys were created visible; section may be paginated so iterate
            pSect->GetFrame().HideAndShowObjects();
        }
    }

    if( pPageMaker )
    {
        pPageMaker->CheckFlyCache( pPage );
        pPageMaker.reset();
        if( rDoc.GetLayoutCache() )
        {
#ifdef DBG_UTIL
            rDoc.GetLayoutCache()->CompareLayout( rDoc );
#endif
            rDoc.GetLayoutCache()->ClearImpl();
        }
    }

    rDoc.getIDocumentTimerAccess().UnblockIdling();
    if( bOldCallbackActionEnabled )
        pLayout->SetCallbackActionEnabled( bOldCallbackActionEnabled );
}

void MakeFrames(SwDoc& rDoc, const SwNode& rSttIdx, const SwNode& rEndIdx, const bool _bObjsDirect)
{
    bObjsDirect = _bObjsDirect;

    SwNodeOffset nEndIdx = rEndIdx.GetIndex();
    // TODO for multiple layouts there should be a loop here
    SwNode* pNd = rDoc.GetNodes().FindPrvNxtFrameNode( rSttIdx,
                    rDoc.GetNodes()[ nEndIdx-1 ],
                    rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
    if ( pNd )
    {
        bool bAfter = *pNd < rSttIdx;
        SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() );
        sw::FrameMode eMode = sw::FrameMode::Existing;
        ::std::vector<SwFrame*> frames;
        while (SwFrame* pFrame = aNode2Layout.NextFrame())
        {   // tdf#150500 new frames may be created that end up merged on pNd
            // so copy the currently existing ones; they shouldn't get deleted
            frames.push_back(pFrame);
        }
        for (SwFrame *const pFrame : frames)
        {
            SwLayoutFrame *pUpper = pFrame->GetUpper();
            SwFootnoteFrame* pFootnoteFrame = pUpper->FindFootnoteFrame();
            bool bOldLock, bOldFootnote;
            if( pFootnoteFrame )
            {
                bOldFootnote = pFootnoteFrame->IsColLocked();
                pFootnoteFrame->ColLock();
            }
            else
                bOldFootnote = true;
            SwSectionFrame* pSct = pUpper->FindSctFrame();
            // Inside of footnotes only those areas are interesting that are inside of them. But
            // not the ones (e.g. column areas) in which are the footnote containers positioned.
            // #109767# Table frame is in section, insert section in cell frame.
            if( pSct && ((pFootnoteFrame && !pSct->IsInFootnote()) || pUpper->IsCellFrame()) )
                pSct = nullptr;
            if( pSct )
            {   // to prevent pTmp->MoveFwd from destroying the SectionFrame
                bOldLock = pSct->IsColLocked();
                pSct->ColLock();
            }
            else
                bOldLock = true;

            // If pFrame cannot be moved, it is not possible to move it to the next page. This applies
            // also for frames (in the first column of a frame pFrame is moveable) and column
            // sections of tables (also here pFrame is moveable).
            bool bMoveNext = nEndIdx - rSttIdx.GetIndex() > SwNodeOffset(120);
            bool bAllowMove = !pFrame->IsInFly() && pFrame->IsMoveable() &&
                 (!pFrame->IsInTab() || pFrame->IsTabFrame() );
            if ( bMoveNext && bAllowMove )
            {
                SwFrame *pMove = pFrame;
                SwFrame *pPrev = pFrame->GetPrev();
                SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pMove );
                assert(pTmp);

                if ( bAfter )
                {
                    // The rest of this page should be empty. Thus, the following one has to move to
                    // the next page (it might also be located in the following column).
                    assert(!pTmp->HasFollow() && "prev. node's frame is not last");
                    pPrev = pFrame;
                    // If the surrounding SectionFrame has a "next" one,
                    // so this one needs to be moved as well.
                    pMove = pFrame->GetIndNext();
                    SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pFrame->FindColFrame());
                    if( pCol )
                        pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
                    do
                    {
                        if( pCol && !pMove )
                        {   // No successor so far, look into the next column
                            pMove = pCol->ContainsAny();
                            if( pCol->GetNext() )
                                pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
                            else if( pCol->IsInSct() )
                            {   // If there is no following column but we are in a column frame,
                                // there might be (page) columns outside of it.
                                pCol = static_cast<SwColumnFrame*>(pCol->FindSctFrame()->FindColFrame());
                                if( pCol )
                                    pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
                            }
                            else
                                pCol = nullptr;
                        }
                        // skip invalid SectionFrames
                        while( pMove && pMove->IsSctFrame() &&
                               !static_cast<SwSectionFrame*>(pMove)->GetSection() )
                            pMove = pMove->GetNext();
                    } while( !pMove && pCol );

                    if( pMove )
                    {
                        if ( pMove->IsContentFrame() )
                            pTmp = static_cast<SwContentFrame*>(pMove);
                        else if ( pMove->IsTabFrame() )
                            pTmp = static_cast<SwTabFrame*>(pMove);
                        else if ( pMove->IsSctFrame() )
                        {
                            pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
                            if( pMove )
                                pTmp = SwFlowFrame::CastFlowFrame( pMove );
                            else
                                pTmp = nullptr;
                        }
                    }
                    else
                        pTmp = nullptr;
                }
                else
                {
                    assert(!pTmp->IsFollow() && "next node's frame is not master");
                    // move the _content_ of a section frame
                    if( pMove->IsSctFrame() )
                    {
                        while( pMove && pMove->IsSctFrame() &&
                               !static_cast<SwSectionFrame*>(pMove)->GetSection() )
                            pMove = pMove->GetNext();
                        if( pMove && pMove->IsSctFrame() )
                            pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
                        if( pMove )
                            pTmp = SwFlowFrame::CastFlowFrame( pMove );
                        else
                            pTmp = nullptr;
                    }
                }

                if( pTmp )
                {
                    SwFrame* pOldUp = pTmp->GetFrame().GetUpper();
                    // MoveFwd==true means that we are still on the same page.
                    // But since we want to move if possible!
                    bool bTmpOldLock = pTmp->IsJoinLocked();
                    pTmp->LockJoin();
                    while( pTmp->MoveFwd( true, false, true ) )
                    {
                        if( pOldUp == pTmp->GetFrame().GetUpper() )
                            break;
                        pOldUp = pTmp->GetFrame().GetUpper();
                    }
                    if( !bTmpOldLock )
                        pTmp->UnlockJoin();
                }
                ::InsertCnt_( pUpper, rDoc, rSttIdx.GetIndex(),
                              pFrame->IsInDocBody(), nEndIdx, pPrev, eMode );
            }
            else
            {
                SwFrame* pPrv = bAfter ? pFrame : pFrame->GetPrev();

                ::InsertCnt_( pUpper, rDoc, rSttIdx.GetIndex(), false,
                              nEndIdx, pPrv, eMode );
                // OD 23.06.2003 #108784# - correction: append objects doesn't
                // depend on value of <bAllowMove>
                if( !isFlyCreationSuppressed )
                {
                    const sw::SpzFrameFormats* pTable = rDoc.GetSpzFrameFormats();
                    if( !pTable->empty() )
                        AppendAllObjs( pTable, pUpper );
                }

                if( pFrame->IsInFly() )
                    pFrame->FindFlyFrame()->Invalidate_();
                if( pFrame->IsInTab() )
                    pFrame->InvalidateSize();
            }

            SwPageFrame *pPage = pUpper->FindPageFrame();
            SwFrame::CheckPageDescs( pPage, false );
            if( !bOldFootnote )
                pFootnoteFrame->ColUnlock();
            if( !bOldLock )
            {
                pSct->ColUnlock();
                // pSct might be empty (e.g. when inserting linked section containing further
                // sections) and can be destroyed in such cases.
                if( !pSct->ContainsContent() )
                {
                    pSct->DelEmpty( true );
                    pUpper->getRootFrame()->RemoveFromList( pSct );
                    SwFrame::DestroyFrame(pSct);
                }
            }
            eMode = sw::FrameMode::New; // use Existing only once!
        }
    }

    bObjsDirect = true;
}

SwBorderAttrs::SwBorderAttrs(const sw::BorderCacheOwner* pOwner, const SwFrame* pConstructor)
    : SwCacheObj(pOwner)
    , m_rAttrSet(pConstructor->IsContentFrame()
                    ? pConstructor->IsTextFrame()
                        ? static_cast<const SwTextFrame*>(pConstructor)->GetTextNodeForParaProps()->GetSwAttrSet()
                        : static_cast<const SwNoTextFrame*>(pConstructor)->GetNode()->GetSwAttrSet()
                    : static_cast<const SwLayoutFrame*>(pConstructor)->GetFormat()->GetAttrSet())
    , m_rUL(m_rAttrSet.GetULSpace())
    , m_rBox(m_rAttrSet.GetBox())
    , m_rShadow(m_rAttrSet.GetShadow())
    , m_aFrameSize(m_rAttrSet.GetFrameSize().GetSize())
    , m_bIsLine(false)
    , m_bJoinedWithPrev(false)
    , m_bJoinedWithNext(false)
    , m_nTopLine(0)
    , m_nBottomLine(0)
    , m_nLeftLine(0)
    , m_nRightLine(0)
    , m_nTop(0)
    , m_nBottom(0)
    , m_nGetTopLine(0)
    , m_nGetBottomLine(0)
    , m_nLineSpacing(0)
{
    // #i96772#
    const SwTextFrame* pTextFrame = pConstructor->DynCastTextFrame();
    if ( pTextFrame )
    {
        m_pFirstLineIndent.reset(m_rAttrSet.GetFirstLineIndent().Clone());
        m_pTextLeftMargin.reset(m_rAttrSet.GetTextLeftMargin().Clone());
        m_pRightMargin.reset(m_rAttrSet.GetRightMargin().Clone());
        assert(m_pFirstLineIndent);
        assert(m_pTextLeftMargin);
    }
    else
    {
        // LRSpaceItem is copied due to the possibility that it is adjusted
        m_xLR.reset(m_rAttrSet.GetLRSpace().Clone());
        if (pConstructor->IsNoTextFrame())
        {
            m_xLR = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
        }
        assert(m_xLR);
    }

    // Caution: The USHORTs for the cached values are not initialized by intention!

    // everything needs to be calculated at least once:
    m_bTopLine = m_bBottomLine = m_bLeftLine = m_bRightLine =
    m_bTop     = m_bBottom     = m_bLine   = true;

    // except this one: calculate line spacing before cell border only for text frames
    m_bLineSpacing = bool(pTextFrame);

    m_bCacheGetLine = m_bCachedGetTopLine = m_bCachedGetBottomLine = false;
    // OD 21.05.2003 #108789# - init cache status for values <m_bJoinedWithPrev>
    // and <m_bJoinedWithNext>, which aren't initialized by default.
    m_bCachedJoinedWithPrev = false;
    m_bCachedJoinedWithNext = false;
}

SwBorderAttrs::~SwBorderAttrs()
{
    const_cast<sw::BorderCacheOwner*>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = false;
}

/* All calc methods calculate a safety distance in addition to the values given by the attributes.
 * This safety distance is only added when working with borders and/or shadows to prevent that
 * e.g. borders are painted over.
 */

void SwBorderAttrs::CalcTop_()
{
    m_nTop = CalcTopLine() + m_rUL.GetUpper();

    bool bGutterAtTop = m_rAttrSet.GetDoc().getIDocumentSettingAccess().get(
        DocumentSettingId::GUTTER_AT_TOP);
    if (bGutterAtTop && m_xLR)
    {
        // Decrease the print area: the top space is the sum of top and gutter margins.
        m_nTop += m_xLR->GetGutterMargin();
    }

    m_bTop = false;
}

void SwBorderAttrs::CalcBottom_()
{
    m_nBottom = CalcBottomLine() + m_rUL.GetLower();
    m_bBottom = false;
}

tools::Long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const
{
    tools::Long nRight=0;

    if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING)) {
    // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
    // and right border are painted on the right respectively left.
    if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
        nRight = CalcLeftLine();
    else
        nRight = CalcRightLine();

    }
    // for paragraphs, "left" is "before text" and "right" is "after text"
    if (pCaller->IsTextFrame())
    {
        // tdf#163913: Only apply the fixed-width part of the margin here.
        // Font-relative margins will be applied as an adjustment later on.
        if (pCaller->IsRightToLeft())
        {
            nRight += m_pTextLeftMargin->ResolveLeftFixedPart(*m_pFirstLineIndent);
        }
        else
        {
            nRight += m_pRightMargin->ResolveRightFixedPart();
        }
    }
    else
        nRight += m_xLR->ResolveRight({});

    // correction: retrieve left margin for numbering in R2L-layout
    if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() )
    {
        nRight += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
    }

    if (pCaller->IsPageFrame())
    {
        const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
        bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
            DocumentSettingId::GUTTER_AT_TOP);
        if (!bGutterAtTop)
        {
            bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
            tools::Long nGutterMargin = bRtlGutter ? m_xLR->GetGutterMargin() : m_xLR->GetRightGutterMargin();
            // Decrease the print area: the right space is the sum of right and right gutter
            // margins.
            nRight += nGutterMargin;
        }
    }

    return nRight;
}

tools::Long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const
{
    tools::Long nLeft=0;

    if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING))
    {
        // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
        // and right border are painted on the right respectively left.
        if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
            nLeft = CalcRightLine();
        else
            nLeft = CalcLeftLine();
    }

    // for paragraphs, "left" is "before text" and "right" is "after text"

    // tdf#163913: Only apply the fixed-width part of the margin here.
    // Font-relative margins will be applied as an adjustment later on.
    if (pCaller->IsTextFrame() && pCaller->IsRightToLeft())
    {
        nLeft += m_pRightMargin->ResolveRightFixedPart();
    }
    else
    {
        if (pCaller->IsTextFrame())
        {
            nLeft += m_pTextLeftMargin->ResolveLeftFixedPart(*m_pFirstLineIndent);
        }
        else
        {
            nLeft += m_xLR->ResolveLeft({});
        }
    }

    // correction: do not retrieve left margin for numbering in R2L-layout
    if ( pCaller->IsTextFrame() && !pCaller->IsRightToLeft() )
    {
        nLeft += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
    }

    if (pCaller->IsPageFrame())
    {
        const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
        bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
            DocumentSettingId::GUTTER_AT_TOP);
        if (!bGutterAtTop)
        {
            bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
            tools::Long nGutterMargin = bRtlGutter ? m_xLR->GetRightGutterMargin() : m_xLR->GetGutterMargin();
            // Decrease the print area: the left space is the sum of left and gutter margins.
            nLeft += nGutterMargin;
        }
    }

    return nLeft;
}

/* Calculated values for borders and shadows.
 * It might be that a distance is wanted even without lines. This will be
 * considered here and not by the attribute (e.g. bBorderDist for cells).
 */

void SwBorderAttrs::CalcTopLine_()
{
    m_nTopLine = m_rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true );
    m_nTopLine = m_nTopLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::TOP);
    m_bTopLine = false;
}

void SwBorderAttrs::CalcBottomLine_()
{
    m_nBottomLine = m_rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM, true );
    m_nBottomLine = m_nBottomLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM);
    m_bBottomLine = false;
}

void SwBorderAttrs::CalcLeftLine_()
{
    m_nLeftLine = m_rBox.CalcLineSpace( SvxBoxItemLine::LEFT, true);
    m_nLeftLine = m_nLeftLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT);
    m_bLeftLine = false;
}

void SwBorderAttrs::CalcRightLine_()
{
    m_nRightLine = m_rBox.CalcLineSpace( SvxBoxItemLine::RIGHT, true );
    m_nRightLine = m_nRightLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT);
    m_bRightLine = false;
}

void SwBorderAttrs::IsLine_()
{
    m_bIsLine = m_rBox.GetTop() || m_rBox.GetBottom() ||
              m_rBox.GetLeft()|| m_rBox.GetRight();
    m_bLine = false;
}

/* The borders of neighboring paragraphs are condensed by following algorithm:
 *
 * 1. No top border if the predecessor has the same top border and (3) applies.
 *    In addition, the paragraph needs to have a border at least one side (left/right/bottom).
 * 2. No bottom border if the successor has the same bottom border and (3) applies.
 *    In addition, the paragraph needs to have a border at least one side (left/right/top).
 * 3. The borders on the left and right side are identical between the current and the
 *    pre-/succeeding paragraph.
 */

static bool CmpLines( const editeng::SvxBorderLine *pL1, const editeng::SvxBorderLine *pL2 )
{
    return ( ((pL1 && pL2) && (*pL1 == *pL2)) || (!pL1 && !pL2) );
}

// OD 21.05.2003 #108789# - change name of 1st parameter - "rAttrs" -> "rCmpAttrs"
// OD 21.05.2003 #108789# - compare <CalcRight()> and <rCmpAttrs.CalcRight()>
//          instead of only the right LR-spacing, because R2L-layout has to be
//          considered.
bool SwBorderAttrs::CmpLeftRight( const SwBorderAttrs &rCmpAttrs,
                                  const SwFrame *pCaller,
                                  const SwFrame *pCmp ) const
{
    return ( CmpLines( rCmpAttrs.GetBox().GetLeft(), GetBox().GetLeft()  ) &&
             CmpLines( rCmpAttrs.GetBox().GetRight(),GetBox().GetRight() ) &&
             CalcLeft( pCaller ) == rCmpAttrs.CalcLeft( pCmp ) &&
             // OD 21.05.2003 #108789# - compare <CalcRight> with <rCmpAttrs.CalcRight>.
             CalcRight( pCaller ) == rCmpAttrs.CalcRight( pCmp ) );
}

bool SwBorderAttrs::JoinWithCmp( const SwFrame& _rCallerFrame,
                                  const SwFrame& _rCmpFrame ) const
{
    bool bReturnVal = false;

    SwBorderAttrAccess aCmpAccess( SwFrame::GetCache(), &_rCmpFrame );
    const SwBorderAttrs &rCmpAttrs = *aCmpAccess.Get();
    if ( m_rShadow == rCmpAttrs.GetShadow() &&
         CmpLines( m_rBox.GetTop(), rCmpAttrs.GetBox().GetTop() ) &&
         CmpLines( m_rBox.GetBottom(), rCmpAttrs.GetBox().GetBottom() ) &&
         CmpLeftRight( rCmpAttrs, &_rCallerFrame, &_rCmpFrame )
       )
    {
        bReturnVal = true;
    }

    return bReturnVal;
}

// OD 21.05.2003 #108789# - method to determine, if borders are joined with
// previous frame. Calculated value saved in cached value <m_bJoinedWithPrev>
// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>
void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame,
                                         const SwFrame* _pPrevFrame ) const
{
    // set default
    m_bJoinedWithPrev = false;

    if ( _rFrame.IsTextFrame() )
    {
        // text frame can potentially join with previous text frame, if
        // corresponding attribute set is set at previous text frame.
        // OD 2004-02-26 #i25029# - If parameter <_pPrevFrame> is set, take this
        // one as previous frame.
        const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev();
        // OD 2004-02-13 #i25029# - skip hidden text frames.
        while (pPrevFrame && pPrevFrame->IsHiddenNow())
        {
            pPrevFrame = pPrevFrame->GetPrev();
        }
        if ( pPrevFrame && pPrevFrame->IsTextFrame() &&
             pPrevFrame->GetAttrSet()->GetParaConnectBorder().GetValue()
           )
        {
            m_bJoinedWithPrev = JoinWithCmp( _rFrame, *pPrevFrame );
        }
    }

    // valid cache status, if demanded
    // OD 2004-02-26 #i25029# - Do not validate cache, if parameter <_pPrevFrame>
    // is set.
    m_bCachedJoinedWithPrev = m_bCacheGetLine && !_pPrevFrame;
}

// OD 21.05.2003 #108789# - method to determine, if borders are joined with
// next frame. Calculated value saved in cached value <m_bJoinedWithNext>
void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame ) const
{
    // set default
    m_bJoinedWithNext = false;

    if ( _rFrame.IsTextFrame() )
    {
        // text frame can potentially join with next text frame, if
        // corresponding attribute set is set at current text frame.
        // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames.
        const SwFrame* pNextFrame = _rFrame.GetNext();
        while (pNextFrame && pNextFrame->IsHiddenNow())
        {
            pNextFrame = pNextFrame->GetNext();
        }
        if ( pNextFrame && pNextFrame->IsTextFrame() &&
             _rFrame.GetAttrSet()->GetParaConnectBorder().GetValue()
           )
        {
            m_bJoinedWithNext = JoinWithCmp( _rFrame, *pNextFrame );
        }
    }

    // valid cache status, if demanded
    m_bCachedJoinedWithNext = m_bCacheGetLine;
}

// OD 21.05.2003 #108789# - accessor for cached values <m_bJoinedWithPrev>
// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>, which is passed to
// method <_CalcJoindWithPrev(..)>.
bool SwBorderAttrs::JoinedWithPrev( const SwFrame& _rFrame,
                                    const SwFrame* _pPrevFrame ) const
{
    if ( !m_bCachedJoinedWithPrev || _pPrevFrame )
    {
        // OD 2004-02-26 #i25029# - pass <_pPrevFrame> as 2nd parameter
        CalcJoinedWithPrev( _rFrame, _pPrevFrame );
    }

    return m_bJoinedWithPrev;
}

bool SwBorderAttrs::JoinedWithNext( const SwFrame& _rFrame ) const
{
    if ( !m_bCachedJoinedWithNext )
    {
        CalcJoinedWithNext( _rFrame );
    }

    return m_bJoinedWithNext;
}

// OD 2004-02-26 #i25029# - added 2nd parameter <_pPrevFrame>, which is passed to
// method <JoinedWithPrev>
void SwBorderAttrs::GetTopLine_( const SwFrame& _rFrame,
                                 const SwFrame* _pPrevFrame )
{
    sal_uInt16 nRet = CalcTopLine();

    // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
    // OD 2004-02-26 #i25029# - add 2nd parameter
    if ( JoinedWithPrev( _rFrame, _pPrevFrame ) )
    {
        nRet = 0;
    }

    m_bCachedGetTopLine = m_bCacheGetLine;

    m_nGetTopLine = nRet;
}

void SwBorderAttrs::GetBottomLine_( const SwFrame& _rFrame )
{
    sal_uInt16 nRet = CalcBottomLine();

    // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
    if ( JoinedWithNext( _rFrame ) )
    {
        nRet = 0;
    }

    m_bCachedGetBottomLine = m_bCacheGetLine;

    m_nGetBottomLine = nRet;
}

void SwBorderAttrs::CalcLineSpacing_()
{
    // tdf#125300 compatibility option AddParaLineSpacingToTableCells needs also line spacing
    const SvxLineSpacingItem &rSpace = m_rAttrSet.GetLineSpacing();
    if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop && rSpace.GetPropLineSpace() > 100 )
    {
        sal_Int32 nFontSize = m_rAttrSet.Get(RES_CHRATR_FONTSIZE).GetHeight();
        m_nLineSpacing = nFontSize * (rSpace.GetPropLineSpace() - 100) * 1.15 / 100;
    }
    m_bLineSpacing = false;
}

static sw::BorderCacheOwner const* GetBorderCacheOwner(SwFrame const& rFrame)
{
    return rFrame.IsContentFrame()
        ? static_cast<sw::BorderCacheOwner const*>(rFrame.IsTextFrame()
        // sw_redlinehide: presumably this caches the border attrs at the model level and can be shared across different layouts so we want the ParaProps node here
            ? static_cast<const SwTextFrame&>(rFrame).GetTextNodeForParaProps()
            : static_cast<const SwNoTextFrame&>(rFrame).GetNode())
        : static_cast<sw::BorderCacheOwner const*>(static_cast<const SwLayoutFrame&>(rFrame).GetFormat());
}

SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) :
    SwCacheAccess( rCach,
        static_cast<void const *>(GetBorderCacheOwner(*pFrame)),
        GetBorderCacheOwner(*pFrame)->IsInCache()),
    m_pConstructor( pFrame )
{
}

SwCacheObj *SwBorderAttrAccess::NewObj()
{
    const_cast<sw::BorderCacheOwner *>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = true;
    return new SwBorderAttrs( static_cast<sw::BorderCacheOwner const *>(m_pOwner), m_pConstructor );
}

SwBorderAttrs *SwBorderAttrAccess::Get()
{
    return static_cast<SwBorderAttrs*>(SwCacheAccess::Get());
}

SwOrderIter::SwOrderIter( const SwPageFrame *pPg ) :
    m_pPage( pPg ),
    m_pCurrent( nullptr )
{
}

void SwOrderIter::Top()
{
    m_pCurrent = nullptr;
    if ( !m_pPage->GetSortedObjs() )
        return;

    const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
    if ( !pObjs->size() )
        return;

    sal_uInt32 nTopOrd = 0;
    (*pObjs)[0]->GetDrawObj()->GetOrdNum();  // force updating
    for (SwAnchoredObject* i : *pObjs)
    {
        const SdrObject* pObj = i->GetDrawObj();
        if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ==  nullptr )
            continue;
        sal_uInt32 nTmp = pObj->GetOrdNumDirect();
        if ( nTmp >= nTopOrd )
        {
            nTopOrd = nTmp;
            m_pCurrent = pObj;
        }
    }
}

const SdrObject *SwOrderIter::Bottom()
{
    m_pCurrent = nullptr;
    if ( m_pPage->GetSortedObjs() )
    {
        sal_uInt32 nBotOrd = USHRT_MAX;
        const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
        if ( pObjs->size() )
        {
            (*pObjs)[0]->GetDrawObj()->GetOrdNum();  // force updating
            for (SwAnchoredObject* i : *pObjs)
            {
                const SdrObject* pObj = i->GetDrawObj();
                if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ==  nullptr )
                    continue;
                sal_uInt32 nTmp = pObj->GetOrdNumDirect();
                if ( nTmp < nBotOrd )
                {
                    nBotOrd = nTmp;
                    m_pCurrent = pObj;
                }
            }
        }
    }
    return m_pCurrent;
}

const SdrObject *SwOrderIter::Next()
{
    const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
    m_pCurrent = nullptr;
    if ( m_pPage->GetSortedObjs() )
    {
        sal_uInt32 nOrd = USHRT_MAX;
        const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
        if ( pObjs->size() )
        {
            (*pObjs)[0]->GetDrawObj()->GetOrdNum();  // force updating
            for (SwAnchoredObject* i : *pObjs)
            {
                const SdrObject* pObj = i->GetDrawObj();
                if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ==  nullptr )
                    continue;
                sal_uInt32 nTmp = pObj->GetOrdNumDirect();
                if ( nTmp > nCurOrd && nTmp < nOrd )
                {
                    nOrd = nTmp;
                    m_pCurrent = pObj;
                }
            }
        }
    }
    return m_pCurrent;
}

void SwOrderIter::Prev()
{
    const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
    m_pCurrent = nullptr;
    if ( !m_pPage->GetSortedObjs() )
        return;

    const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
    if ( !pObjs->size() )
        return;

    sal_uInt32 nOrd = 0;
    (*pObjs)[0]->GetDrawObj()->GetOrdNum();  // force updating
    for (SwAnchoredObject* i : *pObjs)
    {
        const SdrObject* pObj = i->GetDrawObj();
        if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ==  nullptr )
            continue;
        sal_uInt32 nTmp = pObj->GetOrdNumDirect();
        if ( nTmp < nCurOrd && nTmp >= nOrd )
        {
            nOrd = nTmp;
            m_pCurrent = pObj;
        }
    }
}

/// Keep and restore the substructure of a layout frame for an action.
// New algorithm:
//   Do not look at each neighbor one by one to set all pointers correctly.
//   It is sufficient to detach a part of a chain and check if another chain needs to be added
//   when attaching it again. Only the pointers necessary for the chain connection need to be
//   adjusted. The correction happens in RestoreContent(). In between all access is restricted.
//   During this action, the Flys are detached from the page.

// #115759# - 'remove' also drawing object from page and
// at-fly anchored objects from page
static void lcl_RemoveObjsFromPage( SwFrame* _pFrame )
{
    OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_RemoveObjsFromPage." );
    SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
    for (SwAnchoredObject* pObj : rObjs)
    {
        // #115759# - reset member, at which the anchored
        // object orients its vertical position
        pObj->ClearVertPosOrientFrame();
        // #i43913#
        pObj->ResetLayoutProcessBools();
        // #115759# - remove also lower objects of as-character
        // anchored Writer fly frames from page
        if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
        {
            // #115759# - remove also direct lowers of Writer
            // fly frame from page
            if ( pFlyFrame->GetDrawObjs() )
            {
                ::lcl_RemoveObjsFromPage( pFlyFrame );
            }

            SwContentFrame* pCnt = pFlyFrame->ContainsContent();
            while ( pCnt )
            {
                if ( pCnt->GetDrawObjs() )
                    ::lcl_RemoveObjsFromPage( pCnt );
                pCnt = pCnt->GetNextContentFrame();
            }
            if ( pFlyFrame->IsFlyFreeFrame() )
            {
                // #i28701# - use new method <GetPageFrame()>
                if (SwPageFrame *pPg = pFlyFrame->GetPageFrame())
                    pPg->RemoveFlyFromPage(pFlyFrame);
            }
        }
        // #115759# - remove also drawing objects from page
        else if ( auto pDrawObj = dynamic_cast<SwAnchoredDrawObject*>( pObj) )
        {
            if (pObj->GetFrameFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
            {
                if (SwPageFrame *pPg = pObj->GetPageFrame())
                    pPg->RemoveDrawObjFromPage( *pDrawObj );
            }
        }
    }
}

SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart )
{
    if( pLay->IsSctFrame() && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
        sw_RemoveFootnotes( static_cast<SwColumnFrame*>(pLay->Lower()), true, true );

    SwFrame *pSav = pLay->ContainsAny();
    if ( nullptr == pSav )
        return nullptr;

    if( pSav->IsInFootnote() && !pLay->IsInFootnote() )
    {
        do
            pSav = pSav->FindNext();
        while( pSav && pSav->IsInFootnote() );
        if( !pSav || !pLay->IsAnLower( pSav ) )
            return nullptr;
    }

    // Tables should be saved as a whole, exception:
    // The contents of a section or a cell inside a table should be saved
    if ( pSav->IsInTab() && !( ( pLay->IsSctFrame() || pLay->IsCellFrame() ) && pLay->IsInTab() ) )
        while ( !pSav->IsTabFrame() )
            pSav = pSav->GetUpper();

    if( pSav->IsInSct() )
    { // search the upmost section inside of pLay
        SwFrame* pSect = pLay->FindSctFrame();
        SwFrame *pTmp = pSav;
        do
        {
            pSav = pTmp;
            pTmp = (pSav && pSav->GetUpper()) ? pSav->GetUpper()->FindSctFrame() : nullptr;
        } while ( pTmp != pSect );
    }

    SwFrame *pFloat = pSav;
    if( !pStart )
        pStart = pSav;
    bool bGo = pStart == pSav;
    do
    {
        if( bGo )
            pFloat->GetUpper()->m_pLower = nullptr; // detach the chain part

        // search the end of the chain part, remove Flys on the way
        do
        {
            if( bGo )
            {
                if ( pFloat->IsContentFrame() )
                {
                    if ( pFloat->GetDrawObjs() )
                        ::lcl_RemoveObjsFromPage( static_cast<SwContentFrame*>(pFloat) );
                }
                else if ( pFloat->IsTabFrame() || pFloat->IsSctFrame() )
                {
                    SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(pFloat)->ContainsContent();
                    if( pCnt )
                    {
                        do
                        {   if ( pCnt->GetDrawObjs() )
                                ::lcl_RemoveObjsFromPage( pCnt );
                            pCnt = pCnt->GetNextContentFrame();
                        } while ( pCnt && static_cast<SwLayoutFrame*>(pFloat)->IsAnLower( pCnt ) );
                    }
                }
                else {
                    OSL_ENSURE( !pFloat, "new FloatFrame?" );
                }
            }
            if ( pFloat->GetNext()  )
            {
                if( bGo )
                    pFloat->mpUpper = nullptr;
                pFloat = pFloat->GetNext();
                if( !bGo && pFloat == pStart )
                {
                    bGo = true;
                    pFloat->mpPrev->mpNext = nullptr;
                    pFloat->mpPrev = nullptr;
                }
            }
            else
                break;

        } while ( pFloat );

        // search next chain part and connect both chains
        SwFrame *pTmp = pFloat ? pFloat->FindNext() : nullptr;
        if (bGo && pFloat)
            pFloat->mpUpper = nullptr;

        if( !pLay->IsInFootnote() )
            while( pTmp && pTmp->IsInFootnote() )
                pTmp = pTmp->FindNext();

        if ( !pLay->IsAnLower( pTmp ) )
            pTmp = nullptr;

        if ( pTmp && bGo )
        {
            pFloat->mpNext = pTmp; // connect both chains
            pFloat->mpNext->mpPrev = pFloat;
        }
        pFloat = pTmp;
        bGo = bGo || ( pStart == pFloat );
    }  while ( pFloat );

    return bGo ? pStart : nullptr;
}

// #115759# - add also drawing objects to page and at-fly
// anchored objects to page
static void lcl_AddObjsToPage( SwFrame* _pFrame, SwPageFrame* _pPage )
{
    OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_AddObjsToPage." );
    SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
    for (SwAnchoredObject* pObj : rObjs)
    {
        // #115759# - unlock position of anchored object
        // in order to get the object's position calculated.
        pObj->UnlockPosition();
        // #115759# - add also lower objects of as-character
        // anchored Writer fly frames from page
        if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
        {
            if (pFlyFrame->IsFlyFreeFrame())
            {
                _pPage->AppendFlyToPage( pFlyFrame );
            }
            pFlyFrame->InvalidatePos_();
            pFlyFrame->InvalidateSize_();
            pFlyFrame->InvalidatePage( _pPage );

            // #115759# - add also at-fly anchored objects
            // to page
            if ( pFlyFrame->GetDrawObjs() )
            {
                ::lcl_AddObjsToPage( pFlyFrame, _pPage );
            }

            SwContentFrame *pCnt = pFlyFrame->ContainsContent();
            while ( pCnt )
            {
                if ( pCnt->GetDrawObjs() )
                    ::lcl_AddObjsToPage( pCnt, _pPage );
                pCnt = pCnt->GetNextContentFrame();
            }
        }
        // #115759# - remove also drawing objects from page
        else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) !=  nullptr )
        {
            if (pObj->GetFrameFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
            {
                pObj->InvalidateObjPos();
                _pPage->AppendDrawObjToPage(
                                *static_cast<SwAnchoredDrawObject*>(pObj) );
            }
        }
    }
}

void RestoreContent( SwFrame *pSav, SwLayoutFrame *pParent, SwFrame *pSibling )
{
    assert(pSav && pParent && "no Save or Parent provided for RestoreContent.");
    SwRectFnSet aRectFnSet(*pParent);

    // If there are already FlowFrames below the new parent, so add the chain (starting with pSav)
    // after the last one. The parts are inserted and invalidated if needed.
    // On the way, the Flys of the ContentFrames are registered at the page.

    SwPageFrame *pPage = pParent->FindPageFrame();

    if ( pPage )
        pPage->InvalidatePage( pPage );

    // determine predecessor and establish connection or initialize
    pSav->mpPrev = pSibling;
    SwFrame* pNxt;
    if ( pSibling )
    {
        pNxt = pSibling->mpNext;
        pSibling->mpNext = pSav;
        pSibling->InvalidatePrt_();
        pSibling->InvalidatePage( pPage );
        SwFlowFrame *pFlowFrame = dynamic_cast<SwFlowFrame*>(pSibling);
        if (pFlowFrame && pFlowFrame->GetFollow())
            pSibling->Prepare( PrepareHint::Clear, nullptr, false );
    }
    else
    {   pNxt = pParent->m_pLower;
        pParent->m_pLower = pSav;
        pSav->mpUpper = pParent; // set here already, so that it is explicit when invalidating

        if ( pSav->IsContentFrame() )
            static_cast<SwContentFrame*>(pSav)->InvalidatePage( pPage );
        else
        {   // pSav might be an empty SectFrame
            SwContentFrame* pCnt = pParent->ContainsContent();
            if( pCnt )
                pCnt->InvalidatePage( pPage );
        }
    }

    // the parent needs to grow appropriately
    SwTwips nGrowVal = 0;
    SwFrame* pLast;
    do
    {   pSav->mpUpper = pParent;
        nGrowVal += aRectFnSet.GetHeight(pSav->getFrameArea());
        pSav->InvalidateAll_();

        // register Flys, if TextFrames than also invalidate appropriately
        if ( pSav->IsContentFrame() )
        {
            if ( pSav->IsTextFrame() &&
                 static_cast<SwTextFrame*>(pSav)->HasPara() )
                static_cast<SwTextFrame*>(pSav)->Init();  // I am its friend

            if ( pPage && pSav->GetDrawObjs() )
                ::lcl_AddObjsToPage( static_cast<SwContentFrame*>(pSav), pPage );
        }
        else
        {   SwContentFrame *pBlub = static_cast<SwLayoutFrame*>(pSav)->ContainsContent();
            if( pBlub )
            {
                do
                {   if ( pPage && pBlub->GetDrawObjs() )
                        ::lcl_AddObjsToPage( pBlub, pPage );
                    if( pBlub->IsTextFrame() && static_cast<SwTextFrame*>(pBlub)->HasFootnote() &&
                         static_cast<SwTextFrame*>(pBlub)->HasPara() )
                        static_cast<SwTextFrame*>(pBlub)->Init(); // I am its friend
                    pBlub = pBlub->GetNextContentFrame();
                } while ( pBlub && static_cast<SwLayoutFrame*>(pSav)->IsAnLower( pBlub ));
            }
        }
        pLast = pSav;
        pSav = pSav->GetNext();

    } while ( pSav );

    if( pNxt )
    {
        pLast->mpNext = pNxt;
        pNxt->mpPrev = pLast;
    }

    pParent->Grow( nGrowVal );
}

namespace sw {

bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 const nPageNum)
{
    assert(rLayout.GetLower());
    // unfortunately can only get SwPageDesc, not SwFormatPageDesc here...
    auto const nFirstVirtPageNum(rLayout.GetLower()->GetVirtPageNum());
    bool const isFirstPageOfLayoutOdd(nFirstVirtPageNum % 2 == 1);
    return ((nPageNum % 2) == 1) == isFirstPageOfLayoutOdd;
}

} // namespace sw

SwPageFrame * InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper,
        bool const isRightPage, bool const bFirst, bool bInsertEmpty,
        bool const bFootnote,
        SwFrame *pSibling,
        bool const bVeryFirstPage )
{
    assert(pUpper);
    assert(pUpper->IsRootFrame());
    assert(!pSibling || static_cast<SwLayoutFrame const*>(pUpper)->Lower() != pSibling); // currently no insert before 1st page
    SwPageFrame *pRet;
    SwDoc& rDoc = static_cast<SwLayoutFrame*>(pUpper)->GetFormat()->GetDoc();
    if (bFirst)
    {
        if (rDesc.IsFirstShared())
        {
            // We need to fallback to left or right page format, decide it now.
            // FIXME: is this still needed?
            if (isRightPage)
            {
                rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetHeader() );
                rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetFooter() );
                // fdo#60250 copy margins for mirrored pages
                rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetLRSpace() );
            }
            else
            {
                rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetHeader() );
                rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetFooter() );
                rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetLRSpace() );
            }
        }
    }
    SwFrameFormat *pFormat(isRightPage ? rDesc.GetRightFormat(bFirst) : rDesc.GetLeftFormat(bFirst));
    // If there is no FrameFormat for this page, add an empty page
    if ( !pFormat )
    {
        pFormat = isRightPage ? rDesc.GetLeftFormat(bVeryFirstPage) : rDesc.GetRightFormat(bVeryFirstPage);
        OSL_ENSURE( pFormat, "Descriptor without any format?!" );
        bInsertEmpty = !bInsertEmpty;
    }
    if( bInsertEmpty )
    {
        SwPageDesc *pTmpDesc = pSibling && pSibling->GetPrev() ?
                static_cast<SwPageFrame*>(pSibling->GetPrev())->GetPageDesc() : &rDesc;
        pRet = new SwPageFrame( rDoc.GetEmptyPageFormat(), pUpper, pTmpDesc );
        SAL_INFO( "sw.pageframe", "InsertNewPage - insert empty p: " << pRet << " d: " << pTmpDesc );
        pRet->Paste( pUpper, pSibling );
        pRet->PreparePage( bFootnote );
    }
    pRet = new SwPageFrame( pFormat, pUpper, &rDesc );
    SAL_INFO( "sw.pageframe", "InsertNewPage p: " << pRet << " d: " << &rDesc << " f: " << pFormat );
    pRet->Paste( pUpper, pSibling );
    pRet->PreparePage( bFootnote );
    if ( pRet->GetNext() )
        SwRootFrame::AssertPageFlys( pRet );
    return pRet;
}

/* The following two methods search the layout structure recursively and
 * register all Flys at the page that have a Frame in this structure as an anchor.
 */

static void lcl_Regist( SwPageFrame *pPage, const SwFrame *pAnch )
{
    SwSortedObjs *pObjs = const_cast<SwSortedObjs*>(pAnch->GetDrawObjs());
    for (SwAnchoredObject* pObj : *pObjs)
    {
        if (SwFlyFrame* pFly = pObj->DynCastFlyFrame())
        {
            // register (not if already known)
            // #i28701# - use new method <GetPageFrame()>
            SwPageFrame *pPg = pFly->IsFlyFreeFrame()
                             ? pFly->GetPageFrame() : pFly->FindPageFrame();
            if ( pPg != pPage )
            {
                if ( pPg )
                    pPg->RemoveFlyFromPage( pFly );
                pPage->AppendFlyToPage( pFly );
            }
            ::RegistFlys( pPage, pFly );
        }
        else
        {
            // #i87493#
            if ( pPage != pObj->GetPageFrame() )
            {
                pObj->RegisterAtPage(*pPage);
            }
        }

        const SwFlyFrame* pFly = pAnch->FindFlyFrame();
        if ( pFly &&
             pObj->GetDrawObj()->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() &&
             pObj->GetDrawObj()->getSdrPageFromSdrObject() )
        {
            //#i119945# set pFly's OrdNum to pObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
            pObj->DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(),
                                                         pObj->GetDrawObj()->GetOrdNumDirect() );
        }
    }
}

void RegistFlys( SwPageFrame *pPage, const SwLayoutFrame *pLay )
{
    if ( pLay->GetDrawObjs() )
        ::lcl_Regist( pPage, pLay );
    const SwFrame *pFrame = pLay->Lower();
    while ( pFrame )
    {
        if ( pFrame->IsLayoutFrame() )
            ::RegistFlys( pPage, static_cast<const SwLayoutFrame*>(pFrame) );
        else if ( pFrame->GetDrawObjs() )
            ::lcl_Regist( pPage, pFrame );
        pFrame = pFrame->GetNext();
    }
}

/// Notify the background based on the difference between old and new rectangle
void Notify( SwFlyFrame *pFly, SwPageFrame *pOld, const SwRect &rOld,
             const SwRect* pOldPrt )
{
    const SwRect aFrame( pFly->GetObjRectWithSpaces() );
    if ( rOld.Pos() != aFrame.Pos() )
    {   // changed position, invalidate old and new area
        if (rOld.HasArea()
            && rOld.Left() + pFly->GetFormat()->GetLRSpace().ResolveLeft({}) < FAR_AWAY)
        {
            pFly->NotifyBackground( pOld, rOld, PrepareHint::FlyFrameLeave );
        }
        pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
    }
    else if ( rOld.SSize() != aFrame.SSize() )
    {   // changed size, invalidate the area that was left or is now overlapped
        // For simplicity, we purposely invalidate a Twip even if not needed.

        SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
        if( pSh && rOld.HasArea() )
            pSh->InvalidateWindows( rOld );

        // #i51941# - consider case that fly frame isn't
        // registered at the old page <pOld>
        SwPageFrame* pPageFrame = pFly->FindPageFrame();
        if ( pOld != pPageFrame )
        {
            pFly->NotifyBackground( pPageFrame, aFrame, PrepareHint::FlyFrameArrive );
        }

        if ( rOld.Left() != aFrame.Left() )
        {
            SwRect aTmp( rOld );
            aTmp.Union( aFrame );
            aTmp.Left(  std::min(aFrame.Left(), rOld.Left()) );
            aTmp.Right( std::max(aFrame.Left(), rOld.Left()) );
            pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
        }
        SwTwips nOld = rOld.Right();
        SwTwips nNew = aFrame.Right();
        if ( nOld != nNew )
        {
            SwRect aTmp( rOld );
            aTmp.Union( aFrame );
            aTmp.Left(  std::min(nNew, nOld) );
            aTmp.Right( std::max(nNew, nOld) );
            pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
        }
        if ( rOld.Top() != aFrame.Top() )
        {
            SwRect aTmp( rOld );
            aTmp.Union( aFrame );
            aTmp.Top(    std::min(aFrame.Top(), rOld.Top()) );
            aTmp.Bottom( std::max(aFrame.Top(), rOld.Top()) );
            pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
        }
        nOld = rOld.Bottom();
        nNew = aFrame.Bottom();
        if ( nOld != nNew )
        {
            SwRect aTmp( rOld );
            aTmp.Union( aFrame );
            aTmp.Top(    std::min(nNew, nOld) );
            aTmp.Bottom( std::max(nNew, nOld) );
            pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
        }
    }
    else if(pOldPrt && *pOldPrt != pFly->getFramePrintArea())
    {
        bool bNotifyBackground(pFly->GetFormat()->GetSurround().IsContour());

        if(!bNotifyBackground &&
            pFly->IsFlyFreeFrame() &&
            static_cast< const SwFlyFreeFrame* >(pFly)->supportsAutoContour())
        {
            // RotateFlyFrame3: Also notify for FlyFrames which allow AutoContour
            bNotifyBackground = true;
        }

        if(bNotifyBackground)
        {
            // #i24097#
            pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
        }
    }
}

static void lcl_CheckFlowBack( SwFrame* pFrame, const SwRect &rRect )
{
    SwTwips nBottom = rRect.Bottom();
    while( pFrame )
    {
        if( pFrame->IsLayoutFrame() )
        {
            if( rRect.Overlaps( pFrame->getFrameArea() ) )
                lcl_CheckFlowBack( static_cast<SwLayoutFrame*>(pFrame)->Lower(), rRect );
        }
        else if( !pFrame->GetNext() && nBottom > pFrame->getFrameArea().Bottom() )
        {
            if( pFrame->IsContentFrame() && static_cast<SwContentFrame*>(pFrame)->HasFollow() )
                pFrame->InvalidateSize();
            else
                pFrame->InvalidateNextPos();
        }
        pFrame = pFrame->GetNext();
    }
}

static void lcl_NotifyContent( const SdrObject *pThis, SwContentFrame *pCnt,
    const SwRect &rRect, const PrepareHint eHint )
{
    if ( !pCnt->IsTextFrame() )
        return;

    auto pTextFrame = static_cast<SwTextFrame*>(pCnt);
    SwRect aCntPrt( pCnt->getFramePrintArea() );
    aCntPrt.Pos() += pCnt->getFrameArea().Pos();

    if (eHint == PrepareHint::FlyFrameArrive)
    {
        SwTwips nLower = pTextFrame->GetLowerMarginForFlyIntersect();
        if (nLower > 0)
        {
            aCntPrt.AddBottom(nLower);
        }
    }

    if ( eHint == PrepareHint::FlyFrameAttributesChanged )
    {
        // #i35640# - use given rectangle <rRect> instead
        // of current bound rectangle
        if ( aCntPrt.Overlaps( rRect ) )
            pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged );
    }
    // #i23129# - only invalidate, if the text frame
    // printing area overlaps with the given rectangle.
    else if ( aCntPrt.Overlaps( rRect ) )
        pCnt->Prepare( eHint, static_cast<void*>(&aCntPrt.Intersection_( rRect )) );
    if ( !pCnt->GetDrawObjs() )
        return;

    const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
    for (SwAnchoredObject* pObj : rObjs)
    {
        if ( auto pFly = pObj->DynCastFlyFrame() )
        {
            if ( pFly->IsFlyInContentFrame() )
            {
                SwContentFrame *pContent = pFly->ContainsContent();
                while ( pContent )
                {
                    ::lcl_NotifyContent( pThis, pContent, rRect, eHint );
                    pContent = pContent->GetNextContentFrame();
                }
            }
        }
    }
}

void Notify_Background( const SdrObject* pObj,
                        SwPageFrame* pPage,
                        const SwRect& rRect,
                        const PrepareHint eHint,
                        const bool bInva )
{
    // If the frame was positioned correctly for the first time, do not inform the old area
    if ( eHint == PrepareHint::FlyFrameLeave && rRect.Top() == FAR_AWAY )
         return;

    SwLayoutFrame* pArea;
    SwFlyFrame *pFlyFrame = nullptr;
    SwFrame* pAnchor;
    if( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) )
    {
        pFlyFrame = const_cast<SwVirtFlyDrawObj*>(pVirtFlyDrawObj)->GetFlyFrame();
        pAnchor = pFlyFrame->AnchorFrame();
    }
    else
    {
        pFlyFrame = nullptr;
        if (SwDrawContact* pC = static_cast<SwDrawContact*>(GetUserCall(pObj)))
            pAnchor = const_cast<SwFrame*>(pC->GetAnchoredObj(pObj)->GetAnchorFrame());
        else
            return;
    }
    if( PrepareHint::FlyFrameLeave != eHint && pAnchor->IsInFly() )
        pArea = pAnchor->FindFlyFrame();
    else
        pArea = pPage;
    SwContentFrame *pCnt = nullptr;
    if ( pArea )
    {
        if( PrepareHint::FlyFrameArrive != eHint )
            lcl_CheckFlowBack( pArea, rRect );

        // Only the Flys following this anchor are reacting. Thus, those do not
        // need to be processed.
        // An exception is LEAVE, since the Fly might come "from above".
        // If the anchor is positioned on the previous page, the whole page
        // needs to be processed (47722).
        // OD 2004-05-13 #i28701# - If the wrapping style has to be considered
        // on the object positioning, the complete area has to be processed,
        // because content frames before the anchor frame also have to consider
        // the object for the text wrapping.
        // #i3317# - The complete area has always been
        // processed.
        {
            pCnt = pArea->ContainsContent();
        }
    }
    SwFrame *pLastTab = nullptr;

    bool isValidTableBeforeAnchor(false);
    while ( pCnt && pArea && pArea->IsAnLower( pCnt ) )
    {
        ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
        if ( pCnt->IsInTab() )
        {
            SwTabFrame *pTab = pCnt->FindTabFrame();
            if ( pTab != pLastTab )
            {
                pLastTab = pTab;
                isValidTableBeforeAnchor = false;
                if (PrepareHint::FlyFrameArrive == eHint
                    && pFlyFrame // TODO: do it for draw objects too?
                    && pTab->IsFollow() // table starts on previous page?
                    // "through" means they will actually overlap anyway
                    && css::text::WrapTextMode_THROUGH != pFlyFrame->GetFormat()->GetSurround().GetSurround()
                    // if it's anchored in footer it can't move to other page
                    && !pAnchor->FindFooterOrHeader())
                {
                    SwFrame * pTmp(pAnchor->GetPrev());
                    while (pTmp)
                    {
                        if (pTmp == pTab)
                        {
                            // tdf#99460 the table shouldn't be moved by the fly
                            isValidTableBeforeAnchor = true;
                            break;
                        }
                        pTmp = pTmp->GetPrev();
                    }
                }
                // #i40606# - use <GetLastBoundRect()>
                // instead of <GetCurrentBoundRect()>, because a recalculation
                // of the bounding rectangle isn't intended here.
                if (!isValidTableBeforeAnchor
                    && (pTab->getFrameArea().Overlaps(SwRect(pObj->GetLastBoundRect())) ||
                        pTab->getFrameArea().Overlaps(rRect)))
                {
                    if ( !pFlyFrame || !pFlyFrame->IsLowerOf( pTab ) )
                        pTab->InvalidatePrt();
                }
            }
            SwLayoutFrame* pCell = pCnt->GetUpper();
            // #i40606# - use <GetLastBoundRect()>
            // instead of <GetCurrentBoundRect()>, because a recalculation
            // of the bounding rectangle isn't intended here.
            if (!isValidTableBeforeAnchor && pCell->IsCellFrame() &&
                 ( pCell->getFrameArea().Overlaps( SwRect(pObj->GetLastBoundRect()) ) ||
                   pCell->getFrameArea().Overlaps( rRect ) ) )
            {
                const SwFormatVertOrient &rOri = pCell->GetFormat()->GetVertOrient();
                if ( text::VertOrientation::NONE != rOri.GetVertOrient() )
                    pCell->InvalidatePrt();
            }
        }
        pCnt = pCnt->GetNextContentFrame();
    }
    // #128702# - make code robust
    if ( pPage && pPage->GetSortedObjs() )
    {
        pObj->GetOrdNum();
        const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
        for (SwAnchoredObject* pAnchoredObj : rObjs)
        {
            if ( pAnchoredObj->DynCastFlyFrame() !=  nullptr )
            {
                if( pAnchoredObj->GetDrawObj() == pObj )
                    continue;
                SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj);
                if ( pFly->getFrameArea().Top() == FAR_AWAY )
                    continue;

                if ( !pFlyFrame ||
                        (!pFly->IsLowerOf( pFlyFrame ) &&
                        pFly->GetVirtDrawObj()->GetOrdNumDirect() < pObj->GetOrdNumDirect()))
                {
                    pCnt = pFly->ContainsContent();
                    while ( pCnt )
                    {
                        ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
                        pCnt = pCnt->GetNextContentFrame();
                    }
                }
                if( pFly->IsFlyLayFrame() )
                {
                    SwFrame* pLower = pFly->Lower();
                    if( pLower && pLower->IsColumnFrame() &&
                        pFly->getFrameArea().Bottom() >= rRect.Top() &&
                        pFly->getFrameArea().Top() <= rRect.Bottom() &&
                        pFly->getFrameArea().Right() >= rRect.Left() &&
                        pFly->getFrameArea().Left() <= rRect.Right() )
                    {
                        pFly->InvalidateSize();
                    }
                }
                // Flys above myself might sidestep if they have an automatic
                // alignment. This happens independently of my attributes since
                // this might have been changed as well.
                else if ( pFly->IsFlyAtContentFrame() &&
                        pObj->GetOrdNumDirect() <
                        pFly->GetVirtDrawObj()->GetOrdNumDirect() &&
                        pFlyFrame && !pFly->IsLowerOf( pFlyFrame ) )
                {
                    const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient();
                    if ( text::HoriOrientation::NONE != rH.GetHoriOrient()  &&
                            text::HoriOrientation::CENTER != rH.GetHoriOrient()  &&
                            ( !pFly->IsAutoPos() || text::RelOrientation::CHAR != rH.GetRelationOrient() ) &&
                            (pFly->getFrameArea().Bottom() >= rRect.Top() &&
                            pFly->getFrameArea().Top() <= rRect.Bottom()) )
                        pFly->InvalidatePos();
                }
            }
        }
    }
    if ( pFlyFrame && pAnchor->GetUpper() && pAnchor->IsInTab() )//MA_FLY_HEIGHT
        pAnchor->GetUpper()->InvalidateSize();

    // #i82258# - make code robust
    SwViewShell* pSh = nullptr;
    if ( bInva && pPage &&
        nullptr != (pSh = pPage->getRootFrame()->GetCurrShell()) )
    {
        pSh->InvalidateWindows( rRect );
    }
}

/// Provides the Upper of an anchor in paragraph-bound objects. If the latter
/// is a chained border or a footnote, the "virtual" Upper might be returned.
const SwFrame* GetVirtualUpper( const SwFrame* pFrame, const Point& rPos )
{
    if( pFrame->IsTextFrame() )
    {
        pFrame = pFrame->GetUpper();
        if( !pFrame->getFrameArea().Contains( rPos ) )
        {
            if( pFrame->IsFootnoteFrame() )
            {
                const SwFootnoteFrame* pTmp = static_cast<const SwFootnoteFrame*>(pFrame)->GetFollow();
                while( pTmp )
                {
                    if( pTmp->getFrameArea().Contains( rPos ) )
                        return pTmp;
                    pTmp = pTmp->GetFollow();
                }
            }
            else
            {
                SwFlyFrame* pTmp = const_cast<SwFlyFrame*>(pFrame->FindFlyFrame());
                while( pTmp )
                {
                    if( pTmp->getFrameArea().Contains( rPos ) )
                        return pTmp;
                    pTmp = pTmp->GetNextLink();
                }
            }
        }
    }
    return pFrame;
}

bool Is_Lower_Of(const SwFrame *pCurrFrame, const SdrObject* pObj)
{
    Point aPos;
    const SwFrame* pFrame;
    if (const SwVirtFlyDrawObj *pFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(pObj))
    {
        const SwFlyFrame* pFly = pFlyDrawObj->GetFlyFrame();
        pFrame = pFly->GetAnchorFrame();
        aPos = pFly->getFrameArea().Pos();
    }
    else
    {
        if (SwDrawContact* pC = static_cast<SwDrawContact*>(GetUserCall(pObj)))
        {
            pFrame = pC->GetAnchorFrame(pObj);
            aPos = pObj->GetCurrentBoundRect().TopLeft();
        }
        else
            return false;
    }
    OSL_ENSURE( pFrame, "8-( Fly is lost in Space." );
    pFrame = GetVirtualUpper( pFrame, aPos );
    do
    {   if ( pFrame == pCurrFrame )
            return true;
        if( pFrame->IsFlyFrame() )
        {
            aPos = pFrame->getFrameArea().Pos();
            pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
        }
        else
            pFrame = pFrame->GetUpper();
    } while ( pFrame );
    return false;
}

/// provides the area of a frame in that no Fly from another area can overlap
const SwFrame *FindContext( const SwFrame *pFrame, SwFrameType nAdditionalContextType )
{
    const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header   | SwFrameType::Footer | SwFrameType::FootnoteContainer  |
                        SwFrameType::Footnote  | SwFrameType::Fly      |
                        SwFrameType::Tab  | SwFrameType::Row      | SwFrameType::Cell |
                        nAdditionalContextType;
    do
    {   if ( pFrame->GetType() & nTyp )
            break;
        pFrame = pFrame->GetUpper();
    } while( pFrame );
    return pFrame;
}

bool IsFrameInSameContext( const SwFrame *pInnerFrame, const SwFrame *pFrame )
{
    const SwFrame *pContext = FindContext( pInnerFrame, SwFrameType::None );

    const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FootnoteContainer |
                        SwFrameType::Footnote  | SwFrameType::Fly      |
                        SwFrameType::Tab  | SwFrameType::Row      | SwFrameType::Cell;
    do
    {   if ( pFrame->GetType() & nTyp )
        {
            if( pFrame == pContext )
                return true;
            if( pFrame->IsCellFrame() )
                return false;
        }
        if( pFrame->IsFlyFrame() )
        {
            Point aPos( pFrame->getFrameArea().Pos() );
            pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
        }
        else
            pFrame = pFrame->GetUpper();
    } while( pFrame );

    return false;
}

static SwTwips lcl_CalcCellRstHeight( SwLayoutFrame *pCell )
{
    SwFrame *pLow = pCell->Lower();
    if ( pLow && (pLow->IsContentFrame() || pLow->IsSctFrame()) )
    {
        tools::Long nHeight = 0, nFlyAdd = 0;
        do
        {
            tools::Long nLow = pLow->getFrameArea().Height();
            if( pLow->IsTextFrame() && static_cast<SwTextFrame*>(pLow)->IsUndersized() )
                nLow += static_cast<SwTextFrame*>(pLow)->GetParHeight()-pLow->getFramePrintArea().Height();
            else if( pLow->IsSctFrame() && static_cast<SwSectionFrame*>(pLow)->IsUndersized() )
                nLow += static_cast<SwSectionFrame*>(pLow)->Undersize();
            nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLow );
            nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
            nHeight += nLow;
            pLow = pLow->GetNext();
        } while ( pLow );
        if ( nFlyAdd )
            nHeight += nFlyAdd;

        // The border cannot be calculated based on PrtArea and Frame, since both can be invalid.
        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
        const SwBorderAttrs &rAttrs = *aAccess.Get();
        nHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();

        return pCell->getFrameArea().Height() - nHeight;
    }
    else
    {
        tools::Long nRstHeight = 0;
        while (pLow && pLow->IsLayoutFrame())
        {
            nRstHeight += ::CalcRowRstHeight(*static_cast<SwLayoutFrame*>(pLow));
            pLow = pLow->GetNext();
        }
        return nRstHeight;
    }
}

SwTwips CalcRowRstHeight(SwLayoutFrame& rRow)
{
    SwFrame *pLow = rRow.Lower();
    if (!(pLow && pLow->IsLayoutFrame()))
    {
        return 0;
    }
    SwTwips nRstHeight = LONG_MAX;
    while (pLow && pLow->IsLayoutFrame())
    {
        nRstHeight = std::min(nRstHeight, ::lcl_CalcCellRstHeight(static_cast<SwLayoutFrame*>(pLow)));
        pLow = pLow->GetNext();
    }
    return nRstHeight;
}

const SwFrame* FindPage( const SwRect &rRect, const SwFrame *pPage )
{
    if ( !rRect.Overlaps( pPage->getFrameArea() ) )
    {
        const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(pPage->GetUpper());
        const SwFrame* pTmpPage = pRootFrame ? pRootFrame->GetPageAtPos( rRect.TopLeft(), &rRect.SSize(), true ) : nullptr;
        if ( pTmpPage )
            pPage = pTmpPage;
    }

    return pPage;
}

namespace {

class SwFrameHolder : private SfxListener
{
    SwFrame* m_pFrame;
    bool m_bSet;
    virtual void Notify(  SfxBroadcaster& rBC, const SfxHint& rHint ) override;
public:
    SwFrameHolder()
        : m_pFrame(nullptr)
        , m_bSet(false)
    {
    }
    void SetFrame( SwFrame* pHold );
    SwFrame* GetFrame() { return m_pFrame; }
    void Reset();
    bool IsSet() const { return m_bSet; }
};

}

void SwFrameHolder::SetFrame( SwFrame* pHold )
{
    m_bSet = true;
    if (m_pFrame != pHold)
    {
        if (m_pFrame)
            EndListening(*m_pFrame);
        StartListening(*pHold);
        m_pFrame = pHold;
    }
}

void SwFrameHolder::Reset()
{
    if (m_pFrame)
        EndListening(*m_pFrame);
    m_bSet = false;
    m_pFrame = nullptr;
}

void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if (rHint.GetId() == SfxHintId::Dying && &rBC == m_pFrame)
    {
        m_pFrame = nullptr;
    }
}

static sal_uInt64 CalcCurrentDist(sal_Int64 nDiffX, sal_Int64 nDiffY)
{
    sal_Int64 nDiffX2, nDiffY2;
    if (o3tl::checked_multiply(nDiffX, nDiffX, nDiffX2))
    {
        SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: X " << nDiffX);
        return std::numeric_limits<sal_uInt64>::max();
    }
    if (o3tl::checked_multiply(nDiffY, nDiffY, nDiffY2))
    {
        SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: Y " << nDiffY);
        return std::numeric_limits<sal_uInt64>::max();
    }
    sal_uInt64 ret;
    if (o3tl::checked_add<sal_uInt64>(nDiffX2, nDiffY2, ret))
    {
        SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: " << nDiffX2 << " + " << nDiffY2);
        return std::numeric_limits<sal_uInt64>::max();
    }
    return ret;
}

SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, sw::BroadcastingModify const& rMod,
        SwFrameType const nFrameType, SwPosition const*const pPos,
        std::pair<Point, bool> const*const pViewPosAndCalcFrame)
{
    SwFrame *pMinFrame = nullptr, *pTmpFrame;
    SwFrameHolder aHolder;
    SwRect aCalcRect;
    bool bClientIterChanged = false;

    SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
    do {
        pMinFrame = nullptr;
        aHolder.Reset();
        sal_uInt64 nMinDist = 0;
        bClientIterChanged = false;

        for( pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
        {
            if( pTmpFrame->GetType() & nFrameType &&
                ( !pLayout || pLayout == pTmpFrame->getRootFrame() ) &&
                (!pTmpFrame->IsFlowFrame() ||
                 !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow() ))
            {
                if (pViewPosAndCalcFrame)
                {
                    // watch for Frame being deleted
                    if ( pMinFrame )
                        aHolder.SetFrame( pMinFrame );
                    else
                        aHolder.Reset();

                    if (pViewPosAndCalcFrame->second)
                    {
                        // tdf#108118 prevent recursion
                        DisableCallbackAction a(*pTmpFrame->getRootFrame());
                        // - format parent Writer
                        // fly frame, if it isn't been formatted yet.
                        // Note: The Writer fly frame could be the frame itself.
                        SwFlyFrame* pFlyFrame( pTmpFrame->FindFlyFrame() );
                        if ( pFlyFrame &&
                             pFlyFrame->getFrameArea().Pos().X() == FAR_AWAY &&
                             pFlyFrame->getFrameArea().Pos().Y() == FAR_AWAY )
                        {
                            SwObjectFormatter::FormatObj( *pFlyFrame );
                        }
                        pTmpFrame->Calc(pLayout ? pLayout->GetCurrShell()->GetOut() : nullptr);
                    }

                    // aIter.IsChanged checks if the current pTmpFrame has been deleted while
                    // it is the current iterator
                    // FrameHolder watches for deletion of the current pMinFrame
                    if( aIter.IsChanged() || ( aHolder.IsSet() && !aHolder.GetFrame() ) )
                    {
                        // restart iteration
                        bClientIterChanged = true;
                        break;
                    }

                    // for Flys go via the parent if the Fly is not yet "formatted"
                    if (!pViewPosAndCalcFrame->second &&
                        pTmpFrame->GetType() & SwFrameType::Fly &&
                        static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame() &&
                        FAR_AWAY == pTmpFrame->getFrameArea().Pos().getX() &&
                        FAR_AWAY == pTmpFrame->getFrameArea().Pos().getY() )
                        aCalcRect = static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame()->getFrameArea();
                    else
                        aCalcRect = pTmpFrame->getFrameArea();

                    if (aCalcRect.Contains(pViewPosAndCalcFrame->first))
                    {
                        pMinFrame = pTmpFrame;
                        break;
                    }

                    // Point not in rectangle. Compare distances:
                    const Point aCalcRectCenter = aCalcRect.Center();
                    const Point aDiff = aCalcRectCenter - pViewPosAndCalcFrame->first;
                    const sal_uInt64 nCurrentDist = CalcCurrentDist(aDiff.getX(), aDiff.getY());
                    if ( !pMinFrame || nCurrentDist < nMinDist )
                    {
                        pMinFrame = pTmpFrame;
                        nMinDist = nCurrentDist;
                    }
                }
                else
                {
                    // if no pViewPosAndCalcFrame is provided, take the first one
                    pMinFrame = pTmpFrame;
                    break;
                }
            }
        }
    } while( bClientIterChanged );

    if( pPos && pMinFrame && pMinFrame->IsTextFrame() )
    {
        SwTextFrame * pAtPos(static_cast<SwTextFrame*>(pMinFrame)->GetFrameAtPos(*pPos));
        if (!pViewPosAndCalcFrame)
        {
            return pAtPos;
        }
        TextFrameIndex nPos(pAtPos->MapModelToViewPos(*pPos));
        SwPageFrame const*const pPage(pAtPos->getRootFrame()->GetPageAtPos(
                pViewPosAndCalcFrame->first, nullptr, true));
        SwFrame * pOnPage(pAtPos); // if all else fails return first one
        ++nPos; // follow field portions are on follow frames that have mnOffset
            // already incremented past the field, need to check that index too
        while (pAtPos && pAtPos->GetOffset() <= nPos)
        {
            if (pAtPos->getFrameArea().Contains(pViewPosAndCalcFrame->first))
            {
                return pAtPos;
            }
            if (pAtPos->FindPageFrame() == pPage)
            {
                pOnPage = pAtPos;
            }
            pAtPos = pAtPos->GetFollow();
        }
        return pOnPage;
    }

    return pMinFrame;
}

bool IsExtraData( const SwDoc& rDoc )
{
    const SwLineNumberInfo &rInf = rDoc.GetLineNumberInfo();
    if (rInf.IsPaintLineNumbers() ||
           rInf.IsCountInFlys() ||
           (static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos()) != text::HoriOrientation::NONE &&
            !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))
    {
        return true;
    }

    if ( officecfg::Office::Writer::Content::Display::ShowWordSpacingIndicator::get() )
        return true;

    const SwEditShell* pSh = rDoc.GetEditShell();
    const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr;
    return pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton();
}

// OD 22.09.2003 #110978#
SwRect SwPageFrame::PrtWithoutHeaderAndFooter() const
{
    SwRect aPrtWithoutHeaderFooter( getFramePrintArea() );
    aPrtWithoutHeaderFooter.Pos() += getFrameArea().Pos();

    const SwFrame* pLowerFrame = Lower();
    while ( pLowerFrame )
    {
        // Note: independent on text direction page header and page footer are
        //       always at top respectively at bottom of the page frame.
        if ( pLowerFrame->IsHeaderFrame() )
        {
            aPrtWithoutHeaderFooter.AddTop( pLowerFrame->getFrameArea().Height() );
        }
        if ( pLowerFrame->IsFooterFrame() )
        {
            aPrtWithoutHeaderFooter.AddBottom( - pLowerFrame->getFrameArea().Height() );
        }

        pLowerFrame = pLowerFrame->GetNext();
    }

    return aPrtWithoutHeaderFooter;
}

/** method to determine the spacing values of a frame

    OD 2004-03-10 #i28701#
    OD 2009-08-28 #i102458#
    Add output parameter <obIsLineSpacingProportional>
*/
void GetSpacingValuesOfFrame( const SwFrame& rFrame,
                            SwTwips& onLowerSpacing,
                            SwTwips& onLineSpacing,
                            bool& obIsLineSpacingProportional,
                            bool bIdenticalStyles )
{
    if ( !rFrame.IsFlowFrame() )
    {
        onLowerSpacing = 0;
        onLineSpacing = 0;
    }
    else
    {
        const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace();
        // check contextual spacing if the style of actual and next paragraphs are identical
        if (bIdenticalStyles)
            onLowerSpacing = (rULSpace.GetContext() ? 0 : rULSpace.GetLower());
        else
            onLowerSpacing = rULSpace.GetLower();

        onLineSpacing = 0;
        obIsLineSpacingProportional = false;
        if ( rFrame.IsTextFrame() )
        {
            onLineSpacing = static_cast<const SwTextFrame&>(rFrame).GetLineSpace();
            obIsLineSpacingProportional =
                onLineSpacing != 0 &&
                static_cast<const SwTextFrame&>(rFrame).GetLineSpace( true ) == 0;
        }

        OSL_ENSURE( onLowerSpacing >= 0 && onLineSpacing >= 0,
                "<GetSpacingValuesOfFrame(..)> - spacing values aren't positive!" );
    }
}

/// get the content of the table cell, skipping content from nested tables
const SwContentFrame* GetCellContent( const SwLayoutFrame& rCell )
{
    const SwContentFrame* pContent = rCell.ContainsContent();
    const SwTabFrame* pTab = rCell.FindTabFrame();

    while ( pContent && rCell.IsAnLower( pContent ) )
    {
        const SwTabFrame* pTmpTab = pContent->FindTabFrame();
        if ( pTmpTab != pTab )
        {
            SwFrame const*const pTmp = pTmpTab->FindLastContentOrTable();
            if (pTmp)
            {
                pContent = pTmp->FindNextCnt();
            }
            else
            {
                pContent = nullptr;
            }
        }
        else
            break;
    }
    return pContent;
}

SwDeletionChecker::SwDeletionChecker(const SwFrame* pFrame)
    : mpFrame( pFrame )
    , mpRegIn( pFrame
        ? pFrame->IsTextFrame()
            // sw_redlinehide: GetDep() may be a member of SwTextFrame!
            ? static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()
            : const_cast<SwFrame*>(pFrame)->GetDep()
        : nullptr )
{
}

/// Can be used to check if a frame has been deleted
bool SwDeletionChecker::HasBeenDeleted() const
{
    if ( !mpFrame || !mpRegIn )
        return false;

    SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*mpRegIn);
    SwFrame* pLast = aIter.First();
    while ( pLast )
    {
        if ( pLast == mpFrame )
            return false;
        pLast = aIter.Next();
    }

    return true;
}

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