/* -*- 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 <svl/itemiter.hxx>
#include <vcl/imap.hxx>
#include <tools/helpers.hxx>
#include <editeng/protitem.hxx>
#include <editeng/opaqitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <fmtfsize.hxx>
#include <fmtclds.hxx>
#include <fmtcntnt.hxx>
#include <fmturl.hxx>
#include <fmtsrnd.hxx>
#include <fmtornt.hxx>
#include <fmtcnct.hxx>
#include <ndgrf.hxx>
#include <tolayoutanchoredobjectposition.hxx>
#include <fmtfollowtextflow.hxx>
#include <sortedobjs.hxx>
#include <objectformatter.hxx>
#include <ndole.hxx>
#include <swtable.hxx>
#include <svx/svdoashp.hxx>
#include <svx/svdpage.hxx>
#include <layouter.hxx>
#include <layact.hxx>
#include <pagefrm.hxx>
#include <rootfrm.hxx>
#include <viewimp.hxx>
#include <viewopt.hxx>
#include <dcontact.hxx>
#include <dflyobj.hxx>
#include <dview.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <hints.hxx>
#include <tabfrm.hxx>
#include <txtfrm.hxx>
#include <notxtfrm.hxx>
#include <flyfrms.hxx>
#include <sectfrm.hxx>
#include <vcl/svapp.hxx>
#include <calbck.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <textboxhelper.hxx>
#include <txtfly.hxx>
#include <ndindex.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <osl/diagnose.h>
#include <o3tl/string_view.hxx>
#include <rtl/math.hxx>
 
#include <wrtsh.hxx>
#include <view.hxx>
#include <edtwin.hxx>
#include <bodyfrm.hxx>
#include <FrameControlsManager.hxx>
#include <ndtxt.hxx>
#include <formatflysplit.hxx>
 
using namespace ::com::sun::star;
 
namespace
{
/// Gets the bottom position which is a deadline for a split fly.
SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& rAnchor)
{
    SwRectFnSet aRectFnSet(pFly);
 
    const SwPageFrame* pPage = rAnchor.FindPageFrame();
    if (!pPage)
    {
        return 0;
    }
 
    const SwFrame* pBody = pPage->FindBodyCont();
    if (!pBody)
    {
        return 0;
    }
 
    const auto* pFrameFormat = pFly->GetFrameFormat();
    const IDocumentSettingAccess& rIDSA = pFrameFormat->getIDocumentSettingAccess();
    // Allow overlap with bottom margin / footer only in case we're relative to the page frame.
    bool bVertPageFrame = pFrameFormat->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME;
    bool bInBody = rAnchor.IsInDocBody();
    bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN) && (bVertPageFrame || !bInBody);
    if (bLegacy)
    {
        // Word <= 2010 style: the fly can overlap with the bottom margin / footer area in case the
        // fly height fits the body height and the fly bottom fits the page.
        // See if the fly height would fit at least the page height, ignoring the vertical offset.
        SwTwips nFlyHeight = aRectFnSet.GetHeight(pFly->getFrameArea());
        SwTwips nPageHeight = aRectFnSet.GetHeight(pPage->getFramePrintArea());
        SwTwips nFlyTop = aRectFnSet.GetTop(pFly->getFrameArea());
        SwTwips nBodyTop = aRectFnSet.GetTop(pBody->getFrameArea());
        if (nFlyTop < nBodyTop)
        {
            // Fly frame overlaps with the top margin area, ignore that part of the fly frame for
            // top/height purposes.
            nFlyHeight -= nBodyTop - nFlyTop;
            nFlyTop = nBodyTop;
        }
        if (nFlyHeight <= nPageHeight)
        {
            // Yes, it would fit: allow overlap if there is no problematic vertical offset.
            SwTwips nDeadline = aRectFnSet.GetBottom(pPage->getFrameArea());
            SwTwips nBodyHeight = aRectFnSet.GetHeight(pBody->getFramePrintArea());
            if (nDeadline - nFlyTop > nBodyHeight)
            {
                // If the fly would now grow to nDeadline then it would not fit the body height, so
                // limit the height.
                nDeadline = nFlyTop + nBodyHeight;
            }
            return nDeadline;
        }
    }
 
    // Word >= 2013 style: the fly has to stay inside the body frame.
    return aRectFnSet.GetPrtBottom(*pBody);
}
}
 
static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame );
 
SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch, bool bFollow ) :
    SwLayoutFrame( pFormat, pSib ),
     // #i26791#
    m_pPrevLink( nullptr ),
    m_pNextLink( nullptr ),
    m_bInCnt( false ),
    m_bAtCnt( false ),
    m_bLayout( false ),
    m_bAutoPosition( false ),
    m_bDeleted( false ),
    m_nAuthor( std::string::npos ),
    m_bValidContentPos( false )
{
    mnFrameType = SwFrameType::Fly;
 
    m_bInvalid = m_bNotifyBack = true;
    m_bLocked  = m_bMinHeight =
    m_bHeightClipped = m_bWidthClipped = m_bFormatHeightOnly = false;
 
    // Size setting: Fixed size is always the width
    const SwFormatFrameSize &rFrameSize = pFormat->GetFrameSize();
    const SvxFrameDirection nDir = pFormat->GetFormatAttr( RES_FRAMEDIR ).GetValue();
    if( SvxFrameDirection::Environment == nDir )
    {
        mbDerivedVert = true;
        mbDerivedR2L = true;
    }
    else
    {
        mbInvalidVert = false;
        mbDerivedVert = false;
        mbDerivedR2L = false;
        if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
        {
            mbVertLR = false;
            mbVertical = false;
        }
        else
        {
            const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
            if( pSh && pSh->GetViewOptions()->getBrowseMode() )
            {
                mbVertLR = false;
                mbVertical = false;
            }
            else
            {
                mbVertical = true;
 
                if ( SvxFrameDirection::Vertical_LR_TB == nDir )
                    mbVertLR = true;
                else if (nDir == SvxFrameDirection::Vertical_LR_BT)
                {
                    mbVertLR = true;
                    mbVertLRBT = true;
                }
                else
                    mbVertLR = false;
            }
        }
 
        mbInvalidR2L = false;
        if( SvxFrameDirection::Horizontal_RL_TB == nDir )
            mbRightToLeft = true;
        else
            mbRightToLeft = false;
    }
 
    {
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Width( rFrameSize.GetWidth() );
        aFrm.Height( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable ? MINFLY : rFrameSize.GetHeight() );
    }
 
    // Fixed or variable Height?
    if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
        m_bMinHeight = true;
    else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
        mbFixSize = true;
 
    // insert columns, if necessary
    InsertColumns();
 
    // First the Init, then the Content:
    // This is due to the fact that the Content may have Objects/Frames,
    // which are then registered
    InitDrawObj(*pAnch);
 
    Chain( pAnch );
 
    if (!bFollow)
    {
        InsertCnt();
    }
 
    // Put it somewhere outside so that out document is not formatted unnecessarily often
    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
    aFrm.Pos().setX(FAR_AWAY);
    aFrm.Pos().setY(FAR_AWAY);
}
 
void SwFlyFrame::Chain( SwFrame* _pAnch )
{
    // Connect to chain neighbours.
    // No problem, if a neighbor doesn't exist - the construction of the
    // neighbor will make the connection
    const SwFormatChain& rChain = GetFormat()->GetChain();
    if ( !(rChain.GetPrev() || rChain.GetNext()) )
        return;
 
    if ( rChain.GetNext() )
    {
        SwFlyFrame* pFollow = FindChainNeighbour( *rChain.GetNext(), _pAnch );
        if ( pFollow )
        {
            OSL_ENSURE( !pFollow->GetPrevLink(), "wrong chain detected" );
            if ( !pFollow->GetPrevLink() )
                SwFlyFrame::ChainFrames( this, pFollow );
        }
    }
    if ( rChain.GetPrev() )
    {
        SwFlyFrame *pMaster = FindChainNeighbour( *rChain.GetPrev(), _pAnch );
        if ( pMaster )
        {
            OSL_ENSURE( !pMaster->GetNextLink(), "wrong chain detected" );
            if ( !pMaster->GetNextLink() )
                SwFlyFrame::ChainFrames( pMaster, this );
        }
    }
}
 
void SwFlyFrame::InsertCnt()
{
    if ( GetPrevLink() )
        return;
 
    const SwFormatContent& rContent = GetFormat()->GetContent();
    OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." );
    SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
    // Lower() means SwColumnFrame; the Content then needs to be inserted into the (Column)BodyFrame
    ::InsertCnt_( Lower() ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()) : static_cast<SwLayoutFrame*>(this),
                  GetFormat()->GetDoc(), nIndex );
 
    // NoText always have a fixed height.
    if ( Lower() && Lower()->IsNoTextFrame() )
    {
        mbFixSize = true;
        m_bMinHeight = false;
    }
}
 
void SwFlyFrame::InsertColumns()
{
    // #i97379#
    // Check, if column are allowed.
    // Columns are not allowed for fly frames, which represent graphics or embedded objects.
    const SwFormatContent& rContent = GetFormat()->GetContent();
    OSL_ENSURE( rContent.GetContentIdx(), "<SwFlyFrame::InsertColumns()> - no content prepared." );
    SwNodeIndex aFirstContent( *(rContent.GetContentIdx()), 1 );
    if ( aFirstContent.GetNode().IsNoTextNode() )
    {
        return;
    }
 
    const SwFormatCol &rCol = GetFormat()->GetCol();
    if ( rCol.GetNumCols() <= 1 )
        return;
 
    // Start off PrtArea to be as large as Frame, so that we can put in the columns
    // properly. It'll adjust later on.
    {
        SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
        aPrt.Width( getFrameArea().Width() );
        aPrt.Height( getFrameArea().Height() );
    }
 
    const SwFormatCol aOld; // ChgColumns() also needs an old value passed
    ChgColumns( aOld, rCol );
}
 
void SwFlyFrame::DestroyImpl()
{
    // Accessible objects for fly frames will be destroyed in this destructor.
    // For frames bound as char or frames that don't have an anchor we have
    // to do that ourselves. For any other frame the call RemoveFly at the
    // anchor will do that.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if( IsAccessibleFrame() && GetFormat() && (IsFlyInContentFrame() || !GetAnchorFrame()) )
    {
        SwRootFrame *pRootFrame = getRootFrame();
        if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
        {
            SwViewShell *pVSh = pRootFrame->GetCurrShell();
            if( pVSh && pVSh->Imp() )
            {
                // Lowers aren't disposed already, so we have to do a recursive
                // dispose
                pVSh->Imp()->DisposeAccessibleFrame( this, true );
            }
        }
    }
#endif
 
    if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
    {
        ClearTmpConsiderWrapInfluence(); // remove this from SwLayouter
 
        Unchain();
 
        DeleteCnt();
 
        if ( GetAnchorFrame() )
            AnchorFrame()->RemoveFly( this );
    }
 
    FinitDrawObj();
 
    SwLayoutFrame::DestroyImpl();
 
    SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(getRootFrame()->GetCurrShell());
    UpdateUnfloatButton(pWrtSh, false);
}
 
SwFlyFrame::~SwFlyFrame()
{
}
 
const IDocumentDrawModelAccess& SwFlyFrame::getIDocumentDrawModelAccess()
{
    return GetFormat()->getIDocumentDrawModelAccess();
}
 
void SwFlyFrame::Unchain()
{
    if ( GetPrevLink() )
        UnchainFrames( GetPrevLink(), this );
    if ( GetNextLink() )
        UnchainFrames( this, GetNextLink() );
}
 
void SwFlyFrame::DeleteCnt()
{
    SwFrame* pFrame = m_pLower;
    while ( pFrame )
    {
        while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
        {
            SwAnchoredObject *pAnchoredObj = (*pFrame->GetDrawObjs())[0];
            if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
            {
                SwFrame::DestroyFrame(pFlyFrame);
            }
            else if ( dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) !=  nullptr )
            {
                // consider 'virtual' drawing objects
                SdrObject* pObj = pAnchoredObj->DrawObj();
                if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj) )
                {
                    pDrawVirtObj->RemoveFromWriterLayout();
                    pDrawVirtObj->RemoveFromDrawingPage();
                }
                else
                {
                    SwDrawContact* pContact =
                            static_cast<SwDrawContact*>(::GetUserCall( pObj ));
                    if ( pContact )
                    {
                        pContact->DisconnectFromLayout();
                    }
                }
            }
        }
 
        pFrame->RemoveFromLayout();
        SwFrame::DestroyFrame(pFrame);
        pFrame = m_pLower;
    }
 
    InvalidatePage();
}
 
void SwFlyFrame::InitDrawObj(SwFrame& rAnchorFrame)
{
    SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat(), rAnchorFrame));
 
    // Set the right Layer
    const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
    bool isPaintHellOverHF = rIDSA.get(DocumentSettingId::PAINT_HELL_OVER_HEADER_FOOTER);
    IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
    SdrLayerID nHeavenId = rIDDMA.GetHeavenId();
    SdrLayerID nHellId = rIDDMA.GetHellId();
    bool isOpaque = GetFormat()->GetOpaque().GetValue();
    if (!isOpaque && isPaintHellOverHF)
    {
        if (!rAnchorFrame.FindFooterOrHeader())
            nHellId = rIDDMA.GetHeaderFooterHellId();
    }
    bool bNoClippingWithWrapPolygon = rIDSA.get(DocumentSettingId::NO_CLIPPING_WITH_WRAP_POLYGON);
    if (bNoClippingWithWrapPolygon && isOpaque)
    {
        if (GetFrameFormat()->GetSurround().IsContour())
        {
            GetVirtDrawObj()->SetLayer(nHellId);
            return;
        }
 
    }
    GetVirtDrawObj()->SetLayer( isOpaque ? nHeavenId :nHellId );
}
 
static SwPosition ResolveFlyAnchor(SwFrameFormat const& rFlyFrame)
{
    SwFormatAnchor const& rAnch(rFlyFrame.GetAnchor());
    if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
    {   // arbitrarily pick last node
        return SwPosition(rFlyFrame.GetDoc()->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
    }
    else
    {
        SwPosition const*const pPos(rAnch.GetContentAnchor());
        assert(pPos);
        if (SwFrameFormat const*const pParent = pPos->GetNode().GetFlyFormat())
        {
            return ResolveFlyAnchor(*pParent);
        }
        else if (pPos->GetContentNode())
        {
            return *pPos;
        }
        else
        {
            return SwPosition(*pPos->GetNode().GetContentNode(), 0);
        }
    }
}
 
void SwFlyFrame::FinitDrawObj()
{
    if(!GetVirtDrawObj() )
        return;
    SwFormat* pFormat = GetFormat();
    // Deregister from SdrPageViews if the Objects is still selected there.
    if(!pFormat->GetDoc()->IsInDtor())
    {
        SwViewShell* p1St = getRootFrame()->GetCurrShell();
        if(p1St)
        {
            for(SwViewShell& rCurrentShell : p1St->GetRingContainer())
            {   // At the moment the Drawing can do just do an Unmark on everything,
                // as the Object was already removed
                if (rCurrentShell.HasDrawView() &&
                    rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())
                {
                    SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, &rCurrentShell);
                    if (pOldSelFly == this)
                    {
                        assert(rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1);
                        if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell))
                        {   // tdf#131679 move any cursor out of fly
                            rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
                            SwPaM const temp(ResolveFlyAnchor(*pOldSelFly->GetFormat()));
                            pFEShell->SetSelection(temp);
                            // could also call SetCursor() like SwFEShell::SelectObj()
                            // does, but that would access layout a bit much...
                        }
                        else
                        {
                            rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
                        }
                    }
                }
            }
        }
    }
 
    SwVirtFlyDrawObj* pVirtDrawObj = GetVirtDrawObj();
    // Else calls delete of the ContactObj
    pVirtDrawObj->SetUserCall(nullptr);
 
    if ( pVirtDrawObj->getSdrPageFromSdrObject() )
        pVirtDrawObj->getSdrPageFromSdrObject()->RemoveObject( pVirtDrawObj->GetOrdNum() );
    ClearDrawObj();
}
 
void SwFlyFrame::ChainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
{
    assert(pMaster && pFollow && "incomplete chain");
    OSL_ENSURE( !pMaster->GetNextLink(), "link can not be changed" );
    OSL_ENSURE( !pFollow->GetPrevLink(), "link can not be changed" );
 
    pMaster->m_pNextLink = pFollow;
    pFollow->m_pPrevLink = pMaster;
 
    if ( pMaster->ContainsContent() )
    {
        // To get a text flow we need to invalidate
        SwFrame *pInva = pMaster->FindLastLower();
        SwRectFnSet aRectFnSet(pMaster);
        const tools::Long nBottom = aRectFnSet.GetPrtBottom(*pMaster);
        while ( pInva )
        {
            if( aRectFnSet.BottomDist( pInva->getFrameArea(), nBottom ) <= 0 )
            {
                pInva->InvalidateSize();
                pInva->Prepare();
                pInva = pInva->FindPrev();
            }
            else
                pInva = nullptr;
        }
    }
 
    if ( pFollow->ContainsContent() )
    {
        // There's only the content from the Masters left; the content from the Follow
        // does not have any Frames left (should always be exactly one empty TextNode).
        SwFrame *pFrame = pFollow->ContainsContent();
        OSL_ENSURE( !pFrame->IsTabFrame() && !pFrame->FindNext(), "follow in chain contains content" );
        pFrame->Cut();
        SwFrame::DestroyFrame(pFrame);
    }
 
    // invalidate accessible relation set (accessibility wrapper)
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
    if( pSh )
    {
        SwRootFrame* pLayout = pMaster->getRootFrame();
        if( pLayout && pLayout->IsAnyShellAccessible() )
            pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
    }
#endif
}
 
void SwFlyFrame::UnchainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
{
    pMaster->m_pNextLink = nullptr;
    pFollow->m_pPrevLink = nullptr;
 
    if ( pFollow->ContainsContent() )
    {
        // The Master sucks up the content of the Follow
        SwLayoutFrame *pUpper = pMaster;
        if ( pUpper->Lower()->IsColumnFrame() )
        {
            pUpper = static_cast<SwLayoutFrame*>(pUpper->GetLastLower());
            pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); // The (Column)BodyFrame
            OSL_ENSURE( pUpper && pUpper->IsColBodyFrame(), "Missing ColumnBody" );
        }
        SwFlyFrame *pFoll = pFollow;
        while ( pFoll )
        {
            SwFrame *pTmp = ::SaveContent( pFoll );
            if ( pTmp )
                ::RestoreContent( pTmp, pUpper, pMaster->FindLastLower() );
            pFoll->SetCompletePaint();
            pFoll->InvalidateSize();
            pFoll = pFoll->GetNextLink();
        }
    }
 
    // The Follow needs his own content to be served
    const SwFormatContent &rContent = pFollow->GetFormat()->GetContent();
    OSL_ENSURE( rContent.GetContentIdx(), ":-( No content prepared." );
    SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
    // Lower() means SwColumnFrame: this one contains another SwBodyFrame
    ::InsertCnt_( pFollow->Lower() ? const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFollow->Lower())->Lower()))
                                   : static_cast<SwLayoutFrame*>(pFollow),
                  pFollow->GetFormat()->GetDoc(), ++nIndex );
 
    // invalidate accessible relation set (accessibility wrapper)
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
    if( pSh )
    {
        SwRootFrame* pLayout = pMaster->getRootFrame();
        if( pLayout && pLayout->IsAnyShellAccessible() )
            pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
    }
#endif
}
 
SwFlyFrame *SwFlyFrame::FindChainNeighbour( SwFrameFormat const &rChain, SwFrame *pAnch )
{
    // We look for the Fly that's in the same Area.
    // Areas can for now only be Head/Footer or Flys.
 
    if ( !pAnch ) // If an Anchor was passed along, that one counts (ctor!)
        pAnch = AnchorFrame();
 
    SwLayoutFrame *pLay;
    if ( pAnch->IsInFly() )
        pLay = pAnch->FindFlyFrame();
    else
    {
        // FindFooterOrHeader is not appropriate here, as we may not have a
        // connection to the Anchor yet.
        pLay = pAnch->GetUpper();
        while ( pLay && !(pLay->GetType() & (SwFrameType::Header|SwFrameType::Footer)) )
            pLay = pLay->GetUpper();
    }
 
    SwIterator<SwFlyFrame,SwFormat> aIter( rChain );
    SwFlyFrame *pFly = aIter.First();
    if ( pLay )
    {
        while ( pFly )
        {
            if ( pFly->GetAnchorFrame() )
            {
                if ( pFly->GetAnchorFrame()->IsInFly() )
                {
                    if ( pFly->AnchorFrame()->FindFlyFrame() == pLay )
                        break;
                }
                else if ( pLay == pFly->FindFooterOrHeader() )
                    break;
            }
            pFly = aIter.Next();
        }
    }
    else if ( pFly )
    {
        OSL_ENSURE( !aIter.Next(), "chain with more than one instance" );
    }
    return pFly;
}
 
bool SwFlyFrame::IsFlySplitAllowed() const
{
    if (!IsFlyAtContentFrame())
    {
        return false;
    }
 
    const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
    {
        return false;
    }
 
    if (FindFooterOrHeader())
    {
        // Adding a new page would not increase the header/footer area.
        return false;
    }
 
    const SwFrame* pFlyAnchor = GetAnchorFrame();
    if (pFlyAnchor && pFlyAnchor->FindColFrame())
    {
        // No split in multi-column sections, so GetFlyAnchorBottom() can assume that our innermost
        // body frame and the page's body frame is the same.
        // This is also consistent with the Word behavior.
        return false;
    }
 
    if (pFlyAnchor && pFlyAnchor->IsInFootnote())
    {
        // No split in footnotes.
        return false;
    }
 
    const SwFlyFrameFormat* pFormat = GetFormat();
    const SwFormatVertOrient& rVertOrient = pFormat->GetVertOrient();
    if (rVertOrient.GetVertOrient() == text::VertOrientation::BOTTOM)
    {
        // We have to grow from bottom to top, and the fly split code assumes that we grow from top
        // to bottom, so don't split for now.
        if (rVertOrient.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA)
        {
            // Growing from the bottom of the body frame.
            return false;
        }
    }
 
    return pFormat->GetFlySplit().GetValue();
}
 
SwFrame *SwFlyFrame::FindLastLower()
{
    SwFrame *pRet = ContainsAny();
    if ( pRet && pRet->IsInTab() )
        pRet = pRet->FindTabFrame();
    SwFrame *pNxt = pRet;
    while ( pNxt && IsAnLower( pNxt ) )
    {   pRet = pNxt;
        pNxt = pNxt->FindNext();
    }
    return pRet;
}
 
bool SwFlyFrame::FrameSizeChg( const SwFormatFrameSize &rFrameSize )
{
    bool bRet = false;
    SwTwips nDiffHeight = getFrameArea().Height();
    if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable )
        mbFixSize = m_bMinHeight = false;
    else
    {
        if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
        {
            mbFixSize = true;
            m_bMinHeight = false;
        }
        else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
        {
            mbFixSize = false;
            m_bMinHeight = true;
        }
        nDiffHeight -= rFrameSize.GetHeight();
    }
    // If the Fly contains columns, we already need to set the Fly
    // and the Columns to the required value or else we run into problems.
    if ( Lower() )
    {
        if ( Lower()->IsColumnFrame() )
        {
            const SwRect aOld( GetObjRectWithSpaces() );
            const Size   aOldSz( getFramePrintArea().SSize() );
            const SwTwips nDiffWidth = getFrameArea().Width() - rFrameSize.GetWidth();
 
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aFrm.Height( aFrm.Height() - nDiffHeight );
                aFrm.Width ( aFrm.Width()  - nDiffWidth  );
            }
 
            // #i68520#
            InvalidateObjRectWithSpaces();
 
            {
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aPrt.Height( aPrt.Height() - nDiffHeight );
                aPrt.Width ( aPrt.Width()  - nDiffWidth  );
            }
 
            ChgLowersProp( aOldSz );
            ::Notify( this, FindPageFrame(), aOld );
            setFrameAreaPositionValid(false);
            bRet = true;
        }
        else if ( Lower()->IsNoTextFrame() )
        {
            mbFixSize = true;
            m_bMinHeight = false;
        }
    }
    return bRet;
}
 
void SwFlyFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::SwLegacyModify)
    {
        auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
        SwFlyFrameInvFlags eInvFlags = SwFlyFrameInvFlags::NONE;
        if(pLegacy->m_pNew && pLegacy->m_pOld && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
        {
            SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
            SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
            const SfxPoolItem* pNItem = aNIter.GetCurItem();
            const SfxPoolItem* pOItem = aOIter.GetCurItem();
            SwAttrSetChg aOldSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld));
            SwAttrSetChg aNewSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew));
            do
            {
                UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
                pNItem = aNIter.NextItem();
                pOItem = aOIter.NextItem();
            } while(pNItem);
            if(aOldSet.Count() || aNewSet.Count())
                SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
        }
        else
            UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
 
        if(eInvFlags == SwFlyFrameInvFlags::NONE)
            return;
 
        Invalidate_();
        if(eInvFlags & SwFlyFrameInvFlags::InvalidatePos)
        {
            InvalidatePos_();
            // #i68520#
            InvalidateObjRectWithSpaces();
        }
        if(eInvFlags & SwFlyFrameInvFlags::InvalidateSize)
        {
            InvalidateSize_();
            // #i68520#
            InvalidateObjRectWithSpaces();
        }
        if(eInvFlags & SwFlyFrameInvFlags::InvalidatePrt)
            InvalidatePrt_();
        if(eInvFlags & SwFlyFrameInvFlags::SetNotifyBack)
            SetNotifyBack();
        if(eInvFlags & SwFlyFrameInvFlags::SetCompletePaint)
            SetCompletePaint();
        if((eInvFlags & SwFlyFrameInvFlags::ClearContourCache) && Lower() && Lower()->IsNoTextFrame())
            ClrContourCache( GetVirtDrawObj() );
        SwRootFrame *pRoot;
        if(eInvFlags & SwFlyFrameInvFlags::InvalidateBrowseWidth && nullptr != (pRoot = getRootFrame()))
            pRoot->InvalidateBrowseWidth();
        // #i28701#
        if(eInvFlags & SwFlyFrameInvFlags::UpdateObjInSortedList)
        {
            // update sorted object lists, the Writer fly frame is registered at.
            UpdateObjInSortedList();
        }
 
        // #i87645# - reset flags for the layout process (only if something has been invalidated)
        ResetLayoutProcessBools();
    }
    else if (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
    {
        // There's a FlyFrame, so use it
        static_cast<const sw::AutoFormatUsedHint&>(rHint).SetUsed();
        return;
    }
    else if (rHint.GetId() == SfxHintId::SwGetZOrder)
    {
        auto pGetZOrdnerHint = static_cast<const sw::GetZOrderHint*>(&rHint);
        const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
        if (rFormat.Which() == RES_FLYFRMFMT && rFormat.getIDocumentLayoutAccess().GetCurrentViewShell()) // #i11176#
            pGetZOrdnerHint->m_rnZOrder = GetVirtDrawObj()->GetOrdNum();
    }
    else if (rHint.GetId() == SfxHintId::SwGetObjectConnected)
    {
        auto pConnectedHint = static_cast<const sw::GetObjectConnectedHint*>(&rHint);
        const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
        if (!pConnectedHint->m_risConnected && rFormat.Which() == RES_FLYFRMFMT && (!pConnectedHint->m_pRoot || pConnectedHint->m_pRoot == getRootFrame()))
            pConnectedHint->m_risConnected = true;
    }
}
 
void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
                            SwFlyFrameInvFlags &rInvFlags,
                            SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
{
    bool bClear = true;
    const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    switch( nWhich )
    {
        case RES_VERT_ORIENT:
        case RES_HORI_ORIENT:
        //  #i18732# - consider new option 'follow text flow'
        case RES_FOLLOW_TEXT_FLOW:
        {
            // ATTENTION: Always also change Action in ChgRePos()!
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
        }
        break;
        // #i28701# - consider new option 'wrap influence on position'
        case RES_WRAP_INFLUENCE_ON_OBJPOS:
        {
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack
                         | SwFlyFrameInvFlags::UpdateObjInSortedList;
        }
        break;
        case RES_SURROUND:
        {
            //#i28701# - invalidate position on change of
            // wrapping style.
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
            // The background needs to be messaged and invalidated
            const SwRect aTmp( GetObjRectWithSpaces() );
            NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameAttributesChanged );
 
            // By changing the flow of frame-bound Frames, a vertical alignment
            // can be activated/deactivated => MakeFlyPos
            if( RndStdIds::FLY_AT_FLY == GetFormat()->GetAnchor().GetAnchorId() )
                rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
 
            // Delete contour in the Node if necessary
            if ( Lower() && Lower()->IsNoTextFrame() &&
                 !GetFormat()->GetSurround().IsContour() )
            {
                SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
                if ( pNd->HasContour() )
                    pNd->SetContour( nullptr );
            }
            // #i28701# - perform reorder of object lists
            // at anchor frame and at page frame.
            rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
        }
        break;
 
        case RES_PROTECT:
            if (pNew)
            {
                const SvxProtectItem *pP = static_cast<const SvxProtectItem*>(pNew);
                GetVirtDrawObj()->SetMoveProtect( pP->IsPosProtected()   );
                GetVirtDrawObj()->SetResizeProtect( pP->IsSizeProtected() );
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                if( pSh )
                {
                    SwRootFrame* pLayout = getRootFrame();
                    if( pLayout && pLayout->IsAnyShellAccessible() )
                        pSh->Imp()->InvalidateAccessibleEditableState( true, this );
                }
#endif
            }
            break;
        case RES_COL:
            if (pOld && pNew)
            {
                ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
                const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
                if ( FrameSizeChg( rNew ) )
                    NotifyDrawObj();
                rInvFlags |= SwFlyFrameInvFlags::InvalidateSize | SwFlyFrameInvFlags::SetNotifyBack
                             | SwFlyFrameInvFlags::SetCompletePaint;
            }
            break;
 
        case RES_FRM_SIZE:
        case RES_FMT_CHG:
        case RES_FLY_SPLIT:
        {
            const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
            if ( FrameSizeChg( rNew ) )
                NotifyDrawObj();
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
                         | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetNotifyBack
                         | SwFlyFrameInvFlags::SetCompletePaint
                         | SwFlyFrameInvFlags::InvalidateBrowseWidth
                         | SwFlyFrameInvFlags::ClearContourCache;
            if (pOld && RES_FMT_CHG == nWhich)
            {
                SwRect aNew( GetObjRectWithSpaces() );
                SwRect aOld( getFrameArea() );
                const SvxULSpaceItem &rUL = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetULSpace();
                aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
                aOld.AddHeight(rUL.GetLower() );
                const SvxLRSpaceItem &rLR = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetLRSpace();
                aOld.Left  ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
                aOld.AddWidth(rLR.GetRight() );
                aNew.Union( aOld );
                NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
 
                // Special case:
                // When assigning a template we cannot rely on the old column
                // attribute. As there need to be at least enough for ChgColumns,
                // we need to create a temporary attribute.
                SwFormatCol aCol;
                if ( Lower() && Lower()->IsColumnFrame() )
                {
                    sal_uInt16 nCol = 0;
                    SwFrame *pTmp = Lower();
                    do
                    {   ++nCol;
                        pTmp = pTmp->GetNext();
                    } while ( pTmp );
                    aCol.Init( nCol, 0, 1000 );
                }
                ChgColumns( aCol, GetFormat()->GetCol() );
            }
 
            SwFormatURL aURL( GetFormat()->GetURL() );
 
            SwFormatFrameSize *pNewFormatFrameSize = nullptr;
            SwFormatChg *pOldFormatChg = nullptr;
            if (nWhich == RES_FRM_SIZE)
                pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew));
            else if (nWhich == RES_FMT_CHG)
                pOldFormatChg = const_cast<SwFormatChg*>(static_cast<const SwFormatChg*>(pOld));
            else if (nWhich == RES_FLY_SPLIT)
            {
                // If the fly frame has a table lower, invalidate that, so it joins its follow tab
                // frames and re-splits according to the new fly split rule.
                if (Lower() && Lower()->IsTabFrame())
                {
                    Lower()->InvalidateAll_();
                }
            }
 
            if (aURL.GetMap() && (pNewFormatFrameSize || pOldFormatChg))
            {
                const SwFormatFrameSize &rOld = pNewFormatFrameSize ?
                                *pNewFormatFrameSize :
                                pOldFormatChg->pChangedFormat->GetFrameSize();
                //#35091# Can be "times zero", when loading the template
                if ( rOld.GetWidth() && rOld.GetHeight() )
                {
 
                    Fraction aScaleX( rOld.GetWidth(), rNew.GetWidth() );
                    Fraction aScaleY( rOld.GetHeight(), rOld.GetHeight() );
                    aURL.GetMap()->Scale( aScaleX, aScaleY );
                    SwFrameFormat *pFormat = GetFormat();
                    pFormat->LockModify();
                    pFormat->SetFormatAttr( aURL );
                    pFormat->UnlockModify();
                }
            }
            const SvxProtectItem &rP = GetFormat()->GetProtect();
            GetVirtDrawObj()->SetMoveProtect( rP.IsPosProtected()    );
            GetVirtDrawObj()->SetResizeProtect( rP.IsSizeProtected() );
 
            if ( pSh )
                pSh->InvalidateWindows( getFrameArea() );
            const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
            const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
            bool isPaintHellOverHF = rIDSA.get(DocumentSettingId::PAINT_HELL_OVER_HEADER_FOOTER);
            SdrLayerID nHellId = rIDDMA.GetHellId();
 
            if (isPaintHellOverHF && !GetAnchorFrame()->FindFooterOrHeader())
            {
                nHellId = rIDDMA.GetHeaderFooterHellId();
            }
            bool bNoClippingWithWrapPolygon = rIDSA.get(DocumentSettingId::NO_CLIPPING_WITH_WRAP_POLYGON);
            SdrLayerID nId = nHellId;
            if (GetFormat()->GetOpaque().GetValue() &&
                !(bNoClippingWithWrapPolygon && GetFrameFormat()->GetSurround().IsContour()))
                nId = rIDDMA.GetHeavenId();
            GetVirtDrawObj()->SetLayer( nId );
 
            if ( Lower() )
            {
                // Delete contour in the Node if necessary
                if( Lower()->IsNoTextFrame() &&
                     !GetFormat()->GetSurround().IsContour() )
                {
                    SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
                    if ( pNd->HasContour() )
                        pNd->SetContour( nullptr );
                }
                else if( !Lower()->IsColumnFrame() )
                {
                    SwFrame* pFrame = GetLastLower();
                    if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
                        pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
                }
            }
 
            // #i28701# - perform reorder of object lists
            // at anchor frame and at page frame.
            rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
 
            break;
        }
        case RES_UL_SPACE:
        case RES_LR_SPACE:
        {
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
            if( pSh && pSh->GetViewOptions()->getBrowseMode() )
                getRootFrame()->InvalidateBrowseWidth();
            SwRect aNew( GetObjRectWithSpaces() );
            SwRect aOld( getFrameArea() );
            if (pNew)
            {
                if ( RES_UL_SPACE == nWhich )
                {
                    const SvxULSpaceItem &rUL = *static_cast<const SvxULSpaceItem*>(pNew);
                    aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
                    aOld.AddHeight(rUL.GetLower() );
                }
                else
                {
                    const SvxLRSpaceItem &rLR = *static_cast<const SvxLRSpaceItem*>(pNew);
                    aOld.Left  ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
                    aOld.AddWidth(rLR.GetRight() );
                }
            }
            aNew.Union( aOld );
            NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
        }
        break;
 
        case RES_TEXT_VERT_ADJUST:
        {
            InvalidateContentPos();
            rInvFlags |= SwFlyFrameInvFlags::SetCompletePaint;
        }
        break;
 
        case RES_BOX:
        case RES_SHADOW:
            rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
                         | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetCompletePaint;
            break;
 
        case RES_FRAMEDIR :
            SetDerivedVert( false );
            SetDerivedR2L( false );
            CheckDirChange();
            break;
 
        case RES_OPAQUE:
            if (pNew)
            {
                if ( pSh )
                    pSh->InvalidateWindows( getFrameArea() );
 
                const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
                const SdrLayerID nId = static_cast<const SvxOpaqueItem*>(pNew)->GetValue() ?
                                    rIDDMA.GetHeavenId() :
                                    rIDDMA.GetHellId();
                GetVirtDrawObj()->SetLayer( nId );
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
                if( pSh )
                {
                    SwRootFrame* pLayout = getRootFrame();
                    if( pLayout && pLayout->IsAnyShellAccessible() )
                    {
                        pSh->Imp()->DisposeAccessibleFrame( this );
                        pSh->Imp()->AddAccessibleFrame( this );
                    }
                }
#endif
                // #i28701# - perform reorder of object lists
                // at anchor frame and at page frame.
                rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
            }
            break;
 
        case RES_URL:
            // The interface changes the frame size when interacting with text frames,
            // the Map, however, needs to be relative to FrameSize().
            if ( (!Lower() || !Lower()->IsNoTextFrame()) && pNew && pOld &&
                 static_cast<const SwFormatURL*>(pNew)->GetMap() && static_cast<const SwFormatURL*>(pOld)->GetMap() )
            {
                const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
                if ( rSz.GetHeight() != getFrameArea().Height() ||
                     rSz.GetWidth()  != getFrameArea().Width() )
                {
                    SwFormatURL aURL( GetFormat()->GetURL() );
                    Fraction aScaleX( getFrameArea().Width(),  rSz.GetWidth() );
                    Fraction aScaleY( getFrameArea().Height(), rSz.GetHeight() );
                    aURL.GetMap()->Scale( aScaleX, aScaleY );
                    SwFrameFormat *pFormat = GetFormat();
                    pFormat->LockModify();
                    pFormat->SetFormatAttr( aURL );
                    pFormat->UnlockModify();
                }
            }
            // No invalidation necessary
            break;
 
        case RES_CHAIN:
            if (pNew)
            {
                const SwFormatChain *pChain = static_cast<const SwFormatChain*>(pNew);
                if ( pChain->GetNext() )
                {
                    SwFlyFrame *pFollow = FindChainNeighbour( *pChain->GetNext() );
                    if ( GetNextLink() && pFollow != GetNextLink() )
                        SwFlyFrame::UnchainFrames( this, GetNextLink());
                    if ( pFollow )
                    {
                        if ( pFollow->GetPrevLink() &&
                             pFollow->GetPrevLink() != this )
                            SwFlyFrame::UnchainFrames( pFollow->GetPrevLink(),
                                                     pFollow );
                        if ( !GetNextLink() )
                            SwFlyFrame::ChainFrames( this, pFollow );
                    }
                }
                else if ( GetNextLink() )
                    SwFlyFrame::UnchainFrames( this, GetNextLink() );
                if ( pChain->GetPrev() )
                {
                    SwFlyFrame *pMaster = FindChainNeighbour( *pChain->GetPrev() );
                    if ( GetPrevLink() && pMaster != GetPrevLink() )
                        SwFlyFrame::UnchainFrames( GetPrevLink(), this );
                    if ( pMaster )
                    {
                        if ( pMaster->GetNextLink() &&
                             pMaster->GetNextLink() != this )
                            SwFlyFrame::UnchainFrames( pMaster,
                                                     pMaster->GetNextLink() );
                        if ( !GetPrevLink() )
                            SwFlyFrame::ChainFrames( pMaster, this );
                    }
                }
                else if ( GetPrevLink() )
                    SwFlyFrame::UnchainFrames( GetPrevLink(), this );
            }
            [[fallthrough]];
        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 SwFlyFrame::Invalidate_( SwPageFrame const *pPage )
{
    InvalidatePage( pPage );
    m_bNotifyBack = m_bInvalid = true;
 
    SwFlyFrame *pFrame;
    if ( GetAnchorFrame() && nullptr != (pFrame = AnchorFrame()->FindFlyFrame()) )
    {
        // Very bad case: If the Fly is bound within another Fly which
        // contains columns, the Format should be from that one.
        if ( !pFrame->IsLocked() && !pFrame->IsColLocked() &&
             pFrame->Lower() && pFrame->Lower()->IsColumnFrame() )
            pFrame->InvalidateSize();
    }
 
    // #i85216#
    // if vertical position is oriented at a layout frame inside a ghost section,
    // assure that the position is invalidated and that the information about
    // the vertical position oriented frame is cleared
    if ( GetVertPosOrientFrame() && GetVertPosOrientFrame()->IsLayoutFrame() )
    {
        const SwSectionFrame* pSectFrame( GetVertPosOrientFrame()->FindSctFrame() );
        if ( pSectFrame && pSectFrame->GetSection() == nullptr )
        {
            InvalidatePos();
            ClearVertPosOrientFrame();
        }
    }
}
 
/** Change the relative position
 *
 * The position will be Fix automatically and the attribute is changed accordingly.
 */
