/* -*- 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 <sal/config.h>
#include <sal/log.hxx>

#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <svl/itemiter.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <fmtclbl.hxx>
#include <sectfrm.hxx>
#include <cellfrm.hxx>
#include <section.hxx>
#include <IDocumentSettingAccess.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <txtfrm.hxx>
#include <fmtclds.hxx>
#include <colfrm.hxx>
#include <tabfrm.hxx>
#include <ftnfrm.hxx>
#include "layhelp.hxx"
#include <layouter.hxx>
#include <dbg_lay.hxx>
#include <viewopt.hxx>
#include <viewimp.hxx>
#include <editeng/brushitem.hxx>
#include <fmtftntx.hxx>
#include <flyfrm.hxx>
#include <sortedobjs.hxx>
#include <hints.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>

namespace
{
/**
 * Performs the correct type of position invalidation depending on if we're in
 * CalcContent().
 */
void InvalidateFramePos(SwFrame* pFrame, bool bInCalcContent)
{
    if (bInCalcContent)
        pFrame->InvalidatePos_();
    else
        pFrame->InvalidatePos();
}
}

SwSectionFrame::SwSectionFrame( SwSection &rSect, SwFrame* pSib )
    : SwLayoutFrame( rSect.GetFormat(), pSib )
    , SwFlowFrame( static_cast<SwFrame&>(*this) )
    , m_pSection( &rSect )
    , m_bFootnoteAtEnd(false)
    , m_bEndnAtEnd(false)
    , m_bContentLock(false)
    , m_bOwnFootnoteNum(false)
    , m_bFootnoteLock(false)
{
    StartListening(rSect.GetFormat()->GetNotifier());

    mnFrameType = SwFrameType::Section;

    CalcFootnoteAtEndFlag();
    CalcEndAtEndFlag();
}

SwSectionFrame::SwSectionFrame( SwSectionFrame &rSect, bool bMaster ) :
    SwLayoutFrame( rSect.GetFormat(), rSect.getRootFrame() ),
    SwFlowFrame( static_cast<SwFrame&>(*this) ),
    m_pSection( rSect.GetSection() ),
    m_bFootnoteAtEnd( rSect.IsFootnoteAtEnd() ),
    m_bEndnAtEnd( rSect.IsEndnAtEnd() ),
    m_bContentLock( false ),
    m_bOwnFootnoteNum( false ),
    m_bFootnoteLock( false )
{
    m_bEndNoteSection = rSect.m_bEndNoteSection;
    StartListening(rSect.GetFormat()->GetNotifier());

    mnFrameType = SwFrameType::Section;

    PROTOCOL( this, PROT::Section, bMaster ? DbgAction::CreateMaster : DbgAction::CreateFollow, &rSect )

    if( bMaster )
    {
        SwSectionFrame* pMaster = rSect.IsFollow() ? rSect.FindMaster() : nullptr;
        if (pMaster)
            pMaster->SetFollow( this );
        SetFollow( &rSect );
    }
    else
    {
        SetFollow( rSect.GetFollow() );
        rSect.SetFollow( this );
        if( !GetFollow() )
            rSect.SimpleFormat();
        if( !rSect.IsColLocked() )
            rSect.InvalidateSize();
    }
}

// NOTE: call <SwSectionFrame::Init()> directly after creation of a new section
//       frame and its insert in the layout.
void SwSectionFrame::Init()
{
    assert(GetUpper() && "SwSectionFrame::Init before insertion?!");
    SwRectFnSet aRectFnSet(*this);
    tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());

    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aRectFnSet.SetWidth( aFrm, nWidth );
        aRectFnSet.SetHeight( aFrm, 0 );
    }

    // #109700# LRSpace for sections
    const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();

    {
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
        aRectFnSet.SetLeft(aPrt, rLRSpace.ResolveLeft({}));
        aRectFnSet.SetWidth(aPrt, nWidth - rLRSpace.ResolveLeft({}) - rLRSpace.ResolveRight({}));
        aRectFnSet.SetHeight( aPrt, 0 );
    }

    const SwFormatCol &rCol = GetFormat()->GetCol();
    if( ( rCol.GetNumCols() > 1 || IsAnyNoteAtEnd() ) && !IsInFootnote() )
    {
        const SwFormatCol *pOld = Lower() ? &rCol : new SwFormatCol;
        ChgColumns( *pOld, rCol, IsAnyNoteAtEnd() );
        if (!SfxPoolItem::areSame( pOld, &rCol ))
            delete pOld;
    }
}

void SwSectionFrame::DestroyImpl()
{
    if( GetFormat() && !GetFormat()->GetDoc().IsInDtor() )
    {
        SwRootFrame *pRootFrame = getRootFrame();
        if( pRootFrame )
            pRootFrame->RemoveFromList( this );
        if( IsFollow() )
        {
            SwSectionFrame *pMaster = FindMaster();
            if( pMaster )
            {
                PROTOCOL( this, PROT::Section, DbgAction::DelFollow, pMaster )
                pMaster->SetFollow( GetFollow() );
                // A Master always grabs the space until the lower edge of his
                // Upper. If he doesn't have a Follow anymore, he can
                // release it, which is why the Size of the Master is
                // invalidated.
                if( !GetFollow() )
                    pMaster->InvalidateSize();
            }
        }
#if defined DBG_UTIL
        else if( HasFollow() )
        {
            PROTOCOL( this, PROT::Section, DbgAction::DelMaster, GetFollow() )
        }
#endif
    }

    SwLayoutFrame::DestroyImpl();
}

SwSectionFrame::~SwSectionFrame()
{
}

//virtual
bool SwSectionFrame::IsHiddenNow() const
{
    const auto* pSection = GetSection();
    return !pSection || pSection->CalcHiddenFlag();
}

void SwSectionFrame::DelEmpty( bool bRemove )
{
    if( IsColLocked() )
    {
        OSL_ENSURE( !bRemove, "Don't delete locked SectionFrames" );
        return;
    }
    SwFrame* pUp = GetUpper();
    if( pUp )
    {
        // #i27138#
        // notify accessibility paragraphs objects about changed
        // CONTENT_FLOWS_FROM/_TO relation.
        // Relation CONTENT_FLOWS_FROM for current next paragraph will change
        // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        {
            SwViewShell* pViewShell( getRootFrame()->GetCurrShell() );
            if ( pViewShell && pViewShell->GetLayout() &&
                 pViewShell->GetLayout()->IsAnyShellAccessible() )
            {
                auto pNext = FindNextCnt( true );
                auto pPrev = FindPrevCnt();
                pViewShell->InvalidateAccessibleParaFlowRelation(
                                pNext ? pNext->DynCastTextFrame() : nullptr,
                                pPrev ? pPrev->DynCastTextFrame() : nullptr );
            }
        }
#endif
        Cut_( bRemove );
    }
    SwSectionFrame *pMaster = IsFollow() ? FindMaster() : nullptr;
    if (pMaster)
    {
        pMaster->SetFollow( GetFollow() );
        // A Master always grabs the space until the lower edge of his
        // Upper. If he doesn't have a Follow anymore, he can
        // release it, which is why the Size of the Master is
        // invalidated.
        if( !GetFollow() && !pMaster->IsColLocked() )
            pMaster->InvalidateSize();
    }
    SetFollow(nullptr);
    if( !pUp )
        return;

    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Height( 0 );
    }

    // If we are destroyed immediately anyway, we don't need
    // to put us into the list
    if( bRemove )
    {   // If we already were half dead before this DelEmpty,
        // we are likely in the list and have to remove us from
        // it
        if( !m_pSection && getRootFrame() )
            getRootFrame()->RemoveFromList( this );
    }
    else if( getRootFrame() )
    {
        getRootFrame()->InsertEmptySct( this );
    }

    m_pSection = nullptr;  // like this a reanimation is virtually impossible though
}

void SwSectionFrame::Cut()
{
    Cut_( true );
}

void SwSectionFrame::Cut_( bool bRemove )
{
    OSL_ENSURE( GetUpper(), "Cut without Upper()." );

    PROTOCOL( this, PROT::Cut, DbgAction::NONE, GetUpper() )

    SwPageFrame *pPage = FindPageFrame();
    InvalidatePage( pPage );
    SwFrame *pFrame = GetNext();
    SwFrame* pPrepFrame = nullptr;
    while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
        pFrame = pFrame->GetNext();
    if( pFrame )
    {   // The former successor might have calculated a gap to the predecessor
        // which is now obsolete since he becomes the first
        pFrame->InvalidatePrt_();
        pFrame->InvalidatePos_();
        if( pFrame->IsSctFrame() )
            pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
        if ( pFrame && pFrame->IsContentFrame() )
        {
            pFrame->InvalidatePage( pPage );
            if( IsInFootnote() && !GetIndPrev() )
                pPrepFrame = pFrame;
        }
    }
    else
    {
        InvalidateNextPos();
        // Someone has to take over the retouching: predecessor or Upper
        pFrame = GetPrev();
        if ( nullptr != pFrame )
        {
            pFrame->SetRetouche();
            pFrame->Prepare( PrepareHint::WidowsOrphans );
            if ( pFrame->IsContentFrame() )
                pFrame->InvalidatePage( pPage );
        }
        // If I am (was) the only FlowFrame in my Upper, then he has to take over
        // the retouching.
        // Furthermore a blank page could have emerged
        else
        {   SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
            pRoot->SetSuperfluous();
            GetUpper()->SetCompletePaint();
        }
    }
    // First remove, then shrink Upper
    SwLayoutFrame *pUp = GetUpper();
    if( bRemove )
    {
        RemoveFromLayout();
        if( pUp && !pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
            pUp->GetUpper() )
        {
            pUp->Cut();
            SwFrame::DestroyFrame(pUp);
            pUp = nullptr;
        }
    }
    if( pPrepFrame )
        pPrepFrame->Prepare( PrepareHint::FootnoteInvalidation );
    if ( !pUp )
        return;

    SwRectFnSet aRectFnSet(*this);
    SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
    if( nFrameHeight <= 0 )
        return;

    if( !bRemove )
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aRectFnSet.SetHeight( aFrm, 0 );

        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
        aRectFnSet.SetHeight( aPrt, 0 );
    }

    pUp->Shrink( nFrameHeight );
}

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

    PROTOCOL( this, PROT::Paste, DbgAction::NONE, GetUpper() )

    // Add to the tree
    SwSectionFrame* pSect = pParent->FindSctFrame();
    // Assure that parent is not inside a table frame, which is inside the found section frame.
    if ( pSect )
    {
        SwTabFrame* pTableFrame = pParent->FindTabFrame();
        if ( pTableFrame &&
             pSect->IsAnLower( pTableFrame ) )
        {
            pSect = nullptr;
        }
    }

    SwRectFnSet aRectFnSet(*pParent);
    if( pSect && HasToBreak( pSect ) )
    {
        if( pParent->IsColBodyFrame() ) // dealing with a single-column area
        {
            // If we are coincidentally at the end of a column, pSibling
            // has to point to the first frame of the next column in order
            // for the content of the next column to be moved correctly to the
            // newly created pSect by the InsertGroup
            SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pParent->GetUpper());
            while( !pSibling && nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
                pSibling = static_cast<SwLayoutFrame*>(pCol->Lower())->Lower();
            if( pSibling )
            {
                // Even worse: every following column content has to
                // be attached to the pSibling-chain in order to be
                // taken along
                SwFrame *pTmp = pSibling;
                while ( nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
                {
                    while ( pTmp->GetNext() )
                        pTmp = pTmp->GetNext();
                    SwFrame* pSave = ::SaveContent( pCol );
                    if (pSave)
                        ::RestoreContent( pSave, pSibling->GetUpper(), pTmp );
                }
            }
        }
        pParent = pSect;
        pSect = new SwSectionFrame( *static_cast<SwSectionFrame*>(pParent)->GetSection(), pParent );
        // if pParent is decomposed into two parts, its Follow has to be attached
        // to the new second part
        pSect->SetFollow( static_cast<SwSectionFrame*>(pParent)->GetFollow() );
        static_cast<SwSectionFrame*>(pParent)->SetFollow( nullptr );
        if( pSect->GetFollow() )
            pParent->InvalidateSize_();

        const bool bInserted = InsertGroupBefore( pParent, pSibling, pSect );
        if (bInserted)
        {
            pSect->Init();
            aRectFnSet.MakePos(*pSect, pSect->GetUpper(), ::sw::PrevSkipDead(pSect, false), true);
        }
        if( !static_cast<SwLayoutFrame*>(pParent)->Lower() )
        {
            SwSectionFrame::MoveContentAndDelete( static_cast<SwSectionFrame*>(pParent), false );
            pParent = this;
        }
    }
    else
        InsertGroupBefore( pParent, pSibling, nullptr );

    InvalidateAll_();
    SwPageFrame *pPage = FindPageFrame();
    InvalidatePage( pPage );

    if ( pSibling )
    {
        pSibling->InvalidatePos_();
        pSibling->InvalidatePrt_();
        if ( pSibling->IsContentFrame() )
            pSibling->InvalidatePage( pPage );
    }

    SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
    if( nFrameHeight )
        pParent->Grow( nFrameHeight );

    if ( GetPrev() && !IsFollow() )
    {
        GetPrev()->InvalidateSize();
        if ( GetPrev()->IsContentFrame() )
            GetPrev()->InvalidatePage( pPage );
    }
}

