/* -*- 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 <o3tl/any.hxx>
#include <svl/itemiter.hxx>
#include <vcl/imapobj.hxx>
#include <editeng/protitem.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdouno.hxx>
#include <tools/globname.hxx>
#include <sot/exchange.hxx>
#include <com/sun/star/form/FormButtonType.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <comphelper/types.hxx>
#include <osl/diagnose.h>
#include <comphelper/scopeguard.hxx>
#include <fmtanchr.hxx>
#include <fmtcntnt.hxx>
#include <fmtornt.hxx>
#include <fmturl.hxx>
#include <fmtfsize.hxx>
#include <fesh.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <cntfrm.hxx>
#include <txtfrm.hxx>
#include <viewimp.hxx>
#include <viscrs.hxx>
#include <doc.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentState.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <redline.hxx>
#include <dview.hxx>
#include <dflyobj.hxx>
#include <dcontact.hxx>
#include <frmfmt.hxx>
#include <flyfrm.hxx>
#include <ndtxt.hxx>
#include <swtable.hxx>
#include <ndgrf.hxx>
#include <flyfrms.hxx>
#include <fldbas.hxx>
#include <fmtfld.hxx>
#include <swundo.hxx>
#include <txatbase.hxx>
#include <frame.hxx>
#include <notxtfrm.hxx>
#include <HandleAnchorNodeChg.hxx>
#include <frmatr.hxx>
#include <fmtsrnd.hxx>
#include <ndole.hxx>
#include <fefly.hxx>
#include <fmtcnct.hxx>
#include <frameformats.hxx>
#include <textboxhelper.hxx>


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

// Based on the request, changes to the specific layouts will be made, to
// fit to the format
static bool lcl_SetNewFlyPos( const SwNode& rNode, SwFormatAnchor& rAnchor,
                        const Point& rPt )
{
    bool bRet = false;
    const SwStartNode* pStNode = rNode.FindFlyStartNode();
    if( pStNode )
    {
        SwPosition aPos( *pStNode );
        rAnchor.SetAnchor( &aPos );
        bRet = true;
    }
    else
    {
        const SwContentNode *pCntNd = rNode.GetContentNode();
        std::pair<Point, bool> const tmp(rPt, false);
        const SwContentFrame* pCFrame = pCntNd ? pCntNd->getLayoutFrame(
            pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
            nullptr, &tmp) : nullptr;
        const SwPageFrame *pPg = pCFrame ? pCFrame->FindPageFrame() : nullptr;

        rAnchor.SetPageNum( pPg ? pPg->GetPhyPageNum() : 1 );
        rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
    }
    return bRet;
}

static bool lcl_FindAnchorPos(
    SwDoc& rDoc,
    const Point& rPt,
    const SwFrame& rFrame,
    SfxItemSet& rSet )
{
    bool bRet = true;
    SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
    RndStdIds nNew = aNewAnch.GetAnchorId();
    const SwFrame *pNewAnch;

    //determine new anchor
    Point aTmpPnt( rPt );
    switch( nNew )
    {
    case RndStdIds::FLY_AS_CHAR:  // also include this?
    case RndStdIds::FLY_AT_PARA:
    case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
        {
            // starting from the upper-left corner of the Fly,
            // search nearest ContentFrame
            const SwFrame* pFrame = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame&>(rFrame).GetAnchorFrame()
                                                : &rFrame;
            pNewAnch = ::FindAnchor( pFrame, aTmpPnt );
            if( pNewAnch->IsProtected() )
            {
                bRet = false;
                break;
            }
            SwPosition aPos( pNewAnch->IsTextFrame()
                ? *static_cast<SwTextFrame const*>(pNewAnch)->GetTextNodeForParaProps()
                : *static_cast<const SwNoTextFrame*>(pNewAnch)->GetNode() );
            if ((RndStdIds::FLY_AT_CHAR == nNew) || (RndStdIds::FLY_AS_CHAR == nNew))
            {
                // textnode should be found, as only in those
                // a content bound frame can be anchored
                SwCursorMoveState aState( CursorMoveState::SetOnlyText );
                aTmpPnt.setX(aTmpPnt.getX() - 1);                   // do not land in the fly!
                if( !pNewAnch->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState ) )
                {
                    assert(pNewAnch->IsTextFrame()); // because AT_CHAR/AS_CHAR
                    SwTextFrame const*const pTextFrame(
                            static_cast<SwTextFrame const*>(pNewAnch));
                    if( pNewAnch->getFrameArea().Bottom() < aTmpPnt.Y() )
                    {
                        aPos = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
                    }
                    else
                    {
                        aPos = pTextFrame->MapViewToModelPos(
                            TextFrameIndex(pTextFrame->GetText().getLength()));
                    }
                }
                else
                {
                    if ( SwCursorShell::PosInsideInputField( aPos ) )
                    {
                        aPos.SetContent( SwCursorShell::StartOfInputFieldAtPos( aPos ) );
                    }
                }
            }
            aNewAnch.SetAnchor( &aPos );
        }
        break;

    case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
        {
            // starting from the upper-left corner of the Fly
            // search nearest SwFlyFrame
            SwCursorMoveState aState( CursorMoveState::SetOnlyText );
            SwPosition aPos( rDoc.GetNodes() );
            aTmpPnt.setX(aTmpPnt.getX() - 1);                   // do not land in the fly!
            rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState );
            pNewAnch = ::FindAnchor(
                aPos.GetNode().GetContentNode()->getLayoutFrame(rFrame.getRootFrame(), nullptr, nullptr),
                aTmpPnt )->FindFlyFrame();

            if( pNewAnch && &rFrame != pNewAnch && !pNewAnch->IsProtected() )
            {
                aPos.Assign( *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()->GetContent().
                                GetContentIdx() );
                aNewAnch.SetAnchor( &aPos );
                break;
            }
        }

        nNew = RndStdIds::FLY_AT_PAGE;
        aNewAnch.SetType( nNew );
        [[fallthrough]];

    case RndStdIds::FLY_AT_PAGE:
        pNewAnch = rFrame.FindPageFrame();
        aNewAnch.SetPageNum( pNewAnch->GetPhyPageNum() );
        break;

    default:
        OSL_ENSURE( false, "Wrong Id for new anchor." );
    }

    rSet.Put( aNewAnch );
    return bRet;
}

//! also used in unoframe.cxx

bool sw_ChkAndSetNewAnchor(
    const SwFlyFrame& rFly,
    SfxItemSet& rSet )
{
    const SwFrameFormat& rFormat = *rFly.GetFormat();
    const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
    const RndStdIds nOld = rOldAnch.GetAnchorId();

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

    if( nOld == nNew )
        return false;

    SwDoc& rDoc = const_cast<SwDoc&>(rFormat.GetDoc());

#if OSL_DEBUG_LEVEL > 0
    OSL_ENSURE( !(nNew == RndStdIds::FLY_AT_PAGE &&
        (RndStdIds::FLY_AT_PARA==nOld || RndStdIds::FLY_AT_CHAR==nOld || RndStdIds::FLY_AS_CHAR==nOld ) &&
        rDoc.IsInHeaderFooter( *rOldAnch.GetAnchorNode() )),
            "forbidden anchor change in Head/Foot." );
#endif

    return ::lcl_FindAnchorPos( rDoc, rFly.getFrameArea().Pos(), rFly, rSet );
}

void SwFEShell::SelectFlyFrame( SwFlyFrame& rFrame )
{
    CurrShell aCurr( this );

    // The frame is new, thus select it.
    // !! Always select the frame, if it's not selected.
    // - it could be a new "old" one because the anchor was changed
    // - "old" frames have had to be selected previously otherwise they could
    //   not have been changed
    // The frames should not be selected by the document position, because
    // it should have been selected!
    SwViewShellImp *pImpl = Imp();
    if( !GetWin() )
        return;

    OSL_ENSURE( rFrame.IsFlyFrame(), "SelectFlyFrame wants a Fly" );

   // nothing to be done if the Fly already was selected
    if (GetSelectedFlyFrame() == &rFrame)
        return;

    // assure the anchor is drawn
    if( rFrame.IsFlyInContentFrame() && rFrame.GetAnchorFrame() )
         rFrame.GetAnchorFrame()->SetCompletePaint();

    if( pImpl->GetDrawView()->GetMarkedObjectList().GetMarkCount() != 0 )
        pImpl->GetDrawView()->UnmarkAll();

    pImpl->GetDrawView()->MarkObj( rFrame.GetVirtDrawObj(),
                                  pImpl->GetPageView() );

    rFrame.SelectionHasChanged(this);

    KillPams();
    ClearMark();
    SelFlyGrabCursor();
}

void SwFEShell::UnfloatFlyFrame()
{
    GetIDocumentUndoRedo().StartUndo(SwUndoId::DELLAYFMT, nullptr);
    comphelper::ScopeGuard g([this]
                             { GetIDocumentUndoRedo().EndUndo(SwUndoId::DELLAYFMT, nullptr); });

    SwFlyFrame* pFly = GetSelectedFlyFrame();
    if (!pFly)
    {
        return;
    }

    SwFrameFormat* pFlyFormat = pFly->GetFrameFormat();
    const SwFormatContent& rContent = pFlyFormat->GetContent();
    const SwNodeIndex* pFlyStart = rContent.GetContentIdx();
    if (!pFlyStart)
    {
        return;
    }

    const SwEndNode* pFlyEnd = pFlyStart->GetNode().EndOfSectionNode();
    if (!pFlyEnd)
    {
        return;
    }

    // Create an empty paragraph after the table, so the frame's SwNodes section is non-empty after
    // MoveNodeRange(). Undo would ensure it's non-empty and then node offsets won't match.
    IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations();
    {
        SwNodeIndex aInsertIndex(*pFlyEnd);
        --aInsertIndex;
        SwPosition aInsertPos(aInsertIndex);
        StartAllAction();
        rIDCO.AppendTextNode(aInsertPos);
        // Make sure that a layout frame is created for the node, so the fly frame is not deleted,
        // during MoveNodeRange(), either.
        EndAllAction();
    }

    SwNodeRange aRange(pFlyStart->GetNode(), SwNodeOffset(1), *pFlyEnd, SwNodeOffset(-1));
    const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
    SwNode* pAnchor = rAnchor.GetAnchorNode();
    if (!pAnchor)
    {
        return;
    }

    // Move the content outside of the text frame.
    SwNodeIndex aInsertPos(*pAnchor);
    rIDCO.MoveNodeRange(aRange, aInsertPos.GetNode(), SwMoveFlags::CREATEUNDOOBJ);

    // Remove the fly frame frame.
    IDocumentLayoutAccess& rIDLA = pFlyFormat->getIDocumentLayoutAccess();
    rIDLA.DelLayoutFormat(pFlyFormat);
}

// Get selected fly
SwFlyFrame* SwFEShell::GetSelectedFlyFrame() const
{
    if ( Imp()->HasDrawView() )
    {
        // A Fly is only accessible if it is selected
        const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
        if( rMrkList.GetMarkCount() != 1 )
            return nullptr;

        SdrObject *pO = rMrkList.GetMark( 0 )->GetMarkedSdrObj();

        SwVirtFlyDrawObj *pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pO);

        return pFlyObj ? pFlyObj->GetFlyFrame() : nullptr;
    }
    return nullptr;
}

// Get current fly in which the cursor is positioned
SwFlyFrame* SwFEShell::GetCurrFlyFrame(const bool bCalcFrame) const
{
    SwContentFrame *pContent = GetCurrFrame(bCalcFrame);
    return pContent ? pContent->FindFlyFrame() : nullptr;
}

// Get selected fly, but if none Get current fly in which the cursor is positioned
SwFlyFrame* SwFEShell::GetSelectedOrCurrFlyFrame() const
{
    SwFlyFrame *pFly = GetSelectedFlyFrame();
    if (pFly)
        return pFly;
    return GetCurrFlyFrame();
}

// Returns non-null pointer, if the current Fly could be anchored to another one (so it is inside)
const SwFrameFormat* SwFEShell::IsFlyInFly()
{
    CurrShell aCurr( this );

    if ( !Imp()->HasDrawView() )
        return nullptr;

    const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
    if ( !rMrkList.GetMarkCount() )
    {
        SwFlyFrame *pFly = GetCurrFlyFrame(false);
        if (!pFly)
            return nullptr;
        return pFly->GetFormat();
    }
    else if ( rMrkList.GetMarkCount() != 1 )
        return nullptr;

    SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
    SwContact* pContact = GetUserCall( pObj );
    if (!pContact)
        return nullptr;

    SwFrameFormat *pFormat = FindFrameFormat( pObj );
    if( pFormat && RndStdIds::FLY_AT_FLY == pFormat->GetAnchor().GetAnchorId() )
    {
        const SwFrame* pFly;
        if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj *>(pObj))
        {
            pFly = pFlyObj->GetFlyFrame()->GetAnchorFrame();
        }
        else
        {
            pFly = static_cast<SwDrawContact*>(pContact)->GetAnchorFrame(pObj);
        }

        assert(pFly && "IsFlyInFly: Where's my anchor?");
        OSL_ENSURE( pFly->IsFlyFrame(), "IsFlyInFly: Funny anchor!" );
        return static_cast<const SwFlyFrame*>(pFly)->GetFormat();
    }

    Point aTmpPos = pObj->GetCurrentBoundRect().TopLeft();

    SwFrame *pTextFrame;
    {
        SwCursorMoveState aState( CursorMoveState::SetOnlyText );
        SwNodeIndex aSwNodeIndex( GetDoc()->GetNodes() );
        SwPosition aPos( aSwNodeIndex );
        Point aPoint( aTmpPos );
        aPoint.setX(aPoint.getX() - 1);                    //do not land in the fly!!
        GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
        // determine text frame by left-top-corner of object
        SwContentNode *pNd = aPos.GetNode().GetContentNode();
        std::pair<Point, bool> const tmp(aTmpPos, false);
        pTextFrame = pNd ? pNd->getLayoutFrame(GetLayout(), nullptr, &tmp) : nullptr;
    }
    const SwFrame *pTmp = pTextFrame ? ::FindAnchor(pTextFrame, aTmpPos) : nullptr;
    const SwFlyFrame *pFly = pTmp ? pTmp->FindFlyFrame() : nullptr;
    if( pFly )
        return pFly->GetFormat();
    return nullptr;
}

void SwFEShell::SetFlyPos( const Point& rAbsPos )
{
    CurrShell aCurr( this );

    // Determine reference point in document coordinates
    SwFlyFrame *pFly = GetCurrFlyFrame(false);
    if (!pFly)
        return;

    //SwSaveHdl aSaveX( Imp() );

    // Set an anchor starting from the absolute position for paragraph bound Flys
    // Anchor and new RelPos will be calculated and set by the Fly
    if ( pFly->IsFlyAtContentFrame() )
    {
        if(pFly->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(pFly)->isTransformableSwFrame())
        {
            // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used),
            // we need to correct the absolute position (rAbsPos) which was created in
            // transformed coordinates to untransformed state
            TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(pFly)->getTransformableSwFrame());
            const SwRect aUntransformedFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
            const Point aNewAbsPos(
                rAbsPos.X() + aUntransformedFrameArea.Left() - pFly->getFrameArea().Left(),
                rAbsPos.Y() + aUntransformedFrameArea.Top() - pFly->getFrameArea().Top());
            static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos(aNewAbsPos);
        }
        else
        {
            static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( rAbsPos );
        }
    }
    else
    {
        const SwFrame *pAnch = pFly->GetAnchorFrame();
        Point aOrient( pAnch->getFrameArea().Pos() );

        if ( pFly->IsFlyInContentFrame() )
            aOrient.setX(rAbsPos.getX());

        // calculate RelPos.
        aOrient.setX(rAbsPos.getX() - aOrient.getX());
        aOrient.setY(rAbsPos.getY() - aOrient.getY());
        pFly->ChgRelPos( aOrient );
    }
    CallChgLnk();       // call the AttrChangeNotify on the UI-side.
}

Point SwFEShell::FindAnchorPos( const Point& rAbsPos, bool bMoveIt )
{
    Point aRet;

    CurrShell aCurr( this );

    if ( !Imp()->HasDrawView() )
        return aRet;

    const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
    if (rMrkList.GetMarkCount() != 1)
        return aRet;

    SdrObject* pObj = rMrkList.GetMark(0)->GetMarkedSdrObj();

    SwContact* pContact = ::GetUserCall( pObj );
    if (!pContact)
        return aRet;

    assert(pObj);

    // #i28701#
    SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( pObj );
    SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
    const RndStdIds nAnchorId = pFormat->GetAnchor().GetAnchorId();

    if ( RndStdIds::FLY_AS_CHAR == nAnchorId )
        return aRet;

    bool bFlyFrame = dynamic_cast<SwVirtFlyDrawObj *>(pObj) != nullptr;

    bool bTextBox = false;
    if (pFormat->Which() == RES_DRAWFRMFMT)
    {
        bTextBox = SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj);
    }

    SwFlyFrame* pFly = nullptr;
    const SwFrame* pFooterOrHeader = nullptr;

    if( bFlyFrame )
    {
        // Calculate reference point in document coordinates
        SwContentFrame *pContent = GetCurrFrame( false );
        if( !pContent )
            return aRet;
        pFly = pContent->FindFlyFrame();
        if ( !pFly )
            return aRet;
        const SwFrame* pOldAnch = pFly->GetAnchorFrame();
        if( !pOldAnch )
            return aRet;
        if ( RndStdIds::FLY_AT_PAGE != nAnchorId )
        {
            pFooterOrHeader = pContent->FindFooterOrHeader();
        }
    }
    else if (bTextBox)
    {
        auto pFlyFormat
            = dynamic_cast<const SwFlyFrameFormat*>(SwTextBoxHelper::getOtherTextBoxFormat(
                pFormat, RES_DRAWFRMFMT, pObj));
        if (pFlyFormat)
        {
            pFly = pFlyFormat->GetFrame();
        }
    }

    // set <pFooterOrHeader> also for drawing
    // objects, but not for control objects.
    // Necessary for moving 'anchor symbol' at the user interface inside header/footer.
    else if ( !::CheckControlLayer( pObj ) )
    {
        SwContentFrame *pContent = GetCurrFrame( false );
        if( !pContent )
            return aRet;
        pFooterOrHeader = pContent->FindFooterOrHeader();
    }

    // Search nearest SwFlyFrame starting from the upper-left corner
    // of the fly
    SwContentFrame *pTextFrame = nullptr;
    {
        SwCursorMoveState aState( CursorMoveState::SetOnlyText );
        SwPosition aPos( GetDoc()->GetNodes().GetEndOfExtras() );
        Point aTmpPnt( rAbsPos );
        GetLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState );
        if (aPos.GetNode() != GetDoc()->GetNodes().GetEndOfExtras()
            && (nAnchorId != RndStdIds::FLY_AT_CHAR || !PosInsideInputField(aPos)))
        {
            SwContentNode* pCNode = aPos.GetNode().GetContentNode();
            assert(pCNode);
            pTextFrame = pCNode->getLayoutFrame(GetLayout(), &aPos, nullptr);
        }
    }
    const SwFrame *pNewAnch = nullptr;
    if( pTextFrame != nullptr )
    {
        if ( RndStdIds::FLY_AT_PAGE == nAnchorId )
        {
            pNewAnch = pTextFrame->FindPageFrame();
        }
        else
        {
            pNewAnch = ::FindAnchor( pTextFrame, rAbsPos );

            if( RndStdIds::FLY_AT_FLY == nAnchorId ) // LAYER_IMPL
            {
                pNewAnch = pNewAnch->FindFlyFrame();
            }
        }
    }

    if( pNewAnch && !pNewAnch->IsProtected() )
    {
        const SwFlyFrame* pCheck = (bFlyFrame || bTextBox) ? pNewAnch->FindFlyFrame() : nullptr;
        // If we land inside the frame, make sure
        // that the frame does not land inside its own content
        while( pCheck )
        {
            if( pCheck == pFly )
                break;
            const SwFrame *pTmp = pCheck->GetAnchorFrame();
            pCheck = pTmp ? pTmp->FindFlyFrame() : nullptr;
        }

        // Do not switch from header/footer to another area,
        // do not switch to a header/footer
        if( !pCheck &&
            pFooterOrHeader == pNewAnch->FindFooterOrHeader() )
        {
            aRet = pNewAnch->GetFrameAnchorPos( ::HasWrap( pObj ) );

            if ( bMoveIt || (nAnchorId == RndStdIds::FLY_AT_CHAR) )
            {
                SwFormatAnchor aAnch( pFormat->GetAnchor() );
                switch ( nAnchorId )
                {
                    case RndStdIds::FLY_AT_PARA:
                    {
                        SwPosition pos(pTextFrame->IsTextFrame()
                            ? *static_cast<SwTextFrame const*>(pTextFrame)->GetTextNodeForParaProps()
                            : *static_cast<const SwNoTextFrame*>(pTextFrame)->GetNode());
                        aAnch.SetAnchor( &pos );
                        break;
                    }
                    case RndStdIds::FLY_AT_PAGE:
                    {
                        aAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnch)->
                                          GetPhyPageNum() );
                        break;
                    }

                    case RndStdIds::FLY_AT_FLY:
                    {
                        SwPosition aPos( *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()->
                                                  GetContent().GetContentIdx() );
                        aAnch.SetAnchor( &aPos );
                        break;
                    }

                    case RndStdIds::FLY_AT_CHAR:
                        {
                            SwPosition pos = *aAnch.GetContentAnchor();
                            Point aTmpPnt( rAbsPos );
                            if( pTextFrame->GetModelPositionForViewPoint( &pos, aTmpPnt ) )
                            {
                                SwRect aTmpRect;
                                pTextFrame->GetCharRect( aTmpRect, pos );
                                aRet = aTmpRect.Pos();
                            }
                            else
                            {
                                pos = static_cast<SwTextFrame const*>(pTextFrame)->MapViewToModelPos(TextFrameIndex(0));
                            }
                            aAnch.SetAnchor( &pos );
                            break;
                        }
                    default:
                        break;

                }

                if( bMoveIt )
                {
                    StartAllAction();
                    // --> handle change of anchor node:
                    // if count of the anchor frame also change, the fly frames have to be
                    // re-created. Thus, delete all fly frames except the <this> before the
                    // anchor attribute is change and re-create them afterwards.
                    {
                        std::unique_ptr<SwHandleAnchorNodeChg> pHandleAnchorNodeChg;
                        SwFlyFrameFormat* pFlyFrameFormat( dynamic_cast<SwFlyFrameFormat*>(pFormat) );
                        if ( pFlyFrameFormat )
                        {
                            pHandleAnchorNodeChg.reset(
                                new SwHandleAnchorNodeChg( *pFlyFrameFormat, aAnch ));
                        }
                        pFormat->GetDoc().SetAttr( aAnch, *pFormat );
                        if (SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
                            pObj))
                        {
                            if (SdrObjList* pObjList = pObj->getChildrenOfSdrObject())
                            {
                                for (const rtl::Reference<SdrObject>& pChild : *pObjList)
                                    SwTextBoxHelper::changeAnchor(pFormat, pChild.get());
                            }
                            else
                                SwTextBoxHelper::syncFlyFrameAttr(
                                    *pFormat, pFormat->GetAttrSet(), pObj);
                        }
                    }
                    // #i28701# - no call of method
                    // <CheckCharRectAndTopOfLine()> for to-character anchored
                    // Writer fly frame needed. This method call can cause a
                    // format of the anchor frame, which is no longer intended.
                    // Instead clear the anchor character rectangle and
                    // the top of line values for all to-character anchored objects.
                    pAnchoredObj->ClearCharRectAndTopOfLine();
                    EndAllAction();
                }
            }

            SwRect aTmpRect( aRet, rAbsPos );
            if( aTmpRect.HasArea() )
                MakeVisible( aTmpRect );
#if OSL_DEBUG_LEVEL > 0
            //TODO: That doesn't seem to be intended
            if( COL_TRANSPARENT != GetOut()->GetLineColor() )
            {
                OSL_FAIL( "Hey, Joe: Where's my Null Pen?" );
                GetOut()->SetLineColor( COL_TRANSPARENT );
            }
#endif
        }
    }

    return aRet;
}

const SwFrameFormat *SwFEShell::NewFlyFrame( const SfxItemSet& rSet, bool bAnchValid,
                           SwFrameFormat *pParent )
{
    CurrShell aCurr( this );
    StartAllAction();

    SwPaM* pCursor = GetCursor();
    const Point aPt( GetCursorDocPos() );

    SwSelBoxes aBoxes;
    bool bMoveContent = true;
    if( IsTableMode() )
    {
        GetTableSel( *this, aBoxes );
        if( !aBoxes.empty() )
        {
            // Cursor should be removed from the removal area.
            // Always put it after/on the table; via the
            // document position they will be set to the old
            // position
            ParkCursor( *aBoxes[0]->GetSttNd() );

            // #i127787# pCurrentCursor will be deleted in ParkCursor,
            // we better get the current pCurrentCursor instead of working with the
            // deleted one:
            pCursor = GetCursor();
        }
        else
            bMoveContent = false;
    }
    else if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
        bMoveContent = false;

    const SwPosition& rPos = *pCursor->Start();

    SwFormatAnchor& rAnch = const_cast<SwFormatAnchor&>(rSet.Get( RES_ANCHOR ));
    RndStdIds eRndId = rAnch.GetAnchorId();
    switch( eRndId )
    {
    case RndStdIds::FLY_AT_PAGE:
        if( !rAnch.GetPageNum() )       //HotFix: Bug in UpdateByExample
            rAnch.SetPageNum( 1 );
        break;

    case RndStdIds::FLY_AT_FLY:
    case RndStdIds::FLY_AT_PARA:
    case RndStdIds::FLY_AT_CHAR:
    case RndStdIds::FLY_AS_CHAR:
        if( !bAnchValid )
        {
            if( RndStdIds::FLY_AT_FLY != eRndId )
            {
                rAnch.SetAnchor( &rPos );
            }
            else if( lcl_SetNewFlyPos( rPos.GetNode(), rAnch, aPt ) )
            {
                eRndId = RndStdIds::FLY_AT_PAGE;
            }
        }
        break;

    default:
        OSL_ENSURE( false, "What is the purpose of this Fly?" );
        break;
    }

    SwFlyFrameFormat *pRet;
    if( bMoveContent )
    {
        GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr );
        std::unique_ptr<SwFormatAnchor> pOldAnchor;
        bool bHOriChgd = false, bVOriChgd = false;
        std::shared_ptr<SwFormatVertOrient> aOldV;
        std::shared_ptr<SwFormatHoriOrient> aOldH;

        if ( RndStdIds::FLY_AT_PAGE != eRndId )
        {
           // First as with page link. Paragraph/character link on if
            // everything was shifted. Then the position is valid!
            // JP 13.05.98: if necessary also convert the horizontal/vertical
            //              orientation, to prevent correction during re-anchoring
            pOldAnchor.reset(new SwFormatAnchor( rAnch ));
            const_cast<SfxItemSet&>(rSet).Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, 1 ) );

            const SwFormatHoriOrient* pHoriOrientItem;
            if( (pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false ))
                && text::HoriOrientation::NONE == pHoriOrientItem->GetHoriOrient() )
            {
                bHOriChgd = true;
                aOldH.reset(pHoriOrientItem->Clone());
                const_cast<SfxItemSet&>(rSet).Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT ) );
            }
            const SwFormatVertOrient* pVertOrientItem;
            if( (pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false ))
                && text::VertOrientation::NONE == pVertOrientItem->GetVertOrient() )
            {
                bVOriChgd = true;
                aOldV.reset(pVertOrientItem->Clone());
                const_cast<SfxItemSet&>(rSet).Put( SwFormatVertOrient( 0, text::VertOrientation::TOP ) );
            }
        }

        pRet = GetDoc()->MakeFlyAndMove( *pCursor, rSet, &aBoxes, pParent );

        KillPams();

        if( pOldAnchor )
        {
            if( pRet )
            {
                // calculate new position
                // JP 24.03.97: also go via page links
                //              anchor should not lie in the shifted area
                pRet->DelFrames();

                const SwFrame* pAnch = ::FindAnchor( GetLayout(), aPt );
                SwPosition aPos( pAnch->IsTextFrame()
                    ? *static_cast<SwTextFrame const*>(pAnch)->GetTextNodeForParaProps()
                    : *static_cast<const SwNoTextFrame*>(pAnch)->GetNode() );

                if ( RndStdIds::FLY_AS_CHAR == eRndId )
                {
                    assert(pAnch->IsTextFrame());
                    aPos = static_cast<SwTextFrame const*>(pAnch)->MapViewToModelPos(TextFrameIndex(0));
                }
                pOldAnchor->SetAnchor( &aPos );

                // shifting of table selection is not Undo-capable. therefore
                // changing the anchors should not be recorded
                bool const bDoesUndo =
                    GetDoc()->GetIDocumentUndoRedo().DoesUndo();
                SwUndoId nLastUndoId(SwUndoId::EMPTY);
                if (bDoesUndo &&
                    GetDoc()->GetIDocumentUndoRedo().GetLastUndoInfo(nullptr,
                        & nLastUndoId))
                {
                    if (SwUndoId::INSLAYFMT == nLastUndoId)
                    {
                        GetDoc()->GetIDocumentUndoRedo().DoUndo(false);
                    }
                }

                const_cast<SfxItemSet&>(rSet).Put( std::move(pOldAnchor) );

                if( bHOriChgd )
                    const_cast<SfxItemSet&>(rSet).Put( *aOldH );
                if( bVOriChgd )
                    const_cast<SfxItemSet&>(rSet).Put( *aOldV );

                GetDoc()->SetFlyFrameAttr( *pRet, const_cast<SfxItemSet&>(rSet) );
                GetDoc()->GetIDocumentUndoRedo().DoUndo(bDoesUndo);
            }
        }
        GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr );
    }
    else
        /* If called from a shell try to propagate an
            existing adjust item from rPos to the content node of the
            new frame. */
        pRet = GetDoc()->MakeFlySection( eRndId, &rPos, &rSet, pParent, true );

    if( pRet )
    {
        SwFlyFrame* pFrame = pRet->GetFrame( &aPt );
        if( pFrame )
            SelectFlyFrame( *pFrame );
        else
        {
            GetLayout()->SetAssertFlyPages();
            pRet = nullptr;
        }
    }
    EndAllActionAndCall();

    return pRet;
}