void SwFlyFrame::ChgRelPos( const Point &rNewPos )
{
    if ( GetCurrRelPos() == rNewPos )
        return;
 
    SwFrameFormat *pFormat = GetFormat();
    const bool bVert = GetAnchorFrame()->IsVertical();
    const SwTwips nNewY = bVert ? rNewPos.X() : rNewPos.Y();
    SwTwips nTmpY = nNewY == LONG_MAX ? 0 : nNewY;
    if( bVert )
        nTmpY = -nTmpY;
    SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( pFormat->GetDoc()->GetAttrPool() );
 
    SwFormatVertOrient aVert( pFormat->GetVertOrient() );
    const SwTextFrame *pAutoFrame = nullptr;
    // #i34948# - handle also at-page and at-fly anchored
    // Writer fly frames
    const RndStdIds eAnchorType = GetFrameFormat()->GetAnchor().GetAnchorId();
    if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
    {
        aVert.SetVertOrient( text::VertOrientation::NONE );
        aVert.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
    }
    else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
    {
        aVert.SetVertOrient( text::VertOrientation::NONE );
        aVert.SetRelationOrient( text::RelOrientation::FRAME );
    }
    else if ( IsFlyAtContentFrame() || text::VertOrientation::NONE != aVert.GetVertOrient() )
    {
        if( text::RelOrientation::CHAR == aVert.GetRelationOrient() && IsAutoPos() )
        {
            if( LONG_MAX != nNewY )
            {
                aVert.SetVertOrient( text::VertOrientation::NONE );
                assert(GetAnchorFrame()->IsTextFrame());
                pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
                TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
                            *pFormat->GetAnchor().GetContentAnchor()));
                while( pAutoFrame->GetFollow() &&
                       pAutoFrame->GetFollow()->GetOffset() <= nOfs )
                {
                    if( pAutoFrame == GetAnchorFrame() )
                        nTmpY += pAutoFrame->GetRelPos().Y();
                    nTmpY -= pAutoFrame->GetUpper()->getFramePrintArea().Height();
                    pAutoFrame = pAutoFrame->GetFollow();
                }
                nTmpY = static_cast<SwFlyAtContentFrame*>(this)->GetRelCharY(pAutoFrame)-nTmpY;
            }
            else
                aVert.SetVertOrient( text::VertOrientation::CHAR_BOTTOM );
        }
        else
        {
            aVert.SetVertOrient( text::VertOrientation::NONE );
            aVert.SetRelationOrient( text::RelOrientation::FRAME );
        }
    }
    aVert.SetPos( nTmpY );
    aSet.Put( aVert );
 
    // For Flys in the Cnt, the horizontal orientation is of no interest,
    // as it's always 0
    if ( !IsFlyInContentFrame() )
    {
        const SwTwips nNewX = bVert ? rNewPos.Y() : rNewPos.X();
        SwTwips nTmpX = nNewX == LONG_MAX ? 0 : nNewX;
        SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
        // #i34948# - handle also at-page and at-fly anchored
        // Writer fly frames
        if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
        {
            aHori.SetHoriOrient( text::HoriOrientation::NONE );
            aHori.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
            aHori.SetPosToggle( false );
        }
        else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
        {
            aHori.SetHoriOrient( text::HoriOrientation::NONE );
            aHori.SetRelationOrient( text::RelOrientation::FRAME );
            aHori.SetPosToggle( false );
        }
        else if ( IsFlyAtContentFrame() || text::HoriOrientation::NONE != aHori.GetHoriOrient() )
        {
            aHori.SetHoriOrient( text::HoriOrientation::NONE );
            if( text::RelOrientation::CHAR == aHori.GetRelationOrient() && IsAutoPos() )
            {
                if( LONG_MAX != nNewX )
                {
                    if( !pAutoFrame )
                    {
                        assert(GetAnchorFrame()->IsTextFrame());
                        pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
                        TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
                                    *pFormat->GetAnchor().GetContentAnchor()));
                        while( pAutoFrame->GetFollow() &&
                               pAutoFrame->GetFollow()->GetOffset() <= nOfs )
                            pAutoFrame = pAutoFrame->GetFollow();
                    }
                    nTmpX -= static_cast<SwFlyAtContentFrame*>(this)->GetRelCharX(pAutoFrame);
                }
            }
            else
                aHori.SetRelationOrient( text::RelOrientation::FRAME );
            aHori.SetPosToggle( false );
        }
        aHori.SetPos( nTmpX );
        aSet.Put( aHori );
    }
    SetCurrRelPos( rNewPos );
    pFormat->GetDoc()->SetAttr( aSet, *pFormat );
 
}
 