/**
|*  Here it's decided whether the this-SectionFrame should break up
|*  the passed (Section)frm (or not).
|*  Initially, all superior sections are broken up. Later on that could
|*  be made configurable.
|*/
bool SwSectionFrame::HasToBreak( const SwFrame* pFrame ) const
{
    if( !pFrame->IsSctFrame() )
        return false;

    const SwSectionFormat *pTmp = static_cast<const SwSectionFormat*>(GetFormat());

    const SwFrameFormat *pOtherFormat = static_cast<const SwSectionFrame*>(pFrame)->GetFormat();
    do
    {
        pTmp = pTmp->GetParent();
        if( !pTmp )
            return false;
        if( pTmp == pOtherFormat )
            return true;
    } while( true ); // ( pTmp->GetSect().GetValue() );
}

/**
|*  Merges two SectionFrames, in case it's about the same section.
|*  This can be necessary when a (sub)section is deleted that had
|*  divided another part into two.
|*/
void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
{
    if (pNxt->IsDeleteForbidden())
        return;

    if (pNxt->IsJoinLocked() || GetSection() != pNxt->GetSection())
        return;

    PROTOCOL( this, PROT::Section, DbgAction::Merge, pNxt )

    SwFrame* pTmp = ::SaveContent( pNxt );
    if( pTmp )
    {
        SwFrame* pLast = Lower();
        SwLayoutFrame* pLay = this;
        if( pLast )
        {
            while( pLast->GetNext() )
                pLast = pLast->GetNext();
            if( pLast->IsColumnFrame() )
            {   // Columns now with BodyFrame
                pLay = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLast)->Lower());
                pLast = pLay->Lower();
                if( pLast )
                    while( pLast->GetNext() )
                        pLast = pLast->GetNext();
            }
        }
        ::RestoreContent( pTmp, pLay, pLast );
    }
    SetFollow( pNxt->GetFollow() );
    pNxt->SetFollow( nullptr );
    pNxt->Cut();
    SwFrame::DestroyFrame(pNxt);
    InvalidateSize();
}

/**
|*  Divides a SectionFrame into two parts. The content of the second one
|*  starts after pFrameStartAfter; the created second section frame itself
|*  is put after pFramePutAfter.
|*  If pFrameStartAfter is nullptr, the split happens at the start.
|*  This is required when inserting an inner section, because the MoveFwd
|*  cannot have the desired effect within a frame or a table cell.
|*  Splitting at the start/end makes sense, because the empty frame would
|*  be removed after the InsertCnt_ finished.
|*/
SwSectionFrame* SwSectionFrame::SplitSect( SwFrame* pFrameStartAfter, SwFrame* pFramePutAfter )
{
    assert(!pFrameStartAfter || IsAnLower(pFrameStartAfter));
    SwFrame* pSav;
    if (pFrameStartAfter)
    {
        pSav = pFrameStartAfter->FindNext();
        // If pFrameStartAfter is a complex object like table, and it has no next,
        // its FindNext may return its own last subframe. In this case, assume that
        // we are at the end.
        if (pSav && pFrameStartAfter->IsLayoutFrame())
            if (static_cast<SwLayoutFrame*>(pFrameStartAfter)->IsAnLower(pSav))
                pSav = nullptr;
    }
    else
    {
        pSav = ContainsAny();
    }
    if (pSav && !IsAnLower(pSav))
        pSav = nullptr; // we are at the very end

    // Put the content aside
    if (pSav)
        pSav = ::SaveContent( this, pSav );

    // Create a new SctFrame, not as a Follower/master
    if (!pFramePutAfter)
        pFramePutAfter = this;
    SwSectionFrame* pNew = new SwSectionFrame( *GetSection(), this );
    pNew->InsertBehind( pFramePutAfter->GetUpper(), pFramePutAfter );
    pNew->Init();
    SwRectFnSet aRectFnSet(*this);
    assert(!pFramePutAfter->IsSctFrame() || static_cast<SwSectionFrame*>(pFramePutAfter)->GetSection()); // it's not dead
    aRectFnSet.MakePos( *pNew, nullptr, pFramePutAfter, true );
    // OD 25.03.2003 #108339# - restore content:
    // determine layout frame for restoring content after the initialization
    // of the section frame. In the section initialization the columns are
    // created.
    if (pSav)
    {
        SwLayoutFrame* pLay = pNew;
        // Search for last layout frame, e.g. for columned sections.
        SwFrame* pLower = pLay->Lower();
        while( pLower && pLower->IsLayoutFrame() )
        {
            pLay = static_cast<SwLayoutFrame*>(pLower);
            pLower = pLay->Lower();
        }
        ::RestoreContent( pSav, pLay, nullptr );
    }
    InvalidateSize_();
    if( HasFollow() )
    {
        pNew->SetFollow( GetFollow() );
        SetFollow( nullptr );
    }
    return pNew;
}

/**
|*  MoveContent is called for destroying a SectionFrames, due to
|*  the cancellation or hiding of a section, to handle the content.
|*  If the SectionFrame hasn't broken up another one, then the content
|*  is moved to the Upper. Otherwise the content is moved to another
|*  SectionFrame, which has to be potentially merged.
|*/
// If a multi-column section is cancelled, the ContentFrames have to be
// invalidated
static void lcl_InvalidateInfFlags( SwFrame* pFrame, bool bInva )
{
    while ( pFrame )
    {
        pFrame->InvalidateInfFlags();
        if( bInva )
        {
            pFrame->InvalidatePos_();
            pFrame->InvalidateSize_();
            pFrame->InvalidatePrt_();
        }
        pFrame = pFrame->GetNext();
    }
}

// Works like SwContentFrame::ImplGetNextContentFrame, but starts with a LayoutFrame
static SwContentFrame* lcl_GetNextContentFrame( const SwLayoutFrame* pLay, bool bFwd )
{
    if ( bFwd )
    {
        if ( pLay->GetNext() && pLay->GetNext()->IsContentFrame() )
            return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetNext()));
    }
    else
    {
        if ( pLay->GetPrev() && pLay->GetPrev()->IsContentFrame() )
            return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetPrev()));
    }

    const SwFrame* pFrame = pLay;
    SwContentFrame *pContentFrame = nullptr;
    bool bGoingUp = true;
    do {
        const SwFrame *p = nullptr;
        bool bGoingFwdOrBwd = false;

        bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
        if (bGoingDown)
        {
            p = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
            bGoingDown = nullptr != p;
        }
        if ( !bGoingDown )
        {
            p = pFrame->IsFlyFrame() ?
                ( bFwd ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() ) :
                ( bFwd ? pFrame->GetNext() :pFrame->GetPrev() );
            bGoingFwdOrBwd = nullptr != p;
            if ( !bGoingFwdOrBwd )
            {
                p = pFrame->GetUpper();
                bGoingUp = nullptr != p;
                if ( !bGoingUp )
                    return nullptr;
            }
        }

        bGoingUp = !( bGoingFwdOrBwd || bGoingDown );
        assert(p);
        if (!bFwd && bGoingDown)
            while ( p->GetNext() )
                p = p->GetNext();

        pFrame = p;
    } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFrame)) : nullptr) ));

    return pContentFrame;
}

namespace
{
    SwLayoutFrame* FirstLeaf(SwSectionFrame* pLayFrame)
    {
        SwFrame* pLower = pLayFrame->Lower();
        if (pLower && pLower->IsColumnFrame())
            return pLayFrame->GetNextLayoutLeaf();
        return pLayFrame;
    }

    /// Checks if pFrame has a parent that can contain a split section frame.
    bool CanContainSplitSection(const SwFrame* pFrame)
    {
        if (!pFrame->IsInTab())
            return true;

        // The frame is in a table, see if the table is in a section.
        bool bRet = !pFrame->FindTabFrame()->IsInSct();

        if (bRet)
        {
            // Don't try to split if the frame itself is a section frame with
            // multiple columns.
            if (pFrame->IsSctFrame())
            {
                const SwFrame* pLower = pFrame->GetLower();
                if (pLower && pLower->IsColumnFrame())
                    bRet = false;
            }
        }

        return bRet;
    }
}

void SwSectionFrame::MoveContentAndDelete( SwSectionFrame* pDel, bool bSave )
{
    bool bSize = pDel->Lower() && pDel->Lower()->IsColumnFrame();
    SwFrame* pPrv = pDel->GetPrev();
    SwLayoutFrame* pUp = pDel->GetUpper();
    // OD 27.03.2003 #i12711# - initialize local pointer variables.
    SwSectionFrame* pPrvSct = nullptr;
    SwSectionFrame* pNxtSct = nullptr;
    SwSectionFormat* pParent = static_cast<SwSectionFormat*>(pDel->GetFormat())->GetParent();
    if( pDel->IsInTab() && pParent )
    {
        SwTabFrame *pTab = pDel->FindTabFrame();
        // If we are within a table, we can only have broken up sections that
        // are inside as well, but not a section that contains the whole table.
        if( pTab->IsInSct() && pParent == pTab->FindSctFrame()->GetFormat() )
            pParent = nullptr;
    }
    // If our Format has a parent, we have probably broken up another
    // SectionFrame, which has to be checked. To do so we first acquire the
    // succeeding and the preceding ContentFrame, let's see if they
    // lay in the SectionFrames.
    // OD 27.03.2003 #i12711# - check, if previous and next section belonging
    // together and can be joined, *not* only if deleted section contains content.
    if ( pParent )
    {
        SwFrame* pPrvContent = lcl_GetNextContentFrame( pDel, false );
        pPrvSct = pPrvContent ? pPrvContent->FindSctFrame() : nullptr;
        SwFrame* pNxtContent = lcl_GetNextContentFrame( pDel, true );
        pNxtSct = pNxtContent ? pNxtContent->FindSctFrame() : nullptr;
    }
    else
    {
        pParent = nullptr;
        pPrvSct = pNxtSct = nullptr;
    }

    // Now the content is put aside and the frame is destroyed
    SwFrame *pSave = bSave ? ::SaveContent( pDel ) : nullptr;
    bool bOldFootnote = true;
    if( pSave && pUp->IsFootnoteFrame() )
    {
        bOldFootnote = static_cast<SwFootnoteFrame*>(pUp)->IsColLocked();
        static_cast<SwFootnoteFrame*>(pUp)->ColLock();
    }
    pDel->DelEmpty( true );
    SwFrame::DestroyFrame(pDel);
    if( pParent )
    {   // Search for the appropriate insert position
        if( pNxtSct && pNxtSct->GetFormat() == pParent )
        {   // Here we can insert ourselves at the beginning
            pUp = FirstLeaf( pNxtSct );
            pPrv = nullptr;
            if( pPrvSct && ( pPrvSct->GetFormat() != pParent ) )
                pPrvSct = nullptr; // In order that nothing is merged
        }
        else if( pPrvSct && pPrvSct->GetFormat() == pParent )
        {   // Wonderful, here we can insert ourselves at the end
            pUp = pPrvSct;
            SwFrame* pLower = pUp->Lower();
            if( pLower && pLower->IsColumnFrame() )
            {
                pUp = static_cast<SwLayoutFrame*>(pUp->GetLastLower());
                // The body of the last column
                pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
            }
            // In order to perform the insertion after the last one
            pPrv = pUp->GetLastLower();
            pPrvSct = nullptr; // Such that nothing is merged
        }
        else
        {
            if( pSave )
            {   // Following situations: before and after the section-to-be
                // deleted there is the section boundary of the enclosing
                // section, or another (sibling) section connects subsequently,
                // that derives from the same Parent.
                // In that case, there's not (yet) a part of our parent available
                // that can store the content, so we create it here.
                pPrvSct = new SwSectionFrame( *pParent->GetSection(), pUp );
                pPrvSct->InsertBehind( pUp, pPrv );
                pPrvSct->Init();
                SwRectFnSet aRectFnSet(*pUp);
                aRectFnSet.MakePos( *pPrvSct, pUp, pPrv, true );
                pUp = FirstLeaf( pPrvSct );
                pPrv = nullptr;
            }
            pPrvSct = nullptr; // Such that nothing will be merged
        }
    }
    // The content is going to be inserted...
    if( pSave )
    {
        lcl_InvalidateInfFlags( pSave, bSize );
        ::RestoreContent( pSave, pUp, pPrv );
        pUp->FindPageFrame()->InvalidateContent();
        if( !bOldFootnote )
            static_cast<SwFootnoteFrame*>(pUp)->ColUnlock();
    }
    // Now two parts of the superior section could possibly be merged
    if( pPrvSct && !pPrvSct->IsJoinLocked() )
    {
        OSL_ENSURE( pNxtSct, "MoveContent: No Merge" );
        pPrvSct->MergeNext( pNxtSct );
    }
}