namespace
{
/// If pCursor points to an as-char anchored graphic node, then set the node's anchor position on
/// pAnchor and rPam.
bool SetAnchorOnGrfNodeForAsChar(SwShellCursor *pCursor, SwFormatAnchor* pAnchor, std::optional<SwPaM>& rPam)
{
    const SwPosition* pPoint = pCursor->GetPoint();
    if (pAnchor->GetAnchorId() != RndStdIds::FLY_AS_CHAR)
    {
        return false;
    }

    if (!pPoint->GetNode().IsGrfNode())
    {
        return false;
    }

    SwFrameFormat* pFrameFormat = pPoint->GetNode().GetFlyFormat();
    if (!pFrameFormat)
    {
        return false;
    }

    const SwPosition* pContentAnchor = pFrameFormat->GetAnchor().GetContentAnchor();
    if (!pContentAnchor)
    {
        return false;
    }

    SwPosition aPosition(*pContentAnchor);
    ++aPosition.nContent;
    pAnchor->SetAnchor(&aPosition);
    rPam.emplace(aPosition);
    return true;
}
}

void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName,
                        const Graphic* pGraphic,
                        const SfxItemSet* pFlyAttrSet )
{
    SwFlyFrameFormat* pFormat = nullptr;
    CurrShell aCurr( this );
    StartAllAction();
    SwShellCursor *pStartCursor = dynamic_cast<SwShellCursor*>(GetCursor());
    SwShellCursor *pCursor = pStartCursor;
    do
    {
        if (!pCursor)
            break;

        // Has the anchor not been set or been set incompletely?
        std::optional<SwPaM> oPam;
        if( pFlyAttrSet )
        {
            if( const SwFormatAnchor* pItem = pFlyAttrSet->GetItemIfSet( RES_ANCHOR, false ) )
            {
                SwFormatAnchor* pAnchor = const_cast<SwFormatAnchor*>(pItem);
                switch( pAnchor->GetAnchorId())
                {
                case RndStdIds::FLY_AT_PARA:
                case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
                case RndStdIds::FLY_AS_CHAR:
                    if( !pAnchor->GetAnchorNode() )
                    {
                        if (SetAnchorOnGrfNodeForAsChar(pCursor, pAnchor, oPam))
                        {
                            // Don't anchor the image on the previous image, rather insert them next
                            // to each other.
                            break;
                        }

                        pAnchor->SetAnchor( pCursor->GetPoint() );
                    }
                    break;
                case RndStdIds::FLY_AT_FLY:
                    if( !pAnchor->GetAnchorNode() )
                    {
                        lcl_SetNewFlyPos( pCursor->GetPointNode(),
                                *pAnchor, GetCursorDocPos() );
                    }
                    break;
                case RndStdIds::FLY_AT_PAGE:
                    if( !pAnchor->GetPageNum() )
                    {
                        pAnchor->SetPageNum( pCursor->GetPageNum(
                                true, &pCursor->GetPtPos() ) );
                    }
                    break;
                default :
                    break;
                }
            }
        }
        pFormat = GetDoc()->getIDocumentContentOperations().InsertGraphic(
                                oPam.has_value() ? *oPam : *pCursor, rGrfName,
                                rFltName, pGraphic,
                                pFlyAttrSet,
                                nullptr, nullptr );
        OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertGraphic failed.");

        pCursor = pCursor->GetNext();
    } while( pCursor != pStartCursor );

    EndAllAction();

    if( !pFormat )
        return;

    const Point aPt( GetCursorDocPos() );
    SwFlyFrame* pFrame = pFormat->GetFrame( &aPt );

    if( pFrame )
    {
        // add a redline to the anchor point at tracked insertion of picture
        if ( IsRedlineOn() )
        {
            const SwPosition & rPos(*pFormat->GetAnchor().GetContentAnchor());
            SwPaM aPaM(rPos.GetNode(), rPos.GetContentIndex(),
                    rPos.GetNode(), rPos.GetContentIndex() + 1);
            GetDoc()->getIDocumentRedlineAccess().AppendRedline(
                    new SwRangeRedline( RedlineType::Insert, aPaM ), true);
        }

        // fdo#36681: Invalidate the content and layout to refresh
        // the picture anchoring properly
        SwPageFrame* pPageFrame = pFrame->FindPageFrameOfAnchor();
        pPageFrame->InvalidateFlyLayout();
        pPageFrame->InvalidateContent();

        SelectFlyFrame( *pFrame );
    }
    else
        GetLayout()->SetAssertFlyPages();
}