/** "Formats" the Frame; Frame and PrtArea.
 *
 * The FixSize is not inserted here.
 */
void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
{
    OSL_ENSURE( pAttrs, "FlyFrame::Format, pAttrs is 0." );
 
    ColLock();
 
    if ( !isFrameAreaSizeValid() )
    {
        if ( getFrameArea().Top() == FAR_AWAY && getFrameArea().Left() == FAR_AWAY )
        {
            // Remove safety switch (see SwFrame::CTor)
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aFrm.Pos().setX(0);
                aFrm.Pos().setY(0);
            }
 
            // #i68520#
            InvalidateObjRectWithSpaces();
        }
 
        // Check column width and set it if needed
        if ( Lower() && Lower()->IsColumnFrame() )
            AdjustColumns( nullptr, false );
 
        setFrameAreaSizeValid(true);
 
        const SwTwips nUL = pAttrs->CalcTopLine()  + pAttrs->CalcBottomLine();
        const SwTwips nLR = pAttrs->CalcLeftLine() + pAttrs->CalcRightLine();
        const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
        Size aRelSize( CalcRel( rFrameSz ) );
 
        OSL_ENSURE( pAttrs->GetSize().Height() != 0 || rFrameSz.GetHeightPercent(), "FrameAttr height is 0." );
        OSL_ENSURE( pAttrs->GetSize().Width()  != 0 || rFrameSz.GetWidthPercent(), "FrameAttr width is 0." );
 
        SwRectFnSet aRectFnSet(this);
        if( !HasFixSize() )
        {
            tools::Long nMinHeight = 0;
            if( IsMinHeight() )
                nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
 
            SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL);
            if( IsMinHeight() && (nRemaining + nUL) < nMinHeight )
                nRemaining = nMinHeight - nUL;
            // Because the Grow/Shrink of the Flys does not directly
            // set the size - only indirectly by triggering a Format()
            // via Invalidate() - the sizes need to be set here.
            // Notification is running along already.
            // As we already got a lot of zeros per attribute, we block them
            // from now on.
 
            if ( nRemaining < MINFLY )
                nRemaining = MINFLY;
 
            const SwFrame* pAnchor = GetAnchorFrame();
            if (SwFrame* pAnchorChar = FindAnchorCharFrame())
            {
                // If we find a follow of the anchor that is effectively the anchor of this fly,
                // then use that as the anchor for sizing purposes.
                pAnchor = pAnchorChar;
            }
            if (pAnchor && IsFlySplitAllowed())
            {
                // If the fly is allowed to be split, then limit its size to the upper of the
                // anchor.
                SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
                SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
                SwTwips nBottom = aRectFnSet.GetTop(getFrameArea()) + nRemaining;
                if (nBottom > nDeadline)
                {
                    if (nDeadline > nTop)
                    {
                        nRemaining = nDeadline - nTop;
                    }
                    else
                    {
                        // Even the top is below the deadline, set size to empty and mark it as
                        // clipped so we re-format later.
                        nRemaining = 0;
                        m_bHeightClipped = true;
                    }
                }
            }
 
            {
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aRectFnSet.SetHeight( aPrt, nRemaining );
            }
 
            nRemaining -= aRectFnSet.GetHeight(getFrameArea());
 
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom( aFrm, nRemaining + nUL );
            }
 
            // #i68520#
            if ( nRemaining + nUL != 0 )
            {
                InvalidateObjRectWithSpaces();
            }
 
            setFrameAreaSizeValid(true);
 
            if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
            {
                // This fly is a textbox of a draw shape.
                SdrObject* pShape = pShapeFormat->FindSdrObject();
                if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pShape) )
                {
                    // The shape is a customshape: then inform it about the calculated fly size.
                    Size aSize(getFrameArea().Width(), getFrameArea().Height());
                    pCustomShape->SuggestTextFrameSize(aSize);
                    // Do the calculations normally done after touching editeng text of the shape.
                    pCustomShape->NbcSetOutlinerParaObjectForText(std::nullopt, nullptr);
                }
            }
        }
        else
        {
            // Fixed Frames do not Format itself
            setFrameAreaSizeValid(true);
 
            // Flys set their size using the attr
            SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
            nNewSize -= nUL;
            if( nNewSize < MINFLY )
                nNewSize = MINFLY;
 
            {
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aRectFnSet.SetHeight( aPrt, nNewSize );
            }
 
            nNewSize += nUL - aRectFnSet.GetHeight(getFrameArea());
 
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom( aFrm, nNewSize );
            }
 
            // #i68520#
            if ( nNewSize != 0 )
            {
                InvalidateObjRectWithSpaces();
            }
        }
 
        if ( !m_bFormatHeightOnly )
        {
            OSL_ENSURE( aRelSize == CalcRel( rFrameSz ), "SwFlyFrame::Format CalcRel problem" );
            SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Height() : aRelSize.Width();
 
            if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
            {
                // #i9046# Autowidth for fly frames
                const SwTwips nAutoWidth = lcl_CalcAutoWidth( *this );
                if ( nAutoWidth )
                {
                    if( SwFrameSize::Minimum == rFrameSz.GetWidthSizeType() )
                        nNewSize = std::max( nNewSize - nLR, nAutoWidth );
                    else
                        nNewSize = nAutoWidth;
                }
            }
            else
                nNewSize -= nLR;
 
            if( nNewSize < MINFLY )
                nNewSize = MINFLY;
 
            {
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aRectFnSet.SetWidth( aPrt, nNewSize );
            }
 
            nNewSize += nLR - aRectFnSet.GetWidth(getFrameArea());
 
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddRight( aFrm, nNewSize );
            }
 
            // #i68520#
            if ( nNewSize != 0 )
            {
                InvalidateObjRectWithSpaces();
            }
        }
    }
    ColUnlock();
}
 