void SwSectionFrame::MakeAll(vcl::RenderContext* pRenderContext)
{
    if ( IsJoinLocked() || IsColLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
        return;
    if( !m_pSection ) // Via DelEmpty
    {
#ifdef DBG_UTIL
        OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
#endif
        if( !isFrameAreaPositionValid() )
        {
            if( GetUpper() )
            {
                SwRectFnSet aRectFnSet(*GetUpper());
                aRectFnSet.MakePos(*this, GetUpper(), ::sw::PrevSkipDead(this, false), false);
            }

            if (getFrameArea().Height() == 0)
            {
                // SwLayoutFrame::MakeAll() is not called for to-be-deleted
                // section frames (which would invalidate the position of the
                // next frame via the SwLayNotify dtor), so call it manually.
                if (SwFrame* pNext = GetNext())
                    pNext->InvalidatePos();
            }
        }

        setFrameAreaPositionValid(true);
        setFrameAreaSizeValid(true);
        setFramePrintAreaValid(true);
        return;
    }

    if (!GetPrev() && !IsFollow() && IsInDocBody() && IsHiddenNow())
    {
        // This may be the first frame on a page, and it may had moved to that page because its
        // content required that (a page break in the first paragraph, or a tall first line, or
        // "do not break paragraph" setting, or the like). Try to move back, to allow following
        // frames to move back, if possible. Sections cannot move back; workaround by a call to
        // GetPrevSctLeaf(), which may return a candidate upper frame on a previous page, or it
        // may create a new master for this at the end of the previous page. Cut and paste this
        // appropriately; then drop the temporary, if needed.
        if (SwLayoutFrame* moveBackPos = GetPrevSctLeaf())
        {
            if (moveBackPos->IsColBodyFrame())
            {
                moveBackPos = moveBackPos->GetUpper()->GetUpper();
            }
            SwLayoutFrame* newUpper = moveBackPos;
            SwFrame* newSibling = nullptr;
            const bool temporaryMasterCreated = IsFollow();
            if (temporaryMasterCreated)
            {
                assert(moveBackPos == &GetPrecede()->GetFrame());
                newUpper = moveBackPos->GetUpper();
                newSibling = moveBackPos->GetNext(); // actually, will be also nullptr
            }
            if (newUpper != GetUpper())
            {
                // Can't use MoveSubTree, because the move needs to fire events to re-layout
                Cut();
                Paste(newUpper, newSibling);
            }
            if (temporaryMasterCreated)
            {
                moveBackPos->Cut();
                DestroyFrame(moveBackPos);
            }
            assert(!IsFollow());
        }
    }

    LockJoin(); // I don't let myself to be destroyed on the way

    while( GetNext() && GetNext() == GetFollow() )
    {
        const SwFrame* pFoll = GetFollow();
        MergeNext( static_cast<SwSectionFrame*>(GetNext()) );
        if( pFoll == GetFollow() )
            break;
    }

    if (GetFollow() && IsHiddenNow())
    {
        // Merge all the follows of this hidden section
        while (auto* follow = GetFollow())
        {
            MergeNext(follow);
            if (GetFollow() == follow) // failed to merge
                break; // avoid endless loop
        }
    }

    // OD 2004-03-15 #116561# - In online layout join the follows, if section
    // can grow.
    const SwViewShell *pSh = getRootFrame()->GetCurrShell();

    // Split sections inside table cells: need to merge all follows of the
    // section here, as later we won't attempt doing so.
    bool bCanContainSplitSection = false;
    if (IsInTab() && GetUpper())
        bCanContainSplitSection = CanContainSplitSection(GetUpper());

    if( pSh && (pSh->GetViewOptions()->getBrowseMode() || bCanContainSplitSection) &&
         ( Grow( LONG_MAX, true ) > 0 ) )
    {
        while( GetFollow() )
        {
            const SwFrame* pFoll = GetFollow();
            MergeNext( GetFollow() );
            if( pFoll == GetFollow() )
                break;
        }
    }

    // A section with Follow uses all the space until the lower edge of the
    // Upper. If it moves, its size can grow or decrease...
    if( !isFrameAreaPositionValid() && ToMaximize( false ) )
    {
        setFrameAreaSizeValid(false);
    }

    SwLayoutFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());

    if (IsInTab())
    {
        // In case the section is in a table, then calculate the lower right
        // now. Just setting the valid size flag of the lower to false may not
        // be enough, as lcl_RecalcRow() can call
        // SwFrame::ValidateThisAndAllLowers(), and then we don't attempt
        // calculating the proper position of the lower.
        SwFrame* pLower = Lower();
        if (pLower && !pLower->isFrameAreaPositionValid())
            pLower->Calc(pRenderContext);
    }

    UnlockJoin();
    if( m_pSection && IsSuperfluous() )
        DelEmpty( false );
}

bool SwSectionFrame::ShouldBwdMoved( SwLayoutFrame *, bool & )
{
    OSL_FAIL( "Oops, where is my tinfoil hat?" );
    return false;
}

const SwSectionFormat* SwSectionFrame::GetEndSectFormat_() const
{
    const SwSectionFormat *pFormat = m_pSection->GetFormat();
    while( !pFormat->GetEndAtTextEnd().IsAtEnd() )
    {
        if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) )
            pFormat = pNewFormat;
        else
            return nullptr;
    }
    return pFormat;
}

static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame,
    SwFrame* pFrame, bool &rbChkFootnote )
{
    if( !pFrame )
        return;

    while( pFrame->GetNext() )
        pFrame = pFrame->GetNext();
    while( !rpContentFrame && pFrame )
    {
        if( pFrame->IsContentFrame() )
            rpContentFrame = static_cast<SwContentFrame*>(pFrame);
        else if( pFrame->IsLayoutFrame() )
        {
            if( pFrame->IsFootnoteFrame() )
            {
                if( rbChkFootnote )
                {
                    rpFootnoteFrame = static_cast<SwFootnoteFrame*>(pFrame);
                    rbChkFootnote = rpFootnoteFrame->GetAttr()->GetFootnote().IsEndNote();
                }
            }
            else
                lcl_FindContentFrame( rpContentFrame, rpFootnoteFrame,
                    static_cast<SwLayoutFrame*>(pFrame)->Lower(), rbChkFootnote );
        }
        pFrame = pFrame->GetPrev();
    }
}

SwContentFrame *SwSectionFrame::FindLastContent( SwFindMode nMode )
{
    SwContentFrame *pRet = nullptr;
    SwFootnoteFrame *pFootnoteFrame = nullptr;
    SwSectionFrame *pSect = this;
    if( nMode != SwFindMode::None )
    {
        const SwSectionFormat *pFormat = IsEndnAtEnd() ? GetEndSectFormat() :
                                     m_pSection->GetFormat();
        do {
            while( pSect->HasFollow() )
                pSect = pSect->GetFollow();
            SwFrame* pTmp = pSect->FindNext();
            while( pTmp && pTmp->IsSctFrame() &&
                   !static_cast<SwSectionFrame*>(pTmp)->GetSection() )
                pTmp = pTmp->FindNext();
            if( pTmp && pTmp->IsSctFrame() &&
                static_cast<SwSectionFrame*>(pTmp)->IsDescendantFrom( pFormat ) )
                pSect = static_cast<SwSectionFrame*>(pTmp);
            else
                break;
        } while( true );
    }
    bool bFootnoteFound = nMode == SwFindMode::EndNote;
    do
    {
        lcl_FindContentFrame( pRet, pFootnoteFrame, pSect->Lower(), bFootnoteFound );
        if( pRet || !pSect->IsFollow() || nMode == SwFindMode::None ||
            ( SwFindMode::MyLast == nMode && this == pSect ) )
            break;
        pSect = pSect->FindMaster();
    } while( pSect );
    if( ( nMode == SwFindMode::EndNote ) && pFootnoteFrame )
        pRet = pFootnoteFrame->ContainsContent();
    return pRet;
}

bool SwSectionFrame::CalcMinDiff( SwTwips& rMinDiff ) const
{
    if( ToMaximize( true ) )
    {
        SwRectFnSet aRectFnSet(*this);
        rMinDiff = aRectFnSet.GetPrtBottom(*GetUpper());
        rMinDiff = aRectFnSet.BottomDist( getFrameArea(), rMinDiff );
        return true;
    }
    return false;
}

/**
 *  CollectEndnotes looks for endnotes in the sectionfrm and his follows,
 *  the endnotes will cut off the layout and put into the array.
 *  If the first endnote is not a master-SwFootnoteFrame, the whole sectionfrm
 *  contains only endnotes and it is not necessary to collect them.
 */
static SwFootnoteFrame* lcl_FindEndnote( SwSectionFrame* &rpSect, bool &rbEmpty,
    SwLayouter *pLayouter )
{
    // if rEmpty is set, the rpSect is already searched
    SwSectionFrame* pSect = rbEmpty ? rpSect->GetFollow() : rpSect;
    while( pSect )
    {
        SwFrame* pLower = pSect->Lower();
        OSL_ENSURE( (pLower && pLower->IsColumnFrame()) || pSect->GetUpper()->IsFootnoteFrame(),
                "InsertEndnotes: Where's my column?" );

        // i73332: Columned section in endnote
        SwColumnFrame* pCol = nullptr;
        if(pLower && pLower->IsColumnFrame())
            pCol = static_cast<SwColumnFrame*>(pLower);

        while( pCol ) // check all columns
        {
            SwFootnoteContFrame* pFootnoteCont = pCol->FindFootnoteCont();
            if( pFootnoteCont )
            {
                SwFootnoteFrame* pRet = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
                while( pRet ) // look for endnotes
                {
                    /* CollectEndNode can destroy pRet so we need to get the
                       next early
                    */
                    SwFootnoteFrame* pRetNext = static_cast<SwFootnoteFrame*>(pRet->GetNext());
                    if( pRet->GetAttr()->GetFootnote().IsEndNote() )
                    {
                        if( pRet->GetMaster() )
                        {
                            if( pLayouter )
                                pLayouter->CollectEndnote( pRet );
                            else
                                return nullptr;
                        }
                        else
                            return pRet; // Found
                    }
                    pRet = pRetNext;
                }
            }
            pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
        }
        rpSect = pSect;
        pSect = pLayouter ? pSect->GetFollow() : nullptr;
        rbEmpty = true;
    }
    return nullptr;
}

static void lcl_ColumnRefresh( SwSectionFrame* pSect, bool bFollow )
{
    vcl::RenderContext* pRenderContext = pSect->getRootFrame()->GetCurrShell()->GetOut();
    while( pSect )
    {
        bool bOldLock = pSect->IsColLocked();
        pSect->ColLock();
        SwFrame* pLower = pSect->Lower();
        if( pLower && pLower->IsColumnFrame() )
        {
            SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pLower);
            do
            {   pCol->InvalidateSize_();
                pCol->InvalidatePos_();
                if (SwFrame* pColLower = static_cast<SwLayoutFrame*>(pCol)->Lower())
                {
                    pColLower->InvalidateSize_();
                    pCol->Calc(pRenderContext);   // calculation of column and
                    pColLower->Calc(pRenderContext);  // body
                }
                pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
            } while ( pCol );
        }
        if( !bOldLock )
            pSect->ColUnlock();
        if( bFollow )
            pSect = pSect->GetFollow();
        else
            pSect = nullptr;
    }
}

void SwSectionFrame::CollectEndnotes( SwLayouter* pLayouter )
{
    OSL_ENSURE( IsColLocked(), "CollectEndnotes: You love the risk?" );
    // i73332: Section in footnode does not have columns!
    SwFrame* pLower = Lower();
    OSL_ENSURE( (pLower && pLower->IsColumnFrame()) || GetUpper()->IsFootnoteFrame(), "Where's my column?" );

    SwSectionFrame* pSect = this;
    SwFootnoteFrame* pFootnote;
    bool bEmpty = false;
    // pSect is the last sectionfrm without endnotes or the this-pointer
    // the first sectionfrm with endnotes may be destroyed, when the endnotes
    // is cutted
    while( nullptr != (pFootnote = lcl_FindEndnote( pSect, bEmpty, pLayouter )) )
        pLayouter->CollectEndnote( pFootnote );
    if( pLayouter->HasEndnotes() )
        lcl_ColumnRefresh( this, true );
}