SwFlyFrameFormat* SwFEShell::InsertObject( const svt::EmbeddedObjectRef&  xObj,
                        SfxItemSet* pFlyAttrSet )
{
    SwFlyFrameFormat* pFormat = nullptr;
    CurrShell aCurr( this );
    StartAllAction();
    {
        for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
        {
            pFormat = GetDoc()->getIDocumentContentOperations().InsertEmbObject(
                            rPaM, xObj, pFlyAttrSet );
            OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertEmbObject failed.");
        }
    }
    EndAllAction();

    if( pFormat )
    {
        const Point aPt( GetCursorDocPos() );
        SwFlyFrame* pFrame = pFormat->GetFrame( &aPt );

        if( pFrame )
            SelectFlyFrame( *pFrame );
        else
            GetLayout()->SetAssertFlyPages();
    }

    return pFormat;
}

void SwFEShell::InsertDrawObj( SdrObject& rDrawObj,
                               const Point& rInsertPosition )
{
    CurrShell aCurr( this );

    SfxItemSet rFlyAttrSet( GetDoc()->GetAttrPool(), aFrameFormatSetRange );
    rFlyAttrSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
    // #i89920#
    rFlyAttrSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) );
    rDrawObj.SetLayer( getIDocumentDrawModelAccess().GetHeavenId() );

    // find anchor position
    SwPaM aPam( mxDoc->GetNodes() );
    {
        SwCursorMoveState aState( CursorMoveState::SetOnlyText );
        Point aTmpPt( rInsertPosition );
        GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aTmpPt, &aState );
        const SwFrame* pFrame = aPam.GetPointContentNode()->getLayoutFrame(GetLayout(), nullptr, nullptr);
        const Point aRelPos( rInsertPosition.X() - pFrame->getFrameArea().Left(),
                             rInsertPosition.Y() - pFrame->getFrameArea().Top() );
        rDrawObj.SetRelativePos( aRelPos );
        ::lcl_FindAnchorPos( *GetDoc(), rInsertPosition, *pFrame, rFlyAttrSet );
    }
    // insert drawing object into the document creating a new <SwDrawFrameFormat> instance
    SwDrawFrameFormat* pFormat = GetDoc()->getIDocumentContentOperations().InsertDrawObj( aPam, rDrawObj, rFlyAttrSet );

    // move object to visible layer
    SwContact* pContact = static_cast<SwContact*>(rDrawObj.GetUserCall());
    if ( pContact )
    {
        pContact->MoveObjToVisibleLayer( &rDrawObj );
    }

    if (pFormat)
    {
        pFormat->SetFormatName(UIName(rDrawObj.GetName()));
        // select drawing object
        Imp()->GetDrawView()->MarkObj( &rDrawObj, Imp()->GetPageView() );
    }
    else
    {
        GetLayout()->SetAssertFlyPages();
    }
}