// #i11760# - change parameter <bNoColl>: type <bool>;
//   add new parameter <bNoCalcFollow> with
//  new parameter <bNoCalcFollow> was used by method
//                          <FormatWidthCols(..)> to avoid follow formatting
//                          for text frames. But, unformatted follows causes
//                          problems in method <SwContentFrame::WouldFit_(..)>,
//                          which assumes that the follows are formatted.
//                          Thus, <bNoCalcFollow> no longer used by <FormatWidthCols(..)>.
void CalcContent( SwLayoutFrame *pLay, bool bNoColl )
{
    SwViewShell & rShell(*pLay->getRootFrame()->GetCurrShell());
    vcl::RenderContext* pRenderContext = rShell.GetOut();
    SwSectionFrame* pSect;
    bool bCollect = false;
    if( pLay->IsSctFrame() )
    {
        pSect = static_cast<SwSectionFrame*>(pLay);
        if( pSect->IsEndnAtEnd() && !bNoColl )
        {
            bCollect = true;
            SwLayouter::CollectEndnotes( pLay->GetFormat()->GetDoc(), pSect );
        }
        pSect->CalcFootnoteContent();
    }
    else
        pSect = nullptr;
    SwFrame *pFrame = pLay->ContainsAny();
    if ( !pFrame )
    {
        if( pSect )
        {
            if( pSect->HasFollow() )
                pFrame = pSect->GetFollow()->ContainsAny();
            if( !pFrame )
            {
                if( pSect->IsEndnAtEnd() )
                {
                    if( bCollect )
                        pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->
                            InsertEndnotes( pSect );
                    bool bLock = pSect->IsFootnoteLock();
                    pSect->SetFootnoteLock( true );
                    pSect->CalcFootnoteContent();
                    pSect->CalcFootnoteContent();
                    pSect->SetFootnoteLock( bLock );
                }
                return;
            }
            pFrame->InvalidatePos_();
        }
        else
            return;
    }
    pFrame->InvalidatePage();
 
    do
    {
        // local variables to avoid loops caused by anchored object positioning
        SwAnchoredObject* pAgainObj1 = nullptr;
        SwAnchoredObject* pAgainObj2 = nullptr;
 
        // FME 2007-08-30 #i81146# new loop control
        int nLoopControlRuns = 0;
        // tdf#152106 loop control for multi-column sections
        int nLoopControlRunsInMultiCol = 0;
        const int nLoopControlMax = 20;
        const SwFrame* pLoopControlCond = nullptr;
 
        SwFrame* pLast;
        do
        {
            pLast = pFrame;
            bool const wasFrameLowerOfLay(pLay->IsAnLower(pFrame));
            if( pFrame->IsVertical() ?
                ( pFrame->GetUpper()->getFramePrintArea().Height() != pFrame->getFrameArea().Height() )
                : ( pFrame->GetUpper()->getFramePrintArea().Width() != pFrame->getFrameArea().Width() ) )
            {
                pFrame->Prepare( PrepareHint::FixSizeChanged );
                pFrame->InvalidateSize_();
            }
 
            if ( pFrame->IsTabFrame() )
            {
                static_cast<SwTabFrame*>(pFrame)->m_bCalcLowers = true;
                // #i18103# - lock move backward of follow table,
                // if no section content is formatted or follow table belongs
                // to the section, which content is formatted.
                if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() &&
                     ( !pSect || pSect == pFrame->FindSctFrame() ) )
                {
                    static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = true;
                }
            }
 
            {
                SwFrameDeleteGuard aDeletePageGuard(pSect ? pSect->FindPageFrame() : nullptr);
                SwFrameDeleteGuard aDeleteGuard(pSect);
                pFrame->Calc(pRenderContext);
            }
 
            // #i11760# - reset control flag for follow format.
            if ( pFrame->IsTextFrame() )
            {
                static_cast<SwTextFrame*>(pFrame)->AllowFollowFormat();
            }
 
            // The keep-attribute can cause the position
            // of the prev to be invalid:
            // Do not consider invalid previous frame
            // due to its keep-attribute, if current frame is a follow or is locked.
            // #i44049# - do not consider invalid previous
            // frame due to its keep-attribute, if it can't move forward.
            // #i57765# - do not consider invalid previous
            // frame, if current frame has a column/page break before attribute.
            SwFrame* pTmpPrev = pFrame->FindPrev();
            SwFlowFrame* pTmpPrevFlowFrame = pTmpPrev && pTmpPrev->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pTmpPrev) : nullptr;
            SwFlowFrame* pTmpFlowFrame     = pFrame->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pFrame) : nullptr;
 
            bool bPrevInvalid = pTmpPrevFlowFrame && pTmpFlowFrame &&
                               !pTmpFlowFrame->IsFollow() &&
                               !StackHack::IsLocked() && // #i76382#
                               !pTmpFlowFrame->IsJoinLocked() &&
                               !pTmpPrev->isFrameAreaPositionValid() &&
                                pLay->IsAnLower( pTmpPrev ) &&
                                pTmpPrevFlowFrame->IsKeep(pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()) &&
                                pTmpPrevFlowFrame->IsKeepFwdMoveAllowed();
 
            // format floating screen objects anchored to the frame.
            if ( !bPrevInvalid && pFrame->GetDrawObjs() && pLay->IsAnLower( pFrame ) )
            {
                bool bAgain = false;
                bool bRestartLayoutProcess = false;
                size_t nCnt = pFrame->GetDrawObjs()->size();
                size_t i = 0;
                while ( i < nCnt )
                {
                    // pFrame can move to a different page in FormatObj()
                    SwPageFrame *const pPageFrame = pFrame->FindPageFrame();
 
                    // #i28701#
                    SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
                    assert(pAnchoredObj);
 
                    // determine if anchored object has to be
                    // formatted and, in case, format it
                    if ( !pAnchoredObj->PositionLocked() && pAnchoredObj->IsFormatPossible() )
                    {
                        // #i43737# - no invalidation of
                        // anchored object needed - causes loops for as-character
                        // anchored objects.
                        //pAnchoredObj->InvalidateObjPos();
                        SwRect aRect( pAnchoredObj->GetObjRect() );
 
                        SwFrame* pAnchorFrame = pFrame;
                        SwPageFrame* pAnchorPageFrame = pPageFrame;
                        if (SwFlyFrame* pFlyFrame = pAnchoredObj->DynCastFlyFrame())
                        {
                            if (pFlyFrame->IsFlySplitAllowed())
                            {
                                // Split flys are at-para anchored, but the follow fly's anchor char
                                // frame is not the master frame but can be also a follow of pFrame.
                                SwTextFrame* pAnchorCharFrame = pFlyFrame->FindAnchorCharFrame();
                                if (pAnchorCharFrame)
                                {
                                    // Found an anchor char frame, update the anchor frame and the
                                    // anchor page frame accordingly.
                                    pAnchorFrame = pAnchorCharFrame;
                                    pAnchorPageFrame = pAnchorCharFrame->FindPageFrame();
                                }
                            }
                        }
 
                        if (!SwObjectFormatter::FormatObj(*pAnchoredObj, pAnchorFrame, pAnchorPageFrame,
                                rShell.Imp()->IsAction() ? &rShell.Imp()->GetLayAction() : nullptr))
                        {
                            if (rShell.Imp()->IsAction() && rShell.Imp()->GetLayAction().IsAgain())
                            {   // tdf#159015 will always fail, don't loop
                                return;
                            }
                            bRestartLayoutProcess = true;
                            break;
                        }
                        // #i3317# - restart layout process,
                        // if the position of the anchored object is locked now.
                        if ( pAnchoredObj->PositionLocked() )
                        {
                            bRestartLayoutProcess = true;
                            break;
                        }
 
                        if ( aRect != pAnchoredObj->GetObjRect() )
                        {
                            bAgain = true;
                            if ( pAgainObj2 == pAnchoredObj )
                            {
                                OSL_FAIL( "::CalcContent(..) - loop detected, perform attribute changes to avoid the loop" );
                                // Prevent oscillation
                                SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
                                SwFormatSurround aAttr( pFormat->GetSurround() );
                                if( css::text::WrapTextMode_THROUGH != aAttr.GetSurround() )
                                {
                                    // When on auto position, we can only set it to
                                    // flow through
                                    if ((pFormat->GetAnchor().GetAnchorId() ==
                                            RndStdIds::FLY_AT_CHAR) &&
                                        (css::text::WrapTextMode_PARALLEL ==
                                            aAttr.GetSurround()))
                                    {
                                        aAttr.SetSurround( css::text::WrapTextMode_THROUGH );
                                    }
                                    else
                                    {
                                        aAttr.SetSurround( css::text::WrapTextMode_PARALLEL );
                                    }
                                    pFormat->LockModify();
                                    pFormat->SetFormatAttr( aAttr );
                                    pFormat->UnlockModify();
                                }
                            }
                            else
                            {
                                if ( pAgainObj1 == pAnchoredObj )
                                    pAgainObj2 = pAnchoredObj;
                                pAgainObj1 = pAnchoredObj;
                            }
                        }
 
                        if ( !pFrame->GetDrawObjs() )
                            break;
                        if ( pFrame->GetDrawObjs()->size() < nCnt )
                        {
                            --nCnt;
                            // Do not increment index, in this case
                            continue;
                        }
                    }
                    ++i;
                }
 
                // #i28701# - restart layout process, if
                // requested by floating screen object formatting
                if (bRestartLayoutProcess
                    // tdf#152106 loop control in multi-column sections to avoid of freezing
                    && nLoopControlRunsInMultiCol < nLoopControlMax
                    // tdf#142080 if it was already on next page, and still is,
                    // ignore restart, as restart could cause infinite loop
                    && (wasFrameLowerOfLay || pLay->IsAnLower(pFrame)))
                {
                    bool bIsMultiColumn = pSect && pSect->GetSection() && pSect->Lower() &&
                            pSect->Lower()->IsColumnFrame() && pSect->Lower()->GetNext();
                    if ( bIsMultiColumn )
                        ++nLoopControlRunsInMultiCol;
                    pFrame = pLay->ContainsAny();
                    pAgainObj1 = nullptr;
                    pAgainObj2 = nullptr;
                    continue;
                }
 
                // #i28701# - format anchor frame after its objects
                // are formatted, if the wrapping style influence has to be considered.
                if ( pLay->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
                {
                    pFrame->Calc(pRenderContext);
                }
 
                if ( bAgain )
                {
                    pFrame = pLay->ContainsContent();
                    if ( pFrame && pFrame->IsInTab() )
                        pFrame = pFrame->FindTabFrame();
                    if( pFrame && pFrame->IsInSct() )
                    {
                        SwSectionFrame* pTmp = pFrame->FindSctFrame();
                        if( pTmp != pLay && pLay->IsAnLower( pTmp ) )
                            pFrame = pTmp;
                    }
 
                    if ( pFrame == pLoopControlCond )
                        ++nLoopControlRuns;
                    else
                    {
                        nLoopControlRuns = 0;
                        pLoopControlCond = pFrame;
                    }
 
                    if ( nLoopControlRuns < nLoopControlMax )
                        continue;
 
                    OSL_FAIL( "LoopControl in CalcContent" );
                }
            }
            if ( pFrame->IsTabFrame() )
            {
                if (static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove)
                {
                    assert(static_cast<SwTabFrame*>(pFrame)->IsFollow());
                    static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = false;
                    // tdf#150606 encourage it to move back in FormatLayout()
                    if (static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove)
                    {
                        static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove = false;
                        pFrame->InvalidatePos();
                    }
                }
            }
 
            pFrame = bPrevInvalid ? pTmpPrev : pFrame->FindNext();
            if( !bPrevInvalid && pFrame && pFrame->IsSctFrame() && pSect )
            {
                // Empty SectionFrames could be present here
                while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
                    pFrame = pFrame->FindNext();
 
                // If FindNext returns the Follow of the original Area, we want to
                // continue with this content as long as it flows back.
                if( pFrame && pFrame->IsSctFrame() && ( pFrame == pSect->GetFollow() ||
                    static_cast<SwSectionFrame*>(pFrame)->IsAnFollow( pSect ) ) )
                {
                    pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
                    if( pFrame )
                        pFrame->InvalidatePos_();
                }
            }
          // Stay in the pLay.
          // Except for SectionFrames with Follow: the first ContentFrame of the
          // Follow will be formatted, so that it gets a chance to move back
          // into the pLay.  Continue as long as these Frames land in pLay.
        } while ( pFrame &&
                  ( pLay->IsAnLower( pFrame ) ||
                    ( pSect &&
                      ( ( pSect->HasFollow() &&
                          ( pLay->IsAnLower( pLast ) ||
                            ( pLast->IsInSct() &&
                              pLast->FindSctFrame()->IsAnFollow(pSect) ) ) &&
                          pSect->GetFollow()->IsAnLower( pFrame )  ) ||
                        ( pFrame->IsInSct() &&
                          pFrame->FindSctFrame()->IsAnFollow( pSect ) ) ) ) ) );
        if( pSect )
        {
            if( bCollect )
            {
                pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->InsertEndnotes(pSect);
                pSect->CalcFootnoteContent();
            }
            if( pSect->HasFollow() )
            {
                SwSectionFrame* pNxt = pSect->GetFollow();
                while( pNxt && !pNxt->ContainsContent() )
                    pNxt = pNxt->GetFollow();
                if( pNxt )
                    pNxt->CalcFootnoteContent();
            }
            if( bCollect )
            {
                pFrame = pLay->ContainsAny();
                bCollect = false;
                if( pFrame )
                    continue;
            }
        }
        break;
    }
    while( true );
}
 