/** Fits the size to the surroundings.
|*
|*  Those that have a Follow or foot notes, have to extend until
|*  the lower edge of a upper (bMaximize)
|*  They must not extend above the Upper, as the case may be one can
|*  try to grow its upper (bGrow)
|*  If the size had to be changed, the content is calculated.
|*
|*  @note: perform calculation of content, only if height has changed (OD 18.09.2002 #100522#)
|*/
void SwSectionFrame::CheckClipping( bool bGrow, bool bMaximize )
{
    SwRectFnSet aRectFnSet(*this);
    tools::Long nDiff;
    SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
    if( bGrow && ( !IsInFly() || !GetUpper()->IsColBodyFrame() ||
                   !FindFlyFrame()->IsLocked() ) )
    {
        nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
        if( !bMaximize )
            nDiff += Undersize();
        if( nDiff > 0 )
        {
            tools::Long nAdd = GetUpper()->Grow( nDiff );
            if( aRectFnSet.IsVert() )
                nDeadLine -= nAdd;
            else
                nDeadLine += nAdd;
        }
    }
    nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
    SetUndersized( !bMaximize && nDiff >= 0 );
    const bool bCalc = ( IsUndersized() || bMaximize ) &&
                       ( nDiff ||
                         aRectFnSet.GetTop(getFramePrintArea()) > aRectFnSet.GetHeight(getFrameArea()) );
    // OD 03.11.2003 #i19737# - introduce local variable <bExtraCalc> to indicate
    // that a calculation has to be done beside the value of <bCalc>.
    bool bExtraCalc = false;
    if( !bCalc && !bGrow && IsAnyNoteAtEnd() && !IsInFootnote() )
    {
        SwSectionFrame *pSect = this;
        bool bEmpty = false;
        SwLayoutFrame* pFootnote = IsEndnAtEnd() ?
            lcl_FindEndnote( pSect, bEmpty, nullptr ) : nullptr;
        if( pFootnote )
        {
            pFootnote = pFootnote->FindFootnoteBossFrame();
            SwFrame* pTmp = FindLastContent( SwFindMode::LastCnt );
            // OD 08.11.2002 #104840# - use <SwLayoutFrame::IsBefore(..)>
            if ( pTmp && pFootnote->IsBefore( pTmp->FindFootnoteBossFrame() ) )
                bExtraCalc = true;
        }
        else if( GetFollow() && !GetFollow()->ContainsAny() )
            bExtraCalc = true;
    }
    if ( !(bCalc || bExtraCalc) )
        return;

    nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) );
    if( nDiff < 0 )
        nDeadLine = aRectFnSet.GetTop(getFrameArea());
    const Size aOldSz( getFramePrintArea().SSize() );
    tools::Long nTop = aRectFnSet.GetTopMargin(*this);

    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aRectFnSet.SetBottom( aFrm, nDeadLine );
    }

    nDiff = aRectFnSet.GetHeight(getFrameArea());
    if( nTop > nDiff )
        nTop = nDiff;
    aRectFnSet.SetYMargins( *this, nTop, 0 );

    // OD 18.09.2002 #100522#
    // Determine, if height has changed.
    // Note: In vertical layout the height equals the width value.
    bool bHeightChanged = aRectFnSet.IsVert() ?
                        (aOldSz.Width() != getFramePrintArea().Width()) :
                        (aOldSz.Height() != getFramePrintArea().Height());
    // Last but not least we have changed the height again, thus the inner
    // layout (columns) is calculated and the content as well.
    // OD 18.09.2002 #100522#
    // calculate content, only if height has changed.
    // OD 03.11.2003 #i19737# - restriction of content calculation too strong.
    // If an endnote has an incorrect position or a follow section contains
    // no content except footnotes/endnotes, the content has also been calculated.
    if ( !(( bHeightChanged || bExtraCalc ) && Lower()) )
        return;

    SwFrame* pLower = Lower();
    if( pLower && pLower->IsColumnFrame() )
    {
        lcl_ColumnRefresh( this, false );
        ::CalcContent( this );
    }
    else
    {
        ChgLowersProp( aOldSz );
        if( !bMaximize && !IsContentLocked() )
            ::CalcContent( this );
    }
}

void SwSectionFrame::SimpleFormat()
{
    if ( IsJoinLocked() || IsColLocked() )
        return;
    LockJoin();
    SwRectFnSet aRectFnSet(*this);
    SwFrame *const pPrev{::sw::PrevSkipDead(this, false)};
    if (pPrev || GetUpper())
    {
        // assure notifications on position changes.
        const SwLayNotify aNotify( this );
        aRectFnSet.MakePos(*this, GetUpper(), pPrev, false);
        setFrameAreaPositionValid(true);
    }
    SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
    // OD 22.10.2002 #97265# - call always method <lcl_ColumnRefresh(..)>, in
    // order to get calculated lowers, not only if there space left in its upper.
    if (aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0)
    {
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.SetBottom( aFrm, nDeadLine );
        }

        tools::Long nHeight = aRectFnSet.GetHeight(getFrameArea());
        tools::Long nTop = CalcUpperSpace();
        if( nTop > nHeight )
            nTop = nHeight;
        aRectFnSet.SetYMargins( *this, nTop, 0 );
    }
    lcl_ColumnRefresh( this, false );
    UnlockJoin();
}

namespace {

// #i40147# - helper class to perform extra section format
// to position anchored objects and to keep the position of whose objects locked.
class ExtraFormatToPositionObjs
{
    private:
        SwSectionFrame* mpSectFrame;
        bool mbExtraFormatPerformed;

    public:
        explicit ExtraFormatToPositionObjs( SwSectionFrame& _rSectFrame)
            : mpSectFrame( &_rSectFrame ),
              mbExtraFormatPerformed( false )
        {}

        ~ExtraFormatToPositionObjs()
        {
            if ( !mbExtraFormatPerformed )
                return;

            // release keep locked position of lower floating screen objects
            SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
            SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
            if ( pObjs )
            {
                for (SwAnchoredObject* pAnchoredObj : *pObjs)
                {
                    if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
                    {
                        pAnchoredObj->SetKeepPosLocked( false );
                    }
                }
            }
        }

        // #i81555#
        void InitObjs( SwFrame& rFrame )
        {
            SwSortedObjs* pObjs = rFrame.GetDrawObjs();
            if ( pObjs )
            {
                for (SwAnchoredObject* pAnchoredObj : *pObjs)
                {
                    pAnchoredObj->UnlockPosition();
                    pAnchoredObj->SetClearedEnvironment( false );
                }
            }
            if ( rFrame.IsLayoutFrame() )
            {
                SwFrame* pLowerFrame = rFrame.GetLower();
                while ( pLowerFrame != nullptr )
                {
                    InitObjs( *pLowerFrame );

                    pLowerFrame = pLowerFrame->GetNext();
                }
            }
        }

        void FormatSectionToPositionObjs()
        {
            vcl::RenderContext* pRenderContext = mpSectFrame->getRootFrame()->GetCurrShell()->GetOut();
            // perform extra format for multi-columned section.
            SwFrame* pLower = mpSectFrame->Lower();
            if ( !(pLower && pLower->IsColumnFrame() && pLower->GetNext()) )
                return;

            // grow section till bottom of printing area of upper frame
            SwRectFnSet aRectFnSet(*mpSectFrame);
            SwTwips nTopMargin = aRectFnSet.GetTopMargin(*mpSectFrame);
            Size aOldSectPrtSize( mpSectFrame->getFramePrintArea().SSize() );
            SwTwips nDiff = aRectFnSet.BottomDist( mpSectFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*mpSectFrame->GetUpper()) );

            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mpSectFrame);
                aRectFnSet.AddBottom( aFrm, nDiff );
            }

            aRectFnSet.SetYMargins( *mpSectFrame, nTopMargin, 0 );
            // #i59789#
            // suppress formatting, if printing area of section is too narrow
            if ( aRectFnSet.GetHeight(mpSectFrame->getFramePrintArea()) <= 0 )
            {
                return;
            }
            mpSectFrame->ChgLowersProp( aOldSectPrtSize );

            // format column frames and its body and footnote container
            SwColumnFrame* pColFrame = static_cast<SwColumnFrame*>(mpSectFrame->Lower());
            while ( pColFrame )
            {
                pColFrame->Calc(pRenderContext);
                if (SwFrame* pColLower = pColFrame->Lower())
                {
                    pColLower->Calc(pRenderContext);
                    if ( pColLower->GetNext() )
                    {
                        pColLower->GetNext()->Calc(pRenderContext);
                    }
                }

                pColFrame = static_cast<SwColumnFrame*>(pColFrame->GetNext());
            }

            // unlock position of lower floating screen objects for the extra format
            // #i81555#
            // Section frame can already have changed the page and its content
            // can still be on the former page.
            // Thus, initialize objects via lower-relationship
            InitObjs( *mpSectFrame );

            // format content - first with collecting its foot-/endnotes before content
            // format, second without collecting its foot-/endnotes.
            ::CalcContent( mpSectFrame );
            ::CalcContent( mpSectFrame, true );

            // keep locked position of lower floating screen objects
            SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
            SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
            if ( pObjs )
            {
                for (SwAnchoredObject* pAnchoredObj : *pObjs)
                {
                    if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
                    {
                        pAnchoredObj->SetKeepPosLocked( true );
                    }
                }
            }

            mbExtraFormatPerformed = true;

        }
};

}

/// "formats" the frame; Frame and PrtArea
void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttr )
{
    if( !m_pSection ) // via DelEmpty
    {
#ifdef DBG_UTIL
        OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
#endif
        setFrameAreaPositionValid(true);
        setFrameAreaSizeValid(true);
        setFramePrintAreaValid(true);
        return;
    }

    SwRectFnSet aRectFnSet(*this);

    if (GetSection()->CalcHiddenFlag())
    {
        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.SetHeight(aFrm, 0);
        }
        {
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetHeight(aPrt, 0);
        }
        setFrameAreaSizeValid(true);
        setFramePrintAreaValid(true);
    }

    if ( !isFramePrintAreaValid() )
    {
        PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr )
        setFramePrintAreaValid(true);
        SwTwips nUpper = CalcUpperSpace();

        // #109700# LRSpace for sections
        const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
        aRectFnSet.SetXMargins(*this, rLRSpace.ResolveLeft({}), rLRSpace.ResolveRight({}));

        if( nUpper != aRectFnSet.GetTopMargin(*this) )
        {
            setFrameAreaSizeValid(false);
            SwFrame* pOwn = ContainsAny();
            if( pOwn )
                pOwn->InvalidatePos_();
        }
        aRectFnSet.SetYMargins( *this, nUpper, 0 );
    }

    if ( isFrameAreaSizeValid() )
        return;

    PROTOCOL_ENTER( this, PROT::Size, DbgAction::NONE, nullptr )
    const tools::Long nOldHeight = aRectFnSet.GetHeight(getFrameArea());
    bool bOldLock = IsColLocked();
    ColLock();

    setFrameAreaSizeValid(true);

    // The size is only determined by the content, if the SectFrame does not have a
    // Follow. Otherwise it fills (occupies) the Upper down to the lower edge.
    // It is not responsible for the text flow, but the content is.
    bool bMaximize = ToMaximize( false );

    // OD 2004-05-17 #i28701# - If the wrapping style has to be considered
    // on object positioning, an extra formatting has to be performed
    // to determine the correct positions the floating screen objects.
    // #i40147#
    // use new helper class <ExtraFormatToPositionObjs>.
    // This class additionally keep the locked position of the objects
    // and releases this position lock keeping on destruction.
    ExtraFormatToPositionObjs aExtraFormatToPosObjs( *this );
    if ( !bMaximize &&
         GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
         !GetFormat()->GetBalancedColumns().GetValue() )
    {
        aExtraFormatToPosObjs.FormatSectionToPositionObjs();
    }

    // Column widths have to be adjusted before calling CheckClipping.
    // CheckClipping can cause the formatting of the lower frames
    // which still have a width of 0.
    const bool bHasColumns = Lower() && Lower()->IsColumnFrame();
    if ( bHasColumns && Lower()->GetNext() )
        AdjustColumns( nullptr, false );

    if( GetUpper() )
    {
        const tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());

        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.SetWidth( aFrm, nWidth );
        }

        // #109700# LRSpace for sections
        {
            const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetWidth(aPrt,
                                nWidth - rLRSpace.ResolveLeft({}) - rLRSpace.ResolveRight({}));
        }

        // OD 15.10.2002 #103517# - allow grow in online layout
        // Thus, set <..IsBrowseMode()> as parameter <bGrow> on calling
        // method <CheckClipping(..)>.
        const SwViewShell *pSh = getRootFrame()->GetCurrShell();
        CheckClipping( pSh && pSh->GetViewOptions()->getBrowseMode(), bMaximize );
        bMaximize = ToMaximize( false );
        setFrameAreaSizeValid(true);
    }

    // Check the width of the columns and adjust if necessary
    if ( bHasColumns && ! Lower()->GetNext() && bMaximize )
        static_cast<SwColumnFrame*>(Lower())->Lower()->Calc(pRenderContext);

    if ( !bMaximize )
    {
        SwTwips nRemaining = aRectFnSet.GetTopMargin(*this);
        SwFrame *pFrame = m_pLower;
        if( pFrame )
        {
            if( pFrame->IsColumnFrame() && pFrame->GetNext() )
            {
                // #i61435#
                // suppress formatting, if upper frame has height <= 0
                if ( aRectFnSet.GetHeight(GetUpper()->getFrameArea()) > 0 )
                {
                    FormatWidthCols( *pAttr, nRemaining, MINLAY );
                }
                // #126020# - adjust check for empty section
                // #130797# - correct fix #126020#
                while( HasFollow() && !GetFollow()->ContainsContent() &&
                       !GetFollow()->ContainsAny( true ) )
                {
                    SwFrame* pOld = GetFollow();
                    GetFollow()->DelEmpty( false );
                    if( pOld == GetFollow() )
                        break;
                }
                bMaximize = ToMaximize( false );
                nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
            }
            else
            {
                if( pFrame->IsColumnFrame() )
                {
                    pFrame->Calc(pRenderContext);
                    pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
                    pFrame->Calc(pRenderContext);
                    pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
                    CalcFootnoteContent();
                }
                // If we are in a columned frame which calls a CalcContent
                // in the FormatWidthCols, the content might need calculating
                if( pFrame && !pFrame->isFrameAreaDefinitionValid() && IsInFly() &&
                    FindFlyFrame()->IsColLocked() )
                    ::CalcContent( this );
                nRemaining += InnerHeight();
                bMaximize = HasFollow();
            }
        }

        SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
        if( nDiff < 0)
        {
            SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
            {
                tools::Long nBottom = aRectFnSet.GetBottom(getFrameArea());
                nBottom = aRectFnSet.YInc( nBottom, -nDiff );
                tools::Long nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
                if( nTmpDiff > 0 )
                {
                    nTmpDiff = GetUpper()->Grow( nTmpDiff, true );
                    nDeadLine = aRectFnSet.YInc( nDeadLine, nTmpDiff );
                    nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
                    if( nTmpDiff > 0 )
                        nDiff += nTmpDiff;
                    if( nDiff > 0 )
                        nDiff = 0;
                }
            }
        }
        if( nDiff )
        {
            tools::Long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea());
            tools::Long nTop = aRectFnSet.GetTopMargin(*this);

            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom( aFrm, nTmp );
            }

            aRectFnSet.SetYMargins( *this, nTop, 0 );
            InvalidateNextPos();

            if (m_pLower && (!m_pLower->IsColumnFrame() || !m_pLower->GetNext()))
            {
                // If a single-column section just created the space that
                // was requested by the "undersized" paragraphs, then they
                // have to be invalidated and calculated, so they fully cover it
                pFrame = m_pLower;
                if( pFrame->IsColumnFrame() )
                {
                    pFrame->InvalidateSize_();
                    pFrame->InvalidatePos_();
                    pFrame->Calc(pRenderContext);
                    pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
                    pFrame->Calc(pRenderContext);
                    pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
                    CalcFootnoteContent();
                }
                bool bUnderSz = false;
                while( pFrame )
                {
                    if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
                    {
                        pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
                        bUnderSz = true;
                    }
                    pFrame = pFrame->GetNext();
                }
                if( bUnderSz && !IsContentLocked() )
                    ::CalcContent( this );
            }
        }
    }

    // Do not exceed the lower edge of the Upper.
    // Do not extend below the lower edge with Sections with Follows
    if ( GetUpper() )
        CheckClipping( true, bMaximize );
    if( !bOldLock )
        ColUnlock();
    tools::Long nDiff = nOldHeight - aRectFnSet.GetHeight(getFrameArea());

    if( nDiff > 0 )
    {
        if( !GetNext() )
            SetRetouche(); // Take over the retouching ourselves
        if( GetUpper() && !GetUpper()->IsFooterFrame() )
            GetUpper()->Shrink( nDiff );
    }

    if( IsUndersized() )
    {
        setFramePrintAreaValid(true);
    }

}