void SwFEShell::GetPageObjs( std::vector<SwFrameFormat*>& rFillArr )
{
    rFillArr.clear();

    for(sw::SpzFrameFormat* pFormat : *mxDoc->GetSpzFrameFormats() )
    {
        if (RndStdIds::FLY_AT_PAGE == pFormat->GetAnchor().GetAnchorId())
        {
            rFillArr.push_back( pFormat );
        }
    }
}

void SwFEShell::SetPageObjsNewPage( std::vector<SwFrameFormat*>& rFillArr )
{
    if( rFillArr.empty() )
        return;

    StartAllAction();
    StartUndo();

    SwRootFrame* pTmpRootFrame = GetLayout();
    sal_uInt16 nMaxPage = pTmpRootFrame->GetPageNum();
    bool bTmpAssert = false;
    for( auto pFormat : rFillArr )
    {
        if (mxDoc->GetSpzFrameFormats()->IsAlive(static_cast<sw::SpzFrameFormat*>(pFormat)))
        {
            // FlyFormat is still valid, therefore process

            SwFormatAnchor aNewAnchor( pFormat->GetAnchor() );
            if (RndStdIds::FLY_AT_PAGE != aNewAnchor.GetAnchorId())
                // Anchor has been changed, therefore: do not change!
                continue;
            sal_uInt16 nNewPage = aNewAnchor.GetPageNum() + 1;
            if (nNewPage > nMaxPage)
            {
                if ( RES_DRAWFRMFMT == pFormat->Which() )
                    pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PAGE_OUT_OF_BOUNDS));
                else
                    pFormat->DelFrames();
                bTmpAssert = true;
            }
            aNewAnchor.SetPageNum(nNewPage);
            mxDoc->SetAttr( aNewAnchor, *pFormat );
        }
    }

    if( bTmpAssert )
        pTmpRootFrame->SetAssertFlyPages();

    EndUndo();
    EndAllAction();
}