void SwFlyFrame::MakeObjPos()
{
    if ( isFrameAreaPositionValid() )
        return;
 
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    setFrameAreaPositionValid(true);
 
    // use new class to position object
    GetAnchorFrame()->Calc(pRenderContext);
    objectpositioning::SwToLayoutAnchoredObjectPosition
            aObjPositioning( *GetVirtDrawObj() );
    aObjPositioning.CalcPosition();
 
    // #i58280#
    // update relative position
    SetCurrRelPos( aObjPositioning.GetRelPos() );
 
    {
        SwRectFnSet aRectFnSet(GetAnchorFrame());
        SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
        aFrm.Pos( aObjPositioning.GetRelPos() );
        aFrm.Pos() += aRectFnSet.GetPos(GetAnchorFrame()->getFrameArea());
    }
 
    // #i69335#
    InvalidateObjRectWithSpaces();
}
 
void SwFlyFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
{
    if ( !isFramePrintAreaValid() )
    {
        setFramePrintAreaValid(true);
 
        // consider vertical layout
        SwRectFnSet aRectFnSet(this);
        SwTwips nLeftLine = rAttrs.CalcLeftLine();
 
        // The fly frame may be partially outside the page, check for this case.
        SwPageFrame* pPageFrame = FindPageFrame();
        SwFrameFormat* pFormat = GetFormat();
        if (pPageFrame && pFormat)
        {
            const IDocumentSettingAccess& rIDSA = pFormat->getIDocumentSettingAccess();
            bool bDoNotCaptureDrawObjsOnPage = rIDSA.get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE);
            bool bLRTB = pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_LR_TB;
            SwTwips nFlyLeft = getFrameArea().Left();
            SwTwips nPageLeft = pPageFrame->getFrameArea().Left();
            if (bDoNotCaptureDrawObjsOnPage && bLRTB && nFlyLeft < nPageLeft)
            {
                // It is outside: only start the left padding of the text inside the page frame,
                // when we're in Word compatibility mode.
                nLeftLine += (nPageLeft - nFlyLeft);
            }
        }
 
        aRectFnSet.SetXMargins( *this, nLeftLine,
                                        rAttrs.CalcRightLine() );
        aRectFnSet.SetYMargins( *this, rAttrs.CalcTopLine(),
                                        rAttrs.CalcBottomLine() );
    }
}
 
void SwFlyFrame::MakeContentPos( const SwBorderAttrs &rAttrs )
{
    if ( m_bValidContentPos )
        return;
 
    m_bValidContentPos = true;
 
    const SwTwips nUL = rAttrs.CalcTopLine()  + rAttrs.CalcBottomLine();
    Size aRelSize( CalcRel( GetFormat()->GetFrameSize() ) );
 
    SwRectFnSet aRectFnSet(this);
    tools::Long nMinHeight = 0;
    if( IsMinHeight() )
        nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
 
    Point aNewContentPos = getFramePrintArea().Pos();
    const SdrTextVertAdjust nAdjust = GetFormat()->GetTextVertAdjust().GetValue();
 
    if( nAdjust != SDRTEXTVERTADJUST_TOP )
    {
        const SwTwips nContentHeight = CalcContentHeight(&rAttrs, nMinHeight, nUL);
        SwTwips nDiff = 0;
 
        if( nContentHeight != 0)
            nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nContentHeight;
 
        if( nDiff > 0 )
        {
            if( nAdjust == SDRTEXTVERTADJUST_CENTER )
            {
                if( aRectFnSet.IsVertL2R() )
                    aNewContentPos.setX(aNewContentPos.getX() + nDiff/2);
                else if( aRectFnSet.IsVert() )
                    aNewContentPos.setX(aNewContentPos.getX() - nDiff/2);
                else
                    aNewContentPos.setY(aNewContentPos.getY() + nDiff/2);
            }
            else if( nAdjust == SDRTEXTVERTADJUST_BOTTOM )
            {
                if( aRectFnSet.IsVertL2R() )
                    aNewContentPos.setX(aNewContentPos.getX() + nDiff);
                else if( aRectFnSet.IsVert() )
                    aNewContentPos.setX(aNewContentPos.getX() - nDiff);
                else
                    aNewContentPos.setY(aNewContentPos.getY() + nDiff);
            }
        }
    }
    if( aNewContentPos != ContentPos() )
    {
        ContentPos() = aNewContentPos;
        for( SwFrame *pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
        {
            pFrame->InvalidatePos();
        }
    }
 
}
 
void SwFlyFrame::InvalidateContentPos()
{
    m_bValidContentPos = false;
    Invalidate_();
}
 
void SwFlyFrame::SelectionHasChanged(SwFEShell* pShell)
{
    SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >(pShell);
    if (pWrtSh == nullptr)
        return;
 
    UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
}
 
bool SwFlyFrame::IsShowUnfloatButton(SwWrtShell* pWrtSh) const
{
    if (pWrtSh == nullptr)
        return false;
 
    // In read only mode we don't allow unfloat operation
    if (pWrtSh->GetViewOptions()->IsReadonly())
        return false;
 
    const SdrObject *pObj = GetFrameFormat()->FindRealSdrObject();
    if (pObj == nullptr)
        return false;
 
    // SwFlyFrame itself can mean images, ole objects, etc, but we interested in actual text frames
    if (SwFEShell::GetObjCntType(*pObj) != OBJCNT_FLY)
        return false;
 
    // We show the button only for the selected text frame
    SwDrawView *pView = pWrtSh->Imp()->GetDrawView();
    if (pView == nullptr)
        return false;
 
    // Fly frame can be selected only alone
    if (pView->GetMarkedObjectList().GetMarkCount() != 1)
        return false;
 
    if(!pView->IsObjMarked(pObj))
        return false;
 
    // A frame is a floating table if there is only one table (and maybe some whitespaces) inside it
    int nTableCount = 0;
    const SwFrame* pLower = GetLower();
    const SwTabFrame* pTable = nullptr;
    while (pLower)
    {
        if (pLower->IsTabFrame())
        {
            pTable = static_cast<const SwTabFrame*>(pLower);
            ++nTableCount;
            if (nTableCount > 1)
                return false;
        }
 
        if (pLower->IsTextFrame())
        {
            const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pLower);
            if (!o3tl::trim(pTextFrame->GetText()).empty())
                return false;
        }
        pLower = pLower->GetNext();
    }
 
    if (nTableCount != 1 || pTable == nullptr)
        return false;
 
    // Show the unfold button only for multipage tables
    const SwBodyFrame *pBody = GetAnchorFrame()->FindBodyFrame();
    if (pBody == nullptr)
        return false;
 
    tools::Long nBodyHeight = pBody->getFrameArea().Height();
    tools::Long nTableHeight = pTable->getFrameArea().Height();
    tools::Long nFrameOffset = std::abs(GetAnchorFrame()->getFrameArea().Top() - pBody->getFrameArea().Top());
 
    return nBodyHeight < nTableHeight + nFrameOffset;
}
 
void SwFlyFrame::ActiveUnfloatButton(SwWrtShell* pWrtSh)
{
    SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
    SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
    SwFrameControlPtr pControl = rMngr.GetControl(FrameControlType::FloatingTable, this);
    if (pControl && pControl->GetIFacePtr())
    {
        pControl->GetIFacePtr()->GetButton()->clicked();
    }
}
 