/// Returns the next layout sheet where the frame can be moved in.
/// New pages are created only if specified by the parameter.
SwLayoutFrame *SwFrame::GetNextSctLeaf( MakePageType eMakePage )
{
    // Attention: Nested sections are currently not supported

    PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::NextSect, GetUpper()->FindSctFrame() )

    // Shortcuts for "columned" sections, if we're not in the last column
    // Can we slide to the next column of the section?
    if( IsColBodyFrame() && GetUpper()->GetNext() )
        return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetNext())->Lower());
    if( GetUpper()->IsColBodyFrame() && GetUpper()->GetUpper()->GetNext() )
        return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetUpper()->GetNext())->Lower());
    // Inside a table-in-section, or sections of headers/footers, there can be only
    // one column shift be made, one of the above shortcuts should have applied!
    if( !CanContainSplitSection(GetUpper()) || FindFooterOrHeader() )
        return nullptr;

    SwSectionFrame *pSect = FindSctFrame();
    bool bWrongPage = false;
    assert(pSect && "GetNextSctLeaf: Missing SectionFrame");

    // Shortcut for sections with Follows. That's ok,
    // if no columns or pages (except dummy pages) lie in between.
    // In case of linked frames and in footnotes the shortcut would get
    // even more costly
    if( pSect->HasFollow() && pSect->IsInDocBody() && !pSect->IsInTab() )
    {
        if( pSect->GetFollow() == pSect->GetNext() )
        {
            SwPageFrame *pPg = pSect->GetFollow()->FindPageFrame();
            if( WrongPageDesc( pPg ) )
                bWrongPage = true;
            else
                return FirstLeaf( pSect->GetFollow() );
        }
        else
        {
            SwFrame* pTmp;
            if( !pSect->GetUpper()->IsColBodyFrame() ||
                nullptr == ( pTmp = pSect->GetUpper()->GetUpper()->GetNext() ) )
                pTmp = pSect->FindPageFrame()->GetNext();
            if( pTmp ) // is now the next column or page
            {
                SwFrame* pTmpX = pTmp;
                if( pTmp->IsPageFrame() && static_cast<SwPageFrame*>(pTmp)->IsEmptyPage() )
                    pTmp = pTmp->GetNext(); // skip dummy pages
                SwFrame *pUp = pSect->GetFollow()->GetUpper();
                // pUp becomes the next column if the Follow lies in a column
                // that is not a "not first" one, otherwise the page
                if( !pUp->IsColBodyFrame() ||
                    !( pUp = pUp->GetUpper() )->GetPrev() )
                    pUp = pUp->FindPageFrame();
                // Now pUp and pTmp have to be the same page/column, otherwise
                // pages or columns lie between Master and Follow
                if( pUp == pTmp || pUp->GetNext() == pTmpX )
                {
                    SwPageFrame* pNxtPg = pUp->IsPageFrame() ?
                                        static_cast<SwPageFrame*>(pUp) : pUp->FindPageFrame();
                    if( WrongPageDesc( pNxtPg ) )
                        bWrongPage = true;
                    else
                        return FirstLeaf( pSect->GetFollow() );
                }
            }
        }
    }

#ifndef NDEBUG
    std::vector<SwFrame *> parents;
    for (SwFrame * pTmp = GetUpper(); pTmp && !pTmp->IsPageFrame(); pTmp = pTmp->GetUpper())
    {
        parents.push_back(pTmp);
    }
#endif

    // Always end up in the same section: Body again inside Body etc.
    const bool bBody = IsInDocBody();
    const bool bFootnotePage = FindPageFrame()->IsFootnotePage();

    // The "pLayLeaf is in a table" case is rejected by default, so that it
    // can't happen that we try to move a table to one of its own cells.
    bool bLayLeafTableAllowed = false;
    SwLayoutFrame *pLayLeaf;

    SwLayoutFrame* pCellLeaf = nullptr;
    if (GetUpper()->IsInTab())
    {
        if (IsTabFrame())
        {
            return nullptr; // table in section in table: split disabled for now
        }
        // We are *in* a table (not an outermost SwTabFrame), see if there
        // is a follow cell frame created already.
        pCellLeaf = GetNextCellLeaf();
        if (!pCellLeaf)
        {
            SAL_WARN("sw.layout", "section is in table, but the table is not split");
            return nullptr;
        }
    }

    // A shortcut for TabFrames such that not all cells need to be visited
    if( bWrongPage )
        pLayLeaf = nullptr;
    else if( IsTabFrame() )
    {
        SwFrame *const pTmpCnt = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
        pLayLeaf = pTmpCnt ? pTmpCnt->GetUpper() : nullptr;
    }
    else if (pCellLeaf && CanContainSplitSection(this))
    {
        // This frame is in a table-not-in-section, its follow should be
        // inserted under the follow of the frame's cell.
        pLayLeaf = pCellLeaf;
        if (pLayLeaf->FindTabFrame() == FindTabFrame())
            SAL_WARN("sw.layout", "my table frame and my follow's table frame is the same");
        // In this case pLayLeaf pointing to an in-table frame is OK.
        bLayLeafTableAllowed = true;
    }
    else
    {
        pLayLeaf = GetNextLayoutLeaf();
        if( IsColumnFrame() )
        {
            while( pLayLeaf && static_cast<SwColumnFrame*>(this)->IsAnLower( pLayLeaf ) )
                pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
        }
    }

    SwLayoutFrame *pOldLayLeaf = nullptr;           // Such that in case of newly
                                            // created pages, the search is
                                            // not started over at the beginning

    while( true )
    {
        if( pLayLeaf )
        {
            // A layout leaf was found, let's see whether it can store me or
            // another SectionFrame can be inserted here, or we have to continue
            // searching
            SwPageFrame* pNxtPg = pLayLeaf->FindPageFrame();
            if ( !bFootnotePage && pNxtPg->IsFootnotePage() )
            {   // If I reached the end note pages it's over
                pLayLeaf = nullptr;
                continue;
            }
            // Once inBody always inBody, don't step into tables-in-sections and not into other sections
            if ( (bBody && !pLayLeaf->IsInDocBody()) ||
                 (IsInFootnote() != pLayLeaf->IsInFootnote() ) ||
                 (pLayLeaf->IsInTab() && !bLayLeafTableAllowed) ||
                 ( pLayLeaf->IsInSct() && ( !pSect->HasFollow()
                   || pSect->GetFollow() != pLayLeaf->FindSctFrame() ) ) )
            {
                // Rejected - try again.
                pOldLayLeaf = pLayLeaf;
                pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
                continue;
            }
            // Page desc is never wrong in case of sections-in-tables: in that
            // case pLayLeaf points to our section's cell's follow, which is
            // fine to be on the same page. New page creation is handled when
            // creating / moving the cell frame.
            // It doesn't make sense to move to a page that starts with break?
            if (pNxtPg != FindPageFrame() // tdf#156725 not between columns!
                && !FindFlyFrame() // tdf#156419 linked fly frames don't care!
                && (WrongPageDesc(pNxtPg) || HasPageBreakBefore(*pNxtPg))
                && !bLayLeafTableAllowed)
            {
                if( bWrongPage )
                    break; // there's a column between me and my right page
                pLayLeaf = nullptr;
                bWrongPage = true;
                pOldLayLeaf = nullptr;
                continue;
            }
        }
        // There is no further LayoutFrame that fits, so a new page
        // has to be created, although new pages are worthless within a frame
        else if( !pSect->IsInFly() &&
            ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
        {
            InsertPage(pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
                       false );
            // and again the whole thing
            if (pCellLeaf && CanContainSplitSection(this))
                // GetNextLayoutLeaf() would refer to the next cell in the same
                // row, avoid that. pCellLeaf points to the correct cell in the
                // follow table, and in the next round it'll be used, as we now
                // have a next page.
                pLayLeaf = pCellLeaf;
            else
                pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
            continue;
        }
        break;
    }

    if( pLayLeaf )
    {
        // We have found the suitable layout sheet. If there (in the sheet) is
        // already a Follow of our section, we take its first layout sheet,
        // otherwise it is time to create a section follow
        SwSectionFrame* pNew = nullptr;

        // This can be omitted if existing Follows were cut short
        SwFrame* pFirst = pLayLeaf->Lower();
        // Here SectionFrames that are to be deleted must be ignored
        while( pFirst && pFirst->IsSctFrame() && !static_cast<SwSectionFrame*>(pFirst)->GetSection() )
            pFirst = pFirst->GetNext();
        if( pFirst && pFirst->IsSctFrame() && pSect->GetFollow() == pFirst )
            pNew = pSect->GetFollow();
        else if( MAKEPAGE_NOSECTION == eMakePage )
            return pLayLeaf;
        else if (pSect->GetSection())
        {
            pNew = new SwSectionFrame( *pSect, false );
            pNew->InsertBefore( pLayLeaf, pLayLeaf->Lower() );
            pNew->Init();
            SwRectFnSet aRectFnSet(*pNew);
            aRectFnSet.MakePos( *pNew, pLayLeaf, nullptr, true );

#ifndef NDEBUG
            {   // sanity check the parents of the new frame vs. the old frame
                SwFrame * pTmp = pNew;
                auto iter(parents.begin());
                if (parents.size() >= 2 &&
                    parents[0]->IsBodyFrame() && parents[1]->IsColumnFrame())
                {   // this only inserts section frame - remove column
                    assert(parents[2]->IsSctFrame() || IsSctFrame());
                    if (parents[2]->IsSctFrame())
                        std::advance(iter, +2);
                    else
                        pTmp = pTmp->GetUpper();
                }
                else if (IsBodyFrame() && parents.size() >= 1
                         && parents[0]->IsColumnFrame())
                {   // same as above, special case: "this" is the body frame
                    assert(parents[1]->IsSctFrame());
                    std::advance(iter, +1);
                }
                else  if (IsSctFrame()) // special case: "this" is the section
                {
                    pTmp = pTmp->GetUpper();
                }

                for ( ; iter != parents.end(); ++iter)
                {
                    if (pTmp->IsPageFrame())
                    {
                        if ((*iter)->IsColumnFrame() &&
                            (iter + 1) != parents.end() && (*(iter + 1))->IsBodyFrame())
                        {   // page style has columns - evidently these are
                            break; // added later?
                        }
                        assert(!pTmp->IsPageFrame());
                    }
                    assert(pTmp->GetType() == (*iter)->GetType());
                    // for cell frames and table frames:
                    // 1) there may be multiple follow frames of the old one
                    // 2) the new frame may be identical to the old one
                    //    (not sure if this is allowed, but it happens now
                    //     for the outer table of a nested table)
                    if (pTmp->IsCellFrame())
                    {
                        SwCellFrame const*const pNewF(static_cast<SwCellFrame*>(pTmp));
                        SwCellFrame const*const pOldF(static_cast<SwCellFrame*>(*iter));
                        bool bFollowFound(false);
                        for (SwCellFrame const* pOldIter = pOldF;
                            pOldIter; pOldIter = pOldIter->GetFollowCell())
                        {
                            if (pOldIter == pNewF)
                            {
                                bFollowFound = true;
                                break;
                            }
                        }
                        assert(bFollowFound);
                    }
                    else if (pTmp->IsFlowFrame())
                    {
                        SwFlowFrame const*const pNewF(SwFlowFrame::CastFlowFrame(pTmp));
                        SwFlowFrame const*const pOldF(SwFlowFrame::CastFlowFrame(*iter));
                        bool bFollowFound(false);
                        for (SwFlowFrame const* pOldIter = pOldF;
                            pOldIter; pOldIter = pOldIter->GetFollow())
                        {
                            if (pOldIter == pNewF)
                            {
                                bFollowFound = true;
                                break;
                            }
                        }
                        assert(bFollowFound);
                    }
                    pTmp = pTmp->GetUpper();
                }
                assert(pTmp == nullptr /* SwFlyAtContentFrame case */
                    || pTmp->IsPageFrame() // usual case
                       // the new page has columns, but the old page did not
                    || (pTmp->IsColumnFrame() && pTmp->GetUpper()->IsBodyFrame()
                        && pTmp->GetUpper()->GetUpper()->IsPageFrame()));
            }
#endif

            // If our section frame has a successor then that has to be
            // moved behind the new Follow of the section frames
            SwFrame* pTmp = pSect->GetNext();
            if( pTmp && pTmp != pSect->GetFollow() )
            {
                SwFlowFrame* pNxt;
                SwContentFrame* pNxtContent = nullptr;
                if( pTmp->IsContentFrame() )
                {
                    pNxt = static_cast<SwContentFrame*>(pTmp);
                    pNxtContent = static_cast<SwContentFrame*>(pTmp);
                }
                else
                {
                    pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
                    if( pTmp->IsSctFrame() )
                        pNxt = static_cast<SwSectionFrame*>(pTmp);
                    else
                    {
                        assert(pTmp->IsTabFrame());
                        pNxt = static_cast<SwTabFrame*>(pTmp);
                    }
                    while( !pNxtContent && nullptr != ( pTmp = pTmp->GetNext() ) )
                    {
                        if( pTmp->IsContentFrame() )
                            pNxtContent = static_cast<SwContentFrame*>(pTmp);
                        else
                            pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
                    }
                }
                if( pNxtContent )
                {
                    SwFootnoteBossFrame* pOldBoss = pSect->FindFootnoteBossFrame( true );
                    if( pOldBoss == pNxtContent->FindFootnoteBossFrame( true ) )
                    {
                        SwSaveFootnoteHeight aHeight( pOldBoss,
                            pOldBoss->getFrameArea().Top() + pOldBoss->getFrameArea().Height() );
                        pSect->GetUpper()->MoveLowerFootnotes( pNxtContent, pOldBoss,
                                    pLayLeaf->FindFootnoteBossFrame( true ), false );
                    }
                }
                pNxt->MoveSubTree( pLayLeaf, pNew->GetNext() );
            }
            if( pNew->GetFollow() )
                pNew->SimpleFormat();
        }
        // The wanted layout sheet is now the first of the determined SctFrames:
        pLayLeaf = pNew ? FirstLeaf(pNew) : nullptr;
    }
    return pLayLeaf;
}

