/* -*- 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 <memory>
#include <hintids.hxx>

#include <vcl/graph.hxx>
#include <sot/formats.hxx>
#include <svx/xfillit0.hxx>
#include <svx/svdocapt.hxx>
#include <svx/svdouno.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdoole2.hxx>
#include <svx/fmmodel.hxx>
#include <svx/unomodel.hxx>
#include <svx/svditer.hxx>
#include <svx/svdograf.hxx>
#include <tools/stream.hxx>
#include <unotools/streamwrap.hxx>
#include <osl/diagnose.h>
#include <fmtanchr.hxx>
#include <fmtcntnt.hxx>
#include <fmtornt.hxx>
#include <fmtflcnt.hxx>
#include <frmfmt.hxx>
#include <txtfrm.hxx>
#include <txtflcnt.hxx>
#include <fesh.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <DocumentFieldsManager.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <rootfrm.hxx>
#include <ndtxt.hxx>
#include <pam.hxx>
#include <tblsel.hxx>
#include <swtable.hxx>
#include <flyfrm.hxx>
#include <pagefrm.hxx>
#include <fldbas.hxx>
#include <swundo.hxx>
#include <viewimp.hxx>
#include <dview.hxx>
#include <dcontact.hxx>
#include <dflyobj.hxx>
#include <docsh.hxx>
#include <pagedesc.hxx>
#include <mvsave.hxx>
#include <textboxhelper.hxx>
#include <frameformats.hxx>
#include <vcl/virdev.hxx>
#include <svx/svdundo.hxx>

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

// Copy for the internal clipboard. Copies all selections to the clipboard.
void SwFEShell::Copy( SwDoc& rClpDoc, const OUString* pNewClpText )
{
    rClpDoc.GetIDocumentUndoRedo().DoUndo(false); // always false!

    // delete content if ClpDocument contains content
    SwNodeIndex aSttIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 );
    SwNodeIndex aEndNdIdx( *aSttIdx.GetNode().EndOfSectionNode() );
    SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
    if (!pTextNd || !pTextNd->GetText().isEmpty() ||
        aSttIdx.GetIndex()+1 != rClpDoc.GetNodes().GetEndOfContent().GetIndex() )
    {
        rClpDoc.GetNodes().Delete( aSttIdx,
            rClpDoc.GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() );
        pTextNd = rClpDoc.GetNodes().MakeTextNode( aSttIdx.GetNode(),
                            rClpDoc.GetDfltTextFormatColl() );
        --aSttIdx;
    }

    // also delete surrounding FlyFrames if any
    for( const auto pFly : *rClpDoc.GetSpzFrameFormats() )
    {
        SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
        SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
        if (pAnchorNode &&
            ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
             (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
            aSttIdx <= *pAnchorNode && *pAnchorNode <= aEndNdIdx.GetNode() )
        {
            rClpDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
        }
    }

    rClpDoc.GetDocumentFieldsManager().GCFieldTypes();        // delete the FieldTypes

    // if a string was passed, copy it to the clipboard-
    // document. Then also the Calculator can use the internal
    // clipboard
    if( pNewClpText )
    {
        pTextNd->InsertText( *pNewClpText, SwContentIndex( pTextNd ) );
        return;                // that's it
    }

    rClpDoc.getIDocumentFieldsAccess().LockExpFields();
    rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines );

    // do we want to copy a FlyFrame?
    if( IsFrameSelected() )
    {
        // get the FlyFormat
        SwFlyFrame* pFly = GetSelectedFlyFrame();
        SwFrameFormat* pFlyFormat = pFly->GetFormat();
        SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() );

        if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AT_FLY  == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
        {
            SwPosition aPos( aSttIdx );
            if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() )
            {
                aPos.SetContent( 0 );
            }
            aAnchor.SetAnchor( &aPos );
        }
        pFlyFormat = rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true );

        // assure the "RootFormat" is the first element in Spz-Array
        // (if necessary Flys were copied in Flys)
        SwFrameFormats& rSpzFrameFormats = *rClpDoc.GetSpzFrameFormats();
        if( rSpzFrameFormats[ 0 ] != pFlyFormat )
        {
#ifndef NDEBUG
            bool inserted =
#endif
            rSpzFrameFormats.newDefault( pFlyFormat );
            assert( !inserted && "Fly not contained in Spz-Array" );
        }

        if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() )
        {
            // JP 13.02.99  Bug 61863: if a frameselection is passed to the
            //              clipboard, it should be found at pasting. Therefore
            //              the copied TextAttribut should be removed in the node
            //              otherwise it will be recognised as TextSelektion
            const SwPosition& rPos = *pFlyFormat->GetAnchor().GetContentAnchor();
            SwTextFlyCnt *const pTextFly = static_cast<SwTextFlyCnt *>(
                pTextNd->GetTextAttrForCharAt(
                    rPos.GetContentIndex(), RES_TXTATR_FLYCNT));
            if( pTextFly )
            {
                const_cast<SwFormatFlyCnt&>(pTextFly->GetFlyCnt()).SetFlyFormat();
                pTextNd->EraseText( rPos, 1 );
            }
        }
    }
    else if ( IsObjSelected() )
    {
        SwPosition aPos( aSttIdx, pTextNd, 0 );
        const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
        for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
        {
            SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();

            if( Imp()->GetDrawView()->IsGroupEntered() ||
                ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) )
            {
                SfxItemSet aSet( rClpDoc.GetAttrPool(), aFrameFormatSetRange );

                SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA );
                aAnchor.SetAnchor( &aPos );
                aSet.Put( aAnchor );

                rtl::Reference<SdrObject> xNew = rClpDoc.CloneSdrObj( *pObj );

                SwPaM aTemp(aPos);
                rClpDoc.getIDocumentContentOperations().InsertDrawObj(aTemp, *xNew, aSet );
            }
            else
            {
                SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj ));
                SwFrameFormat *pFormat = pContact->GetFormat();
                SwFormatAnchor aAnchor( pFormat->GetAnchor() );
                if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
                    (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
                    (RndStdIds::FLY_AT_FLY  == aAnchor.GetAnchorId()) ||
                    (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
                {
                    aAnchor.SetAnchor( &aPos );
                }

                rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true );
            }
        }
    }
    else
        CopySelToDoc(rClpDoc);     // copy the selections

    rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE );
    rClpDoc.getIDocumentFieldsAccess().UnlockExpFields();
    if( !rClpDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() )
        rClpDoc.getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
}

static const Point &lcl_FindBasePos( const SwFrame *pFrame, const Point &rPt )
{
    const SwFrame *pF = pFrame;
    while ( pF && !pF->getFrameArea().Contains( rPt ) )
    {
        if ( pF->IsContentFrame() )
            pF = static_cast<const SwContentFrame*>(pF)->GetFollow();
        else
            pF = nullptr;
    }
    if ( pF )
        return pF->getFrameArea().Pos();
    else
        return pFrame->getFrameArea().Pos();
}

static bool lcl_SetAnchor( const SwPosition& rPos, const SwNode& rNd, SwFlyFrame const * pFly,
                const Point& rInsPt, SwFEShell const & rDestShell, SwFormatAnchor& rAnchor,
                Point& rNewPos, bool bCheckFlyRecur )
{
    bool bRet = true;
    rAnchor.SetAnchor( &rPos );
    std::pair<Point, bool> const tmp(rInsPt, false);
    SwContentFrame *const pTmpFrame = rNd.GetContentNode()->getLayoutFrame(
            rDestShell.GetLayout(), nullptr, &tmp);
    SwFlyFrame *pTmpFly = pTmpFrame->FindFlyFrame();
    if( pTmpFly && bCheckFlyRecur && pFly->IsUpperOf( *pTmpFly ) )
    {
        bRet = false;
    }
    else if ( RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId() )
    {
        if( pTmpFly )
        {
            const SwNodeIndex& rIdx = *pTmpFly->GetFormat()->GetContent().GetContentIdx();
            SwPosition aPos( rIdx );
            rAnchor.SetAnchor( &aPos );
            rNewPos = pTmpFly->getFrameArea().Pos();
        }
        else
        {
            rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
            rAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
            const SwFrame *pPg = pTmpFrame->FindPageFrame();
            rNewPos = pPg->getFrameArea().Pos();
        }
    }
    else
        rNewPos = ::lcl_FindBasePos( pTmpFrame, rInsPt );
    return bRet;
}

bool SwFEShell::CopyDrawSel( SwFEShell& rDestShell, const Point& rSttPt,
                    const Point& rInsPt, bool bIsMove, bool bSelectInsert )
{
    bool bRet = true;

    // The list should be copied, because below new objects will be selected
    const SdrMarkList aMrkList( Imp()->GetDrawView()->GetMarkedObjectList() );
    const size_t nMarkCount = aMrkList.GetMarkCount();
    if( !rDestShell.Imp()->GetDrawView() )
        // should create it now
        rDestShell.MakeDrawView();
    else if( bSelectInsert )
        rDestShell.Imp()->GetDrawView()->UnmarkAll();

    SdrPageView *pDestPgView = rDestShell.Imp()->GetPageView(),
                *pSrcPgView = Imp()->GetPageView();
    SwDrawView *pDestDrwView = rDestShell.Imp()->GetDrawView(),
                *pSrcDrwView = Imp()->GetDrawView();
    SwDoc* pDestDoc = rDestShell.GetDoc();

    Size aSiz( rInsPt.X() - rSttPt.X(), rInsPt.Y() - rSttPt.Y() );
    for( size_t i = 0; i < nMarkCount; ++i )
    {
        SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj();

        SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj ));
        SwFrameFormat *pFormat = pContact->GetFormat();
        const SwFormatAnchor& rAnchor = pFormat->GetAnchor();

        bool bInsWithFormat = true;

        if( pDestDrwView->IsGroupEntered() )
        {
            // insert into the group, when it belongs to an entered group
            // or when the object is not anchored as a character
            if( pSrcDrwView->IsGroupEntered() ||
                (RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId()) )

            {
                rtl::Reference<SdrObject> xNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
                                        GetDoc() == pDestDoc, false );
                xNew->NbcMove( aSiz );
                pDestDrwView->InsertObjectAtView( xNew.get(), *pDestPgView );
                bInsWithFormat = false;
            }
        }

        if( bInsWithFormat )
        {
            SwFormatAnchor aAnchor( rAnchor );
            Point aNewAnch;

            if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
                (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
                (RndStdIds::FLY_AT_FLY  == aAnchor.GetAnchorId()) ||
                (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
            {
                if ( this == &rDestShell )
                {
                    // same shell? Then request the position
                    // from the passed DocumentPosition
                    SwPosition aPos( *GetCursor()->GetPoint() );
                    Point aPt( rInsPt );
                    aPt -= rSttPt - pObj->GetSnapRect().TopLeft();
                    SwCursorMoveState aState( CursorMoveState::SetOnlyText );
                    GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState );
                    const SwNode *pNd;
                    if( (pNd = &aPos.GetNode())->IsNoTextNode() )
                        bRet = false;
                    else
                        bRet = ::lcl_SetAnchor( aPos, *pNd, nullptr, rInsPt,
                                rDestShell, aAnchor, aNewAnch, false );
                }
                else
                {
                    SwPaM *pCursor = rDestShell.GetCursor();
                    if( pCursor->GetPointNode().IsNoTextNode() )
                        bRet = false;
                    else
                        bRet = ::lcl_SetAnchor( *pCursor->GetPoint(),
                                                pCursor->GetPointNode(), nullptr, rInsPt,
                                                rDestShell, aAnchor,
                                                aNewAnch, false );
                }
            }
            else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() )
            {
                aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
                const SwRootFrame* pTmpRoot = rDestShell.GetLayout();
                const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true );
                if ( pPg )
                    aNewAnch = pPg->getFrameArea().Pos();
            }

            if( bRet )
            {
                if( pSrcDrwView->IsGroupEntered() ||
                    ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) )
                {
                    SfxItemSet aSet( pDestDoc->GetAttrPool(),aFrameFormatSetRange);
                    aSet.Put( aAnchor );
                    rtl::Reference<SdrObject> xNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
                                                GetDoc() == pDestDoc );
                    pFormat = pDestDoc->getIDocumentContentOperations().InsertDrawObj( *rDestShell.GetCursor(), *xNew, aSet );
                }
                else
                    pFormat = pDestDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true );

                // Can be 0, as Draws are not allowed in Headers/Footers
                if ( pFormat )
                {
                    // #tdf33692 - drawing object has to be made visible on ctrl+drag copy.
                    pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING));
                    SdrObject* pNew = pFormat->FindSdrObject();
                    if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() )
                    {
                        Point aPos( rInsPt );
                        aPos -= aNewAnch;
                        aPos -= rSttPt - pObj->GetSnapRect().TopLeft();
                        // OD 2004-04-05 #i26791# - change attributes instead of
                        // direct positioning
                        pFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
                        pFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
                        // #i47455# - notify draw frame format
                        // that position attributes are already set.
                        if (SwDrawFrameFormat *pDrawFormat = dynamic_cast<SwDrawFrameFormat*>(pFormat))
                            pDrawFormat->PosAttrSet();
                    }
                    if (SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pObj))
                    {
                        SwTextBoxHelper::syncFlyFrameAttr(*pFormat, pFormat->GetAttrSet(), pObj);
                    }

                    if( bSelectInsert )
                        pDestDrwView->MarkObj( pNew, pDestPgView );
                }
            }
        }
    }

    if ( bIsMove && bRet )
    {
        if( &rDestShell == this )
        {
            const SdrMarkList aList( pSrcDrwView->GetMarkedObjectList() );
            pSrcDrwView->UnmarkAll();

            for ( size_t i = 0, nMrkCnt = aMrkList.GetMarkCount(); i < nMrkCnt; ++i )
            {
                SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj();
                pSrcDrwView->MarkObj( pObj, pSrcPgView );
            }
            DelSelectedObj();
            for ( size_t i = 0, nMrkCnt = aList.GetMarkCount(); i < nMrkCnt; ++i )
            {
                SdrObject *pObj = aList.GetMark( i )->GetMarkedSdrObj();
                pSrcDrwView->MarkObj( pObj, pSrcPgView );
            }
        }
        else
            DelSelectedObj();
    }

    return bRet;
}

bool SwFEShell::Copy( SwFEShell& rDestShell, const Point& rSttPt,
                    const Point& rInsPt, bool bIsMove, bool bSelectInsert )
{
    bool bRet = false;

    OSL_ENSURE( this == &rDestShell || !rDestShell.IsObjSelected(),
            "Dest-Shell cannot be in Obj-Mode" );

    CurrShell aCurr( &rDestShell );

    rDestShell.StartAllAction();
    rDestShell.GetDoc()->getIDocumentFieldsAccess().LockExpFields();

    // Shift references
    bool bCopyIsMove = mxDoc->IsCopyIsMove();
    if( bIsMove )
        // set a flag in Doc, handled in TextNodes
        mxDoc->SetCopyIsMove( true );

    RedlineFlags eOldRedlMode = rDestShell.GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags();
    rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode | RedlineFlags::DeleteRedlines );

    // If there are table formulas in the area, then display the table first
    // so that the table formula can calculate a new value first
    // (individual boxes in the area are retrieved via the layout)
    SwFieldType* pTableFieldTyp = rDestShell.GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table );

    if( IsFrameSelected() )
    {
        SwFlyFrame* pFly = GetSelectedFlyFrame();
        SwFrameFormat* pFlyFormat = pFly->GetFormat();
        SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() );
        bRet = true;
        Point aNewAnch;

        if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AT_FLY  == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
        {
            if ( this == &rDestShell )
            {
                // same shell? Then request the position
                // from the passed DocumentPosition
                SwPosition aPos( *GetCursor()->GetPoint() );
                Point aPt( rInsPt );
                aPt -= rSttPt - pFly->getFrameArea().Pos();
                SwCursorMoveState aState( CursorMoveState::SetOnlyText );
                GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState );
                const SwNode *pNd;
                if( (pNd = &aPos.GetNode())->IsNoTextNode() )
                    bRet = false;
                else
                {
                    // do not copy in itself
                    const SwNodeIndex *pTmp = pFlyFormat->GetContent().GetContentIdx();
                    if ( aPos.GetNodeIndex() > pTmp->GetIndex() &&
                         aPos.GetNodeIndex() < pTmp->GetNode().EndOfSectionIndex() )
                    {
                        bRet = false;
                    }
                    else
                        bRet = ::lcl_SetAnchor( aPos, *pNd, pFly, rInsPt,
                                        rDestShell, aAnchor, aNewAnch, true );
                }
            }
            else
            {
                const SwPaM *pCursor = rDestShell.GetCursor();
                if( pCursor->GetPointNode().IsNoTextNode() )
                    bRet = false;
                else
                    bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), pCursor->GetPointNode(),
                                            pFly, rInsPt, rDestShell, aAnchor,
                                    aNewAnch, GetDoc() == rDestShell.GetDoc());
            }
        }
        else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() )
        {
            aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
            const SwRootFrame* pTmpRoot = rDestShell.GetLayout();
            const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true );
            if ( pPg )
                aNewAnch = pPg->getFrameArea().Pos();
        }
        else {
            OSL_ENSURE( false, "what anchor is it?" );
        }

        if( bRet )
        {
            SwFrameFormat *pOldFormat = pFlyFormat;
            pFlyFormat = rDestShell.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true );

            if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() )
            {
                Point aPos( rInsPt );
                aPos -= aNewAnch;
                aPos -= rSttPt - pFly->getFrameArea().Pos();
                pFlyFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(),text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
                pFlyFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(),text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
            }

            const Point aPt( rDestShell.GetCursorDocPos() );

            if( bIsMove )
                GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pOldFormat );

            // only select if it can be shifted/copied in the same shell
            if( bSelectInsert )
            {
                SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aPt );
                if( pFlyFrame )
                {
                    //JP 12.05.98: should this be in SelectFlyFrame???
                    rDestShell.Imp()->GetDrawView()->UnmarkAll();
                    rDestShell.SelectFlyFrame( *pFlyFrame );
                }
            }

            if (this != &rDestShell && !rDestShell.HasShellFocus())
                rDestShell.Imp()->GetDrawView()->hideMarkHandles();
        }
    }
    else if ( IsObjSelected() )
        bRet = CopyDrawSel( rDestShell, rSttPt, rInsPt, bIsMove, bSelectInsert );
    else if( IsTableMode() )
    {
        // Copy parts from a table: create a table with the same
        // width as the original and copy the selected boxes.
        // Sizes will be corrected by percentage.

        // find boxes via the layout
        SwSelBoxes aBoxes;
        GetTableSel( *this, aBoxes );
        SwTableNode const*const pTableNd(
            aBoxes.empty() ? nullptr : aBoxes[0]->GetSttNd()->FindTableNode());
        if (nullptr != pTableNd)
        {
            std::optional<SwPosition> oDstPos;
            if( this == &rDestShell )
            {
                // same shell? Then create new Cursor at the
                // DocumentPosition passed
                oDstPos.emplace( *GetCursor()->GetPoint() );
                Point aPt( rInsPt );
                GetLayout()->GetModelPositionForViewPoint( &*oDstPos, aPt );
                if( !oDstPos->GetNode().IsNoTextNode() )
                    bRet = true;
            }
            else if( !rDestShell.GetCursor()->GetPointNode().IsNoTextNode() )
            {
                oDstPos.emplace( *rDestShell.GetCursor()->GetPoint() );
                bRet = true;
            }

            if( bRet )
            {
                if( GetDoc() == rDestShell.GetDoc() )
                    ParkTableCursor();

                bRet = rDestShell.GetDoc()->InsCopyOfTable( *oDstPos, aBoxes,nullptr,
                                        bIsMove && this == &rDestShell &&
                                        aBoxes.size() == pTableNd->GetTable().
                                        GetTabSortBoxes().size(),
                                        this != &rDestShell );

                if( this != &rDestShell )
                    *rDestShell.GetCursor()->GetPoint() = *oDstPos;

                // create all parked Cursor?
                if( GetDoc() == rDestShell.GetDoc() )
                    GetCursor();

                // JP 16.04.99: Bug 64908 - Set InsPos, to assure the parked
                //              Cursor is positioned at the insert position
                if( this == &rDestShell )
                    GetCursorDocPos() = rInsPt;
            }
        }
    }
    else
    {
        bRet = true;
        if( this == &rDestShell )
        {
            // same shell? then request the position
            // at the passed document position
            SwPosition aPos( *GetCursor()->GetPoint() );
            Point aPt( rInsPt );
            GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
            bRet = !aPos.GetNode().IsNoTextNode();
        }
        else if( rDestShell.GetCursor()->GetPointNode().IsNoTextNode() )
            bRet = false;

        if( bRet )
            bRet = SwEditShell::Copy( rDestShell );
    }

    rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode );
    mxDoc->SetCopyIsMove( bCopyIsMove );

    // have new table formulas been inserted?
    if( pTableFieldTyp->HasWriterListeners() )
    {
        // finish old actions: the table frames are created and
        // a selection can be made
        sal_uInt16 nActCnt;
        for( nActCnt = 0; rDestShell.ActionPend(); ++nActCnt )
            rDestShell.EndAllAction();

        for( ; nActCnt; --nActCnt )
            rDestShell.StartAllAction();
    }
    rDestShell.GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
    rDestShell.GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);

    rDestShell.EndAllAction();
    return bRet;
}

// Paste for the internal clipboard. Copy the content of the clipboard
// in the document
namespace {
    typedef std::shared_ptr<SwPaM> PaMPtr;
    typedef std::shared_ptr<SwPosition> PositionPtr;
    typedef std::pair< PaMPtr, PositionPtr > Insertion;

    bool PamHasSelection(const SwPaM& rPaM)
    {
        return rPaM.HasMark() && *rPaM.GetPoint() != *rPaM.GetMark();
    }

    /// Is pFormat anchored in a fly frame which has an associated draw format?
    bool IsInTextBox(const SwFrameFormat* pFormat)
    {
        const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
        const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
        if (!pAnchorNode)
        {
            return false;
        }

        const SwStartNode* pFlyNode = pAnchorNode->FindFlyStartNode();
        if (!pFlyNode)
        {
            return false;
        }

        for ( const auto& pSpzFormat : *pFormat->GetDoc()->GetSpzFrameFormats() )
        {
            if (pSpzFormat->Which() != RES_FLYFRMFMT)
            {
                continue;
            }

            const SwNodeIndex* pIdx = pSpzFormat->GetContent().GetContentIdx();
            if (!pIdx || pFlyNode != &pIdx->GetNode())
            {
                continue;
            }

            return SwTextBoxHelper::isTextBox(pSpzFormat, RES_FLYFRMFMT);
        }

        return false;
    }
}

namespace {
    SwFrameFormat* lcl_PasteFlyOrDrawFormat(SwPaM& rPaM, SwFrameFormat* pCpyFormat, SwFEShell& rSh)
    {
        auto& rImp = *rSh.Imp();
        auto& rDoc = *rSh.GetDoc();
        auto& rDrawView = *rImp.GetDrawView();
        if(rDrawView.IsGroupEntered() &&
           RES_DRAWFRMFMT == pCpyFormat->Which() &&
           (RndStdIds::FLY_AS_CHAR != pCpyFormat->GetAnchor().GetAnchorId()))
        {
            const SdrObject* pSdrObj = pCpyFormat->FindSdrObject();
            if(pSdrObj)
            {
                rtl::Reference<SdrObject> xNew = rDoc.CloneSdrObj(*pSdrObj, false, false);
                // Insert object sets any anchor position to 0.
                // Therefore we calculate the absolute position here
                // and after the insert the anchor of the object
                // is set to the anchor of the group object.
                tools::Rectangle aSnapRect = xNew->GetSnapRect();
                if(xNew->GetAnchorPos().X() || xNew->GetAnchorPos().Y())
                {
                    const Point aPoint(0, 0);
                    // OD 2004-04-05 #i26791# - direct drawing object
                    // positioning for group members
                    xNew->NbcSetAnchorPos(aPoint);
                    xNew->NbcSetSnapRect(aSnapRect);
                }

                rDrawView.InsertObjectAtView(xNew.get(), *rImp.GetPageView());

                Point aGrpAnchor(0, 0);
                SdrObjList* pList = xNew->getParentSdrObjListFromSdrObject();
                if(pList)
                {
                    SdrObjGroup* pOwner(dynamic_cast<SdrObjGroup*>(pList->getSdrObjectFromSdrObjList()));

                    if(nullptr != pOwner)
                        aGrpAnchor = pOwner->GetAnchorPos();
                }

                // OD 2004-04-05 #i26791# - direct drawing object
                // positioning for group members
                xNew->NbcSetAnchorPos(aGrpAnchor);
                xNew->SetSnapRect(aSnapRect);
                return nullptr;
            }
        }
        SwFormatAnchor aAnchor(pCpyFormat->GetAnchor());
        if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
            (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
        {
            SwPosition* pPos = rPaM.GetPoint();
            // allow shapes (no controls) in header/footer
            if(RES_DRAWFRMFMT == pCpyFormat->Which() && rDoc.IsInHeaderFooter(pPos->GetNode()))
            {
                const SdrObject *pCpyObj = pCpyFormat->FindSdrObject();
                if(pCpyObj && CheckControlLayer(pCpyObj))
                    return nullptr;
            }
            else if(pCpyFormat->Which() == RES_FLYFRMFMT && IsInTextBox(pCpyFormat))
            {
                // This is a fly frame which is anchored in a TextBox, ignore it as
                // it's already copied as part of copying the content of the
                // TextBox.
                return nullptr;
            }
            // Ignore TextBoxes, they are already handled in sw::DocumentLayoutManager::CopyLayoutFormat().
            if(SwTextBoxHelper::isTextBox(pCpyFormat, RES_FLYFRMFMT))
                return nullptr;
            aAnchor.SetAnchor(pPos);
        }
        else if(RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId())
        {
            aAnchor.SetPageNum(rSh.GetPhyPageNum());
        }
        else if(RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId())
        {
            Point aPt;
            (void)lcl_SetAnchor(*rPaM.GetPoint(), rPaM.GetPointNode(), nullptr, aPt, rSh, aAnchor, aPt, false);
        }

        SwFrameFormat* pNew = rDoc.getIDocumentLayoutAccess().CopyLayoutFormat(*pCpyFormat, aAnchor, true, true);
        return pNew;
    }

    void lcl_SelectFlyFormat(SwFrameFormat *const pNew, SwFEShell& rSh)
    {
        if(!pNew)
            return;
        switch(pNew->Which())
        {
            case RES_FLYFRMFMT:
            {
                assert(dynamic_cast<SwFlyFrameFormat*>(pNew));
                const Point aPt(rSh.GetCursorDocPos());
                SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pNew)->GetFrame(&aPt);
                if(pFlyFrame)
                    rSh.SelectFlyFrame(*pFlyFrame);
                break;
            }
            case RES_DRAWFRMFMT:
            {
                auto& rDrawView = *rSh.Imp()->GetDrawView();
                assert(dynamic_cast<SwDrawFrameFormat*>(pNew));
                SwDrawFrameFormat* pDrawFormat = static_cast<SwDrawFrameFormat*>(pNew);
                // #i52780# - drawing object has to be made visible on paste.
                pDrawFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING));
                SdrObject* pObj = pDrawFormat->FindSdrObject();
                rDrawView.MarkObj(pObj, rDrawView.GetSdrPageView());
                // #i47455# - notify draw frame format
                // that position attributes are already set.
                pDrawFormat->PosAttrSet();
                break;
            }
            default:
                SAL_WARN("sw.core", "unknown fly type");
        }
    }
}

bool SwFEShell::Paste(SwDoc& rClpDoc, bool bNestedTable)
{
    CurrShell aCurr( this );
    // then till end of the nodes array
    SwNodeIndex aIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 );
    // select content section, whatever it may contain
    SwPaM aCpyPam(aIdx, SwNodeIndex(rClpDoc.GetNodes().GetEndOfContent(), -1));
    if (SwContentNode *const pAtEnd = aCpyPam.GetPointNode().GetContentNode())
    {
        aCpyPam.GetPoint()->AssignEndIndex(*pAtEnd);
    }

    // If there are table formulas in the area, then display the table first
    // so that the table formula can calculate a new value first
    // (individual boxes in the area are retrieved via the layout)
    SwFieldType* pTableFieldTyp = GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table );

    SwTableNode *const pSrcNd = aCpyPam.GetMarkNode().GetTableNode();

    bool bRet = true;
    StartAllAction();
    GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
    GetDoc()->getIDocumentFieldsAccess().LockExpFields();

    // When the clipboard content has been created by a rectangular selection
    // the pasting is more sophisticated:
    // every paragraph will be inserted into another position.
    // The first positions are given by the actual cursor ring,
    // if there are more text portions to insert than cursor in this ring,
    // the additional insert positions will be created by moving the last
    // cursor position into the next line (like pressing the cursor down key)
    if( rClpDoc.IsColumnSelection() && !IsTableMode() )
    {
        // Creation of the list of insert positions
        std::vector< Insertion > aCopyVector;
        // The number of text portions of the rectangular selection
        const SwNodeOffset nSelCount = aCpyPam.GetPoint()->GetNodeIndex()
                       - aCpyPam.GetMark()->GetNodeIndex();
        SwNodeOffset nCount = nSelCount;
        SwNodeIndex aClpIdx( aIdx );
        SwPaM* pStartCursor = GetCursor();
        SwPaM* pCurrCursor = pStartCursor;
        SwNodeOffset nCursorCount( pStartCursor->GetRingContainer().size() );
        // If the target selection is a multi-selection, often the last and first
        // cursor of the ring points to identical document positions. Then
        // we should avoid double insertion of text portions...
        while( nCursorCount > SwNodeOffset(1) && *pCurrCursor->GetPoint() ==
            *(pCurrCursor->GetPrev()->GetPoint()) )
        {
            --nCursorCount;
            pCurrCursor = pCurrCursor->GetNext();
            pStartCursor = pCurrCursor;
        }
        SwPosition aStartPos( *pStartCursor->GetPoint() );
        SwPosition aInsertPos( aStartPos ); // first insertion position
        bool bCompletePara = false;
        sal_uInt16 nMove = 0;
        while( nCount )
        {
            --nCount;
            OSL_ENSURE( aIdx.GetNode().GetContentNode(), "Who filled the clipboard?!" );
            if( aIdx.GetNode().GetContentNode() ) // robust
            {
                Insertion aInsertion( std::make_shared<SwPaM>( aIdx ),
                    std::make_shared<SwPosition>( aInsertPos ) );
                ++aIdx;
                aInsertion.first->SetMark();
                if( pStartCursor == pCurrCursor->GetNext() )
                {   // Now we have to look for insertion positions...
                    if( !nMove ) // Annotate the last given insert position
                        aStartPos = aInsertPos;
                    SwCursor aCursor( aStartPos, nullptr);
                    // Check if we find another insert position by moving
                    // down the last given position
                    if (aCursor.UpDown(false, ++nMove, nullptr, 0, *GetLayout()))
                        aInsertPos = *aCursor.GetPoint();
                    else // if there is no paragraph we have to create it
                        bCompletePara = nCount > SwNodeOffset(0);
                    nCursorCount = SwNodeOffset(0);
                }
                else // as long as we find more insert positions in the cursor ring
                {    // we'll take them
                    pCurrCursor = pCurrCursor->GetNext();
                    aInsertPos = *pCurrCursor->GetPoint();
                    --nCursorCount;
                }
                // If there are no more paragraphs e.g. at the end of a document,
                // we insert complete paragraphs instead of text portions
                if( bCompletePara )
                    aInsertion.first->GetPoint()->Assign(aIdx);
                else
                    aInsertion.first->GetPoint()->SetContent(
                        aInsertion.first->GetPointContentNode()->Len() );
                aCopyVector.push_back( aInsertion );
            }
            // If there are no text portions left but there are some more
            // cursor positions to fill we have to restart with the first
            // text portion
            if( !nCount && nCursorCount )
            {
                nCount = min( nSelCount, nCursorCount );
                aIdx = aClpIdx; // Start of clipboard content
            }
        }
        for (auto const& item : aCopyVector)
        {
            SwPosition& rInsPos = *item.second;
            SwPaM& rCopy = *item.first;
            const SwStartNode* pBoxNd = rInsPos.GetNode().FindTableBoxStartNode();
            if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() - pBoxNd->GetIndex() &&
                rCopy.GetPoint()->GetNode() != rCopy.GetMark()->GetNode() )
            {
                // if more than one node will be copied into a cell
                // the box attributes have to be removed
                GetDoc()->ClearBoxNumAttrs( rInsPos.GetNode() );
            }
            {
                SwNodeIndex aIndexBefore(rInsPos.GetNode());
                --aIndexBefore;
                rClpDoc.getIDocumentContentOperations().CopyRange(rCopy, rInsPos, SwCopyFlags::CheckPosInFly);
                {
                    ++aIndexBefore;
                    SwPaM aPaM(SwPosition(aIndexBefore),
                               SwPosition(rInsPos.GetNode()));
                    aPaM.GetDoc().MakeUniqueNumRules(aPaM);
                }
            }
            SaveTableBoxContent( &rInsPos );
        }
    }
    else
    {
        bool bDelTable = true;

        for(SwPaM& rPaM : GetCursor()->GetRingContainer())
        {

            SwTableNode *const pDestNd(SwDoc::IsInTable(rPaM.GetPoint()->GetNode()));
            if (pSrcNd && nullptr != pDestNd &&
                // not a forced nested table insertion
                !bNestedTable &&
                // Heuristics to allow copying table rows or nesting tables without
                // using Edit -> Paste Special -> Paste as Nested Table:
                // Using table cursor, or if the text selection starts in the
                // first paragraph, or if there is no selection and the text cursor
                // is there in the first paragraph, overwrite content of the cell(s)
                // (else insert a nested table later, i.e. if nothing selected and
                // the cursor is not in the first paragraph, or the selected text
                // doesn't contain the first paragraph of the cell)
                rPaM.GetPointNode().GetIndex() == rPaM.GetPointNode().FindTableBoxStartNode()->GetIndex() + 1)
            {
                SwPosition aDestPos( *rPaM.GetPoint() );

                bool bParkTableCursor = false;
                const SwStartNode* pSttNd =  rPaM.GetPointNode().FindTableBoxStartNode();

                // TABLE IN TABLE: copy table in table
                // search boxes via the layout
                SwSelBoxes aBoxes;
                if( IsTableMode() )     // table selection?
                {
                    GetTableSel( *this, aBoxes );
                    ParkTableCursor();
                    bParkTableCursor = true;
                }
                else if( !PamHasSelection(rPaM) && rPaM.GetNext() == &rPaM &&
                     ( !pSrcNd->GetTable().IsTableComplex() ||
                       pDestNd->GetTable().IsNewModel() ) )
                {
                    // make relative table copy
                    SwTableBox* pBox = pDestNd->GetTable().GetTableBox(
                                        pSttNd->GetIndex() );
                    OSL_ENSURE( pBox, "Box is not in this table" );
                    aBoxes.insert( pBox );
                }

                SwNodeIndex aNdIdx( *pDestNd->EndOfSectionNode());
                if( !bParkTableCursor )
                {
                    // exit first the complete table
                    // ???? what about only table in a frame ?????
                    SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx );
                    SwPosition aPos( aNdIdx, pCNd, 0 );
                    // #i59539: Don't remove all redline
                    SwPaM const tmpPaM(*pDestNd, *pDestNd->EndOfSectionNode());
                    ::PaMCorrAbs(tmpPaM, aPos);
                }

                bRet = GetDoc()->InsCopyOfTable( aDestPos, aBoxes, &pSrcNd->GetTable() );

                if( bParkTableCursor )
                    GetCursor();
                else
                {
                    // return to the box
                    aNdIdx = *pSttNd;
                    SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx );
                    SwPosition aPos( aNdIdx, pCNd, 0 );
                    // #i59539: Don't remove all redline
                    SwNode & rNode(rPaM.GetPoint()->GetNode());
                    SwContentNode *const pContentNode( rNode.GetContentNode() );
                    SwPaM const tmpPam(rNode, 0,
                                   rNode, pContentNode ? pContentNode->Len() : 0);
                    ::PaMCorrAbs(tmpPam, aPos);
                }

                break;      // exit the "while-loop"
            }
            else if(*aCpyPam.GetPoint() == *aCpyPam.GetMark() && !rClpDoc.GetSpzFrameFormats()->empty())
            {
                // we need a DrawView
                if(!Imp()->GetDrawView())
                    MakeDrawView();
                ::std::vector<SwFrameFormat*> inserted;
                for (auto const pFlyFormat : *rClpDoc.GetSpzFrameFormats())
                {
                    // if anchored inside other fly, will be copied when copying
                    // top-level fly, so skip here! (other non-body anchor
                    // shouldn't happen here)
                    SwFormatAnchor const& rAnchor(pFlyFormat->GetAnchor());
                    if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()
                        || rClpDoc.GetNodes().GetEndOfExtras().GetIndex() < rAnchor.GetAnchorNode()->GetIndex())
                    {
                        inserted.emplace_back(
                            lcl_PasteFlyOrDrawFormat(rPaM, pFlyFormat, *this));
                    }
                }
                for (auto const pFlyFormat : inserted)
                {
                    lcl_SelectFlyFormat(pFlyFormat, *this);
                }
            }
            else
            {
                if( bDelTable && IsTableMode() )
                {
                    SwEditShell::Delete(false);
                    bDelTable = false;
                }

                SwPosition& rInsPos = *rPaM.GetPoint();
                const SwStartNode* pBoxNd = rInsPos.GetNode().
                                                    FindTableBoxStartNode();
                if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() -
                                pBoxNd->GetIndex() &&
                    aCpyPam.GetPoint()->GetNode() != aCpyPam.GetMark()->GetNode() )
                {
                    // Copy more than 1 node in the current box. But
                    // then the BoxAttribute should be removed
                    GetDoc()->ClearBoxNumAttrs( rInsPos.GetNode() );
                }

                // **
                // ** Update SwDoc::Append, if you change the following code **
                // **
                {
                    SwNodeIndex aIndexBefore(rInsPos.GetNode());

                    --aIndexBefore;

                    rClpDoc.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly);
                    // Note: aCpyPam is invalid now

                    ++aIndexBefore;
                    SwPaM aPaM(aIndexBefore.GetNode(), rInsPos.GetNode());

                    aPaM.GetDoc().MakeUniqueNumRules(aPaM);

                    // Update the rsid of each pasted text node.
                    SwNodes &rDestNodes = GetDoc()->GetNodes();
                    SwNodeOffset const nEndIdx = aPaM.End()->GetNodeIndex();

                    for (SwNodeOffset nIdx = aPaM.Start()->GetNodeIndex();
                        nIdx <= nEndIdx; ++nIdx)
                    {
                        SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode();
                        if ( pTextNode )
                        {
                            GetDoc()->UpdateParRsid( pTextNode );
                        }
                    }
                }

                SaveTableBoxContent( &rInsPos );
            }
        }
    }

    GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );

    // have new table formulas been inserted?
    if( pTableFieldTyp->HasWriterListeners() )
    {
        // finish old action: table-frames have been created
        // a selection can be made now
        sal_uInt16 nActCnt;
        for( nActCnt = 0; ActionPend(); ++nActCnt )
            EndAllAction();

        for( ; nActCnt; --nActCnt )
            StartAllAction();
    }
    GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
    GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
    EndAllAction();

    return bRet;
}

void SwFEShell::PastePages( SwFEShell& rToFill, sal_uInt16 nStartPage, sal_uInt16 nEndPage)
{
    Push();
    if(!GotoPage(nStartPage))
    {
        Pop(PopMode::DeleteCurrent);
        return;
    }
    MovePage( GetThisFrame, GetFirstSub );
    ::std::optional<SwPaM> oSourcePam( *GetCursor()->GetPoint() );
    OUString sStartingPageDesc = GetPageDesc( GetCurPageDesc()).GetName();
    SwPageDesc* pDesc = rToFill.FindPageDescByName( sStartingPageDesc, true );
    if( pDesc )
        rToFill.ChgCurPageDesc( *pDesc );

    if(!GotoPage(nEndPage))
    {
        Pop(PopMode::DeleteCurrent);
        return;
    }
    //if the page starts with a table a paragraph has to be inserted before
    SwNode *const pTableNode = oSourcePam->GetPointNode().FindTableNode();
    if(pTableNode)
    {
        //insert a paragraph
        StartUndo(SwUndoId::INSERT);
        SwNodeIndex aTableIdx(  *pTableNode, -1 );
        SwPosition aBefore(aTableIdx);
        if(GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore ))
        {
            SwPaM aTmp(aBefore);
            *oSourcePam = aTmp;
        }
        EndUndo(SwUndoId::INSERT);
    }

    MovePage( GetThisFrame, GetLastSub );
    oSourcePam->SetMark();
    *oSourcePam->GetMark() = *GetCursor()->GetPoint();

    CurrShell aCurr( this );

    StartAllAction();
    GetDoc()->getIDocumentFieldsAccess().LockExpFields();
    SetSelection(*oSourcePam);
    // copy the text of the selection
    SwEditShell::Copy(rToFill);
    oSourcePam.reset(); // delete it because Undo will remove its node!

    if(pTableNode)
    {
        //remove the inserted paragraph
        Undo();
        //remove the paragraph in the second doc, too
        SwPaM aPara( rToFill.GetDoc()->GetNodes().GetEndOfExtras(), SwNodeOffset(2) ); //DocStart
        rToFill.GetDoc()->getIDocumentContentOperations().DelFullPara(aPara);
    }
    // now the page bound objects
    // additionally copy page bound frames
    if( !GetDoc()->GetSpzFrameFormats()->empty() )
    {
        // create a draw view if necessary
        if( !rToFill.Imp()->GetDrawView() )
            rToFill.MakeDrawView();

        for ( auto pCpyFormat : *GetDoc()->GetSpzFrameFormats() )
        {
            SwFormatAnchor aAnchor( pCpyFormat->GetAnchor() );
            if ((RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) &&
                    aAnchor.GetPageNum() >= nStartPage && aAnchor.GetPageNum() <= nEndPage)
            {
                aAnchor.SetPageNum( aAnchor.GetPageNum() - nStartPage + 1);
            }
            else
                continue;
            rToFill.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pCpyFormat, aAnchor, true, true );
        }
    }
    GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
    GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
    Pop(PopMode::DeleteCurrent);
    EndAllAction();
}

comphelper::OInterfaceContainerHelper3<css::text::XPasteListener>& SwFEShell::GetPasteListeners() { return m_aPasteListeners; }

bool SwFEShell::GetDrawObjGraphic( SotClipboardFormatId nFormat, Graphic& rGrf ) const
{
    OSL_ENSURE( Imp()->HasDrawView(), "GetDrawObjGraphic without DrawView?" );
    const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
    bool bConvert = true;
    if( rMrkList.GetMarkCount() )
    {
        if( rMrkList.GetMarkCount() == 1 &&
            dynamic_cast< const SwVirtFlyDrawObj* >(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) != nullptr )
        {
            // select frame
            if( CNT_GRF == GetCntType() )
            {
                const Graphic* pGrf( GetGraphic() );
                if ( pGrf )
                {
                    Graphic aGrf( *pGrf );
                    if( SotClipboardFormatId::GDIMETAFILE == nFormat )
                    {
                        if( GraphicType::Bitmap != aGrf.GetType() )
                        {
                            rGrf = aGrf;
                            bConvert = false;
                        }
                        else if( GetWin() )
                        {
                            Size aSz;
                            Point aPt;
                            GetGrfSize( aSz );

                            ScopedVclPtrInstance< VirtualDevice > pVirtDev;
                            pVirtDev->EnableOutput( false );

                            MapMode aTmp( GetWin()->GetMapMode() );
                            aTmp.SetOrigin( aPt );
                            pVirtDev->SetMapMode( aTmp );

                            GDIMetaFile aMtf;
                            aMtf.Record( pVirtDev.get() );
                            aGrf.Draw(*pVirtDev, aPt, aSz);
                            aMtf.Stop();
                            aMtf.SetPrefMapMode( aTmp );
                            aMtf.SetPrefSize( aSz );
                            rGrf = aMtf;
                        }
                    }
                    else if( GraphicType::Bitmap == aGrf.GetType() )
                    {
                        rGrf = aGrf;
                        bConvert = false;
                    }
                    else
                    {
                        // Not the original size, but the current one.
                        // Otherwise it could happen that for vector graphics
                        // many MB's of memory are allocated.
                        const Size aSz( GetSelectedFlyFrame()->getFramePrintArea().SSize() );
                        ScopedVclPtrInstance< VirtualDevice > pVirtDev(*GetWin()->GetOutDev());

                        MapMode aTmp( MapUnit::MapTwip );
                        pVirtDev->SetMapMode( aTmp );
                        if( pVirtDev->SetOutputSize( aSz ) )
                        {
                            aGrf.Draw(*pVirtDev, Point(), aSz);
                            rGrf = pVirtDev->GetBitmapEx( Point(), aSz );
                        }
                        else
                        {
                            rGrf = aGrf;
                            bConvert = false;
                        }
                    }
                }
            }
        }
        else if( SotClipboardFormatId::GDIMETAFILE == nFormat )
            rGrf = Imp()->GetDrawView()->GetMarkedObjMetaFile();
        else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat )
            rGrf = Imp()->GetDrawView()->GetMarkedObjBitmapEx();
    }
    return bConvert;
}

// #i50824#
// replace method <lcl_RemoveOleObjsFromSdrModel> by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs>
static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs( SdrModel& _rModel )
{
    for ( sal_uInt16 nPgNum = 0; nPgNum < _rModel.GetPageCount(); ++nPgNum )
    {
        // setup object iterator in order to iterate through all objects
        // including objects in group objects, but exclusive group objects.
        SdrObjListIter aIter(_rModel.GetPage(nPgNum));
        while( aIter.IsMore() )
        {
            SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( aIter.Next() );
            if( pOle2Obj )
            {
                // found an ole2 shape
                SdrObjList* pObjList = pOle2Obj->getParentSdrObjListFromSdrObject();

                // get its graphic
                Graphic aGraphic;
                pOle2Obj->Connect();
                const Graphic* pGraphic = pOle2Obj->GetGraphic();
                if( pGraphic )
                    aGraphic = *pGraphic;
                pOle2Obj->Disconnect();

                // create new graphic shape with the ole graphic and shape size
                rtl::Reference<SdrGrafObj> pGraphicObj = new SdrGrafObj(
                    _rModel,
                    aGraphic,
                    pOle2Obj->GetCurrentBoundRect());
                // apply layer of ole2 shape at graphic shape
                pGraphicObj->SetLayer( pOle2Obj->GetLayer() );

                // replace ole2 shape with the new graphic object and delete the ol2 shape
                pObjList->ReplaceObject( pGraphicObj.get(), pOle2Obj->GetOrdNum() );
            }
        }
    }
}

void SwFEShell::Paste( SvStream& rStrm, SwPasteSdr nAction, const Point* pPt )
{
    CurrShell aCurr( this );
    StartAllAction();
    StartUndo();

    std::unique_ptr< FmFormModel > pModel(
        new FmFormModel(
            nullptr,
            GetDoc()->GetDocShell()));

    pModel->GetItemPool().FreezeIdRanges();

    rStrm.Seek(0);

    uno::Reference< io::XInputStream > xInputStream( new utl::OInputStreamWrapper( rStrm ) );
    SvxDrawingLayerImport( pModel.get(), xInputStream );

    if ( !Imp()->HasDrawView() )
        Imp()->MakeDrawView();

    Point aPos( pPt ? *pPt : GetCharRect().Pos() );
    SdrView *pView = Imp()->GetDrawView();

    // drop on the existing object: replace object or apply new attributes
    if( pModel->GetPageCount() > 0 &&
        1 == pModel->GetPage(0)->GetObjCount() &&
        1 == pView->GetMarkedObjectList().GetMarkCount() )
    {
        // replace a marked 'virtual' drawing object
        // by its corresponding 'master' drawing object in the mark list.
        SwDrawView::ReplaceMarkedDrawVirtObjs( *pView );

        SdrObject* pClpObj = pModel->GetPage(0)->GetObj(0);
        SdrObject* pOldObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj();

        if( SwPasteSdr::SetAttr == nAction && dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) !=  nullptr )
            nAction = SwPasteSdr::Replace;

        switch( nAction )
        {
        case SwPasteSdr::Replace:
            {
                const SwFrameFormat* pFormat(nullptr);
                const SwFrame* pAnchor(nullptr);
                if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) !=  nullptr )
                {
                    pFormat = FindFrameFormat( pOldObj );

                    Point aNullPt;
                    SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrameFormat*>(pFormat)->GetFrame( &aNullPt );
                    pAnchor = pFlyFrame ? pFlyFrame->GetAnchorFrame() : nullptr;

                    if (!pAnchor || pAnchor->FindFooterOrHeader())
                    {
                        // if there is a textframe in the header/footer:
                        // do not replace but insert
                        nAction = SwPasteSdr::Insert;
                        break;
                    }
                }

                rtl::Reference<SdrObject> pNewObj(pClpObj->CloneSdrObject(pOldObj->getSdrModelFromSdrObject()));
                tools::Rectangle aOldObjRect( pOldObj->GetCurrentBoundRect() );
                Size aOldObjSize( aOldObjRect.GetSize() );
                tools::Rectangle aNewRect( pNewObj->GetCurrentBoundRect() );
                Size aNewSize( aNewRect.GetSize() );

                Fraction aScaleWidth( aOldObjSize.Width(), aNewSize.Width() );
                Fraction aScaleHeight( aOldObjSize.Height(), aNewSize.Height());
                pNewObj->NbcResize( aNewRect.TopLeft(), aScaleWidth, aScaleHeight);

                Point aVec = aOldObjRect.TopLeft() - aNewRect.TopLeft();
                pNewObj->NbcMove(Size(aVec.getX(), aVec.getY()));

                if( dynamic_cast<const SdrUnoObj*>( pNewObj.get()) !=  nullptr )
                    pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() );
                else if( dynamic_cast<const SdrUnoObj*>( pOldObj) !=  nullptr )
                    pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() );
                else
                    pNewObj->SetLayer( pOldObj->GetLayer() );

                if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) !=  nullptr )
                {
                    // store attributes, then set SdrObject
                    SfxItemSetFixed<RES_SURROUND, RES_ANCHOR> aFrameSet( mxDoc->GetAttrPool() );
                    aFrameSet.Set( pFormat->GetAttrSet() );

                    Point aNullPt;
                    if( pAnchor->IsTextFrame() && static_cast<const SwTextFrame*>(pAnchor)->IsFollow() )
                    {
                        const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnchor);
                        do {
                            pTmp = pTmp->FindMaster();
                            OSL_ENSURE( pTmp, "Where's my Master?" );
                        } while( pTmp->IsFollow() );
                        pAnchor = pTmp;
                    }
                    if( auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pOldObj))
                        aNullPt = pCaptionObj->GetTailPos();
                    else
                        aNullPt = aOldObjRect.TopLeft();

                    Point aNewAnchor = pAnchor->GetFrameAnchorPos( ::HasWrap( pOldObj ) );
                    // OD 2004-04-05 #i26791# - direct positioning of Writer
                    // fly frame object for <SwDoc::Insert(..)>
                    pNewObj->NbcSetRelativePos( aNullPt - aNewAnchor );
                    pNewObj->NbcSetAnchorPos( aNewAnchor );

                    pOldObj->GetOrdNum();

                    DelSelectedObj();

                    GetDoc()->getIDocumentContentOperations().InsertDrawObj( *GetCursor(), *pNewObj, aFrameSet );
                }
                else
                {
                    // #i123922#  for handling MasterObject and virtual ones correctly, SW
                    // wants us to call ReplaceObject at the page, but that also
                    // triggers the same assertion (I tried it), so stay at the view method
                    pView->ReplaceObjectAtView(pOldObj, *Imp()->GetPageView(), pNewObj.get());
                }
            }
            break;

        case SwPasteSdr::SetAttr:
            {
                SfxItemSet aSet( GetAttrPool() );
                const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pClpObj);

                if(pSdrGrafObj)
                {
                    SdrObject* pTarget = nullptr;

                    if(0 != pView->GetMarkedObjectList().GetMarkCount())
                    {
                        // try to get target (if it's at least one, take first)
                        SdrMark* pMark = pView->GetMarkedObjectList().GetMark(0);

                        if(pMark)
                        {
                            pTarget = pMark->GetMarkedSdrObj();
                        }
                    }

                    if(pTarget)
                    {
                        // copy ItemSet from target
                        aSet.Set(pTarget->GetMergedItemSet());
                    }

                    // for SdrGrafObj, use the graphic as fill style argument
                    const Graphic& rGraphic = pSdrGrafObj->GetGraphic();

                    if(GraphicType::NONE != rGraphic.GetType() && GraphicType::Default != rGraphic.GetType())
                    {
                        aSet.Put(XFillBitmapItem(OUString(), rGraphic));
                        aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
                    }
                }
                else
                {
                    aSet.Put(pClpObj->GetMergedItemSet());
                }

                pView->SetAttributes( aSet );
            }
            break;

        default:
            nAction = SwPasteSdr::Insert;
            break;
        }
    }
    else
        nAction = SwPasteSdr::Insert;

    if( SwPasteSdr::Insert == nAction )
    {
        ::sw::DrawUndoGuard drawUndoGuard(GetDoc()->GetIDocumentUndoRedo());

        bool bDesignMode = pView->IsDesignMode();
        if( !bDesignMode )
            pView->SetDesignMode();

        // #i50824#
        // method <lcl_RemoveOleObjsFromSdrModel> replaced by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs>
        lcl_ConvertSdrOle2ObjsToSdrGrafObjs(*pModel);
        pView->Paste(*pModel, aPos, nullptr, SdrInsertFlags::NONE);

        const size_t nCnt = pView->GetMarkedObjectList().GetMarkCount();
        if( nCnt )
        {
            const Point aNull( 0, 0 );
            for( size_t i=0; i < nCnt; ++i )
            {
                SdrObject *pObj = pView->GetMarkedObjectList().GetMark(i)->GetMarkedSdrObj();
                pObj->ImpSetAnchorPos( aNull );
            }

            pView->SetCurrentObj( SdrObjKind::Group );
            if ( nCnt > 1 )
                pView->GroupMarked();
            SdrObject *pObj = pView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
            if( dynamic_cast<const SdrUnoObj*>( pObj) !=  nullptr )
            {
                pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() );
                bDesignMode = true;
            }
            else
                pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() );
            const tools::Rectangle &rSnap = pObj->GetSnapRect();
            const Size aDiff( rSnap.GetWidth()/2, rSnap.GetHeight()/2 );
            pView->MoveMarkedObj( aDiff );
            ImpEndCreate();
            if( !bDesignMode )
                pView->SetDesignMode( false );
        }
    }
    EndUndo();
    EndAllAction();
}

bool SwFEShell::Paste(const Graphic &rGrf, const OUString& rURL)
{
    CurrShell aCurr( this );
    SdrObject* pObj = nullptr;
    SdrView *pView = Imp()->GetDrawView();

    bool bRet = 1 == pView->GetMarkedObjectList().GetMarkCount();
    if (bRet)
    {
        pObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj();
        bRet = pObj->IsClosedObj() && dynamic_cast<const SdrOle2Obj*>( pObj) == nullptr;
    }

    if( bRet && pObj )
    {
        // #i123922# added code to handle the two cases of SdrGrafObj and a fillable, non-
        // OLE object in focus
        SdrObject* pResult = pObj;

        if(auto pGrafObj = dynamic_cast< SdrGrafObj* >(pObj))
        {
            rtl::Reference<SdrGrafObj> pNewGrafObj = SdrObject::Clone(*pGrafObj, pGrafObj->getSdrModelFromSdrObject());

            pNewGrafObj->SetGraphic(rGrf);

            // #i123922#  for handling MasterObject and virtual ones correctly, SW
            // wants us to call ReplaceObject at the page, but that also
            // triggers the same assertion (I tried it), so stay at the view method
            pView->ReplaceObjectAtView(pObj, *pView->GetSdrPageView(), pNewGrafObj.get());

            // set in all cases - the Clone() will have copied an existing link (!)
            pNewGrafObj->SetGraphicLink(rURL);

            pResult = pNewGrafObj.get();
        }
        else
        {
            pView->AddUndo(std::make_unique<SdrUndoAttrObj>(*pObj));

            SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(pView->GetModel()->GetItemPool());

            aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
            aSet.Put(XFillBitmapItem(OUString(), rGrf));
            pObj->SetMergedItemSetAndBroadcast(aSet);
        }

        // we are done; mark the modified/new object
        pView->MarkObj(pResult, pView->GetSdrPageView());
    }

    return bRet;
}

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