// All attributes in the "baskets" will be filled with the attributes of the
// current FlyFrames. Attributes which cannot be filled due to being at the
// wrong place or which are ambiguous (multiple selections) will be removed.
bool SwFEShell::GetFlyFrameAttr( SfxItemSet &rSet ) const
{
    SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
    if (!pFly)
    {
        OSL_ENSURE( false, "GetFlyFrameAttr, no Fly selected." );
        return false;
    }

    CurrShell aCurr( const_cast<SwFEShell*>(this) );

    if( !rSet.Set( pFly->GetFormat()->GetAttrSet() ) )
        return false;

    // now examine all attributes. Remove forbidden attributes, then
    // get all remaining attributes and enter them

    if( const SwFormatAnchor* pAnchor = rSet.GetItemIfSet( RES_ANCHOR, false ) )
    {
        RndStdIds eType = pAnchor->GetAnchorId();

        if ( RndStdIds::FLY_AT_PAGE != eType )
        {
            // OD 12.11.2003 #i22341# - content anchor of anchor item is needed.
            // Thus, don't overwrite anchor item by default constructed anchor item.
            if ( RndStdIds::FLY_AS_CHAR == eType )
            {
                rSet.ClearItem( RES_OPAQUE );
                rSet.ClearItem( RES_SURROUND );
            }
        }
    }
    rSet.SetParent( pFly->GetFormat()->GetAttrSet().GetParent() );
    // attributes must be removed
    rSet.ClearItem( RES_FILL_ORDER );
    rSet.ClearItem( RES_CNTNT );
    //MA: remove first (Template by example etc.)
    rSet.ClearItem( RES_CHAIN );
    return true;
}

// Attributes of the current fly will change.
bool SwFEShell::SetFlyFrameAttr( SfxItemSet& rSet )
{
    CurrShell aCurr( this );
    bool bRet = false;

    if( rSet.Count() )
    {
        SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
        OSL_ENSURE(pFly, "SetFlyFrameAttr, no Fly selected.");
        if (pFly)
        {
            StartAllAction();
            const Point aPt( pFly->getFrameArea().Pos() );

            if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
                sw_ChkAndSetNewAnchor( *pFly, rSet );
            SwFlyFrameFormat* pFlyFormat = pFly->GetFormat();

            if( GetDoc()->SetFlyFrameAttr( *pFlyFormat, rSet ))
            {
                bRet = true;
                SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt );
                if( pFrame )
                    SelectFlyFrame( *pFrame );
                else
                    GetLayout()->SetAssertFlyPages();
            }

            EndAllActionAndCall();
        }
    }
    return bRet;
}

SfxItemSet SwFEShell::makeItemSetFromFormatAnchor(SfxItemPool& rPool, const SwFormatAnchor &rAnchor)
{
    // The set also includes VERT/HORI_ORIENT, because the align
    // shall be changed in FEShell::SetFlyFrameAttr/SetFlyFrameAnchor,
    // possibly as a result of the anchor change.
    SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<RES_VERT_ORIENT, RES_ANCHOR>(rPool));
    aSet.Put(rAnchor);
    return aSet;
}

bool SwFEShell::SetDrawingAttr( SfxItemSet& rSet )
{
    bool bRet = false;
    CurrShell aCurr( this );
    if ( !rSet.Count() ||
            !Imp()->HasDrawView() )
        return bRet;

    const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
    if ( rMrkList.GetMarkCount() != 1 )
        return bRet;

    StartUndo();
    SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
    SwFrameFormat *pFormat = FindFrameFormat( pObj );
    StartAllAction();
    if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
    {
        RndStdIds nNew = rSet.Get( RES_ANCHOR ).GetAnchorId();
        if ( nNew != pFormat->GetAnchor().GetAnchorId() )
        {
            ChgAnchor( nNew );
            // #i26791# - clear anchor attribute in item set,
            // because method <ChgAnchor(..)> takes care of it.
            rSet.ClearItem( RES_ANCHOR );
        }
    }

    if( GetDoc()->SetFlyFrameAttr( *pFormat, rSet ))
    {
        bRet = true;
        SelectObj( Point(), 0, pObj );
    }
    EndAllActionAndCall();
    EndUndo();
    return bRet;
}

// Reset attributes contained in the set.
void SwFEShell::ResetFlyFrameAttr( const SfxItemSet* pSet )
{
    CurrShell aCurr( this );

    SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
    OSL_ENSURE( pFly, "SetFlyFrameAttr, no Fly selected." );
    if( !pFly )
        return;

    StartAllAction();

    SfxItemIter aIter( *pSet );
    for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
    {
        if( !IsInvalidItem( pItem ) )
        {
            sal_uInt16 nWhich = pItem->Which();
            if( RES_ANCHOR != nWhich && RES_CHAIN != nWhich && RES_CNTNT != nWhich )
                pFly->GetFormat()->ResetFormatAttr( nWhich );
        }
    }

    EndAllActionAndCall();
    GetDoc()->getIDocumentState().SetModified();
}

// Returns frame-format if frame, otherwise 0
SwFrameFormat* SwFEShell::GetSelectedFrameFormat() const
{
    SwFrameFormat* pRet = nullptr;
    SwLayoutFrame *pFly = GetSelectedFlyFrame();
    if( pFly && ( pRet = static_cast<SwFrameFormat*>(pFly->GetFormat()->DerivedFrom()) ) ==
                                            GetDoc()->GetDfltFrameFormat() )
        pRet = nullptr;
    return pRet;
}

void SwFEShell::SetFrameFormat( SwFrameFormat *pNewFormat, bool bKeepOrient, Point const * pDocPos )
{
    SwFlyFrame *pFly = nullptr;
    if(pDocPos)
    {
        const SwFrameFormat* pFormat = GetFormatFromObj( *pDocPos );

        if (const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(pFormat))
            pFly = pFlyFormat->GetFrame();
    }
    else
        pFly = GetSelectedFlyFrame();
    OSL_ENSURE( pFly, "SetFrameFormat: no frame" );
    if( !pFly )
        return;

    StartAllAction();
    CurrShell aCurr( this );

    SwFlyFrameFormat* pFlyFormat = pFly->GetFormat();
    const Point aPt( pFly->getFrameArea().Pos() );

    std::optional<SfxItemSet> oSet;
    const SfxPoolItem* pItem;
    if( SfxItemState::SET == pNewFormat->GetItemState( RES_ANCHOR, false, &pItem ))
    {
        oSet.emplace( GetDoc()->GetAttrPool(), aFrameFormatSetRange );
        oSet->Put( *pItem );
        if( !sw_ChkAndSetNewAnchor( *pFly, *oSet ))
        {
            oSet.reset();
        }
    }

    if( GetDoc()->SetFrameFormatToFly( *pFlyFormat, *pNewFormat, oSet ? &*oSet : nullptr, bKeepOrient ))
    {
        SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt );
        if( pFrame )
            SelectFlyFrame( *pFrame );
        else
            GetLayout()->SetAssertFlyPages();
    }
    oSet.reset();

    EndAllActionAndCall();
}

const SwFrameFormat* SwFEShell::GetFlyFrameFormat() const
{
    const SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame();
    if (pFly)
        return pFly->GetFormat();
    return nullptr;
}

SwFrameFormat* SwFEShell::GetFlyFrameFormat()
{
    SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame();
    if (pFly)
        return pFly->GetFormat();
    return nullptr;
}

SwRect SwFEShell::GetFlyRect() const
{
    SwFlyFrame *pFly = GetCurrFlyFrame(false);
    if (!pFly)
    {
        SwRect aRect;
        return aRect;
    }
    else
        return pFly->getFrameArea();
}

SwRect SwFEShell::GetObjRect() const
{
    if( Imp()->HasDrawView() )
        return SwRect(Imp()->GetDrawView()->GetAllMarkedRect());
    else
    {
        SwRect aRect;
        return aRect;
    }
}