/// Returns the preceding layout sheet where the frame can be moved into
SwLayoutFrame *SwFrame::GetPrevSctLeaf()
{
    PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::PrevSect, GetUpper()->FindSctFrame() )

    SwLayoutFrame* pCol;
    // ColumnFrame always contain a BodyFrame now
    if( IsColBodyFrame() )
        pCol = GetUpper();
    else if( GetUpper()->IsColBodyFrame() )
        pCol = GetUpper()->GetUpper();
    else
        pCol = nullptr;
    bool bJump = false;
    if( pCol )
    {
        if( pCol->GetPrev() )
        {
            do
            {
                pCol = static_cast<SwLayoutFrame*>(pCol->GetPrev());
                // Is there any content?
                if( static_cast<SwLayoutFrame*>(pCol->Lower())->Lower() )
                {
                    if( bJump )     // Did we skip a blank page?
                        SwFlowFrame::SetMoveBwdJump( true );
                    return static_cast<SwLayoutFrame*>(pCol->Lower());  // The columnm body
                }
                bJump = true;
            } while( pCol->GetPrev() );

            // We get here when all columns are empty, pCol is now the
            // first column, we need the body though
            pCol = static_cast<SwLayoutFrame*>(pCol->Lower());
        }
        else
            pCol = nullptr;
    }

    if( bJump )     // Did we skip a blank page?
        SwFlowFrame::SetMoveBwdJump( true );

    SwSectionFrame *pSect = FindSctFrame();
    if (!pCol && pSect && IsInTab() && CanContainSplitSection(this))
    {
        // We don't have a previous section yet, and we're in a
        // section-in-table.
        if (SwFlowFrame* pPrecede = pSect->GetPrecede())
        {
            // Our section has a precede, work with that.
            if (pPrecede->GetFrame().IsLayoutFrame())
                pCol = static_cast<SwLayoutFrame*>(&pPrecede->GetFrame());
        }
    }

    // Within sections in tables or section in headers/footers there can
    // be only one column change be made, one of the above shortcuts should
    // have applied, also when the section has a pPrev.
    // Now we even consider an empty column...
    OSL_ENSURE( pSect, "GetNextSctLeaf: Missing SectionFrame" );
    if (!pSect || (IsInTab() && !IsTabFrame()) || FindFooterOrHeader())
        return pCol;

    // === IMPORTANT ===
    // Precondition, which needs to be hold, is that the <this> frame can be
    // inside a table, but then the found section frame <pSect> is also inside
    // this table.

    // #i95698#
    // A table cell containing directly a section does not break - see lcl_FindSectionsInRow(..)
    // Thus, a table inside a section, which is inside another table can only
    // flow backward in the columns of its section.
    // Note: The table cell, which contains the section, can not have a master table cell.
    if ( IsTabFrame() && pSect->IsInTab() )
    {
        return pCol;
    }

    {
        if (SwFrame *pPrv = pSect->GetIndPrev())
        {
            // Mooching, half dead SectionFrames shouldn't confuse us
            while( pPrv && pPrv->IsSctFrame() && !static_cast<SwSectionFrame*>(pPrv)->GetSection() )
                pPrv = pPrv->GetPrev();
            if( pPrv )
                return pCol;
        }
    }

    const bool bBody = IsInDocBody();
    const bool bFly  = IsInFly();

    SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
    SwLayoutFrame *pPrevLeaf = nullptr;

    while ( pLayLeaf )
    {
        // Never step into tables or sections
        if ( pLayLeaf->IsInTab() || pLayLeaf->IsInSct() )
        {
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
        }
        else if ( bBody && pLayLeaf->IsInDocBody() )
        {
            // If there is a pLayLeaf has a lower pLayLeaf is the frame we are looking for.
            // Exception: pLayLeaf->Lower() is a zombie section frame
            const SwFrame* pTmp = pLayLeaf->Lower();
            // OD 11.04.2003 #108824# - consider, that the zombie section frame
            // can have frame below it in the found layout leaf.
            // Thus, skipping zombie section frame, if possible.
            while ( pTmp && pTmp->IsSctFrame() &&
                    !( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) &&
                    pTmp->GetNext()
                  )
            {
                pTmp = pTmp->GetNext();
            }
            if ( pTmp &&
                 ( !pTmp->IsSctFrame() ||
                   ( static_cast<const SwSectionFrame*>(pTmp)->GetSection() )
                 )
               )
            {
                break;
            }
            pPrevLeaf = pLayLeaf;
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
            if ( pLayLeaf )
                SwFlowFrame::SetMoveBwdJump( true );
        }
        else if ( bFly )
            break;  // Contents in Flys every layout sheet should be right. Why?
        else
            pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
    }
    if( !pLayLeaf )
    {
        if( !pPrevLeaf )
            return pCol;
        pLayLeaf = pPrevLeaf;
    }

    SwSectionFrame* pNew = nullptr;
    // At first go to the end of the layout sheet
    SwFrame *pTmp = pLayLeaf->Lower();
    if( pTmp )
    {
        while( pTmp->GetNext() )
            pTmp = pTmp->GetNext();
        if( pTmp->IsSctFrame() )
        {
            // Half dead ones only interfere here
            while( !static_cast<SwSectionFrame*>(pTmp)->GetSection() && pTmp->GetPrev() &&
                    pTmp->GetPrev()->IsSctFrame() )
                pTmp = pTmp->GetPrev();
            if( static_cast<SwSectionFrame*>(pTmp)->GetFollow() == pSect )
                pNew = static_cast<SwSectionFrame*>(pTmp);
        }
    }
    if( !pNew )
    {
        pNew = new SwSectionFrame( *pSect, true );
        pNew->InsertBefore( pLayLeaf, nullptr );
        pNew->Init();
        SwRectFnSet aRectFnSet(*pNew);
        aRectFnSet.MakePos(*pNew, pLayLeaf, ::sw::PrevSkipDead(pNew, false), true);

        pLayLeaf = FirstLeaf( pNew );
        if( !pNew->Lower() )    // Format single column sections
        {
            pNew->MakePos();
            pLayLeaf->Format(getRootFrame()->GetCurrShell()->GetOut()); // In order that the PrtArea is correct for the MoveBwd
        }
        else
            pNew->SimpleFormat();
    }
    else
    {
        pLayLeaf = FirstLeaf( pNew );
        if( pLayLeaf->IsColBodyFrame() )
        {
            // In existent section columns we're looking for the last not empty
            // column.
            SwLayoutFrame *pTmpLay = pLayLeaf;
            while( pLayLeaf->GetUpper()->GetNext() )
            {
                pLayLeaf = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLayLeaf->GetUpper()->GetNext())->Lower());
                if( pLayLeaf->Lower() )
                    pTmpLay = pLayLeaf;
            }
            // If we skipped an empty column, we've to set the jump-flag
            if( pLayLeaf != pTmpLay )
            {
                pLayLeaf = pTmpLay;
                SwFlowFrame::SetMoveBwdJump( true );
            }
        }
    }
    return pLayLeaf;
}

static SwTwips lcl_DeadLine( const SwFrame* pFrame )
{
    const SwLayoutFrame* pUp = pFrame->GetUpper();
    while( pUp && pUp->IsInSct() )
    {
        if( pUp->IsSctFrame() )
            pUp = pUp->GetUpper();
        // Columns now with BodyFrame
        else if( pUp->IsColBodyFrame() && pUp->GetUpper()->GetUpper()->IsSctFrame() )
            pUp = pUp->GetUpper()->GetUpper();
        else
            break;
    }
    SwRectFnSet aRectFnSet(*pFrame);
    return pUp ? aRectFnSet.GetPrtBottom(*pUp) :
                 aRectFnSet.GetBottom(pFrame->getFrameArea());
}

/// checks whether the SectionFrame is still able to grow, as case may be the environment has to be asked
bool SwSectionFrame::Growable() const
{
    SwRectFnSet aRectFnSet(*this);
    if( aRectFnSet.YDiff( lcl_DeadLine( this ),
        aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
        return true;

    return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->Grow( LONG_MAX, true ) );
}

SwTwips SwSectionFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool bTst)
{
    SwSection* pSection = GetSection();
    if (pSection && pSection->CalcHiddenFlag())
    {
        reason = SwResizeLimitReason::Unspecified;
        return 0;
    }

    if (IsColLocked() || HasFixSize())
    {
        reason = HasFixSize() ? SwResizeLimitReason::FixedSizeFrame : SwResizeLimitReason::Unspecified;
        return 0;
    }

    const auto nOrigDist = nDist;
    reason = SwResizeLimitReason::Unspecified;
    SwRectFnSet aRectFnSet(*this);
    tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
    if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
        nDist = LONG_MAX - nFrameHeight;

    if ( nDist <= 0 )
        return 0;

    bool bInCalcContent = GetUpper() && IsInFly() && FindFlyFrame()->IsLocked();
    // OD 2004-03-15 #116561# - allow grow in online layout
    bool bGrow = !Lower() || !Lower()->IsColumnFrame() || !Lower()->GetNext();
    if (!bGrow)
    {
        bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue();
        if (!bGrow && nOrigDist)
            reason = SwResizeLimitReason::BalancedColumns;
    }
    if( !bGrow )
    {
         const SwViewShell *pSh = getRootFrame()->GetCurrShell();
         bGrow = pSh && pSh->GetViewOptions()->getBrowseMode();
    }
    if (!bGrow)
    {
        if (!bTst)
        {
            if (bInCalcContent)
                InvalidateSize_();
            else
                InvalidateSize();
        }
        return 0;
    }
    reason = SwResizeLimitReason::Unspecified; // reset what maybe was set in balanced columns check
    SwTwips nGrow;
    if( IsInFootnote() )
        nGrow = 0;
    else
    {
        nGrow = lcl_DeadLine( this );
        nGrow = aRectFnSet.YDiff( nGrow, aRectFnSet.GetBottom(getFrameArea()) );
    }
    SwTwips nSpace = nGrow;
    if( !bInCalcContent && nGrow < nDist && GetUpper() )
        nGrow = o3tl::saturating_add(
            nGrow, GetUpper()->Grow( LONG_MAX, true ));

    if( nGrow > nDist )
        nGrow = nDist;
    if( nGrow <= 0 )
    {
        nGrow = 0;
        if (!bTst)
        {
            if( bInCalcContent )
                InvalidateSize_();
            else
                InvalidateSize();
        }
    }
    else if( !bTst )
    {
        if( bInCalcContent )
            InvalidateSize_();
        else if( nSpace < nGrow &&  nDist != nSpace + GetUpper()->
                 Grow(nGrow - nSpace, reason, false, false))
            InvalidateSize();
        else
        {
            const SvxGraphicPosition ePos =
                GetAttrSet()->GetBackground().GetGraphicPos();
            if ( GPOS_RT < ePos && GPOS_TILED != ePos )
            {
                SetCompletePaint();
                InvalidatePage();
            }
            if (GetUpper() && (GetUpper()->IsHeaderFrame() || GetUpper()->IsCellFrame()))
                GetUpper()->InvalidateSize();
        }

        {
            SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
            aRectFnSet.AddBottom( aFrm, nGrow );
        }

        {
            const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) + nGrow;
            SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
            aRectFnSet.SetHeight( aPrt, nPrtHeight );
        }

        SwFrame* pLower = Lower();
        if( pLower && pLower->IsColumnFrame() && pLower->GetNext() )
        {
            SwFrame* pTmp = pLower;
            do
            {
                pTmp->InvalidateSize_();
                pTmp = pTmp->GetNext();
            } while ( pTmp );
            InvalidateSize_();
        }
        if( GetNext() )
        {
            // Own height changed, need to invalidate the position of
            // next frames.
            SwFrame *pFrame = GetNext();
            while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
            {
                // Invalidate all in-between frames, otherwise position
                // calculation (which only looks back to one relative
                // frame) will have an incorrect result.
                InvalidateFramePos(pFrame, bInCalcContent);
                pFrame = pFrame->GetNext();
            }
            if( pFrame )
            {
                InvalidateFramePos(pFrame, bInCalcContent);
            }
        }
        // #i28701# - Due to the new object positioning
        // the frame on the next page/column can flow backward (e.g. it
        // was moved forward due to the positioning of its objects ).
        // Thus, invalivate this next frame, if document compatibility
        // option 'Consider wrapping style influence on object positioning' is ON.
        else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
        {
            InvalidateNextPos();
        }
    }
    return nGrow;
}

SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst )
{
    if ( Lower() && !IsColLocked() && !HasFixSize() )
    {
        if( ToMaximize( false ) )
        {
            if( !bTst )
                InvalidateSize();
        }
        else
        {
            SwRectFnSet aRectFnSet(*this);
            tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
            if ( nDist > nFrameHeight )
                nDist = nFrameHeight;

            if ( Lower()->IsColumnFrame() && Lower()->GetNext() && // FootnoteAtEnd
                 !GetSection()->GetFormat()->GetBalancedColumns().GetValue() )
            {   // With column bases the format takes over the control of the
                // growth (because of the balance)
                if ( !bTst )
                    InvalidateSize();
                return nDist;
            }
            else if( !bTst )
            {
                const SvxGraphicPosition ePos =
                    GetAttrSet()->GetBackground().GetGraphicPos();
                if ( GPOS_RT < ePos && GPOS_TILED != ePos )
                {
                    SetCompletePaint();
                    InvalidatePage();
                }

                {
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    aRectFnSet.AddBottom( aFrm, -nDist );
                }

                {
                    const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nDist;
                    SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                    aRectFnSet.SetHeight( aPrt, nPrtHeight );
                }

                // We do not allow a section frame to shrink the its upper
                // footer frame. This is because in the calculation of a
                // footer frame, the content of the section frame is _not_
                // calculated. If there is a fly frame overlapping with the
                // footer frame, the section frame is not affected by this
                // during the calculation of the footer frame size.
                // The footer frame does not grow in its FormatSize function
                // but during the calculation of the content of the section
                // frame. The section frame grows until some of its text is
                // located on top of the fly frame. The next call of CalcContent
                // tries to shrink the section and here it would also shrink
                // the footer. This may not happen, because shrinking the footer
                // would cause the top of the section frame to overlap with the
                // fly frame again, this would result in a perfect loop.
                if( GetUpper() && !GetUpper()->IsFooterFrame() )
                    GetUpper()->Shrink( nDist, bTst );

                SwFrame* pLower = Lower();
                if( pLower && pLower->IsColumnFrame() && pLower->GetNext() )
                {
                    SwFrame* pTmp = pLower;
                    do
                    {
                        pTmp->InvalidateSize_();
                        pTmp = pTmp->GetNext();
                    } while ( pTmp );
                }
                if( GetNext() )
                {
                    SwFrame* pFrame = GetNext();
                    while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
                        pFrame = pFrame->GetNext();
                    if( pFrame )
                        pFrame->InvalidatePos();
                    else
                        SetRetouche();
                }
                else
                    SetRetouche();
                return nDist;
            }
        }
    }
    return 0;
}

/*
|*  When are Frames within a SectionFrames moveable?
|*  If they are not in the last column of a SectionFrames yet,
|*  if there is no Follow,
|*  if the SectionFrame cannot grow anymore, then it gets more complicated,
|*  in that case it depends on whether the SectionFrame can find a next
|*  layout sheet. In (column based/chained) Flys this is checked via
|*  GetNextLayout, in tables and headers/footers there is none, however in the
|*  DocBody and in foot notes there is always one.
|*
|*  This routine is used in the TextFormatter to decided whether it's allowed to
|*  create a (paragraph-)Follow or whether the paragraph has to stick together
|*/
bool SwSectionFrame::MoveAllowed( const SwFrame* pFrame) const
{
    // Is there a Follow or is the Frame not in the last column?
    if( HasFollow() || ( pFrame->GetUpper()->IsColBodyFrame() &&
        pFrame->GetUpper()->GetUpper()->GetNext() ) )
        return true;
    if( pFrame->IsInFootnote() )
    {
        if( IsInFootnote() )
        {
            if( GetUpper()->IsInSct() )
            {
                if( Growable() )
                    return false;
                return GetUpper()->FindSctFrame()->MoveAllowed( this );
            }
            else
                return true;
        }
        // The content of footnote inside a columned sectionfrm is moveable
        // except in the last column
        const SwLayoutFrame *pLay = pFrame->FindFootnoteFrame()->GetUpper()->GetUpper();
        if( pLay->IsColumnFrame() && pLay->GetNext() )
        {
            // The first paragraph in the first footnote in the first column
            // in the sectionfrm at the top of the page is not moveable,
            // if the columnbody is empty.
            bool bRet = false;
            if( pLay->GetIndPrev() || pFrame->GetIndPrev() ||
                pFrame->FindFootnoteFrame()->GetPrev() )
                bRet = true;
            else
            {
                const SwLayoutFrame* pBody = static_cast<const SwColumnFrame*>(pLay)->FindBodyCont();
                if( pBody && pBody->Lower() )
                    bRet = true;
            }
            if( bRet && ( IsFootnoteAtEnd() || !Growable() ) )
                return true;
        }
    }
    // Or can the section still grow?
    if( !IsColLocked() && Growable() )
        return false;
    // Now it has to be examined whether there is a layout sheet wherein
    // a section Follow can be created
    if( !CanContainSplitSection(this) || ( !IsInDocBody() && FindFooterOrHeader() ) )
        return false; // It doesn't work in table-in-sections/nested tables/headers/footers
    if( IsInFly() ) // In column based or chained frames
        return nullptr != const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->GetNextLeaf( MAKEPAGE_NONE );
    return true;
}

/** Called for a frame inside a section with no direct previous frame (or only
    previous empty section frames) the previous frame of the outer section is
    returned, if the frame is the first flowing content of this section.

    Note: For a frame inside a table frame, which is inside a section frame,
          NULL is returned.
*/
SwFrame* SwFrame::GetIndPrev_() const
{
    SwFrame *pRet = nullptr;
    // #i79774#
    // Do not assert, if the frame has a direct previous frame, because it
    // could be an empty section frame. The caller has to assure, that the
    // frame has no direct previous frame or only empty section frames as
    // previous frames.
    OSL_ENSURE( /*!pPrev &&*/ IsInSct(), "Why?" );
    const SwFrame* pSct = GetUpper();
    if( !pSct )
        return nullptr;
    if( pSct->IsSctFrame() )
        pRet = pSct->GetIndPrev();
    else if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
    {
        // Do not return the previous frame of the outer section, if in one
        // of the previous columns is content.
        const SwFrame* pCol = GetUpper()->GetUpper()->GetPrev();
        while( pCol )
        {
            assert(pCol->IsColumnFrame());
            const SwFrame* pLower = pCol->GetLower();
            assert(pLower && pLower->IsBodyFrame());
            if( static_cast<const SwLayoutFrame*>(pLower)->Lower() )
                return nullptr;
            pCol = pCol->GetPrev();
        }
        pRet = pSct->GetIndPrev();
    }

    // skip empty section frames
    while( pRet && pRet->IsSctFrame() && !static_cast<SwSectionFrame*>(pRet)->GetSection() )
        pRet = pRet->GetIndPrev();
    return pRet;
}

SwFrame* SwFrame::GetIndNext_()
{
    OSL_ENSURE( !mpNext && IsInSct(), "Why?" );
    SwFrame* pSct = GetUpper();
    if( !pSct )
        return nullptr;
    if( pSct->IsSctFrame() )
        return pSct->GetIndNext();
    if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
    {   // We can only return the successor of the SectionFrames if there is no
        // content in the successive columns
        SwFrame* pCol = GetUpper()->GetUpper()->GetNext();
        while( pCol )
        {
            assert(pCol->IsColumnFrame());
            assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
            if( static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pCol)->Lower())->Lower() )
                return nullptr;
            pCol = pCol->GetNext();
        }
        return pSct->GetIndNext();
    }
    return nullptr;
}

bool SwSectionFrame::IsDescendantFrom( const SwSectionFormat* pFormat ) const
{
    if( !m_pSection || !pFormat )
        return false;
    const SwSectionFormat *pMyFormat = m_pSection->GetFormat();
    while( pFormat != pMyFormat )
    {
        if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) )
            pMyFormat = pNewFormat;
        else
            return false;
    }
    return true;
}

void SwSectionFrame::CalcFootnoteAtEndFlag()
{
    SwSectionFormat *pFormat = GetSection()->GetFormat();
    sal_uInt16 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
    m_bFootnoteAtEnd = FTNEND_ATPGORDOCEND != nVal;
    m_bOwnFootnoteNum = FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
                 FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
    while( !m_bFootnoteAtEnd && !m_bOwnFootnoteNum )
    {
        if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
            pFormat = pNewFormat;
        else
            break;
        nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
        if( FTNEND_ATPGORDOCEND != nVal )
        {
            m_bFootnoteAtEnd = true;
            m_bOwnFootnoteNum = m_bOwnFootnoteNum ||FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
                         FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
        }
    }
}

bool SwSectionFrame::IsEndnoteAtMyEnd() const
{
    return m_pSection->GetFormat()->GetEndAtTextEnd( false ).IsAtEnd();
}

void SwSectionFrame::CalcEndAtEndFlag()
{
    SwSectionFormat *pFormat = GetSection()->GetFormat();
    m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
    while( !m_bEndnAtEnd )
    {
        if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
            pFormat = pNewFormat;
        else
            break;
        m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
    }
}

static void InvalidateFramesInSection(SwFrame * pFrame)
{
    while (pFrame)
    {
        pFrame->InvalidateAll();
        pFrame->InvalidateObjs(false);
        if (pFrame->IsLayoutFrame())
        {
            InvalidateFramesInSection(pFrame->GetLower());
        }
        else if (pFrame->IsTextFrame())
        {
            pFrame->Prepare(PrepareHint::Clear, nullptr, false);
        }
        pFrame = pFrame->GetNext();
    }
}