void SwFlyFrame::UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const
{
    if (pWrtSh == nullptr)
        return;
 
    SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
    SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
    Point aTopRightPixel = rEditWin.LogicToPixel( getFrameArea().TopRight() );
    rMngr.SetUnfloatTableButton(this, bShow,  aTopRightPixel);
}
 
SwFlyAtContentFrame* SwFlyFrame::DynCastFlyAtContentFrame()
{
    return IsFlyAtContentFrame() ? static_cast<SwFlyAtContentFrame*>(this) : nullptr;
}
 
SwTwips SwFlyFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool bTst)
{
    if (!Lower())
    {
        reason = SwResizeLimitReason::Unspecified; // refusing because we have no content?
        return 0;
    }
    if (IsColLocked() || HasFixSize())
    {
        if (nDist <= 0 || !HasFixSize())
            reason = SwResizeLimitReason::Unspecified;
        else
            reason = GetNextLink() ? SwResizeLimitReason::FlowToFollow
                                   : SwResizeLimitReason::FixedSizeFrame;
        return 0;
    }
 
    SwRectFnSet aRectFnSet(this);
    SwTwips nSize = aRectFnSet.GetHeight(getFrameArea());
    if( nSize > 0 && nDist > ( LONG_MAX - nSize ) )
        nDist = LONG_MAX - nSize;
 
    if ( nDist <= 0 )
    {
        reason = SwResizeLimitReason::Unspecified;
        return 0;
    }
 
    if ( Lower()->IsColumnFrame() )
    {   // If it's a Column Frame, the Format takes control of the
        // resizing (due to the adjustment).
        if ( !bTst )
        {
            // #i28701# - unlock position of Writer fly frame
            UnlockPosition();
            InvalidatePos_();
            InvalidateSize();
        }
        reason = SwResizeLimitReason::BalancedColumns;
        return 0;
    }
 
    reason = SwResizeLimitReason::Unspecified;
 
    if (bTst)
    {
        // We're in test mode. Don't promise infinite growth for split flys, rather limit the
        // max size to the bottom of the upper.
        const SwFrame* pAnchor = GetAnchorFrame();
        if (SwFrame* pAnchorChar = FindAnchorCharFrame())
        {
            pAnchor = pAnchorChar;
        }
        if (pAnchor && IsFlySplitAllowed())
        {
            SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
            SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
            SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea());
            // Calculate max grow and compare to the requested growth, adding to nDist may
            // overflow when it's LONG_MAX.
            SwTwips nMaxGrow = nDeadline - nBottom;
            if (nDist > nMaxGrow)
            {
                nDist = nMaxGrow;
                reason = SwResizeLimitReason::FlowToFollow;
            }
        }
        return nDist;
    }
 
    const SwRect aOld( GetObjRectWithSpaces() );
    InvalidateSize_();
    const bool bOldLock = m_bLocked;
    Unlock();
    if ( IsFlyFreeFrame() )
    {
        // #i37068# - no format of position here
        // and prevent move in method <CheckClip(..)>.
        // This is needed to prevent layout loop caused by nested
        // Writer fly frames - inner Writer fly frames format its
        // anchor, which grows/shrinks the outer Writer fly frame.
        // Note: position will be invalidated below.
        setFrameAreaPositionValid(true);
 
        // #i55416#
        // Suppress format of width for autowidth frame, because the
        // format of the width would call <SwTextFrame::CalcFitToContent()>
        // for the lower frame, which initiated this grow.
        const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
        const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
        if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
        {
            m_bFormatHeightOnly = true;
        }
        SwViewShell* pSh = getRootFrame()->GetCurrShell();
        if (pSh)
        {
            static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
            static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(pSh->GetOut());
            static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
        }
        // #i55416#
        if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
        {
            m_bFormatHeightOnly = bOldFormatHeightOnly;
        }
    }
    else
        MakeAll(getRootFrame()->GetCurrShell()->GetOut());
    InvalidateSize_();
    InvalidatePos();
    if ( bOldLock )
        Lock();
    SwRect aNew(GetObjRectWithSpaces());
    if (IsFlySplitAllowed() && aNew.Height() - aOld.Height() < nDist)
    {
        // We are allowed to split and the actual growth is less than the requested growth.
        const SwFrame* pAnchor = GetAnchorFrame();
        if (SwFrame* pAnchorChar = FindAnchorCharFrame())
        {
            pAnchor = pAnchorChar;
        }
        if (pAnchor)
        {
            SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
            SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
            SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea());
            SwTwips nMaxGrow = nDeadline - nBottom;
            if (nDist > nMaxGrow)
            {
                // The requested growth is more than what we can provide, limit it.
                nDist = nMaxGrow;
                reason = SwResizeLimitReason::FlowToFollow;
            }
            // Grow & invalidate the size.
            SwTwips nRemaining = nDist - (aNew.Height() - aOld.Height());
            {
                SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                aRectFnSet.AddBottom(aFrm, nRemaining);
            }
            InvalidateObjRectWithSpaces();
            {
                // Margins are unchanged, so increase the print height similar to the frame
                // height.
                SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                aRectFnSet.AddBottom(aPrt, nRemaining );
            }
            aNew = GetObjRectWithSpaces();
        }
    }
    if ( aOld != aNew )
        ::Notify( this, FindPageFrame(), aOld );
    return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
}
 
SwTwips SwFlyFrame::Shrink_( SwTwips nDist, bool bTst )
{
    if( Lower() && !IsColLocked() && !HasFixSize() )
    {
        SwRectFnSet aRectFnSet(this);
        SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
        if ( nDist > nHeight )
            nDist = nHeight;
 
        SwTwips nVal = nDist;
        if ( IsMinHeight() )
        {
            const SwFormatFrameSize& rFormatSize = GetFormat()->GetFrameSize();
            SwTwips nFormatHeight = aRectFnSet.IsVert() ? rFormatSize.GetWidth() : rFormatSize.GetHeight();
 
            nVal = std::min( nDist, nHeight - nFormatHeight );
        }
 
        if ( nVal <= 0 )
            return 0;
 
        if ( Lower()->IsColumnFrame() )
        {   // If it's a Column Frame, the Format takes control of the
            // resizing (due to the adjustment).
            if ( !bTst )
            {
                SwRect aOld( GetObjRectWithSpaces() );
 
                {
                    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
                    aRectFnSet.SetHeight( aFrm, nHeight - nVal );
                }
 
                // #i68520#
                if ( nHeight - nVal != 0 )
                {
                    InvalidateObjRectWithSpaces();
                }
 
                nHeight = aRectFnSet.GetHeight(getFramePrintArea());
 
                {
                    SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
                    aRectFnSet.SetHeight( aPrt, nHeight - nVal );
                }
 
                InvalidatePos_();
                InvalidateSize();
                ::Notify( this, FindPageFrame(), aOld );
                NotifyDrawObj();
                if ( GetAnchorFrame()->IsInFly() )
                    AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
            }
            return 0;
        }
 
        if ( !bTst )
        {
            const SwRect aOld( GetObjRectWithSpaces() );
            InvalidateSize_();
            const bool bOldLocked = m_bLocked;
            Unlock();
            if ( IsFlyFreeFrame() )
            {
                // #i37068# - no format of position here
                // and prevent move in method <CheckClip(..)>.
                // This is needed to prevent layout loop caused by nested
                // Writer fly frames - inner Writer fly frames format its
                // anchor, which grows/shrinks the outer Writer fly frame.
                // Note: position will be invalidated below.
                setFrameAreaPositionValid(true);
 
                // #i55416#
                // Suppress format of width for autowidth frame, because the
                // format of the width would call <SwTextFrame::CalcFitToContent()>
                // for the lower frame, which initiated this shrink.
                const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
                const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
                if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
                {
                    m_bFormatHeightOnly = true;
                }
                static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
                static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
                static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
                // #i55416#
                if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
                {
                    m_bFormatHeightOnly = bOldFormatHeightOnly;
                }
            }
            else
                MakeAll(getRootFrame()->GetCurrShell()->GetOut());
            InvalidateSize_();
            InvalidatePos();
            if ( bOldLocked )
                Lock();
            const SwRect aNew( GetObjRectWithSpaces() );
            if ( aOld != aNew )
            {
                ::Notify( this, FindPageFrame(), aOld );
                if ( GetAnchorFrame()->IsInFly() )
                    AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
            }
            return aRectFnSet.GetHeight(aOld) -
                   aRectFnSet.GetHeight(aNew);
        }
        return nVal;
    }
    return 0;
}
 
bool SwFlyFrame::IsResizeValid(const SwBorderAttrs *pAttrs, Size aTargetSize)
{
    SwFormatFrameSize rFrameSz = GetFormat()->GetFrameSize();
    Size aFrameSize = rFrameSz.GetSize();
    bool bAutosizeHeight = !HasFixSize() && IsMinHeight();
    bool bAutosizeWidth =  !m_bFormatHeightOnly && rFrameSz.GetWidthSizeType() == SwFrameSize::Minimum;
 
    if (!bAutosizeHeight && !bAutosizeWidth)
        return true;
 
    bool bIsValidResize = true;
 
    /**
    if (either AutoSizeWidth or AutoSizeHeight, not both),
        if the autosize dimension goes smaller than min value and the other dimension changed
            return valid
        else
            remember invalid
    */
    tools::Long nMinFrameHeight = 0;
    if (bAutosizeHeight)
    {
        const SwTwips nUL = pAttrs->CalcTopLine()  + pAttrs->CalcBottomLine();
        rFrameSz.SetHeight(aTargetSize.Height());
        rFrameSz.SetWidth(aTargetSize.Width());
        Size aRelSize( CalcRel( rFrameSz ) );
 
        tools::Long nMinHeight = 0;
        SwRectFnSet aRectFnSet(this);
        nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
        SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL);
        nMinFrameHeight = nRemaining + nUL;
 
        if (nMinHeight < nMinFrameHeight)
        {
            bIsValidResize = false;
            // if height less than minHeight and width changed when not AutoSizeWidth
            if (!bAutosizeWidth && aTargetSize.Width() != aFrameSize.Width())
                return true;
        }
    }
 
    tools::Long nMinFrameWidth = 0;
    if (bAutosizeWidth)
    {
        const SwTwips nLR = pAttrs->CalcLeftLine() + pAttrs->CalcRightLine();
        const SwTwips nAutoWidth = lcl_CalcAutoWidth( *this );
        nMinFrameWidth = nAutoWidth + nLR;
 
        if (aTargetSize.Width() < nMinFrameWidth)
            bIsValidResize = false;
        if (!bAutosizeHeight && aTargetSize.Height() != aFrameSize.Height())
            return true;
    }
 
    // if not valid resize, and both AutoSizeWidth and AutoSizeHeight,
    // then consider resize is valid if any one of the dimensions was changed from its original size
    // (the frame's dimensions), and the destination dimension is a valid one.
    if (bAutosizeWidth && bAutosizeHeight && !bIsValidResize)
    {
        return (aTargetSize.Width() != aFrameSize.Width() && aTargetSize.Width() >= nMinFrameWidth) ||
            (aTargetSize.Height() != aFrameSize.Height() && aTargetSize.Height() >= nMinFrameHeight);
    }
 
    return bIsValidResize;
}
 
Size SwFlyFrame::ChgSize( const Size& aNewSize )
{
    // #i53298#
    // If the fly frame anchored at-paragraph or at-character contains an OLE
    // object, assure that the new size fits into the current clipping area
    // of the fly frame
    Size aAdjustedNewSize( aNewSize );
    {
        if ( dynamic_cast<SwFlyAtContentFrame*>(this) &&
             Lower() && dynamic_cast<SwNoTextFrame*>(Lower()) &&
             static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() )
        {
            SwRect aClipRect;
            ::CalcClipRect( GetVirtDrawObj(), aClipRect, false );
            if ( aAdjustedNewSize.Width() > aClipRect.Width() )
            {
                aAdjustedNewSize.setWidth( aClipRect.Width() );
            }
            if ( aAdjustedNewSize.Height() > aClipRect.Height() )
            {
                aAdjustedNewSize.setWidth( aClipRect.Height() );
            }
        }
    }
 
    if ( aAdjustedNewSize != getFrameArea().SSize() )
    {
        SwFrameFormat *pFormat = GetFormat();
        SwFormatFrameSize aSz( pFormat->GetFrameSize() );
        aSz.SetWidth( aAdjustedNewSize.Width() );
        aSz.SetHeight( aAdjustedNewSize.Height() );
        // go via the Doc for UNDO
        pFormat->GetDoc()->SetAttr( aSz, *pFormat );
        return aSz.GetSize();
    }
    else
        return getFrameArea().SSize();
}
 
bool SwFlyFrame::IsLowerOf( const SwLayoutFrame* pUpperFrame ) const
{
    OSL_ENSURE( GetAnchorFrame(), "8-( Fly is lost in Space." );
    const SwFrame* pFrame = GetAnchorFrame();
    do
    {
        if ( pFrame == pUpperFrame )
            return true;
        pFrame = pFrame->IsFlyFrame()
               ? static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame()
               : pFrame->GetUpper();
    } while ( pFrame );
    return false;
}
 
void SwFlyFrame::Cut()
{
}
 
void SwFrame::AppendFly( SwFlyFrame *pNew )
{
    if (!m_pDrawObjs)
    {
        m_pDrawObjs.reset(new SwSortedObjs());
    }
    m_pDrawObjs->Insert( *pNew );
    pNew->ChgAnchorFrame( this );
 
    // Register at the page
    // If there's none present, register via SwPageFrame::PreparePage
    SwPageFrame* pPage = FindPageFrame();
    if ( pPage != nullptr )
    {
        pPage->AppendFlyToPage( pNew );
    }
}
 