void SwFEShell::SetObjRect( const SwRect& rRect )
{
    if ( Imp()->HasDrawView() )
    {
        Imp()->GetDrawView()->SetAllMarkedRect( rRect.SVRect() );
        CallChgLnk();   // call AttrChangeNotify on the UI-side.
    }
}

Size SwFEShell::RequestObjectResize( const SwRect &rRect, const uno::Reference < embed::XEmbeddedObject >& xObj )
{
    Size aResult;

    SwFlyFrame *pFly = FindFlyFrame( xObj );
    if ( !pFly )
    {
        aResult = rRect.SSize();
        return aResult;
    }

    aResult = pFly->getFramePrintArea().SSize();

    bool bPosProt = pFly->GetFormat()->GetProtect().IsPosProtected();
    bool bSizeProt = pFly->GetFormat()->GetProtect().IsSizeProtected();

    StartAllAction();

    // MA we do not allow to clip the Fly, as the OLE server can
    // request various wishes. Clipping is done via the formatting.
    // Correct display is done by scaling.
    // Scaling is done by SwNoTextFrame::Format by calling
    // SwWrtShell::CalcAndSetScale()
    if ( rRect.SSize() != pFly->getFramePrintArea().SSize() && !bSizeProt )
    {
        Size aSz( rRect.SSize() );

        //JP 28.02.2001: Task 74707 - ask for fly in fly with automatic size

        const SwFrame* pAnchor;
        const SwFormatFrameSize& rFrameSz = pFly->GetFormat()->GetFrameSize();
        if (m_bCheckForOLEInCaption &&
            0 != rFrameSz.GetWidthPercent() &&
            nullptr != (pAnchor = pFly->GetAnchorFrame()) &&
            pAnchor->IsTextFrame() &&
            !pAnchor->GetNext() && !pAnchor->GetPrev() &&
            pAnchor->GetUpper()->IsFlyFrame())
        {
            // search for a sequence field:
            sw::MergedAttrIter iter(*static_cast<SwTextFrame const*>(pAnchor));
            for (SwTextAttr const* pHint = iter.NextAttr(); pHint; pHint = iter.NextAttr())
            {
                const SfxPoolItem* pItem = &pHint->GetAttr();
                if( RES_TXTATR_FIELD == pItem->Which()
                    && SwFieldTypesEnum::Sequence == static_cast<const SwFormatField*>(pItem)->GetField()->GetTypeId() )
                {
                    // sequence field found
                    SwFlyFrame* pChgFly = const_cast<SwFlyFrame*>(static_cast<const SwFlyFrame*>(pAnchor->GetUpper()));
                    // calculate the changed size:
                    // width must change, height can change
                    Size aNewSz( aSz.Width() + pChgFly->getFrameArea().Width() -
                                   pFly->getFramePrintArea().Width(), aSz.Height() );

                    SwFrameFormat *pFormat = pChgFly->GetFormat();
                    SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() );
                    aFrameSz.SetWidth( aNewSz.Width() );
                    if( SwFrameSize::Minimum != aFrameSz.GetHeightSizeType() )
                    {
                        aNewSz.AdjustHeight(pChgFly->getFrameArea().Height() -
                                               pFly->getFramePrintArea().Height() );
                        if( std::abs( aNewSz.Height() - pChgFly->getFrameArea().Height()) > 1 )
                            aFrameSz.SetHeight( aNewSz.Height() );
                    }
                    // via Doc for the Undo!
                    pFormat->GetDoc().SetAttr( aFrameSz, *pFormat );
                    break;
                }
            }
        }

        // set the new Size at the fly themself
        if ( !pFly->getFramePrintArea().IsEmpty() )
        {
            aSz.AdjustWidth(pFly->getFrameArea().Width() - pFly->getFramePrintArea().Width() );
            aSz.AdjustHeight(pFly->getFrameArea().Height()- pFly->getFramePrintArea().Height() );
        }
        aResult = pFly->ChgSize( aSz );

        // if the object changes, the contour is outside the object
        SwFrame* pLower = pFly->Lower();
        assert(pLower && pLower->IsNoTextFrame());
        SwNoTextNode *pNd = static_cast<SwNoTextFrame*>(pLower)->GetNode()->GetNoTextNode();
        assert(pNd);
        pNd->SetContour( nullptr );
        ClrContourCache();
    }

    // if only the size is to be adjusted, a position is transported with
    // allocated values
    Point aPt( pFly->getFramePrintArea().Pos() );
    aPt += pFly->getFrameArea().Pos();
    if ( rRect.Top() != LONG_MIN && rRect.Pos() != aPt && !bPosProt )
    {
        aPt = rRect.Pos();
        aPt.setX(aPt.getX() - pFly->getFramePrintArea().Left());
        aPt.setY(aPt.getY() - pFly->getFramePrintArea().Top());

        // in case of paragraph-bound Flys, starting from the new position,
        // a new anchor is to be set. The anchor and the new RelPos are
        // calculated by the Fly and set
        if( pFly->IsFlyAtContentFrame() )
            static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( aPt );
        else
        {
            const SwFrameFormat *pFormat = pFly->GetFormat();
            const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
            const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
            const tools::Long lXDiff = aPt.getX() - pFly->getFrameArea().Left();
            const tools::Long lYDiff = aPt.getY() - pFly->getFrameArea().Top();
            const Point aTmp( rHori.GetPos() + lXDiff,
                              rVert.GetPos() + lYDiff );
            pFly->ChgRelPos( aTmp );
        }
    }

    SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat();
    OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" );
    if ( pFlyFrameFormat )
        pFlyFrameFormat->SetLastFlyFramePrtRectPos( pFly->getFramePrintArea().Pos() ); //stores the value of last Prt rect

    EndAllAction();

    return aResult;
}

SwFrameFormat* SwFEShell::WizardGetFly()
{
    // do not search the Fly via the layout. Now we can delete a frame
    // without a valid layout. ( e.g. for the wizards )
    sw::SpzFrameFormats& rSpzArr = *mxDoc->GetSpzFrameFormats();
    if( !rSpzArr.empty() )
    {
        SwNode& rCursorNd = GetCursor()->GetPoint()->GetNode();
        if( rCursorNd > mxDoc->GetNodes().GetEndOfExtras() )
            // Cursor is in the body area!
            return nullptr;

        for( auto pFormat : rSpzArr )
        {
            const SwNodeIndex* pIdx = pFormat->GetContent( false ).GetContentIdx();
            SwStartNode* pSttNd;
            if( pIdx &&
                nullptr != ( pSttNd = pIdx->GetNode().GetStartNode() ) &&
                *pSttNd < rCursorNd &&
                rCursorNd < *pSttNd->EndOfSectionNode() )
            {
                // found: return immediately
                return pFormat;
            }
        }
    }
    return nullptr;
}

void SwFEShell::SetFlyName( const UIName& rName )
{
    SwLayoutFrame *pFly = GetSelectedFlyFrame();
    if( pFly )
        GetDoc()->SetFlyName( *static_cast<SwFlyFrameFormat*>(pFly->GetFormat()), rName );
    else {
        OSL_ENSURE( false, "no FlyFrame selected" );
    }
}

UIName SwFEShell::GetFlyName() const
{
    SwLayoutFrame *pFly = GetSelectedFlyFrame();
    if( pFly )
        return pFly->GetFormat()->GetName();

    OSL_ENSURE( false, "no FlyFrame selected" );
    return UIName();
}

uno::Reference < embed::XEmbeddedObject > SwFEShell::GetOleRef() const
{
    uno::Reference < embed::XEmbeddedObject > xObj;
    SwFlyFrame * pFly = GetSelectedFlyFrame();
    SwFrame* pLower = pFly ? pFly->Lower() : nullptr;
    if (pLower && pLower->IsNoTextFrame())
    {
        SwOLENode *pNd = static_cast<SwNoTextFrame*>(pLower)->GetNode()->GetOLENode();
        if (pNd)
            xObj = pNd->GetOLEObj().GetOleRef();
    }
    return xObj;
}

UIName SwFEShell::GetUniqueGrfName() const
{
    return GetDoc()->GetUniqueGrfName();
}

const SwFrameFormat* SwFEShell::IsURLGrfAtPos( const Point& rPt, OUString* pURL,
                                        OUString *pTargetFrameName,
                                        OUString *pDescription ) const
{
    if( !Imp()->HasDrawView() )
        return nullptr;

    SdrPageView* pPV;
    const SwFrameFormat* pRet = nullptr;
    SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());

    const auto nOld = pDView->GetHitTolerancePixel();
    pDView->SetHitTolerancePixel( 2 );

    SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO);
    SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
    if (pFlyObj)
    {
        SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
        const SwFormatURL &rURL = pFly->GetFormat()->GetURL();
        if( !rURL.GetURL().isEmpty() || rURL.GetMap() )
        {
            bool bSetTargetFrameName = pTargetFrameName != nullptr;
            bool bSetDescription = pDescription != nullptr;
            if ( rURL.GetMap() )
            {
                IMapObject *pObject = pFly->GetFormat()->GetIMapObject( rPt, pFly );
                if ( pObject && !pObject->GetURL().isEmpty() )
                {
                    if( pURL )
                        *pURL = pObject->GetURL();
                    if ( bSetTargetFrameName && !pObject->GetTarget().isEmpty() )
                    {
                        bSetTargetFrameName = false;
                        *pTargetFrameName = pObject->GetTarget();
                    }
                    if ( bSetDescription )
                    {
                        bSetDescription = false;
                        *pDescription = pObject->GetAltText();
                    }
                    pRet = pFly->GetFormat();
                }
            }
            else
            {
                if( pURL )
                {
                    *pURL = rURL.GetURL();
                    if( rURL.IsServerMap() )
                    {
                       // append the relative pixel position !!
                        Point aPt( rPt );
                        aPt -= pFly->getFrameArea().Pos();
                        // without MapMode-Offset, without Offset, o ... !!!!!
                        aPt = GetOut()->LogicToPixel(
                                aPt, MapMode( MapUnit::MapTwip ) );
                        *pURL = *pURL + "?" + OUString::number( aPt.getX() )
                                + "," + OUString::number(aPt.getY() );
                    }
                }
                pRet = pFly->GetFormat();
            }
            if ( bSetTargetFrameName )
                *pTargetFrameName = rURL.GetTargetFrameName();
            if ( bSetDescription )
                *pDescription = pFly->GetFormat()->GetName().toString();
        }
    }
    pDView->SetHitTolerancePixel( nOld );
    return pRet;
}

