    /* -*- 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 <hintids.hxx>
    #include <svl/itemiter.hxx>
    #include <svx/svdobj.hxx>
    #include <svx/svdpage.hxx>
    #include <svx/svdmodel.hxx>
    #include <svx/svdocapt.hxx>
    #include <svx/svdmark.hxx>
    #include <fmtfsize.hxx>
    #include <fmtornt.hxx>
    #include <fmtsrnd.hxx>
    #include <dcontact.hxx>
    #include <ndgrf.hxx>
    #include <doc.hxx>
    #include <IDocumentUndoRedo.hxx>
    #include <IDocumentDrawModelAccess.hxx>
    #include <IDocumentState.hxx>
    #include <IDocumentLayoutAccess.hxx>
    #include <ndindex.hxx>
    #include <docary.hxx>
    #include <drawdoc.hxx>
    #include <fmtcntnt.hxx>
    #include <fmtanchr.hxx>
    #include <txtflcnt.hxx>
    #include <fmtflcnt.hxx>
    #include <txtfrm.hxx>
    #include <notxtfrm.hxx>
    #include <pagefrm.hxx>
    #include <rootfrm.hxx>
    #include <flyfrms.hxx>
    #include <textboxhelper.hxx>
    #include <frmtool.hxx>
    #include <frmfmt.hxx>
    #include <ndtxt.hxx>
    #include <pam.hxx>
    #include <tblsel.hxx>
    #include <swundo.hxx>
    #include <swtable.hxx>
    #include <crstate.hxx>
    #include <UndoCore.hxx>
    #include <UndoAttribute.hxx>
    #include <fmtcnct.hxx>
    #include <dflyobj.hxx>
    #include <undoflystrattr.hxx>
    #include <calbck.hxx>
    #include <memory>
    #include <svx/xbtmpit.hxx>
    #include <svx/xflftrit.hxx>
    #include <svx/xlndsit.hxx>
    #include <svx/xlnstit.hxx>
    #include <svx/xlnedit.hxx>
    #include <svx/xflhtit.hxx>

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

    size_t SwDoc::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
    {
        const SwFrameFormats& rFormats = *GetSpzFrameFormats();
        const size_t nSize = rFormats.size();
        size_t nCount = 0;
        const SwNodeIndex* pIdx;

        for ( size_t i = 0; i < nSize; ++i)
        {
            const SwFrameFormat* pFlyFormat = rFormats[ i ];

            if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
                continue;

            if( RES_FLYFRMFMT == pFlyFormat->Which()
                && nullptr != ( pIdx = pFlyFormat->GetContent().GetContentIdx() )
                && pIdx->GetNodes().IsDocNodes()
                )
            {
                const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];

                switch( eType )
                {
                case FLYCNTTYPE_FRM:
                    if(!pNd->IsNoTextNode())
                        nCount++;
                    break;

                case FLYCNTTYPE_GRF:
                    if( pNd->IsGrfNode() )
                        nCount++;
                    break;

                case FLYCNTTYPE_OLE:
                    if(pNd->IsOLENode())
                        nCount++;
                    break;

                default:
                    nCount++;
                }
            }
        }
        return nCount;
    }

    /// @attention If you change this, also update SwXFrameEnumeration in unocoll.
    SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes )
    {
        SwFrameFormats& rFormats = *GetSpzFrameFormats();
        SwFrameFormat* pRetFormat = nullptr;
        const size_t nSize = rFormats.size();
        const SwNodeIndex* pIdx;
        size_t nCount = 0;

        for( size_t i = 0; !pRetFormat && i < nSize; ++i )
        {
            SwFrameFormat* pFlyFormat = rFormats[ i ];

            if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
                continue;

            if( RES_FLYFRMFMT == pFlyFormat->Which()
                && nullptr != ( pIdx = pFlyFormat->GetContent().GetContentIdx() )
                && pIdx->GetNodes().IsDocNodes()
                )
            {
                const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
                switch( eType )
                {
                case FLYCNTTYPE_FRM:
                    if( !pNd->IsNoTextNode() && nIdx == nCount++)
                        pRetFormat = pFlyFormat;
                    break;
                case FLYCNTTYPE_GRF:
                    if(pNd->IsGrfNode() && nIdx == nCount++ )
                        pRetFormat = pFlyFormat;
                    break;
                case FLYCNTTYPE_OLE:
                    if(pNd->IsOLENode() && nIdx == nCount++)
                        pRetFormat = pFlyFormat;
                    break;
                default:
                    if(nIdx == nCount++)
                        pRetFormat = pFlyFormat;
                }
            }
        }
        return pRetFormat;
    }

    std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats(
        FlyCntType const eType, bool const bIgnoreTextBoxes)
    {
        SwFrameFormats& rFormats = *GetSpzFrameFormats();
        const size_t nSize = rFormats.size();

        std::vector<SwFrameFormat const*> ret;
        ret.reserve(nSize);

        for (size_t i = 0; i < nSize; ++i)
        {
            SwFrameFormat const*const pFlyFormat = rFormats[ i ];

            if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
            {
                continue;
            }

            if (RES_FLYFRMFMT != pFlyFormat->Which())
            {
                continue;
            }

            SwNodeIndex const*const pIdx(pFlyFormat->GetContent().GetContentIdx());
            if (pIdx && pIdx->GetNodes().IsDocNodes())
            {
                SwNode const*const pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
                switch (eType)
                {
                case FLYCNTTYPE_FRM:
                    if (!pNd->IsNoTextNode())
                        ret.push_back(pFlyFormat);
                    break;
                case FLYCNTTYPE_GRF:
                    if (pNd->IsGrfNode())
                        ret.push_back(pFlyFormat);
                    break;
                case FLYCNTTYPE_OLE:
                    if (pNd->IsOLENode())
                        ret.push_back(pFlyFormat);
                    break;
                default:
                    ret.push_back(pFlyFormat);
                }
            }
        }

        return ret;
    }

    static Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFormatAnchor& rAnch,
                                const SwFrameFormat* pFlyFormat )
    {
        Point aRet;
        if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
            switch( rAnch.GetAnchorId() )
            {
            case RndStdIds::FLY_AS_CHAR:
                if( pFlyFormat && rAnch.GetContentAnchor() )
                {
                    const SwFrame* pOld = static_cast<const SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aRet );
                    if( pOld )
                        aRet = pOld->getFrameArea().Pos();
                }
                break;

            case RndStdIds::FLY_AT_PARA:
            case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
                if( rAnch.GetContentAnchor() )
                {
                    const SwPosition *pPos = rAnch.GetContentAnchor();
                    const SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode();
                    const SwFrame* pOld = pNd ? pNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), &aRet, nullptr, false ) : nullptr;
                    if( pOld )
                        aRet = pOld->getFrameArea().Pos();
                }
                break;

            case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
                if( rAnch.GetContentAnchor() )
                {
                    const SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>(rAnch.GetContentAnchor()->
                                                    nNode.GetNode().GetFlyFormat());
                    const SwFrame* pOld = pFormat ? pFormat->GetFrame( &aRet ) : nullptr;
                    if( pOld )
                        aRet = pOld->getFrameArea().Pos();
                }
                break;

            case RndStdIds::FLY_AT_PAGE:
                {
                    sal_uInt16 nPgNum = rAnch.GetPageNum();
                    const SwPageFrame *pPage = static_cast<SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
                    for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
                                        pPage =static_cast<const SwPageFrame*>(pPage->GetNext()) )
                        if( i == nPgNum )
                        {
                            aRet = pPage->getFrameArea().Pos();
                            break;
                        }
                }
                break;
            default:
                break;
            }
        return aRet;
    }

    #define MAKEFRMS 0
    #define IGNOREANCHOR 1
    #define DONTMAKEFRMS 2

    sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
    {
        // Changing anchors is almost always allowed.
        // Exception: Paragraph and character bound frames must not become
        // page bound, if they are located in the header or footer.
        const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
        const RndStdIds nOld = rOldAnch.GetAnchorId();

        SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
        RndStdIds nNew = aNewAnch.GetAnchorId();

        // Is the new anchor valid?
        if( !aNewAnch.GetContentAnchor() && (RndStdIds::FLY_AT_FLY == nNew ||
            (RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
            (RndStdIds::FLY_AT_CHAR == nNew) ))
        {
            return IGNOREANCHOR;
        }

        if( nOld == nNew )
            return DONTMAKEFRMS;

        Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
        Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));

        // Destroy the old Frames.
        // The Views are hidden implicitly, so hiding them another time would be
        // kind of a show!
        rFormat.DelFrames();

        if ( RndStdIds::FLY_AS_CHAR == nOld )
        {
            // We need to handle InContents in a special way:
            // The TextAttribut needs to be destroyed which, unfortunately, also
            // destroys the format. To avoid that, we disconnect the format from
            // the attribute.
            const SwPosition *pPos = rOldAnch.GetContentAnchor();
            SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
            OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
            const sal_Int32 nIdx = pPos->nContent.GetIndex();
            SwTextAttr * const  pHint =
                pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
            OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT,
                        "Missing FlyInCnt-Hint." );
            OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat,
                        "Wrong TextFlyCnt-Hint." );
            if (pHint)
                const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();

            // They are disconnected. We now have to destroy the attribute.
            pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
        }

        // We can finally set the attribute. It needs to be the first one!
        // Undo depends on it!
        rFormat.SetFormatAttr( aNewAnch );

        // Correct the position
        const SfxPoolItem* pItem;
        switch( nNew )
        {
        case RndStdIds::FLY_AS_CHAR:
                // If no position attributes are received, we have to make sure
                // that no forbidden automatic alignment is left.
            {
                const SwPosition *pPos = aNewAnch.GetContentAnchor();
                SwTextNode *pNd = pPos->nNode.GetNode().GetTextNode();
                OSL_ENSURE( pNd, "Cursor does not point to TextNode." );

                SwFormatFlyCnt aFormat( static_cast<SwFlyFrameFormat*>(&rFormat) );
                pNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 );
            }

            if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem ))
            {
                SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
                bool bSet = true;
                switch( aOldV.GetVertOrient() )
                {
                case text::VertOrientation::LINE_TOP:     aOldV.SetVertOrient( text::VertOrientation::TOP );   break;
                case text::VertOrientation::LINE_CENTER:  aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
                case text::VertOrientation::LINE_BOTTOM:  aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
                case text::VertOrientation::NONE:         aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
                default:
                    bSet = false;
                }
                if( bSet )
                    rSet.Put( aOldV );
            }
            break;

        case RndStdIds::FLY_AT_PARA:
        case RndStdIds::FLY_AT_CHAR:   // LAYER_IMPL
        case RndStdIds::FLY_AT_FLY:    // LAYER_IMPL
        case RndStdIds::FLY_AT_PAGE:
            {
                // If no position attributes are coming in, we correct the position in a way
                // such that the fly's document coordinates are preserved.
                // If only the alignment changes in the position attributes (text::RelOrientation::FRAME
                // vs. text::RelOrientation::PRTAREA), we also correct the position.
                if( SfxItemState::SET != rSet.GetItemState( RES_HORI_ORIENT, false, &pItem ))
                    pItem = nullptr;

                SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() );

                if( text::HoriOrientation::NONE == aOldH.GetHoriOrient() && ( !pItem ||
                    aOldH.GetPos() == static_cast<const SwFormatHoriOrient*>(pItem)->GetPos() ))
                {
                    SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
                    nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX();

                    if( pItem )
                    {
                        SwFormatHoriOrient* pH = const_cast<SwFormatHoriOrient*>(static_cast<const SwFormatHoriOrient*>(pItem));
                        aOldH.SetHoriOrient( pH->GetHoriOrient() );
                        aOldH.SetRelationOrient( pH->GetRelationOrient() );
                    }
                    aOldH.SetPos( nPos );
                    rSet.Put( aOldH );
                }

                if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem ))
                    pItem = nullptr;
                SwFormatVertOrient aOldV( rFormat.GetVertOrient() );

                // #i28922# - correction: compare <aOldV.GetVertOrient() with
                // <text::VertOrientation::NONE>
                if( text::VertOrientation::NONE == aOldV.GetVertOrient() && (!pItem ||
                    aOldV.GetPos() == static_cast<const SwFormatVertOrient*>(pItem)->GetPos() ) )
                {
                    SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
                    nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY();
                    if( pItem )
                    {
                        SwFormatVertOrient* pV = const_cast<SwFormatVertOrient*>(static_cast<const SwFormatVertOrient*>(pItem));
                        aOldV.SetVertOrient( pV->GetVertOrient() );
                        aOldV.SetRelationOrient( pV->GetRelationOrient() );
                    }
                    aOldV.SetPos( nPos );
                    rSet.Put( aOldV );
                }
            }
            break;
        default:
            break;
        }

        if( bNewFrames )
            rFormat.MakeFrames();

        return MAKEFRMS;
    }

    static bool
    lcl_SetFlyFrameAttr(SwDoc & rDoc,
            sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
            SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
    {
        // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two
        // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
        ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());

        // Is the anchor attribute included?
        // If so, we pass it to a special method, which returns true
        // if the Fly needs to be created anew, because we e.g change the FlyType.
        sal_Int8 const nMakeFrames =
            (SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
                 ?  (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
                 :  DONTMAKEFRMS;

        const SfxPoolItem* pItem;
        SfxItemIter aIter( rSet );
        SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
        sal_uInt16 nWhich = aIter.GetCurItem()->Which();
        do {
            switch( nWhich )
            {
            case RES_FILL_ORDER:
            case RES_BREAK:
            case RES_PAGEDESC:
            case RES_CNTNT:
            case RES_FOOTER:
                OSL_FAIL( "Unknown Fly attribute." );
                SAL_FALLTHROUGH;
            case RES_CHAIN:
                rSet.ClearItem( nWhich );
                break;
            case RES_ANCHOR:
                if( DONTMAKEFRMS != nMakeFrames )
                    break;
                SAL_FALLTHROUGH;
            default:
                if( !IsInvalidItem( aIter.GetCurItem() ) && ( SfxItemState::SET !=
                    rFlyFormat.GetAttrSet().GetItemState( nWhich, true, &pItem ) ||
                    *pItem != *aIter.GetCurItem() ))
                    aTmpSet.Put( *aIter.GetCurItem() );
                break;
            }

            if( aIter.IsAtEnd() )
                break;

        } while( 0 != ( nWhich = aIter.NextItem()->Which() ) );

        if( aTmpSet.Count() )
            rFlyFormat.SetFormatAttr( aTmpSet );

        if( MAKEFRMS == nMakeFrames )
            rFlyFormat.MakeFrames();

        return aTmpSet.Count() || MAKEFRMS == nMakeFrames;
    }

    void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
    {
        SwDrawModel* pDrawModel = getIDocumentDrawModelAccess().GetOrCreateDrawModel();
        SfxItemIter aIter(rSet);

        for(const SfxPoolItem* pItem = aIter.FirstItem(); pItem; pItem = aIter.NextItem())
        {
            if (IsInvalidItem(pItem))
                continue;
            const SfxPoolItem* pResult = nullptr;

            switch(pItem->Which())
            {
                case XATTR_FILLBITMAP:
                {
                    pResult = static_cast< const XFillBitmapItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_LINEDASH:
                {
                    pResult = static_cast< const XLineDashItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_LINESTART:
                {
                    pResult = static_cast< const XLineStartItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_LINEEND:
                {
                    pResult = static_cast< const XLineEndItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_FILLGRADIENT:
                {
                    pResult = static_cast< const XFillGradientItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_FILLFLOATTRANSPARENCE:
                {
                    pResult = static_cast< const XFillFloatTransparenceItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
                case XATTR_FILLHATCH:
                {
                    pResult = static_cast< const XFillHatchItem* >(pItem)->checkForUniqueItem(pDrawModel);
                    break;
                }
            }

            if(pResult)
            {
                rSet.Put(*pResult);
                delete pResult;
            }
        }
    }

    bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet )
    {
        if( !rSet.Count() )
            return false;

        std::unique_ptr<SwUndoFormatAttrHelper> pSaveUndo;

        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
            pSaveUndo.reset( new SwUndoFormatAttrHelper( rFlyFormat ) );
        }

        bool const bRet = lcl_SetFlyFrameAttr(*this, &SwDoc::SetFlyFrameAnchor, rFlyFormat, rSet);

        if ( pSaveUndo.get() )
        {
            if ( pSaveUndo->GetUndo() )
            {
                GetIDocumentUndoRedo().AppendUndo( pSaveUndo->ReleaseUndo() );
            }
        }

        getIDocumentState().SetModified();

        SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);

        return bRet;
    }

    // #i73249#
    void SwDoc::SetFlyFrameTitle( SwFlyFrameFormat& rFlyFrameFormat,
                                const OUString& sNewTitle )
    {
        if ( rFlyFrameFormat.GetObjTitle() == sNewTitle )
        {
            return;
        }

        ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());

        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo( new SwUndoFlyStrAttr( rFlyFrameFormat,
                                              SwUndoId::FLYFRMFMT_TITLE,
                                              rFlyFrameFormat.GetObjTitle(),
                                              sNewTitle ) );
        }

        rFlyFrameFormat.SetObjTitle( sNewTitle, true );

        getIDocumentState().SetModified();
    }

    void SwDoc::SetFlyFrameDescription( SwFlyFrameFormat& rFlyFrameFormat,
                                      const OUString& sNewDescription )
    {
        if ( rFlyFrameFormat.GetObjDescription() == sNewDescription )
        {
            return;
        }

        ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());

        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo( new SwUndoFlyStrAttr( rFlyFrameFormat,
                                              SwUndoId::FLYFRMFMT_DESCRIPTION,
                                              rFlyFrameFormat.GetObjDescription(),
                                              sNewDescription ) );
        }

        rFlyFrameFormat.SetObjDescription( sNewDescription, true );

        getIDocumentState().SetModified();
    }

    bool SwDoc::SetFrameFormatToFly( SwFrameFormat& rFormat, SwFrameFormat& rNewFormat,
                                SfxItemSet* pSet, bool bKeepOrient )
    {
        bool bChgAnchor = false, bFrameSz = false;

        const SwFormatFrameSize aFrameSz( rFormat.GetFrameSize() );

        SwUndoSetFlyFormat* pUndo = nullptr;
        bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
        if (bUndo)
        {
            pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
            GetIDocumentUndoRedo().AppendUndo(pUndo);
        }

        // #i32968# Inserting columns in the section causes MakeFrameFormat to put
        // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
        ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());

        // Set the column first, or we'll have trouble with
        //Set/Reset/Synch. and so on
        const SfxPoolItem* pItem;
        if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
            rFormat.ResetFormatAttr( RES_COL );

        if( rFormat.DerivedFrom() != &rNewFormat )
        {
            rFormat.SetDerivedFrom( &rNewFormat );

            // 1. If not automatic = ignore; else = dispose
            // 2. Dispose of it!
            if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
            {
                rFormat.ResetFormatAttr( RES_FRM_SIZE );
                bFrameSz = true;
            }

            const SfxItemSet* pAsk = pSet;
            if( !pAsk ) pAsk = &rNewFormat.GetAttrSet();
            if( SfxItemState::SET == pAsk->GetItemState( RES_ANCHOR, false, &pItem )
                && static_cast<const SwFormatAnchor*>(pItem)->GetAnchorId() !=
                    rFormat.GetAnchor().GetAnchorId() )
            {
                if( pSet )
                    bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false );
                else
                {
                    // Needs to have the FlyFormat range, because we set attributes in it,
                    // in SetFlyFrameAnchor.
                    SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
                                        rNewFormat.GetAttrSet().GetRanges() );
                    aFlySet.Put( *pItem );
                    bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
                }
            }
        }

        // Only reset vertical and horizontal orientation, if we have automatic alignment
        // set in the template. Otherwise use the old value.
        // If we update the frame template the Fly should NOT lose its orientation (which
        // is not being updated!).
        // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
        if (!bKeepOrient)
        {
            rFormat.ResetFormatAttr(RES_VERT_ORIENT);
            rFormat.ResetFormatAttr(RES_HORI_ORIENT);
        }

        rFormat.ResetFormatAttr( RES_PRINT, RES_SURROUND );
        rFormat.ResetFormatAttr( RES_LR_SPACE, RES_UL_SPACE );
        rFormat.ResetFormatAttr( RES_BACKGROUND, RES_COL );
        rFormat.ResetFormatAttr( RES_URL, RES_EDIT_IN_READONLY );

        if( !bFrameSz )
            rFormat.SetFormatAttr( aFrameSz );

        if( bChgAnchor )
            rFormat.MakeFrames();

        if( pUndo )
            pUndo->EndListeningAll();

        getIDocumentState().SetModified();

        return bChgAnchor;
    }

    void SwDoc::GetGrfNms( const SwFlyFrameFormat& rFormat, OUString* pGrfName,
                           OUString* pFltName )
    {
        SwNodeIndex aIdx( *rFormat.GetContent().GetContentIdx(), 1 );
        const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
        if( pGrfNd && pGrfNd->IsLinkedFile() )
            pGrfNd->GetFileFilterNms( pGrfName, pFltName );
    }

    bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
                               RndStdIds _eAnchorType,
                               const bool _bSameOnly,
                               const bool _bPosCorr )
    {
        OSL_ENSURE( getIDocumentLayoutAccess().GetCurrentLayout(), "No layout!" );

        if ( !_rMrkList.GetMarkCount() ||
             _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
        {
            return false;
        }

        GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );

        bool bUnmark = false;
        for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
        {
            SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
            if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ==  nullptr )
            {
                SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));

                // consider, that drawing object has
                // no user call. E.g.: a 'virtual' drawing object is disconnected by
                // the anchor type change of the 'master' drawing object.
                // Continue with next selected object and assert, if this isn't excepted.
                if ( !pContact )
                {
    #if OSL_DEBUG_LEVEL > 0
                    bool bNoUserCallExcepted =
                            dynamic_cast<const SwDrawVirtObj*>( pObj) !=  nullptr &&
                            !static_cast<SwDrawVirtObj*>(pObj)->IsConnected();
                    OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
    #endif
                    continue;
                }

                // #i26791#
                const SwFrame* pOldAnchorFrame = pContact->GetAnchorFrame( pObj );
                const SwFrame* pNewAnchorFrame = pOldAnchorFrame;

                // #i54336#
                // Instead of only keeping the index position for an as-character
                // anchored object the complete <SwPosition> is kept, because the
                // anchor index position could be moved, if the object again is
                // anchored as character.
                std::unique_ptr<const SwPosition> xOldAsCharAnchorPos;
                const RndStdIds eOldAnchorType = pContact->GetAnchorId();
                if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
                {
                    xOldAsCharAnchorPos.reset(new SwPosition(pContact->GetContentAnchor()));
                }

                if ( _bSameOnly )
                    _eAnchorType = eOldAnchorType;

                SwFormatAnchor aNewAnch( _eAnchorType );
                SwAnchoredObject *pAnchoredObj = pContact->GetAnchoredObj(pObj);
                tools::Rectangle aObjRect(pAnchoredObj->GetObjRect().SVRect());
                const Point aPt( aObjRect.TopLeft() );

                switch ( _eAnchorType )
                {
                case RndStdIds::FLY_AT_PARA:
                case RndStdIds::FLY_AT_CHAR:
                    {
                        const Point aNewPoint = ( pOldAnchorFrame->IsVertical() ||
                                                  pOldAnchorFrame->IsRightToLeft() )
                                                ? aObjRect.TopRight()
                                                : aPt;

                        // allow drawing objects in header/footer
                        pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aNewPoint );
                        if ( pNewAnchorFrame->IsTextFrame() && static_cast<const SwTextFrame*>(pNewAnchorFrame)->IsFollow() )
                        {
                            pNewAnchorFrame = static_cast<const SwTextFrame*>(pNewAnchorFrame)->FindMaster();
                        }
                        if ( pNewAnchorFrame->IsProtected() )
                        {
                            pNewAnchorFrame = nullptr;
                        }
                        else
                        {
                            SwPosition aPos( pNewAnchorFrame->IsTextFrame()
                                ? *static_cast<SwTextFrame const*>(pNewAnchorFrame)->GetTextNodeForParaProps()
                                : *static_cast<SwNoTextFrame const*>(pNewAnchorFrame)->GetNode() );

                            aNewAnch.SetType( _eAnchorType );
                            aNewAnch.SetAnchor( &aPos );
                        }
                    }
                    break;

                case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
                    {
                        // Search the closest SwFlyFrame starting from the upper left corner.
                        SwFrame *pTextFrame;
                        {
                            SwCursorMoveState aState( MV_SETONLYTEXT );
                            SwPosition aPos( GetNodes() );
                            Point aPoint( aPt );
                            aPoint.setX(aPoint.getX() - 1);
                            getIDocumentLayoutAccess().GetCurrentLayout()->GetCursorOfst( &aPos, aPoint, &aState );
                            // consider that drawing objects can be in
                            // header/footer. Thus, <GetFrame()> by left-top-corner
                            pTextFrame = aPos.nNode.GetNode().
                                            GetContentNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout(), &aPt, nullptr, false );
                        }
                        const SwFrame *pTmp = ::FindAnchor( pTextFrame, aPt );
                        pNewAnchorFrame = pTmp->FindFlyFrame();
                        if( pNewAnchorFrame && !pNewAnchorFrame->IsProtected() )
                        {
                            const SwFrameFormat *pTmpFormat = static_cast<const SwFlyFrame*>(pNewAnchorFrame)->GetFormat();
                            const SwFormatContent& rContent = pTmpFormat->GetContent();
                            SwPosition aPos( *rContent.GetContentIdx() );
                            aNewAnch.SetAnchor( &aPos );
                            break;
                        }

                        aNewAnch.SetType( RndStdIds::FLY_AT_PAGE );
                        SAL_FALLTHROUGH;
                    }
                case RndStdIds::FLY_AT_PAGE:
                    {
                        pNewAnchorFrame = getIDocumentLayoutAccess().GetCurrentLayout()->Lower();
                        while ( pNewAnchorFrame && !pNewAnchorFrame->getFrameArea().IsInside( aPt ) )
                            pNewAnchorFrame = pNewAnchorFrame->GetNext();
                        if ( !pNewAnchorFrame )
                            continue;

                        aNewAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnchorFrame)->GetPhyPageNum());
                    }
                    break;
                case RndStdIds::FLY_AS_CHAR:
                    if( _bSameOnly )    // Change of position/size
                    {
                        if( !pOldAnchorFrame )
                        {
                            pContact->ConnectToLayout();
                            pOldAnchorFrame = pContact->GetAnchorFrame();
                        }
                        const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pOldAnchorFrame))->Prepare();
                    }
                    else            // Change of anchors
                    {
                        // allow drawing objects in header/footer
                        pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aPt );
                        if( pNewAnchorFrame->IsProtected() )
                        {
                            pNewAnchorFrame = nullptr;
                            break;
                        }

                        bUnmark = ( 0 != i );
                        Point aPoint( aPt );
                        aPoint.setX(aPoint.getX() - 1);    // Do not load in the DrawObj!
                        aNewAnch.SetType( RndStdIds::FLY_AS_CHAR );
                        assert(pNewAnchorFrame->IsTextFrame()); // because AS_CHAR
                        SwTextFrame const*const pFrame(
                                static_cast<SwTextFrame const*>(pNewAnchorFrame));
                        SwPosition aPos( *pFrame->GetTextNodeForParaProps() );
                        if ( pNewAnchorFrame->getFrameArea().IsInside( aPoint ) )
                        {
                        // We need to find a TextNode, because only there we can anchor a
                        // content-bound DrawObject.
                            SwCursorMoveState aState( MV_SETONLYTEXT );
                            getIDocumentLayoutAccess().GetCurrentLayout()->GetCursorOfst( &aPos, aPoint, &aState );
                        }
                        else
                        {
                            if ( pNewAnchorFrame->getFrameArea().Bottom() < aPt.Y() )
                            {
                                aPos = pFrame->MapViewToModelPos(TextFrameIndex(0));
                            }
                            else
                            {
                                aPos = pFrame->MapViewToModelPos(
                                    TextFrameIndex(pFrame->GetText().getLength()));
                            }
                        }
                        aNewAnch.SetAnchor( &aPos );
                        SetAttr( aNewAnch, *pContact->GetFormat() );
                        // #i26791# - adjust vertical positioning to 'center to
                        // baseline'
                        SetAttr( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFormat() );
                        SwTextNode *pNd = aPos.nNode.GetNode().GetTextNode();
                        OSL_ENSURE( pNd, "Cursor not positioned at TextNode." );

                        SwFormatFlyCnt aFormat( pContact->GetFormat() );
                        pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 );
                    }
                    break;
                default:
                    OSL_ENSURE( false, "unexpected AnchorId." );
                }

                if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
                     pNewAnchorFrame &&
                     ( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
                {
                    // #i26791# - Direct object positioning no longer needed. Apply
                    // of attributes (method call <SetAttr(..)>) takes care of the
                    // invalidation of the object position.
                    SetAttr( aNewAnch, *pContact->GetFormat() );
                    if ( _bPosCorr )
                    {
                        // #i33313# - consider not connected 'virtual' drawing
                        // objects
                        if ( dynamic_cast<const SwDrawVirtObj*>( pObj) !=  nullptr &&
                             !static_cast<SwDrawVirtObj*>(pObj)->IsConnected() )
                        {
                            SwRect aNewObjRect( aObjRect );
                            static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
                                            ->AdjustPositioningAttr( pNewAnchorFrame,
                                                                     &aNewObjRect );
                        }
                        else
                        {
                            static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
                                        ->AdjustPositioningAttr( pNewAnchorFrame );
                        }
                    }
                }

                // we have changed the anchoring attributes, and those are used to
                // order the object in its sorted list, so update its position
                pAnchoredObj->UpdateObjInSortedList();

                // #i54336#
                if (xOldAsCharAnchorPos)
                {
                    if ( pNewAnchorFrame)
                    {
                        // We need to handle InContents in a special way:
                        // The TextAttribut needs to be destroyed which, unfortunately, also
                        // destroys the format. To avoid that, we disconnect the format from
                        // the attribute.
                        const sal_Int32 nIndx( xOldAsCharAnchorPos->nContent.GetIndex() );
                        SwTextNode* pTextNode( xOldAsCharAnchorPos->nNode.GetNode().GetTextNode() );
                        assert(pTextNode && "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object");
                        SwTextAttr * const pHint =
                            pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
                        assert(pHint && "Missing FlyInCnt-Hint.");
                        const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();

                        // They are disconnected. We now have to destroy the attribute.
                        pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
                    }
                }
            }
        }

        GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
        getIDocumentState().SetModified();

        return bUnmark;
    }

    SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
    {
        // The Source must not yet have a Follow.
        const SwFormatChain &rOldChain = rSource.GetChain();
        if ( rOldChain.GetNext() )
            return SwChainRet::SOURCE_CHAINED;

        // Target must not be equal to Source and we also must not have a closed chain.
        const SwFrameFormat *pFormat = &rDest;
        do {
            if( pFormat == &rSource )
                return SwChainRet::SELF;
            pFormat = pFormat->GetChain().GetNext();
        } while ( pFormat );

        // There must not be a chaining from outside to inside or the other way around.
        if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
            return SwChainRet::SELF;

        // The Target must not yet have a Master.
        const SwFormatChain &rChain = rDest.GetChain();
        if( rChain.GetPrev() )
            return SwChainRet::IS_IN_CHAIN;

        // Target must be empty.
        const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
        if( !pCntIdx )
            return SwChainRet::NOT_FOUND;

        SwNodeIndex aNxtIdx( *pCntIdx, 1 );
        const SwTextNode* pTextNd = aNxtIdx.GetNode().GetTextNode();
        if( !pTextNd )
            return SwChainRet::NOT_FOUND;

        const sal_uLong nFlySttNd = pCntIdx->GetIndex();
        if( 2 != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
            pTextNd->GetText().getLength() )
        {
            return SwChainRet::NOT_EMPTY;
        }

        for( auto pSpzFrameFm : *GetSpzFrameFormats() )
        {
            const SwFormatAnchor& rAnchor = pSpzFrameFm->GetAnchor();
            sal_uLong nTstSttNd;
            // #i20622# - to-frame anchored objects are allowed.
            if ( ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
                  (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) &&
                 nullptr != rAnchor.GetContentAnchor() &&
                 nFlySttNd <= ( nTstSttNd =
                             rAnchor.GetContentAnchor()->nNode.GetIndex() ) &&
                 nTstSttNd < nFlySttNd + 2 )
            {
                return SwChainRet::NOT_EMPTY;
            }
        }

        // We also need to consider the right area.
        // Both Flys need to be located in the same area (Body, Header/Footer, Fly).
        // If the Source is not the selected frame, it's enough to find a suitable
        // one. e.g. if it's requested by the API.

        // both in the same fly, header, footer or on the page?
        const SwFormatAnchor &rSrcAnchor = rSource.GetAnchor(),
                          &rDstAnchor = rDest.GetAnchor();
        sal_uLong nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
        bool bAllowed = false;
        if ( RndStdIds::FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
        {
            if ( (RndStdIds::FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
                ( rDstAnchor.GetContentAnchor() &&
                  rDstAnchor.GetContentAnchor()->nNode.GetIndex() > nEndOfExtras ))
                bAllowed = true;
        }
        else if( rSrcAnchor.GetContentAnchor() && rDstAnchor.GetContentAnchor() )
        {
            const SwNodeIndex &rSrcIdx = rSrcAnchor.GetContentAnchor()->nNode,
                                &rDstIdx = rDstAnchor.GetContentAnchor()->nNode;
            const SwStartNode* pSttNd = nullptr;
            if( rSrcIdx == rDstIdx ||
                ( !pSttNd &&
                    nullptr != ( pSttNd = rSrcIdx.GetNode().FindFlyStartNode() ) &&
                    pSttNd == rDstIdx.GetNode().FindFlyStartNode() ) ||
                ( !pSttNd &&
                    nullptr != ( pSttNd = rSrcIdx.GetNode().FindFooterStartNode() ) &&
                    pSttNd == rDstIdx.GetNode().FindFooterStartNode() ) ||
                ( !pSttNd &&
                    nullptr != ( pSttNd = rSrcIdx.GetNode().FindHeaderStartNode() ) &&
                    pSttNd == rDstIdx.GetNode().FindHeaderStartNode() ) ||
                ( !pSttNd && rDstIdx.GetIndex() > nEndOfExtras &&
                                rSrcIdx.GetIndex() > nEndOfExtras ))
                bAllowed = true;
        }

        return bAllowed ? SwChainRet::OK : SwChainRet::WRONG_AREA;
    }

    SwChainRet SwDoc::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
    {
        SwChainRet nErr = Chainable( rSource, rDest );
        if ( nErr == SwChainRet::OK )
        {
            GetIDocumentUndoRedo().StartUndo( SwUndoId::CHAINE, nullptr );

            SwFlyFrameFormat& rDestFormat = const_cast<SwFlyFrameFormat&>(static_cast<const SwFlyFrameFormat&>(rDest));

            // Attach Follow to the Master.
            SwFormatChain aChain = rDestFormat.GetChain();
            aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
            SetAttr( aChain, rDestFormat );

            SfxItemSet aSet( GetAttrPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE,
                                            RES_CHAIN,  RES_CHAIN>{} );

            // Attach Follow to the Master.
            aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
            SetAttr( aChain, rDestFormat );

            // Attach Master to the Follow.
            // Make sure that the Master has a fixed height.
            aChain = rSource.GetChain();
            aChain.SetNext( &rDestFormat );
            aSet.Put( aChain );

            SwFormatFrameSize aSize( rSource.GetFrameSize() );
            if ( aSize.GetHeightSizeType() != ATT_FIX_SIZE )
            {
                SwFlyFrame *pFly = SwIterator<SwFlyFrame,SwFormat>( rSource ).First();
                if ( pFly )
                    aSize.SetHeight( pFly->getFrameArea().Height() );
                aSize.SetHeightSizeType( ATT_FIX_SIZE );
                aSet.Put( aSize );
            }
            SetAttr( aSet, rSource );

            GetIDocumentUndoRedo().EndUndo( SwUndoId::CHAINE, nullptr );
        }
        return nErr;
    }

    void SwDoc::Unchain( SwFrameFormat &rFormat )
    {
        SwFormatChain aChain( rFormat.GetChain() );
        if ( aChain.GetNext() )
        {
            GetIDocumentUndoRedo().StartUndo( SwUndoId::UNCHAIN, nullptr );
            SwFrameFormat *pFollow = aChain.GetNext();
            aChain.SetNext( nullptr );
            SetAttr( aChain, rFormat );
            aChain = pFollow->GetChain();
            aChain.SetPrev( nullptr );
            SetAttr( aChain, *pFollow );
            GetIDocumentUndoRedo().EndUndo( SwUndoId::UNCHAIN, nullptr );
        }
    }

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

↑ V560 A part of conditional expression is always true: !pSttNd.