void SwFrame::RemoveFly( SwFlyFrame *pToRemove )
{
    // Deregister from the page
    // Could already have happened, if the page was already destructed
    SwPageFrame *pPage = pToRemove->FindPageFrame();
    if ( pPage && pPage->GetSortedObjs() )
    {
        pPage->RemoveFlyFromPage( pToRemove );
    }
    // #i73201#
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    else
    {
        if ( pToRemove->IsAccessibleFrame() &&
             pToRemove->GetFormat() &&
             !pToRemove->IsFlyInContentFrame() )
        {
            SwRootFrame *pRootFrame = getRootFrame();
            if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
            {
                SwViewShell *pVSh = pRootFrame->GetCurrShell();
                if( pVSh && pVSh->Imp() )
                {
                    pVSh->Imp()->DisposeAccessibleFrame( pToRemove );
                }
            }
        }
    }
#endif
 
    m_pDrawObjs->Remove(*pToRemove);
    if (!m_pDrawObjs->size())
    {
        m_pDrawObjs.reset();
    }
 
    pToRemove->ChgAnchorFrame( nullptr );
 
    if ( !pToRemove->IsFlyInContentFrame() && GetUpper() && IsInTab() )//MA_FLY_HEIGHT
        GetUpper()->InvalidateSize();
}
 
void SwFrame::AppendDrawObj( SwAnchoredObject& _rNewObj )
{
    assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
 
    if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) ==  nullptr )
    {
        OSL_FAIL( "SwFrame::AppendDrawObj(..) - anchored object of unexpected type -> object not appended" );
        return;
    }
 
    if ( dynamic_cast<const SwDrawVirtObj*>(_rNewObj.GetDrawObj()) ==  nullptr &&
         _rNewObj.GetAnchorFrame() && _rNewObj.GetAnchorFrame() != this )
    {
        assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
        // perform disconnect from layout, if 'master' drawing object is appended
        // to a new frame.
        if (SwDrawContact* pContact = static_cast<SwDrawContact*>(::GetUserCall( _rNewObj.GetDrawObj() )))
            pContact->DisconnectFromLayout( false );
        assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
    }
 
    if ( _rNewObj.GetAnchorFrame() != this )
    {
        if (!m_pDrawObjs)
        {
            m_pDrawObjs.reset(new SwSortedObjs());
        }
        m_pDrawObjs->Insert(_rNewObj);
        _rNewObj.ChgAnchorFrame( this );
    }
 
    // #i113730#
    // Assure the control objects and group objects containing controls are on the control layer
    if ( ::CheckControlLayer( _rNewObj.DrawObj() ) )
    {
        const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
        const SdrLayerID aCurrentLayer(_rNewObj.DrawObj()->GetLayer());
        const SdrLayerID aControlLayerID(rIDDMA.GetControlsId());
        const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId());
 
        if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID)
        {
            if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() ||
                 aCurrentLayer == rIDDMA.GetInvisibleHeavenId() )
            {
                _rNewObj.DrawObj()->SetLayer(aInvisibleControlLayerID);
            }
            else
            {
                _rNewObj.DrawObj()->SetLayer(aControlLayerID);
            }
            //The layer is part of the key used to sort the obj, so update
            //its position if the layer changed.
            m_pDrawObjs->Update(_rNewObj);
        }
    }
 
    // no direct positioning needed, but invalidate the drawing object position
    _rNewObj.InvalidateObjPos();
 
    // register at page frame
    SwPageFrame* pPage = FindPageFrame();
    if ( pPage )
    {
        pPage->AppendDrawObjToPage( _rNewObj );
    }
 
    // Notify accessible layout.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    SwViewShell* pSh = getRootFrame()->GetCurrShell();
    if( pSh )
    {
        SwRootFrame* pLayout = getRootFrame();
        if( pLayout && pLayout->IsAnyShellAccessible() )
        {
            pSh->Imp()->AddAccessibleObj( _rNewObj.GetDrawObj() );
        }
    }
#endif
 
    assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
}
 
void SwFrame::RemoveDrawObj( SwAnchoredObject& _rToRemoveObj )
{
    // Notify accessible layout.
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if (!mbInDtor)
    {
        SwViewShell* pSh = getRootFrame()->GetCurrShell();
        if (pSh)
        {
            SwRootFrame* pLayout = getRootFrame();
            if (pLayout && pLayout->IsAnyShellAccessible())
                pSh->Imp()->DisposeAccessibleObj(_rToRemoveObj.GetDrawObj(), false);
        }
    }
#endif
 
    // deregister from page frame
    SwPageFrame* pPage = _rToRemoveObj.GetPageFrame();
    if ( pPage && pPage->GetSortedObjs() )
        pPage->RemoveDrawObjFromPage( _rToRemoveObj );
 
    m_pDrawObjs->Remove(_rToRemoveObj);
    if (!m_pDrawObjs->size())
    {
        m_pDrawObjs.reset();
    }
    _rToRemoveObj.ChgAnchorFrame( nullptr );
 
    assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
}
 
void SwFrame::InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs )
{
    if ( !GetDrawObjs() )
        return;
 
    // #i26945# - determine page the frame is on,
    // in order to check, if anchored object is registered at the same
    // page.
    const SwPageFrame* pPageFrame = FindPageFrame();
    // #i28701# - re-factoring
    for (SwAnchoredObject* pAnchoredObj : *GetDrawObjs())
    {
        if ( _bNoInvaOfAsCharAnchoredObjs &&
             (pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
                == RndStdIds::FLY_AS_CHAR) )
        {
            continue;
        }
        // #i26945# - no invalidation, if anchored object
        // isn't registered at the same page and instead is registered at
        // the page, where its anchor character text frame is on.
        if ( pAnchoredObj->GetPageFrame() &&
             pAnchoredObj->GetPageFrame() != pPageFrame )
        {
            SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
            if ( pAnchorCharFrame &&
                 pAnchoredObj->GetPageFrame() == pAnchorCharFrame->FindPageFrame() )
            {
                continue;
            }
            // #115759# - unlock its position, if anchored
            // object isn't registered at the page, where its anchor
            // character text frame is on, respectively if it has no
            // anchor character text frame.
            else
            {
                pAnchoredObj->UnlockPosition();
            }
        }
        // #i51474# - reset flag, that anchored object
        // has cleared environment, and unlock its position, if the anchored
        // object is registered at the same page as the anchor frame is on.
        if ( pAnchoredObj->ClearedEnvironment() &&
             pAnchoredObj->GetPageFrame() &&
             pAnchoredObj->GetPageFrame() == pPageFrame )
        {
            pAnchoredObj->UnlockPosition();
            pAnchoredObj->SetClearedEnvironment( false );
        }
        // distinguish between writer fly frames and drawing objects
        if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
        {
            pFly->Invalidate_();
            pFly->InvalidatePos_();
        }
        else
        {
            pAnchoredObj->InvalidateObjPos();
        }
    } // end of loop on objects, which are connected to the frame
}
 
// #i26945# - correct check, if anchored object is a lower
// of the layout frame. E.g., anchor character text frame can be a follow text
// frame.
// #i44016# - add parameter <_bUnlockPosOfObjs> to
// force an unlockposition call for the lower objects.
void SwLayoutFrame::NotifyLowerObjs( const bool _bUnlockPosOfObjs )
{
    // invalidate lower floating screen objects
    SwPageFrame* pPageFrame = FindPageFrame();
    if ( !(pPageFrame && pPageFrame->GetSortedObjs()) )
        return;
 
    SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
    for (SwAnchoredObject* pObj : rObjs)
    {
        // #i26945# - check if anchored object is a lower
        // of the layout frame is changed to check, if its anchor frame
        // is a lower of the layout frame.
        // Determine the anchor frame - usually it's the anchor frame,
        // for at-character/as-character anchored objects the anchor character
        // text frame is taken.
        const SwFrame* pAnchorFrame = pObj->GetAnchorFrameContainingAnchPos();
        if ( auto pFly = pObj->DynCastFlyFrame() )
        {
            if ( pFly->getFrameArea().Left() == FAR_AWAY )
                continue;
 
            if ( pFly->IsAnLower( this ) )
                continue;
 
            // #i26945# - use <pAnchorFrame> to check, if
            // fly frame is lower of layout frame resp. if fly frame is
            // at a different page registered as its anchor frame is on.
            const bool bLow = IsAnLower( pAnchorFrame );
            if ( bLow || pAnchorFrame->FindPageFrame() != pPageFrame )
            {
                pFly->Invalidate_( pPageFrame );
                if ( !bLow || pFly->IsFlyAtContentFrame() )
                {
                    // #i44016#
                    if ( _bUnlockPosOfObjs )
                    {
                        pFly->UnlockPosition();
                    }
                    pFly->InvalidatePos_();
                }
                else
                    pFly->InvalidatePrt_();
            }
        }
        else
        {
            assert( dynamic_cast<const SwAnchoredDrawObject*>( pObj) &&
                    "<SwLayoutFrame::NotifyFlys() - anchored object of unexpected type" );
            // tdf#156728 invalidate fly positioned dependent on header/footer size
            bool isPositionedByHF(false);
            if (IsHeaderFrame() || IsFooterFrame())
            {
                auto const nO(pObj->GetFrameFormat()->GetVertOrient().GetRelationOrient());
                if (nO == text::RelOrientation::PAGE_PRINT_AREA
                    || nO == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM
                    || nO == text::RelOrientation::PAGE_PRINT_AREA_TOP)
                {
                    isPositionedByHF = true;
                }
            }
            // #i26945# - use <pAnchorFrame> to check, if
            // fly frame is lower of layout frame resp. if fly frame is
            // at a different page registered as its anchor frame is on.
            if ( IsAnLower( pAnchorFrame ) ||
                 isPositionedByHF ||
                 pAnchorFrame->FindPageFrame() != pPageFrame )
            {
                // #i44016#
                if ( _bUnlockPosOfObjs )
                {
                    pObj->UnlockPosition();
                }
                pObj->InvalidateObjPos();
            }
        }
    }
}
 
void SwFlyFrame::NotifyDrawObj()
{
    SwVirtFlyDrawObj* pObj = GetVirtDrawObj();
    pObj->SetRect();
    pObj->SetBoundAndSnapRectsDirty();
    pObj->SetChanged();
    pObj->BroadcastObjectChange();
 
    if ( GetFormat()->GetSurround().IsContour() )
    {
        ClrContourCache( pObj );
    }
    else if(IsFlyFreeFrame() && static_cast< const SwFlyFreeFrame* >(this)->supportsAutoContour())
    {
        // RotateFlyFrame3: Also need to clear when changes happen
        // Caution: isTransformableSwFrame is already reset when resetting rotation, so
        // *additionally* reset in SwFlyFreeFrame::MakeAll when no more rotation
        ClrContourCache( pObj );
    }
}
 
Size SwFlyFrame::CalcRel( const SwFormatFrameSize &rSz ) const
{
    Size aRet( rSz.GetSize() );
 
    const SwFrame *pRel = IsFlyLayFrame() ? GetAnchorFrame() : GetAnchorFrame()->GetUpper();
    if( pRel ) // LAYER_IMPL
    {
        tools::Long nRelWidth = LONG_MAX, nRelHeight = LONG_MAX;
        const SwViewShell *pSh = getRootFrame()->GetCurrShell();
        if ( ( pRel->IsBodyFrame() || pRel->IsPageFrame() ) &&
             pSh && pSh->GetViewOptions()->getBrowseMode() &&
             pSh->VisArea().HasArea() )
        {
            nRelWidth  = pSh->GetBrowseWidth();
            nRelHeight = pSh->VisArea().Height();
            Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
            nRelWidth  = std::min( nRelWidth,  pRel->getFramePrintArea().Width() );
            nRelHeight -= 2*aBorder.Height();
            nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
        }
 
        // At the moment only the "== PAGE_FRAME" and "!= PAGE_FRAME" cases are handled.
        // When size is a relative to page size, ignore size of SwBodyFrame.
        if (rSz.GetWidthPercentRelation() != text::RelOrientation::PAGE_FRAME)
            nRelWidth  = std::min( nRelWidth,  pRel->getFramePrintArea().Width() );
        else if ( pRel->IsPageFrame() )
            nRelWidth  = std::min( nRelWidth,  pRel->getFrameArea().Width() );
 
        if (rSz.GetHeightPercentRelation() != text::RelOrientation::PAGE_FRAME)
            nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
        else if ( pRel->IsPageFrame() )
            nRelHeight = std::min( nRelHeight, pRel->getFrameArea().Height() );
 
        if( !pRel->IsPageFrame() )
        {
            const SwPageFrame* pPage = FindPageFrame();
            if( pPage )
            {
                if (rSz.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME)
                    // Ignore margins of pPage.
                    nRelWidth  = std::min( nRelWidth,  pPage->getFrameArea().Width() );
                else
                    nRelWidth  = std::min( nRelWidth,  pPage->getFramePrintArea().Width() );
                if (rSz.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME)
                    // Ignore margins of pPage.
                    nRelHeight = std::min( nRelHeight, pPage->getFrameArea().Height() );
                else
                    nRelHeight = std::min( nRelHeight, pPage->getFramePrintArea().Height() );
            }
        }
 
        if ( rSz.GetWidthPercent() && rSz.GetWidthPercent() != SwFormatFrameSize::SYNCED )
            aRet.setWidth(rtl::math::round(double(nRelWidth) * rSz.GetWidthPercent() / 100));
        if ( rSz.GetHeightPercent() && rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED )
            aRet.setHeight(rtl::math::round(double(nRelHeight) * rSz.GetHeightPercent() / 100));
 
        if ( rSz.GetHeight() && rSz.GetWidthPercent() == SwFormatFrameSize::SYNCED )
        {
            aRet.setWidth( aRet.Width() * ( aRet.Height()) );
            aRet.setWidth( aRet.Width() / ( rSz.GetHeight()) );
        }
        else if ( rSz.GetWidth() && rSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
        {
            aRet.setHeight( aRet.Height() * ( aRet.Width()) );
            aRet.setHeight( aRet.Height() / ( rSz.GetWidth()) );
        }
    }
    return aRet;
}
 
static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame )
{
    SwTwips nRet = 0;
    SwTwips nMin = 0;
    const SwFrame* pFrame = rFrame.Lower();
 
    // No autowidth defined for columned frames
    if ( !pFrame || pFrame->IsColumnFrame() )
        return nRet;
 
    int nParagraphCount = 0;
    while ( pFrame )
    {
        nParagraphCount++;
        if ( pFrame->IsSctFrame() )
        {
            nMin = lcl_CalcAutoWidth( *static_cast<const SwSectionFrame*>(pFrame) );
        }
        if ( pFrame->IsTextFrame() )
        {
            nMin = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent();
            auto const& rParaSet(static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet());
            SvxFirstLineIndentItem const& rFirstLine(rParaSet.GetFirstLineIndent());
            SvxTextLeftMarginItem const& rLeftMargin(rParaSet.GetTextLeftMargin());
            SvxRightMarginItem const& rRightMargin(rParaSet.GetRightMargin());
            if (!static_cast<const SwTextFrame*>(pFrame)->IsLocked())
            {
                nMin += rRightMargin.GetRight() + rLeftMargin.GetTextLeft()
                        + rFirstLine.ResolveTextFirstLineOffset({});
            }
        }
        else if ( pFrame->IsTabFrame() )
        {
            const SwFormatFrameSize& rTableFormatSz = static_cast<const SwTabFrame*>(pFrame)->GetTable()->GetFrameFormat()->GetFrameSize();
            if ( USHRT_MAX == rTableFormatSz.GetSize().Width() ||
                 text::HoriOrientation::NONE == static_cast<const SwTabFrame*>(pFrame)->GetFormat()->GetHoriOrient().GetHoriOrient() )
            {
                const SwPageFrame* pPage = rFrame.FindPageFrame();
                // auto width table
                nMin = pFrame->GetUpper()->IsVertical() ?
                    pPage->getFramePrintArea().Height() :
                    pPage->getFramePrintArea().Width();
            }
            else
            {
                nMin = rTableFormatSz.GetSize().Width();
            }
        }
 
        if ( nMin > nRet )
            nRet = nMin;
 
        pFrame = pFrame->GetNext();
    }
 
    // tdf#124423 In Microsoft compatibility mode: widen the frame to max (PrintArea of the frame it anchored to) if it contains at least 2 paragraphs,
    // or 1 paragraph wider than its parent area.
    if (rFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA))
    {
        const SwFrame* pFrameRect = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame*>(&rFrame)->GetAnchorFrame() : rFrame.Lower()->FindPageFrame();
        SwTwips nParentWidth = rFrame.IsVertical() ? pFrameRect->getFramePrintArea().Height() : pFrameRect->getFramePrintArea().Width();
        if (nParagraphCount > 1 || nRet > nParentWidth)
        {
            return nParentWidth;
        }
    }
 
    return nRet;
}
 