void SwSectionFrame::Notify(SfxHint const& rHint)
{
    SwSectionFormat *const pFormat(GetSection()->GetFormat());
    assert(pFormat);
    SwClientNotify(*pFormat, rHint);
}

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

        if (eInvFlags != SwSectionFrameInvFlags::NONE)
        {
            if(eInvFlags & SwSectionFrameInvFlags::InvalidateSize)
                InvalidateSize();
            if(eInvFlags & SwSectionFrameInvFlags::SetCompletePaint)
                SetCompletePaint();
        }
    }
    else if(rHint.GetId() == SfxHintId::SwSectionFrameMoveAndDelete)
    {
        const auto pHint = static_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint);
        // #i117863#
        if(&rMod != GetDep())
            return;
        SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent());
    }
    else if (rHint.GetId() == SfxHintId::SwSectionHidden)
    {
        InvalidateAll();
        InvalidateObjs(false);

        InvalidateFramesInSection(Lower());
        if (Lower())
            Lower()->HideAndShowObjects(); // recursive
        // Check if any page-breaks have been unhidden, create the new pages.
        // Call IsHiddenNow() because a parent section could still hide.
        if (!IsFollow() && IsInDocBody() && !IsInTab() && !IsHiddenNow())
        {
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            SwViewShell *const pViewShell(getRootFrame()->GetCurrShell());
            // no notification if SwViewShell is in construction
            if (pViewShell && !pViewShell->IsInConstructor()
                && pViewShell->GetLayout()
                && pViewShell->GetLayout()->IsAnyShellAccessible())
            {
                auto pNext = FindNextCnt(true);
                auto pPrev = FindPrevCnt();
                pViewShell->InvalidateAccessibleParaFlowRelation(
                    pNext ? pNext->DynCastTextFrame() : nullptr,
                    pPrev ? pPrev->DynCastTextFrame() : nullptr );
            }
#endif
            SwSectionFrame * pFollow{this};
            SwPageFrame * pPage{FindPageFrame()};
            SwLayoutFrame * pLay{nullptr};
            bool isBreakAfter{false};
            SwFrame * pFirstOnPage{pPage->FindFirstBodyContent()};
            while (pFirstOnPage->GetUpper()->IsInTab())
            {
                pFirstOnPage = pFirstOnPage->GetUpper();
            }
            assert(pFirstOnPage->IsContentFrame() || pFirstOnPage->IsTabFrame());
            SwColumnFrame* pColumn{ Lower() && Lower()->IsColumnFrame()
                                        ? static_cast<SwColumnFrame*>(Lower())
                                        : nullptr };
            auto IterateLower = [&pColumn](SwFrame *const pLowerFrame) -> SwFrame*
            {
                if (pLowerFrame->GetNext())
                {
                    return pLowerFrame->GetNext();
                }
                if (pColumn)
                {
                    pColumn = static_cast<SwColumnFrame*>(pColumn->GetNext());
                    if (pColumn)
                    {
                        return static_cast<SwLayoutFrame*>(pColumn->Lower())->Lower();
                    }
                }
                return nullptr;
            };
            for (SwFrame* pLowerFrame = pColumn
                        ? static_cast<SwLayoutFrame*>(pColumn->Lower())->Lower()
                        : Lower();
                 pLowerFrame;
                 pLowerFrame = IterateLower(pLowerFrame))
            {
                if (pLowerFrame == pFirstOnPage)
                {
                    continue;
                }
                assert(pLowerFrame->IsContentFrame() || pLowerFrame->IsTabFrame());
                if (SwLayHelper::CheckInsertPage(pPage, pLay, pLowerFrame, isBreakAfter))
                {
                    if (pLowerFrame == Lower())
                    {   // move the whole section
                        assert(pFollow == this);
                        MoveSubTree(pLay, nullptr);
                    }
                    else
                    {
                        if (pFollow->GetNext())
                        {
                            assert(pFollow->GetNext()->IsFlowFrame());
                            SwFlowFrame::CastFlowFrame(pFollow->GetNext())->MoveSubTree(pLay, nullptr);
                        }
                        pFollow = new SwSectionFrame(*pFollow, false);
                        SimpleFormat();
                        pFollow->InsertBehind(pLay, nullptr);
                        pFollow->Init();
                        SwLayoutFrame *const pTarget{pColumn
                            ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pFollow->Lower())->Lower())
                            : pFollow};
                        SwFlowFrame::CastFlowFrame(pLowerFrame)->MoveSubTree(pTarget, nullptr);
                    }
                }
            }
            CheckPageDescs(FindPageFrame());
        }
    }
    else
        SwFrame::SwClientNotify(rMod, rHint);
}

void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
                            SwSectionFrameInvFlags &rInvFlags,
                            SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
{
    bool bClear = true;
    const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
    switch( nWhich )
    {
        case RES_COL:
            if( !IsInFootnote() )
            {
                assert(pOld && pNew);
                if (pOld && pNew)
                {
                    ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
                    rInvFlags |= SwSectionFrameInvFlags::InvalidateSize | SwSectionFrameInvFlags::SetCompletePaint;
                }
            }
            break;

        case RES_FTN_AT_TXTEND:
            if( !IsInFootnote() )
            {
                bool const bOld = IsFootnoteAtEnd();
                CalcFootnoteAtEndFlag();
                if (bOld != IsFootnoteAtEnd())
                {
                    const SwFormatCol& rNewCol = GetFormat()->GetCol();
                    ChgColumns( rNewCol, rNewCol, true );
                    rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
                }
            }
            break;

        case RES_END_AT_TXTEND:
            if( !IsInFootnote() )
            {
                bool const bOld = IsEndnAtEnd();
                bool const bMyOld = IsEndnoteAtMyEnd();
                CalcEndAtEndFlag();
                if (bOld != IsEndnAtEnd() || bMyOld != IsEndnoteAtMyEnd())
                {
                    const SwFormatCol& rNewCol = GetFormat()->GetCol();
                    ChgColumns( rNewCol, rNewCol, true );
                    rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
                }
            }
            break;
        case RES_COLUMNBALANCE:
            rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
            break;

        case RES_FRAMEDIR :
            SetDerivedR2L( false );
            CheckDirChange();
            break;

        case RES_PROTECT:
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
            {
                SwViewShell *pSh = getRootFrame()->GetCurrShell();
                if( pSh && pSh->GetLayout()->IsAnyShellAccessible() )
                    pSh->Imp()->InvalidateAccessibleEditableState( true, this );
            }
#endif
            break;

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

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

void SwSectionFrame::UpdateAttrForFormatChange( SwSectionFrameInvFlags &rInvFlags )
{
    // Suppress multi columns in foot notes
    const SwFormatCol& rNewCol = GetFormat()->GetCol();
    if( !IsInFootnote() )
    {
        // Nasty case. When allocating a template we can not count
        // on the old column attribute. We're left with creating a
        // temporary attribute here.
        SwFormatCol aCol;
        SwFrame* pLower = Lower();
        if ( pLower && pLower->IsColumnFrame() )
        {
            sal_uInt16 nCol = 0;
            SwFrame *pTmp = pLower;
            do
            {   ++nCol;
                pTmp = pTmp->GetNext();
            } while ( pTmp );
            aCol.Init( nCol, 0, 1000 );
        }
        bool bChgFootnote = IsFootnoteAtEnd();
        bool const bChgEndn = IsEndnAtEnd();
        bool const bChgMyEndn = IsEndnoteAtMyEnd();
        CalcFootnoteAtEndFlag();
        CalcEndAtEndFlag();
        bChgFootnote = ( bChgFootnote != IsFootnoteAtEnd() ) ||
                  ( bChgEndn != IsEndnAtEnd() ) ||
                  ( bChgMyEndn != IsEndnoteAtMyEnd() );
        ChgColumns( aCol, rNewCol, bChgFootnote );
        rInvFlags |= SwSectionFrameInvFlags::SetCompletePaint;
    }
    rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
}

/// A follow or a ftncontainer at the end of the page causes a maximal Size of the sectionframe.
bool SwSectionFrame::ToMaximize( bool bCheckFollow ) const
{
    if( HasFollow() )
    {
        if( !bCheckFollow ) // Don't check superfluous follows
            return true;
        const SwSectionFrame* pFoll = GetFollow();
        while( pFoll && pFoll->IsSuperfluous() )
            pFoll = pFoll->GetFollow();
        if( pFoll )
            return true;
    }
    if( IsFootnoteAtEnd() )
        return false;
    const SwFootnoteContFrame* pCont = ContainsFootnoteCont();
    if( !IsEndnAtEnd() )
        return nullptr != pCont;
    bool bRet = false;
    while( pCont && !bRet )
    {
        if( pCont->FindFootNote() )
            bRet = true;
        else
            pCont = ContainsFootnoteCont( pCont );
    }
    return bRet;
}

/// Check every Column for FootnoteContFrames.
SwFootnoteContFrame* SwSectionFrame::ContainsFootnoteCont( const SwFootnoteContFrame* pCont ) const
{
    SwFootnoteContFrame* pRet = nullptr;
    const SwLayoutFrame* pLay;
    if( pCont )
    {
        pLay = pCont->FindFootnoteBossFrame();
        OSL_ENSURE( IsAnLower( pLay ), "ContainsFootnoteCont: Wrong FootnoteContainer" );
        pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
    }
    else
    {
        const SwFrame* pLower = Lower();
        if( pLower && pLower->IsColumnFrame() )
            pLay = static_cast<const SwLayoutFrame*>(pLower);
        else
            pLay = nullptr;
    }

    while ( !pRet && pLay )
    {
        const SwFrame* pLower = pLay->Lower();
        if( pLower && pLower->GetNext() )
        {
            OSL_ENSURE( pLower->GetNext()->IsFootnoteContFrame(),
                    "ToMaximize: Unexpected Frame" );
            pRet = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pLower->GetNext()));
        }
        OSL_ENSURE( !pLay->GetNext() || pLay->GetNext()->IsLayoutFrame(),
                "ToMaximize: ColFrame expected" );
        pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
    }
    return pRet;
}

void SwSectionFrame::InvalidateFootnotePos()
{
    SwFootnoteContFrame* pCont = ContainsFootnoteCont();
    if( pCont )
    {
        SwFrame *pTmp = pCont->ContainsContent();
        if( pTmp )
            pTmp->InvalidatePos_();
    }
}

SwTwips SwSectionFrame::CalcUndersize() const
{
    SwRectFnSet aRectFnSet(*this);
    return InnerHeight() - aRectFnSet.GetHeight(getFramePrintArea());
}

SwTwips SwSectionFrame::Undersize()
{
    const auto nRet = CalcUndersize();
    m_bUndersized = (nRet > 0);
    return nRet <= 0 ? 0 : nRet;
}

void SwSectionFrame::CalcFootnoteContent()
{
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    SwFootnoteContFrame* pCont = ContainsFootnoteCont();
    if( !pCont )
        return;

    SwFrame* pFrame = pCont->ContainsAny();
    if( pFrame )
        pCont->Calc(pRenderContext);
    while( pFrame && IsAnLower( pFrame ) )
    {
        SwFootnoteFrame* pFootnote = pFrame->FindFootnoteFrame();
        if( pFootnote )
            pFootnote->Calc(pRenderContext);
        pFrame->Calc(pRenderContext);
        if( pFrame->IsSctFrame() )
        {
            SwFrame *pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
            if( pTmp )
            {
                pFrame = pTmp;
                continue;
            }
        }
        pFrame = pFrame->FindNext();
    }
}

/*
 * If a SectionFrame gets empty, e.g. because its content changes the page/column,
 * it is not destroyed immediately (there could be a pointer left to it on the
 * stack), instead it puts itself in a list at the RootFrame, which is processed
 * later on (in Layaction::Action among others). Its size is set to Null and
 * the pointer to its page as well. Such SectionFrames that are to be deleted
 * must be ignored by the layout/during formatting.
 *
 * With InsertEmptySct the RootFrame stores a SectionFrame in the list,
 * with RemoveFromList it can be removed from the list (Dtor),
 * with DeleteEmptySct the list is processed and the SectionFrames are destroyed.
 */
void SwRootFrame::InsertEmptySct( SwSectionFrame* pDel )
{
    if( !mpDestroy )
        mpDestroy.reset( new SwDestroyList );
    mpDestroy->insert( pDel );
}

void SwRootFrame::DeleteEmptySct_()
{
    assert(mpDestroy);
    while( !mpDestroy->empty() )
    {
        SwSectionFrame* pSect = *mpDestroy->begin();
        mpDestroy->erase( mpDestroy->begin() );
        OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(),
                "DeleteEmptySct: Locked SectionFrame" );
        SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame");
        if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() )
        {
            SwLayoutFrame* pUp = pSect->GetUpper();
            pSect->RemoveFromLayout();
            SwFrame::DestroyFrame(pSect);
            if( pUp && !pUp->Lower() )
            {
                if( pUp->IsPageBodyFrame() )
                    pUp->getRootFrame()->SetSuperfluous();
                else if( pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
                    pUp->GetUpper() )
                {
                    pUp->Cut();
                    SwFrame::DestroyFrame(pUp);
                }
            }
        }
        else {
            OSL_ENSURE( pSect->GetSection(), "DeleteEmptySct: Half-dead SectionFrame?!" );
        }
    }
}

void SwRootFrame::RemoveFromList_( SwSectionFrame* pSct )
{
    assert(mpDestroy && "Where's my list?");
    mpDestroy->erase( pSct );
}

#ifdef DBG_UTIL
bool SwRootFrame::IsInDelList( SwSectionFrame* pSct ) const
{
    return mpDestroy && mpDestroy->find( pSct ) != mpDestroy->end();
}
#endif

bool SwSectionFrame::IsBalancedSection() const
{
    bool bRet = false;
    const SwFrame* pLower = Lower();
    if ( GetSection() && pLower && pLower->IsColumnFrame() && pLower->GetNext() )
    {
        bRet = !GetSection()->GetFormat()->GetBalancedColumns().GetValue();
    }
    return bRet;
}

void SwSectionFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, BAD_CAST("section"));
    dumpAsXmlAttributes( writer );
    if ( HasFollow() )
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );

    if (m_pPrecede != nullptr)
        (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, m_pPrecede->GetFrame().GetFrameId() );

    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    dumpChildrenAsXml(writer);

    (void)xmlTextWriterEndElement(writer);
}

void SwSectionFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
{
    SwLayoutFrame::dumpAsXmlAttributes(writer);

    SwSectionNode const*const pNode(GetSection() ? GetSection()->GetFormat()->GetSectionNode() : nullptr);
    (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("sectionNodeIndex"), "%" SAL_PRIdINT32, pNode ? sal_Int32(pNode->GetIndex()) : -1);
}

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