const Graphic *SwFEShell::GetGrfAtPos( const Point &rPt,
                                       OUString &rName, bool &rbLink ) const
{
    if( !Imp()->HasDrawView() )
        return nullptr;

    SdrPageView* pPV;
    SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());

    SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV);
    SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
    if (pFlyObj)
    {
        SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
        SwFrame* pLower = pFly->Lower();
        if ( pLower && pLower->IsNoTextFrame() )
        {
            SwGrfNode *const pNd = static_cast<SwNoTextFrame*>(pLower)->GetNode()->GetGrfNode();
            if ( pNd )
            {
                if ( pNd->IsGrfLink() )
                {
                    // halfway ready graphic?
                    ::sfx2::SvLinkSource* pLnkObj = pNd->GetLink()->GetObj();
                    if( pLnkObj && pLnkObj->IsPending() )
                        return nullptr;
                    rbLink = true;
                }

                pNd->GetFileFilterNms( &rName, nullptr );
                if ( rName.isEmpty() )
                    rName = pFly->GetFormat()->GetName().toString();
                return &pNd->GetGrf(true);
            }
        }
    }
    return nullptr;
}

const SwFrameFormat* SwFEShell::GetFormatFromObj( const Point& rPt, SwRect** pRectToFill ) const
{
    SwFrameFormat* pRet = nullptr;

    if( Imp()->HasDrawView() )
    {
        SdrPageView* pPView;

        SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());

        const auto nOld = pDView->GetHitTolerancePixel();
        // tolerance for Drawing-SS
        pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );

        SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
        if (pObj)
        {
           // first check it:
            if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
                pRet = pFlyObj->GetFormat();
            else if ( pObj->GetUserCall() ) //not for group objects
                pRet = static_cast<SwDrawContact*>(pObj->GetUserCall())->GetFormat();
            if(pRet && pRectToFill)
                **pRectToFill = SwRect(pObj->GetCurrentBoundRect());
        }
        pDView->SetHitTolerancePixel( nOld );
    }
    return pRet;
}

// returns a format too, if the point is over the text of any fly
const SwFrameFormat* SwFEShell::GetFormatFromAnyObj( const Point& rPt ) const
{
    const SwFrameFormat* pRet = GetFormatFromObj( rPt );
    if( !pRet || RES_FLYFRMFMT == pRet->Which() )
    {
        SwPosition aPos( *GetCursor()->GetPoint() );
        Point aPt( rPt );
        GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
        SwContentNode *pNd = aPos.GetNode().GetContentNode();
        std::pair<Point, bool> const tmp(rPt, false);
        SwFrame* pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp)->FindFlyFrame();
        pRet = pFrame ? static_cast<SwLayoutFrame*>(pFrame)->GetFormat() : nullptr;
    }
    return pRet;
}

ObjCntType SwFEShell::GetObjCntType( const SdrObject& rObj )
{
    ObjCntType eType = OBJCNT_NONE;

    // investigate 'master' drawing object, if method
    // is called for a 'virtual' drawing object.
    const SdrObject* pInvestigatedObj;
    if (const SwDrawVirtObj* pDrawVirtObj = dynamic_cast<const SwDrawVirtObj*>( &rObj))
    {
        pInvestigatedObj = &(pDrawVirtObj->GetReferencedObj());
    }
    else
    {
        pInvestigatedObj = &rObj;
    }

    if( SdrInventor::FmForm == pInvestigatedObj->GetObjInventor() )
    {
        eType = OBJCNT_CONTROL;
        uno::Reference< awt::XControlModel >  xModel =
                static_cast<const SdrUnoObj&>(*pInvestigatedObj).GetUnoControlModel();
        if( xModel.is() )
        {
            uno::Any aVal;
            OUString sName(u"ButtonType"_ustr);
            uno::Reference< beans::XPropertySet >  xSet(xModel, uno::UNO_QUERY);

            uno::Reference< beans::XPropertySetInfo >  xInfo = xSet->getPropertySetInfo();
            if(xInfo->hasPropertyByName( sName ))
            {
                aVal = xSet->getPropertyValue( sName );
                if( aVal.hasValue() && form::FormButtonType_URL == *o3tl::doAccess<form::FormButtonType>(aVal) )
                    eType = OBJCNT_URLBUTTON;
            }
        }
    }
    else if (const SwVirtFlyDrawObj *pFlyObj = dynamic_cast<const SwVirtFlyDrawObj*>(pInvestigatedObj))
    {
        const SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
        const SwFrame* pLower = pFly->Lower();
        if ( pLower && pLower->IsNoTextFrame() )
        {
            if (static_cast<const SwNoTextFrame*>(pLower)->GetNode()->GetGrfNode())
                eType = OBJCNT_GRF;
            else
                eType = OBJCNT_OLE;
        }
        else
            eType = OBJCNT_FLY;
    }
    else if ( dynamic_cast<const SdrObjGroup*>( pInvestigatedObj) != nullptr )
    {
        SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(GetUserCall( pInvestigatedObj ) ) );
        if ( !pDrawContact )
        {
            OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing draw contact object" );
            eType = OBJCNT_NONE;
        }
        else
        {
            SwFrameFormat* pFrameFormat( pDrawContact->GetFormat() );
            if ( !pFrameFormat )
            {
                OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing frame format" );
                eType = OBJCNT_NONE;
            }
            else if ( RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() )
            {
                eType = OBJCNT_GROUPOBJ;
            }
        }
    }
    else
        eType = OBJCNT_SIMPLE;
    return eType;
}

ObjCntType SwFEShell::GetObjCntType( const Point &rPt, SdrObject *&rpObj ) const
{
    ObjCntType eType = OBJCNT_NONE;

    if( Imp()->HasDrawView() )
    {
        SdrPageView* pPView;

        SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());

        const auto nOld = pDView->GetHitTolerancePixel();
        // tolerance for Drawing-SS
        pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );

        SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
        if (pObj)
        {
            rpObj = pObj;
            eType = GetObjCntType( *rpObj );
        }

        pDView->SetHitTolerancePixel( nOld );
    }
    return eType;
}

ObjCntType SwFEShell::GetObjCntTypeOfSelection() const
{
    ObjCntType eType = OBJCNT_NONE;

    if( Imp()->HasDrawView() )
    {
        const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
        for( size_t i = 0, nE = rMrkList.GetMarkCount(); i < nE; ++i )
        {
            SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
            if( !pObj )
                continue;
            ObjCntType eTmp = GetObjCntType( *pObj );
            if( !i )
            {
                eType = eTmp;
            }
            else if( eTmp != eType )
            {
                eType = OBJCNT_DONTCARE;
                // once DontCare, always DontCare!
                break;
            }
        }
    }
    return eType;
}

void SwFEShell::ReplaceSdrObj( const OUString& rGrfName, const Graphic* pGrf )
{
    CurrShell aCurr( this );

    const SdrMarkList *pMrkList;
    if( !(Imp()->HasDrawView() &&  1 ==
        ( pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList())->GetMarkCount()) )
        return;

    SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
    assert(pObj);
    SwFrameFormat *pFormat = FindFrameFormat( pObj );

    // store attributes, then set the graphic
    SfxItemSet aFrameSet( mxDoc->GetAttrPool(),
                        pFormat->GetAttrSet().GetRanges() );
    aFrameSet.Set( pFormat->GetAttrSet() );

    // set size and position?
    if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
    {
        // then let's do it:
        const tools::Rectangle &rBound = pObj->GetSnapRect();
        Point aRelPos( pObj->GetRelativePos() );

        const tools::Long nWidth = rBound.Right()  - rBound.Left();
        const tools::Long nHeight= rBound.Bottom() - rBound.Top();
        aFrameSet.Put( SwFormatFrameSize( SwFrameSize::Minimum,
                            std::max( nWidth,  tools::Long(MINFLY) ),
                            std::max( nHeight, tools::Long(MINFLY) )));

        if( SfxItemState::SET != aFrameSet.GetItemState( RES_HORI_ORIENT ))
            aFrameSet.Put( SwFormatHoriOrient( aRelPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME ));

        if( SfxItemState::SET != aFrameSet.GetItemState( RES_VERT_ORIENT ))
            aFrameSet.Put( SwFormatVertOrient( aRelPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME ));

    }

    pObj->GetOrdNum();

    StartAllAction();
    StartUndo();

    // delete "Sdr-Object", insert the graphic instead
    DelSelectedObj();

    GetDoc()->getIDocumentContentOperations().InsertGraphic(
        *GetCursor(), rGrfName, u""_ustr, pGrf, &aFrameSet, nullptr, nullptr);

    EndUndo();
    EndAllAction();
}