///  #i13147# -  If called for paint and the <SwNoTextFrame> contains
/// a graphic, load of intrinsic graphic has to be avoided.
bool SwFlyFrame::GetContour( tools::PolyPolygon&   rContour,
                           const bool _bForPaint ) const
{
    vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
    bool bRet = false;
    const bool bIsCandidate(Lower() && Lower()->IsNoTextFrame());
 
    if(bIsCandidate)
    {
        if(GetFormat()->GetSurround().IsContour())
        {
            SwNoTextNode *pNd = const_cast<SwNoTextNode*>(static_cast<const SwNoTextNode*>(static_cast<const SwNoTextFrame*>(Lower())->GetNode()));
            // #i13147# - determine <GraphicObject> instead of <Graphic>
            // in order to avoid load of graphic, if <SwNoTextNode> contains a graphic
            // node and method is called for paint.
            std::unique_ptr<GraphicObject> xTmpGrfObj;
            const GraphicObject* pGrfObj = nullptr;
            const SwGrfNode* pGrfNd = pNd->GetGrfNode();
            if ( pGrfNd && _bForPaint )
            {
                pGrfObj = &(pGrfNd->GetGrfObj());
            }
            else
            {
                xTmpGrfObj.reset(new GraphicObject(pNd->GetGraphic()));
                pGrfObj = xTmpGrfObj.get();
            }
            assert(pGrfObj && "SwFlyFrame::GetContour() - No Graphic/GraphicObject found at <SwNoTextNode>.");
            if (pGrfObj->GetType() != GraphicType::NONE)
            {
                if( !pNd->HasContour() )
                {
                    //#i13147# - no <CreateContour> for a graphic
                    // during paint. Thus, return (value of <bRet> should be <false>).
                    if ( pGrfNd && _bForPaint )
                    {
                        OSL_FAIL( "SwFlyFrame::GetContour() - No Contour found at <SwNoTextNode> during paint." );
                        return bRet;
                    }
                    pNd->CreateContour();
                }
                pNd->GetContour( rContour );
                // The Node holds the Polygon matching the original size of the graphic
                // We need to include the scaling here
                SwRect aClip;
                SwRect aOrig;
                Lower()->Calc(pRenderContext);
                static_cast<const SwNoTextFrame*>(Lower())->GetGrfArea( aClip, &aOrig );
                // #i13147# - copy method code <SvxContourDlg::ScaleContour(..)>
                // in order to avoid that graphic has to be loaded for contour scale.
                //SvxContourDlg::ScaleContour( rContour, aGrf, MapUnit::MapTwip, aOrig.SSize() );
                {
                    OutputDevice*   pOutDev = Application::GetDefaultDevice();
                    const MapMode   aDispMap( MapUnit::MapTwip );
                    const MapMode   aGrfMap( pGrfObj->GetPrefMapMode() );
                    const Size      aGrfSize( pGrfObj->GetPrefSize() );
                    Size            aOrgSize;
                    Point           aNewPoint;
                    bool            bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
 
                    if ( bPixelMap )
                        aOrgSize = pOutDev->PixelToLogic( aGrfSize, aDispMap );
                    else
                        aOrgSize = OutputDevice::LogicToLogic( aGrfSize, aGrfMap, aDispMap );
 
                    if ( aOrgSize.Width() && aOrgSize.Height() )
                    {
                        double fScaleX = static_cast<double>(aOrig.Width()) / aOrgSize.Width();
                        double fScaleY = static_cast<double>(aOrig.Height()) / aOrgSize.Height();
 
                        for ( sal_uInt16 j = 0, nPolyCount = rContour.Count(); j < nPolyCount; j++ )
                        {
                            tools::Polygon& rPoly = rContour[ j ];
 
                            for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
                            {
                                if ( bPixelMap )
                                    aNewPoint = pOutDev->PixelToLogic( rPoly[ i ], aDispMap  );
                                else
                                    aNewPoint = OutputDevice::LogicToLogic( rPoly[ i ], aGrfMap, aDispMap  );
 
                                rPoly[ i ] = Point( basegfx::fround<tools::Long>( aNewPoint.getX() * fScaleX ), basegfx::fround<tools::Long>( aNewPoint.getY() * fScaleY ) );
                            }
                        }
                    }
                }
                // destroy created <GraphicObject>.
                xTmpGrfObj.reset();
                rContour.Move( aOrig.Left(), aOrig.Top() );
                if( !aClip.Width() )
                    aClip.Width( 1 );
                if( !aClip.Height() )
                    aClip.Height( 1 );
                rContour.Clip( aClip.SVRect() );
                rContour.Optimize(PolyOptimizeFlags::CLOSE);
                bRet = true;
            }
        }
        else if (IsFlyFreeFrame())
        {
            const SwFlyFreeFrame* pSwFlyFreeFrame(static_cast< const SwFlyFreeFrame* >(this));
 
            if(nullptr != pSwFlyFreeFrame &&
                pSwFlyFreeFrame->supportsAutoContour() &&
                // isTransformableSwFrame already used in supportsAutoContour(), but
                // better check twice when it may get changed there...
                pSwFlyFreeFrame->isTransformableSwFrame())
            {
                // RotateFlyFrame: use untransformed SwFrame to allow text floating around.
                // Will be transformed below
                const TransformableSwFrame* pTransformableSwFrame(pSwFlyFreeFrame->getTransformableSwFrame());
                const SwRect aFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
                rContour = tools::PolyPolygon(tools::Polygon(aFrameArea.SVRect()));
                bRet = (0 != rContour.Count());
            }
        }
 
        if(bRet && 0 != rContour.Count())
        {
            if (IsFlyFreeFrame() &&
                static_cast< const SwFlyFreeFrame* >(this)->isTransformableSwFrame())
            {
                // Need to adapt contour to transformation
                basegfx::B2DVector aScale, aTranslate;
                double fRotate, fShearX;
                getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
 
                if(!basegfx::fTools::equalZero(fRotate))
                {
                    basegfx::B2DPolyPolygon aSource(rContour.getB2DPolyPolygon());
                    const basegfx::B2DPoint aCenter(getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
                    const basegfx::B2DHomMatrix aRotateAroundCenter(
                        basegfx::utils::createRotateAroundPoint(
                            aCenter.getX(),
                            aCenter.getY(),
                            fRotate));
                    aSource.transform(aRotateAroundCenter);
                    rContour = tools::PolyPolygon(aSource);
                }
            }
        }
    }
 
    return bRet;
}
 
 
const SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj() const
{
    return static_cast<const SwVirtFlyDrawObj*>(GetDrawObj());
}
SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj()
{
    return static_cast<SwVirtFlyDrawObj*>(DrawObj());
}
 
// implementation of pure virtual method declared in
// base class <SwAnchoredObject>
 
void SwFlyFrame::InvalidateObjPos()
{
    InvalidatePos();
    // #i68520#
    InvalidateObjRectWithSpaces();
}
 
SwFrameFormat* SwFlyFrame::GetFrameFormat()
{
    OSL_ENSURE( GetFormat(),
            "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
    return GetFormat();
}
const SwFrameFormat* SwFlyFrame::GetFrameFormat() const
{
    OSL_ENSURE( GetFormat(),
            "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
    return GetFormat();
}
 
SwRect SwFlyFrame::GetObjRect() const
{
    return getFrameArea();
}
 
// #i70122#
// for Writer fly frames the bounding rectangle equals the object rectangles
SwRect SwFlyFrame::GetObjBoundRect() const
{
    return GetObjRect();
}
 
// #i68520#
bool SwFlyFrame::SetObjTop_( const SwTwips _nTop )
{
    const bool bChanged( getFrameArea().Pos().getY() != _nTop );
    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
    aFrm.Pos().setY(_nTop);
 
    return bChanged;
}
bool SwFlyFrame::SetObjLeft_( const SwTwips _nLeft )
{
    const bool bChanged( getFrameArea().Pos().getX() != _nLeft );
    SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
    aFrm.Pos().setX(_nLeft);
 
    return bChanged;
}
 
/** method to assure that anchored object is registered at the correct
    page frame
 
    OD 2004-07-02 #i28701#
*/
void SwFlyFrame::RegisterAtCorrectPage()
{
    // default behaviour is to do nothing.
}
 
void SwFlyFrame::RegisterAtPage(SwPageFrame &)
{
    // default behaviour is to do nothing.
}
 
/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
 
    OD 2004-05-11 #i28701#
*/
bool SwFlyFrame::IsFormatPossible() const
{
    return SwAnchoredObject::IsFormatPossible() &&
           !IsLocked() && !IsColLocked();
}
 
void SwFlyFrame::GetAnchoredObjects( std::vector<SwAnchoredObject*>& aVector, const SwFormat& rFormat )
{
    SwIterator<SwFlyFrame,SwFormat> aIter( rFormat );
    for( SwFlyFrame* pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next() )
        aVector.push_back( pFlyFrame );
}
 
const SwFlyFrameFormat * SwFlyFrame::GetFormat() const
{
    return static_cast< const SwFlyFrameFormat * >( GetDep() );
}
 
SwFlyFrameFormat * SwFlyFrame::GetFormat()
{
    return static_cast< SwFlyFrameFormat * >( GetDep() );
}
 
void SwFlyFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("fly"));
    dumpAsXmlAttributes(writer);
 
    SwLayoutFrame::dumpAsXml(writer);
 
    SwAnchoredObject::dumpAsXml(writer);
 
    (void)xmlTextWriterEndElement(writer);
}
 
void SwFlyFrame::Calc(vcl::RenderContext* pRenderContext) const
{
    if ( !m_bValidContentPos )
        const_cast<SwFlyFrame*>(this)->PrepareMake(pRenderContext);
    else
        SwLayoutFrame::Calc(pRenderContext);
}
 
SwTwips SwFlyFrame::CalcContentHeight(const SwBorderAttrs *pAttrs, const SwTwips nMinHeight, const SwTwips nUL)
{
    SwRectFnSet aRectFnSet(this);
    SwTwips nHeight = 0;
    if ( Lower() )
    {
        if ( Lower()->IsColumnFrame() )
        {
            FormatWidthCols( *pAttrs, nUL, nMinHeight );
            nHeight = aRectFnSet.GetHeight(Lower()->getFrameArea());
        }
        else
        {
            SwFrame *pFrame = Lower();
            while ( pFrame )
            {
                nHeight += aRectFnSet.GetHeight(pFrame->getFrameArea());
                if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
                // This TextFrame would like to be a bit larger
                    nHeight += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
                            - aRectFnSet.GetHeight(pFrame->getFramePrintArea());
                else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
                    nHeight += static_cast<SwSectionFrame*>(pFrame)->Undersize();
                pFrame = pFrame->GetNext();
            }
        }
        if ( GetDrawObjs() )
        {
            const size_t nCnt = GetDrawObjs()->size();
            SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
            SwTwips nBorder = aRectFnSet.GetHeight(getFrameArea()) -
            aRectFnSet.GetHeight(getFramePrintArea());
            for ( size_t i = 0; i < nCnt; ++i )
            {
                SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
                if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
                {
                    // consider only Writer fly frames, which follow the text flow.
                    if ( pFly->IsFlyLayFrame() &&
                        pFly->getFrameArea().Top() != FAR_AWAY &&
                        pFly->GetFormat()->GetFollowTextFlow().GetValue() )
                    {
                        SwTwips nDist = -aRectFnSet.BottomDist( pFly->getFrameArea(), nTop );
                        if( nDist > nBorder + nHeight )
                            nHeight = nDist - nBorder;
                    }
                }
            }
        }
    }
    return nHeight;
}
 
const SwFormatAnchor* SwFlyFrame::GetAnchorFromPoolItem(const SfxPoolItem& rItem)
{
    switch(rItem.Which())
    {
        case RES_ATTRSET_CHG:
            return rItem.StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItem(RES_ANCHOR, false);
        case RES_ANCHOR:
            return static_cast<const SwFormatAnchor*>(&rItem);
        default:
            return nullptr;
    }
}
 
const SwFlyFrame* SwFlyFrame::DynCastFlyFrame() const
{
    return this;
}
 
SwFlyFrame* SwFlyFrame::DynCastFlyFrame()
{
    return this;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V522 Dereferencing of the null pointer 'pAnchoredObj' might take place.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V1053 Calling the 'GetFormat' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'fly.cxx:229', 'fly.cxx:305', 'flyfrm.hxx:287'.
↑ V560 A part of conditional expression is always true: nullptr != pSwFlyFreeFrame.
↑ V1004 The 'pTmpPrev' pointer was used unsafely after it was verified against nullptr. Check lines: 1709, 1716.