static sal_uInt16 SwFormatGetPageNum(const SwFlyFrameFormat * pFormat)
{
    assert(pFormat != nullptr && "invalid argument");

    SwFlyFrame * pFrame = pFormat->GetFrame();

    sal_uInt16 aResult;

    if (pFrame != nullptr)
        aResult = pFrame->GetPhyPageNum();
    else
        aResult = pFormat->GetAnchor().GetPageNum();

    return aResult;
}

void SwFEShell::GetConnectableFrameFormats(SwFrameFormat & rFormat,
                                      std::u16string_view rReference,
                                      bool bSuccessors,
                                      std::vector< UIName > & aPrevPageVec,
                                      std::vector< UIName > & aThisPageVec,
                                      std::vector< UIName > & aNextPageVec,
                                      std::vector< UIName > & aRestVec)
{
    StartAction();

    SwFormatChain rChain = rFormat.GetChain();
    SwFrameFormat * pOldChainNext = rChain.GetNext();
    SwFrameFormat * pOldChainPrev = rChain.GetPrev();

    if (pOldChainNext)
        mxDoc->Unchain(rFormat);

    if (pOldChainPrev)
        mxDoc->Unchain(*pOldChainPrev);

    /* potential successors resp. predecessors */
    std::vector<const SwFrameFormat *> aTmpSpzArray = mxDoc->GetFlyFrameFormats(FLYCNTTYPE_FRM, false);

    for (auto it = aTmpSpzArray.begin(); it != aTmpSpzArray.end(); ++it)
    {
        /*
           pFormat is a potential successor of rFormat if it is chainable after
           rFormat.

           pFormat is a potential predecessor of rFormat if rFormat is chainable
           after pFormat.
        */

        SwChainRet nChainState;

        if (bSuccessors)
            nChainState = mxDoc->Chainable(rFormat, **it);
        else
            nChainState = mxDoc->Chainable(**it, rFormat);

        if (nChainState != SwChainRet::OK)
            *it = nullptr;
    }
    std::erase(aTmpSpzArray, nullptr);

    if  (!aTmpSpzArray.empty())
    {
        aPrevPageVec.clear();
        aThisPageVec.clear();
        aNextPageVec.clear();
        aRestVec.clear();

        /* number of page rFormat resides on */
        sal_uInt16 nPageNum = SwFormatGetPageNum(static_cast<SwFlyFrameFormat *>(&rFormat));

        for (const auto& rpFormat : aTmpSpzArray)
        {
            const UIName aString = rpFormat->GetName();

            /* rFormat is not a valid successor or predecessor of
               itself */
            if (aString != rReference && aString != rFormat.GetName())
            {
                sal_uInt16 nNum1 =
                    SwFormatGetPageNum(static_cast<const SwFlyFrameFormat *>(rpFormat));

                if (nNum1 == nPageNum -1)
                    aPrevPageVec.push_back(aString);
                else if (nNum1 == nPageNum)
                    aThisPageVec.push_back(aString);
                else if (nNum1 == nPageNum + 1)
                    aNextPageVec.push_back(aString);
                else
                    aRestVec.push_back(aString);
            }
        }

    }

    if (pOldChainNext)
        mxDoc->Chain(rFormat, *pOldChainNext);

    if (pOldChainPrev)
        mxDoc->Chain(*pOldChainPrev, rFormat);

    EndAction();
}

// #i73249#
OUString SwFEShell::GetObjTitle() const
{
    if ( Imp()->HasDrawView() )
    {
        const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
        if ( pMrkList->GetMarkCount() == 1 )
        {
            const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
            const SwFrameFormat* pFormat = FindFrameFormat( pObj );
            if ( pFormat->Which() == RES_FLYFRMFMT )
            {
                return static_cast<const SwFlyFrameFormat*>(pFormat)->GetObjTitle();
            }
            return pObj->GetTitle();
        }
    }

    return OUString();
}

void SwFEShell::SetObjTitle( const OUString& rTitle )
{
    if ( !Imp()->HasDrawView() )
        return;

    const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
    if ( pMrkList->GetMarkCount() != 1 )
        return;

    SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
    SwFrameFormat* pFormat = FindFrameFormat( pObj );
    if ( pFormat->Which() == RES_FLYFRMFMT )
    {
        GetDoc()->SetFlyFrameTitle( dynamic_cast<SwFlyFrameFormat&>(*pFormat),
                                  rTitle );
    }
    else
    {
        pObj->SetTitle( rTitle );
    }
}

OUString SwFEShell::GetObjDescription() const
{
    if ( Imp()->HasDrawView() )
    {
        const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
        if ( pMrkList->GetMarkCount() == 1 )
        {
            const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
            const SwFrameFormat* pFormat = FindFrameFormat( pObj );
            if ( pFormat->Which() == RES_FLYFRMFMT )
            {
                return dynamic_cast<const SwFlyFrameFormat&>(*pFormat).GetObjDescription();
            }
            return pObj->GetDescription();
        }
    }

    return OUString();
}

void SwFEShell::SetObjDescription( const OUString& rDescription )
{
    if ( !Imp()->HasDrawView() )
        return;

    const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
    if ( pMrkList->GetMarkCount() != 1 )
        return;

    SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
    SwFrameFormat* pFormat = FindFrameFormat( pObj );
    if ( pFormat->Which() == RES_FLYFRMFMT )
    {
        GetDoc()->SetFlyFrameDescription(dynamic_cast<SwFlyFrameFormat&>(*pFormat),
                                       rDescription);
    }
    else
    {
        pObj->SetDescription( rDescription );
    }
}

bool SwFEShell::IsObjDecorative() const
{
    if (!Imp()->HasDrawView())
    {
        return false;
    }

    SdrMarkList const& rMarkList(Imp()->GetDrawView()->GetMarkedObjectList());
    if (rMarkList.GetMarkCount() != 1)
    {
        return false;
    }

    SdrObject const*const pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
    SwFrameFormat const*const pFormat(FindFrameFormat(pObj));
    if (pFormat->Which() == RES_FLYFRMFMT)
    {
        return dynamic_cast<const SwFlyFrameFormat&>(*pFormat).GetAttrSet().Get(RES_DECORATIVE).GetValue();
    }
    return pObj->IsDecorative();
}

void SwFEShell::SetObjDecorative(bool const isDecorative)
{
    if (!Imp()->HasDrawView())
    {
        return;
    }

    SdrMarkList const& rMarkList(Imp()->GetDrawView()->GetMarkedObjectList());
    if (rMarkList.GetMarkCount() != 1)
    {
        return;
    }

    SdrObject *const pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
    SwFrameFormat *const pFormat(FindFrameFormat(pObj));
    if (pFormat->Which() == RES_FLYFRMFMT)
    {
        GetDoc()->SetFlyFrameDecorative(dynamic_cast<SwFlyFrameFormat&>(*pFormat),
                                       isDecorative);
    }
    else
    {
        pObj->SetDecorative(isDecorative);
    }
}


void SwFEShell::AlignFormulaToBaseline( const uno::Reference < embed::XEmbeddedObject >& xObj )
{
#if OSL_DEBUG_LEVEL > 0
    SvGlobalName aCLSID( xObj->getClassID() );
    const bool bStarMath = ( SotExchange::IsMath( aCLSID ) != 0 );
    OSL_ENSURE( bStarMath, "AlignFormulaToBaseline should only be called for Math objects" );

    if ( !bStarMath )
        return;
#endif

    SwFlyFrame * pFly = FindFlyFrame( xObj );
    OSL_ENSURE( pFly , "No fly frame!" );
    SwFrameFormat * pFrameFormat = pFly ? pFly->GetFormat() : nullptr;

    // baseline to baseline alignment should only be applied to formulas anchored as char
    if ( !pFly || !pFrameFormat || RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() )
        return;

    // get baseline from Math object
    uno::Any aBaseline;
    if( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
    {
        uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
        if ( xSet.is() )
        {
            try
            {
                aBaseline = xSet->getPropertyValue(u"BaseLine"_ustr);
            }
            catch ( uno::Exception& )
            {
                OSL_FAIL( "Baseline could not be retrieved from Starmath!" );
            }
        }
    }

    sal_Int32 nBaseline = ::comphelper::getINT32(aBaseline);
    nBaseline = o3tl::toTwips( nBaseline, o3tl::Length::mm100 );

    OSL_ENSURE( nBaseline > 0, "Wrong value of Baseline while retrieving from Starmath!" );
    //nBaseline must be moved by aPrt position
    const SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat();
    OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" );
    if ( pFlyFrameFormat )
        nBaseline += pFlyFrameFormat->GetLastFlyFramePrtRectPos().Y();

    const SwFormatVertOrient &rVert = pFrameFormat->GetVertOrient();
    SwFormatVertOrient aVert( rVert );
    aVert.SetPos( -nBaseline );
    aVert.SetVertOrient( css::text::VertOrientation::NONE );

    pFrameFormat->LockModify();
    pFrameFormat->SetFormatAttr( aVert );
    pFrameFormat->UnlockModify();
    pFly->InvalidatePos();

}

void SwFEShell::AlignAllFormulasToBaseline()
{
    StartAllAction();

    SwStartNode *pStNd;
    SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
    while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
    {
        ++aIdx;
        SwOLENode *pOleNode = aIdx.GetNode().GetOLENode();
        if ( pOleNode )
        {
            const uno::Reference < embed::XEmbeddedObject > & xObj( pOleNode->GetOLEObj().GetOleRef() );
            if (xObj.is())
            {
                SvGlobalName aCLSID( xObj->getClassID() );
                if ( SotExchange::IsMath( aCLSID ) )
                    AlignFormulaToBaseline( xObj );
            }
        }

        aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
    }

    EndAllAction();
}

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