    /* -*- 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 <scitems.hxx>
    #include <svx/svdetc.hxx>
    #include <svx/svditer.hxx>
    #include <svx/svdoole2.hxx>
    #include <svx/svdpage.hxx>
    #include <sfx2/dispatch.hxx>
    #include <sfx2/docfile.hxx>
    #include <svl/stritem.hxx>
    #include <svl/ptitem.hxx>
    #include <svl/urlbmk.hxx>
    #include <comphelper/classids.hxx>
    #include <sot/formats.hxx>
    #include <sot/storage.hxx>
    #include <vcl/graph.hxx>
    #include <vcl/virdev.hxx>
    #include <vcl/weld.hxx>
    #include <tools/urlobj.hxx>
    #include <sot/exchange.hxx>
    #include <memory>

    #include <sfx2/lokhelper.hxx>

    #include <attrib.hxx>
    #include <patattr.hxx>
    #include <dociter.hxx>
    #include <viewfunc.hxx>
    #include <tabvwsh.hxx>
    #include <docsh.hxx>
    #include <docfunc.hxx>
    #include <undoblk.hxx>
    #include <refundo.hxx>
    #include <globstr.hrc>
    #include <scresid.hxx>
    #include <global.hxx>
    #include <transobj.hxx>
    #include <drwtrans.hxx>
    #include <rangenam.hxx>
    #include <dbdata.hxx>
    #include <impex.hxx>
    #include <chgtrack.hxx>
    #include <waitoff.hxx>
    #include <scmod.hxx>
    #include <sc.hrc>
    #include <inputopt.hxx>
    #include <warnbox.hxx>
    #include <drwlayer.hxx>
    #include <editable.hxx>
    #include <docuno.hxx>
    #include <clipparam.hxx>
    #include <undodat.hxx>
    #include <drawview.hxx>
    #include <cliputil.hxx>
    #include <clipoptions.hxx>
    #include <gridwin.hxx>
    #include <com/sun/star/util/XCloneable.hpp>

    using namespace com::sun::star;

    //  GlobalName of writer-DocShell from comphelper/classids.hxx

    //      C U T

    void ScViewFunc::CutToClip()
    {
        UpdateInputLine();

        ScEditableTester aTester( this );
        if (!aTester.IsEditable())                  // selection editable?
        {
            ErrorMessage( aTester.GetMessageId() );
            return;
        }

        ScRange aRange;                             // delete this range
        if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
        {
            ScDocument* pDoc = GetViewData().GetDocument();
            ScDocShell* pDocSh = GetViewData().GetDocShell();
            ScMarkData& rMark = GetViewData().GetMarkData();
            const bool bRecord(pDoc->IsUndoEnabled());                  // Undo/Redo

            ScDocShellModificator aModificator( *pDocSh );

            if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )          // mark the range if not marked yet
            {
                DoneBlockMode();
                InitOwnBlockMode();
                rMark.SetMarkArea( aRange );
                MarkDataChanged();
            }

            CopyToClip( nullptr, true, false, true/*bIncludeObjects*/ );           // copy to clipboard

            ScAddress aOldEnd( aRange.aEnd );       //  combined cells in this range?
            pDoc->ExtendMerge( aRange, true );

            ScDocumentUniquePtr pUndoDoc;
            if ( bRecord )
            {
                pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
                pUndoDoc->InitUndoSelected( pDoc, rMark );
                // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
                ScRange aCopyRange = aRange;
                aCopyRange.aStart.SetTab(0);
                aCopyRange.aEnd.SetTab(pDoc->GetTableCount()-1);
                pDoc->CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
                pDoc->BeginDrawUndo();
            }

            sal_uInt16 nExtFlags = 0;
            pDocSh->UpdatePaintExt( nExtFlags, aRange );

            rMark.MarkToMulti();
            pDoc->DeleteSelection( InsertDeleteFlags::ALL, rMark );
            pDoc->DeleteObjectsInSelection( rMark );
            rMark.MarkToSimple();

            if ( !AdjustRowHeight( aRange.aStart.Row(), aRange.aEnd.Row() ) )
                pDocSh->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags );

            if ( bRecord )                          // Draw-Undo now available
                pDocSh->GetUndoManager()->AddUndoAction(
                    new ScUndoCut( pDocSh, aRange, aOldEnd, rMark, std::move(pUndoDoc) ) );

            aModificator.SetDocumentModified();
            pDocSh->UpdateOle(&GetViewData());

            CellContentChanged();
        }
        else
            ErrorMessage( STR_NOMULTISELECT );
    }

    //      C O P Y

    bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
    {
        ScRange aRange;
        ScMarkType eMarkType = GetViewData().GetSimpleArea( aRange );
        ScMarkData& rMark = GetViewData().GetMarkData();
        bool bDone = false;

        if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
        {
           ScRangeList aRangeList( aRange );
           bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
        }
        else if (eMarkType == SC_MARK_MULTI)
        {
            ScRangeList aRangeList;
            rMark.MarkToSimple();
            rMark.FillRangeListWithMarks(&aRangeList, false);
            bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
        }
        else
        {
            if (!bApi)
                ErrorMessage(STR_NOMULTISELECT);
        }

        return bDone;
    }

    // Copy the content of the Range into clipboard.
    bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
    {
        if ( rRanges.empty() )
            return false;
        if ( bStopEdit )
            UpdateInputLine();

        bool bDone;
        if (rRanges.size() > 1) // isMultiRange
            bDone = CopyToClipMultiRange(pClipDoc, rRanges, bCut, bApi, bIncludeObjects);
        else
            bDone = CopyToClipSingleRange(pClipDoc, rRanges, bCut, bIncludeObjects);
        return bDone;
    }

    bool ScViewFunc::CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bIncludeObjects )
    {
        ScRange aRange = rRanges[0];
        ScClipParam aClipParam( aRange, bCut );
        aClipParam.maRanges = rRanges;
        ScDocument* pDoc = GetViewData().GetDocument();
        ScMarkData& rMark = GetViewData().GetMarkData();

        if ( !pDoc
            || pDoc->HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) )
            return false;

        bool bSysClip = false;
        if ( !pClipDoc )                                    // no clip doc specified
        {
            // Create one (deleted by ScTransferObj).
            pClipDoc = new ScDocument( SCDOCMODE_CLIP );
            bSysClip = true;                                // and copy into system
        }
        if ( !bCut )
        {
            ScChangeTrack* pChangeTrack = pDoc->GetChangeTrack();
            if ( pChangeTrack )
                pChangeTrack->ResetLastCut();
        }

        if ( bSysClip && bIncludeObjects )
        {
            bool bAnyOle = pDoc->HasOLEObjectsInArea( aRange );
            // Update ScGlobal::xDrawClipDocShellRef.
            ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
        }

        // is this necessary?, will setting the doc id upset the
        // following paste operation with range? would be nicer to just set this always
        // and lose the 'if' above
        aClipParam.setSourceDocID( pDoc->GetDocumentID() );

        if (SfxObjectShell* pObjectShell = pDoc->GetDocumentShell())
        {
            // Copy document properties from pObjectShell to pClipDoc (to its clip options, as it has no object shell).
            uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(pObjectShell->GetModel(), uno::UNO_QUERY);
            uno::Reference<util::XCloneable> xCloneable(xDocumentPropertiesSupplier->getDocumentProperties(), uno::UNO_QUERY);
            std::unique_ptr<ScClipOptions> pOptions(new ScClipOptions);
            pOptions->m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY);
            pClipDoc->SetClipOptions(std::move(pOptions));
        }

        pDoc->CopyToClip( aClipParam, pClipDoc, &rMark, false, bIncludeObjects );
        if ( pDoc && pClipDoc )
        {
            ScDrawLayer* pDrawLayer = pClipDoc->GetDrawLayer();
            if ( pDrawLayer )
            {
                ScClipParam& rClipDocClipParam = pClipDoc->GetClipParam();
                ScRangeListVector& rRangesVector = rClipDocClipParam.maProtectedChartRangesVector;
                SCTAB nTabCount = pClipDoc->GetTableCount();
                for ( SCTAB nTab = 0; nTab < nTabCount; ++nTab )
                {
                    SdrPage* pPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
                    if ( pPage )
                    {
                        ScChartHelper::FillProtectedChartRangesVector( rRangesVector, pDoc, pPage );
                    }
                }
            }
        }

        if ( bSysClip )
        {
            ScDrawLayer::SetGlobalDrawPersist(nullptr);
            ScGlobal::SetClipDocName( pDoc->GetDocumentShell()->GetTitle( SFX_TITLE_FULLNAME ) );
        }
        pClipDoc->ExtendMerge( aRange, true );

        if ( bSysClip )
        {
            ScDocShell* pDocSh = GetViewData().GetDocShell();
            TransferableObjectDescriptor aObjDesc;
            pDocSh->FillTransferableObjectDescriptor( aObjDesc );
            aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
            // maSize is set in ScTransferObj ctor

            ScTransferObj* pTransferObj = new ScTransferObj( ScDocumentUniquePtr(pClipDoc), aObjDesc );
            uno::Reference<css::datatransfer::XTransferable2> xTransferObj = pTransferObj;
            if ( ScGlobal::xDrawClipDocShellRef.is() )
            {
                SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
                pTransferObj->SetDrawPersist( aPersistRef );// keep persist for ole objects alive
            }
            pTransferObj->CopyToClipboard( GetActiveWin() );
        }

        return true;
    }

    bool ScViewFunc::CopyToClipMultiRange( ScDocument* pInputClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects )
    {
        if (bCut)
        {
            // We don't support cutting of multi-selections.
            if (!bApi)
                ErrorMessage(STR_NOMULTISELECT);
            return false;
        }
        if (pInputClipDoc)
        {
            // TODO: What's this for?
            if (!bApi)
                ErrorMessage(STR_NOMULTISELECT);
            return false;
        }

        ScClipParam aClipParam( rRanges[0], bCut );
        aClipParam.maRanges = rRanges;
        ScDocument* pDoc = GetViewData().GetDocument();
        ScMarkData& rMark = GetViewData().GetMarkData();
        bool bDone = false;
        bool bSuccess = false;
        aClipParam.mbCutMode = false;

        do
        {
            ScDocumentUniquePtr pDocClip(new ScDocument(SCDOCMODE_CLIP));

            // Check for geometrical feasibility of the ranges.
            bool bValidRanges = true;
            ScRange const * p = &aClipParam.maRanges.front();
            SCCOL nPrevColDelta = 0;
            SCROW nPrevRowDelta = 0;
            SCCOL nPrevCol = p->aStart.Col();
            SCROW nPrevRow = p->aStart.Row();
            SCCOL nPrevColSize = p->aEnd.Col() - p->aStart.Col() + 1;
            SCROW nPrevRowSize = p->aEnd.Row() - p->aStart.Row() + 1;
            for ( size_t i = 1; i < aClipParam.maRanges.size(); ++i )
            {
                p = &aClipParam.maRanges[i];
                if ( pDoc->HasSelectedBlockMatrixFragment(
                    p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), rMark) )
                {
                    if (!bApi)
                        ErrorMessage(STR_MATRIXFRAGMENTERR);
                    return false;
                }

                SCCOL nColDelta = p->aStart.Col() - nPrevCol;
                SCROW nRowDelta = p->aStart.Row() - nPrevRow;

                if ((nColDelta && nRowDelta) || (nPrevColDelta && nRowDelta) || (nPrevRowDelta && nColDelta))
                {
                    bValidRanges = false;
                    break;
                }

                if (aClipParam.meDirection == ScClipParam::Unspecified)
                {
                    if (nColDelta)
                        aClipParam.meDirection = ScClipParam::Column;
                    if (nRowDelta)
                        aClipParam.meDirection = ScClipParam::Row;
                }

                SCCOL nColSize = p->aEnd.Col() - p->aStart.Col() + 1;
                SCROW nRowSize = p->aEnd.Row() - p->aStart.Row() + 1;

                if (aClipParam.meDirection == ScClipParam::Column && nRowSize != nPrevRowSize)
                {
                    // column-oriented ranges must have identical row size.
                    bValidRanges = false;
                    break;
                }
                if (aClipParam.meDirection == ScClipParam::Row && nColSize != nPrevColSize)
                {
                    // likewise, row-oriented ranges must have identical
                    // column size.
                    bValidRanges = false;
                    break;
                }

                nPrevCol = p->aStart.Col();
                nPrevRow = p->aStart.Row();
                nPrevColDelta = nColDelta;
                nPrevRowDelta = nRowDelta;
                nPrevColSize  = nColSize;
                nPrevRowSize  = nRowSize;
            }
            if (!bValidRanges)
                break;
            pDoc->CopyToClip(aClipParam, pDocClip.get(), &rMark, false, bIncludeObjects );

            ScChangeTrack* pChangeTrack = pDoc->GetChangeTrack();
            if ( pChangeTrack )
                pChangeTrack->ResetLastCut();   // no more cut-mode

            ScDocShell* pDocSh = GetViewData().GetDocShell();
            TransferableObjectDescriptor aObjDesc;
            pDocSh->FillTransferableObjectDescriptor( aObjDesc );
            aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
            // maSize is set in ScTransferObj ctor

            ScTransferObj* pTransferObj = new ScTransferObj( std::move(pDocClip), aObjDesc );
            uno::Reference<css::datatransfer::XTransferable2> xTransferObj = pTransferObj;
            if ( ScGlobal::xDrawClipDocShellRef.is() )
            {
                SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
                pTransferObj->SetDrawPersist( aPersistRef );    // keep persist for ole objects alive
            }
            pTransferObj->CopyToClipboard( GetActiveWin() );    // system clipboard

            bSuccess = true;
        }
        while (false);

        if (!bSuccess && !bApi)
            ErrorMessage(STR_NOMULTISELECT);

        bDone = bSuccess;

        return bDone;
    }

    ScTransferObj* ScViewFunc::CopyToTransferable()
    {
        ScRange aRange;
        if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
        {
            ScDocument* pDoc = GetViewData().GetDocument();
            ScMarkData& rMark = GetViewData().GetMarkData();
            if ( !pDoc->HasSelectedBlockMatrixFragment(
                            aRange.aStart.Col(), aRange.aStart.Row(),
                            aRange.aEnd.Col(),   aRange.aEnd.Row(),
                            rMark ) )
            {
                ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));    // create one (deleted by ScTransferObj)

                bool bAnyOle = pDoc->HasOLEObjectsInArea( aRange, &rMark );
                ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );

                ScClipParam aClipParam(aRange, false);
                pDoc->CopyToClip(aClipParam, pClipDoc.get(), &rMark, false, true);

                ScDrawLayer::SetGlobalDrawPersist(nullptr);
                pClipDoc->ExtendMerge( aRange, true );

                ScDocShell* pDocSh = GetViewData().GetDocShell();
                TransferableObjectDescriptor aObjDesc;
                pDocSh->FillTransferableObjectDescriptor( aObjDesc );
                aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
                ScTransferObj* pTransferObj = new ScTransferObj( std::move(pClipDoc), aObjDesc );
                return pTransferObj;
            }
        }

        return nullptr;
    }

    //      P A S T E

    void ScViewFunc::PasteDraw()
    {
        ScViewData& rViewData = GetViewData();
        SCCOL nPosX = rViewData.GetCurX();
        SCROW nPosY = rViewData.GetCurY();
        vcl::Window* pWin = GetActiveWin();
        Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY,
                                         rViewData.GetActivePart() ) );
        const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin()));
        if (pDrawClip)
        {
            OUString aSrcShellID = pDrawClip->GetShellID();
            OUString aDestShellID = SfxObjectShell::CreateShellID(rViewData.GetDocShell());
            PasteDraw(aPos, pDrawClip->GetModel(), false, aSrcShellID, aDestShellID);
        }
    }

    void ScViewFunc::PasteFromSystem()
    {
        UpdateInputLine();

        vcl::Window* pWin = GetActiveWin();
        css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(ScTabViewShell::GetClipData(pWin));
        const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(xTransferable2);
        // keep a reference in case the clipboard is changed during PasteFromClip
        const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(xTransferable2);
        if (pOwnClip)
        {
            PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
                            ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
                            true );     // allow warning dialog
        }
        else if (pDrawClip)
            PasteDraw();
        else
        {
            TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );

            {
                SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
                SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");

                SotClipboardFormatId nFormat; // output param for GetExchangeAction
                sal_uInt8 nEventAction;      // output param for GetExchangeAction

                uno::Reference<css::datatransfer::XTransferable> xTransferable( aDataHelper.GetXTransferable() );
                sal_uInt8 nAction = SotExchange::GetExchangeAction(
                                        aDataHelper.GetDataFlavorExVector(),
                                        SotExchangeDest::SCDOC_FREE_AREA,
                                        EXCHG_IN_ACTION_COPY,
                                        EXCHG_IN_ACTION_DEFAULT,
                                        nFormat, nEventAction, SotClipboardFormatId::NONE,
                                        &xTransferable );

                if ( nAction != EXCHG_INOUT_ACTION_NONE )
                {
                    switch( nAction )
                    {
                    case EXCHG_OUT_ACTION_INSERT_SVXB:
                    case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE:
                    case EXCHG_OUT_ACTION_INSERT_BITMAP:
                    case EXCHG_OUT_ACTION_INSERT_GRAPH:
                        // SotClipboardFormatId::BITMAP
                        // SotClipboardFormatId::PNG
                        // SotClipboardFormatId::GDIMETAFILE
                        // SotClipboardFormatId::SVXB
                        PasteFromSystem(nFormat);
                        break;
                    default:
                        nAction = EXCHG_INOUT_ACTION_NONE;
                    }
                }

                if ( nAction == EXCHG_INOUT_ACTION_NONE )
                {
                    //  first SvDraw-model, then drawing
                    //  (only one drawing is allowed)

                    if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
                    {
                        // special case for tables from drawing
                        if( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
                        {
                            PasteFromSystem( SotClipboardFormatId::RTF );
                        }
                        else if( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
                        {
                            PasteFromSystem( SotClipboardFormatId::RICHTEXT );
                        }
                        else
                        {
                            PasteFromSystem( SotClipboardFormatId::DRAWING );
                        }
                    }
                    else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
                    {
                        //  If it's a Writer object, insert RTF instead of OLE

                        //  Else, if the class id is all-zero, and SYLK is available,
                        //  it probably is spreadsheet cells that have been put
                        //  on the clipboard by OOo, so use the SYLK. (fdo#31077)

                        bool bDoRtf = false;
                        TransferableObjectDescriptor aObjDesc;
                        if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
                        {
                            bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
                                         aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
                                       && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
                        }
                        if ( bDoRtf )
                            PasteFromSystem( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT );
                        else if ( aObjDesc.maClassName == SvGlobalName( 0,0,0,0,0,0,0,0,0,0,0 )
                                  && aDataHelper.HasFormat( SotClipboardFormatId::SYLK ))
                            PasteFromSystem( SotClipboardFormatId::SYLK );
                        else
                            PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE );
                    }
                    else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
                        PasteFromSystem( SotClipboardFormatId::LINK_SOURCE );
                        // the following format can not affect scenario from #89579#
                    else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
                        PasteFromSystem( SotClipboardFormatId::EMBEDDED_OBJ_OLE );
                        // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
                    else if (aDataHelper.HasFormat(nBiff8))      // before xxx_OLE formats
                        PasteFromSystem(nBiff8);
                    else if (aDataHelper.HasFormat(nBiff5))
                        PasteFromSystem(nBiff5);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
                        PasteFromSystem(SotClipboardFormatId::RTF);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
                        PasteFromSystem(SotClipboardFormatId::RICHTEXT);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
                        PasteFromSystem(SotClipboardFormatId::HTML);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
                        PasteFromSystem(SotClipboardFormatId::HTML_SIMPLE);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
                        PasteFromSystem(SotClipboardFormatId::SYLK);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
                        PasteFromSystem(SotClipboardFormatId::STRING_TSVC);
                    else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
                        PasteFromSystem(SotClipboardFormatId::STRING);
                    // xxx_OLE formats come last, like in SotExchange tables
                    else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
                        PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE_OLE );
                    else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
                        PasteFromSystem( SotClipboardFormatId::LINK_SOURCE_OLE );
                }
            }
        }
        //  no exception-> SID_PASTE has FastCall-flag from idl
        //  will be called in case of empty clipboard (#42531#)
    }

    void ScViewFunc::PasteFromTransferable( const uno::Reference<datatransfer::XTransferable>& rxTransferable )
    {
        ScTransferObj *pOwnClip=nullptr;
        ScDrawTransferObj *pDrawClip=nullptr;
        uno::Reference<lang::XUnoTunnel> xTunnel( rxTransferable, uno::UNO_QUERY );
        if ( xTunnel.is() )
        {
            sal_Int64 nHandle = xTunnel->getSomething( ScTransferObj::getUnoTunnelId() );
            if ( nHandle )
                pOwnClip = reinterpret_cast<ScTransferObj*>( static_cast<sal_IntPtr>(nHandle));
            else
            {
                nHandle = xTunnel->getSomething( ScDrawTransferObj::getUnoTunnelId() );
                if ( nHandle )
                    pDrawClip = reinterpret_cast<ScDrawTransferObj*>( static_cast<sal_IntPtr>(nHandle) );
            }
        }

        if (pOwnClip)
        {
            PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
                            ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
                            true );     // allow warning dialog
        }
        else if (pDrawClip)
        {
            ScViewData& rViewData = GetViewData();
            SCCOL nPosX = rViewData.GetCurX();
            SCROW nPosY = rViewData.GetCurY();
            vcl::Window* pWin = GetActiveWin();
            Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) );
            PasteDraw(
                aPos, pDrawClip->GetModel(), false,
                pDrawClip->GetShellID(), SfxObjectShell::CreateShellID(rViewData.GetDocShell()));
        }
        else
        {
                TransferableDataHelper aDataHelper( rxTransferable );
            {
                SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
                SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");
                SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
                    //  first SvDraw-model, then drawing
                    //  (only one drawing is allowed)

                if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
                    nFormatId = SotClipboardFormatId::DRAWING;
                else if (aDataHelper.HasFormat( SotClipboardFormatId::SVXB ))
                    nFormatId = SotClipboardFormatId::SVXB;
                else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
                {
                    //  If it's a Writer object, insert RTF instead of OLE
                    bool bDoRtf = false;
                    TransferableObjectDescriptor aObjDesc;
                    if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
                    {
                        bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
                                     aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
                                   && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ));
                    }
                    if ( bDoRtf )
                        nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
                    else
                        nFormatId = SotClipboardFormatId::EMBED_SOURCE;
                }
                else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
                    nFormatId = SotClipboardFormatId::LINK_SOURCE;
                // the following format can not affect scenario from #89579#
                else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
                    nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
                // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
                else if (aDataHelper.HasFormat(nBiff8))      // before xxx_OLE formats
                    nFormatId = nBiff8;
                else if (aDataHelper.HasFormat(nBiff5))
                    nFormatId = nBiff5;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
                    nFormatId = SotClipboardFormatId::RTF;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
                    nFormatId = SotClipboardFormatId::RICHTEXT;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
                    nFormatId = SotClipboardFormatId::HTML;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
                    nFormatId = SotClipboardFormatId::HTML_SIMPLE;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
                    nFormatId = SotClipboardFormatId::SYLK;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
                    nFormatId = SotClipboardFormatId::STRING_TSVC;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
                    nFormatId = SotClipboardFormatId::STRING;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::GDIMETAFILE))
                    nFormatId = SotClipboardFormatId::GDIMETAFILE;
                else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP))
                    nFormatId = SotClipboardFormatId::BITMAP;
                // xxx_OLE formats come last, like in SotExchange tables
                else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
                    nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
                else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
                    nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
                else
                    return;

                PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
                    GetViewData().GetCurX(), GetViewData().GetCurY(), nullptr );
            }
        }
    }

    bool ScViewFunc::PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi )
    {
        UpdateInputLine();

        bool bRet = true;
        vcl::Window* pWin = GetActiveWin();
        // keep a reference in case the clipboard is changed during PasteFromClip
        const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
        if ( nFormatId == SotClipboardFormatId::NONE && pOwnClip )
        {
            PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
                            ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
                            !bApi );        // allow warning dialog
        }
        else
        {
            TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
            if ( !aDataHelper.GetTransferable().is() )
                return false;

            SCCOL nPosX = 0;
            SCROW nPosY = 0;

            ScViewData& rViewData = GetViewData();
            ScRange aRange;
            if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
            {
                nPosX = aRange.aStart.Col();
                nPosY = aRange.aStart.Row();
            }
            else
            {
                nPosX = rViewData.GetCurX();
                nPosY = rViewData.GetCurY();
            }

            bRet = PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
                                    nPosX, nPosY,
                                    nullptr, false, !bApi );       // allow warning dialog

            if ( !bRet && !bApi )
                ErrorMessage(STR_PASTE_ERROR);
        }
        return bRet;
    }

    //      P A S T E

    bool ScViewFunc::PasteOnDrawObjectLinked(
        const uno::Reference<datatransfer::XTransferable>& rxTransferable,
        SdrObject& rHitObj)
    {
        TransferableDataHelper aDataHelper( rxTransferable );

        if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
        {
            tools::SvRef<SotStorageStream> xStm;
            ScDrawView* pScDrawView = GetScDrawView();

            if( pScDrawView && aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) )
            {
                Graphic aGraphic;

                ReadGraphic( *xStm, aGraphic );

                const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));

                if(pScDrawView->ApplyGraphicToObject( rHitObj, aGraphic, aBeginUndo, "", "" ))
                {
                    return true;
                }
            }
        }
        else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
        {
            GDIMetaFile aMtf;
            ScDrawView* pScDrawView = GetScDrawView();

            if( pScDrawView && aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) )
            {
                const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));

                if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aMtf), aBeginUndo, "", "" ))
                {
                    return true;
                }
            }
        }
        else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) || aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
        {
            BitmapEx aBmpEx;
            ScDrawView* pScDrawView = GetScDrawView();

            if( pScDrawView && aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) )
            {
                const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));

                if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aBmpEx), aBeginUndo, "", "" ))
                {
                    return true;
                }
            }
        }

        return false;
    }

    static bool lcl_SelHasAttrib( const ScDocument* pDoc, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
                            const ScMarkData& rTabSelection, HasAttrFlags nMask )
    {
        ScMarkData::const_iterator itr = rTabSelection.begin(), itrEnd = rTabSelection.end();
        for (; itr != itrEnd; ++itr)
            if ( pDoc->HasAttrib( nCol1, nRow1, *itr, nCol2, nRow2, *itr, nMask ) )
                return true;
        return false;
    }

    //  paste into sheet:

    //  internal paste

    namespace {

    bool checkDestRangeForOverwrite(const ScRangeList& rDestRanges, const ScDocument* pDoc, const ScMarkData& rMark, weld::Window* pParentWnd)
    {
        bool bIsEmpty = true;
        ScMarkData::const_iterator itrTab = rMark.begin(), itrTabEnd = rMark.end();
        size_t nRangeSize = rDestRanges.size();
        for (; itrTab != itrTabEnd && bIsEmpty; ++itrTab)
        {
            for (size_t i = 0; i < nRangeSize && bIsEmpty; ++i)
            {
                const ScRange& rRange = rDestRanges[i];
                bIsEmpty = pDoc->IsBlockEmpty(
                    *itrTab, rRange.aStart.Col(), rRange.aStart.Row(),
                    rRange.aEnd.Col(), rRange.aEnd.Row());
            }
        }

        if (!bIsEmpty)
        {
            ScReplaceWarnBox aBox(pParentWnd);
            if (aBox.execute() != RET_YES)
            {
                //  changing the configuration is within the ScReplaceWarnBox
                return false;
            }
        }
        return true;
    }

    }

    bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc,
                                    ScPasteFunc nFunction, bool bSkipEmpty,
                                    bool bTranspose, bool bAsLink,
                                    InsCellCmd eMoveMode, InsertDeleteFlags nUndoExtraFlags,
                                    bool bAllowDialogs )
    {
        if (!pClipDoc)
        {
            OSL_FAIL("PasteFromClip: pClipDoc=0 not allowed");
            return false;
        }

        if (GetViewData().SelectionForbidsCellFill())
            return false;

        //  undo: save all or no content
        InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE;
        if (nFlags & InsertDeleteFlags::CONTENTS)
            nContFlags |= InsertDeleteFlags::CONTENTS;
        if (nFlags & InsertDeleteFlags::ATTRIB)
            nContFlags |= InsertDeleteFlags::ATTRIB;
        // move attributes to undo without copying them from clip to doc
        InsertDeleteFlags nUndoFlags = nContFlags;
        if (nUndoExtraFlags & InsertDeleteFlags::ATTRIB)
            nUndoFlags |= InsertDeleteFlags::ATTRIB;
        // do not copy note captions into undo document
        nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;

        ScClipParam& rClipParam = pClipDoc->GetClipParam();
        if (rClipParam.isMultiRange())
        {
            // Source data is multi-range.
            return PasteMultiRangesFromClip(
                nFlags, pClipDoc, nFunction, bSkipEmpty, bTranspose, bAsLink, bAllowDialogs,
                eMoveMode, nUndoFlags);
        }

        ScMarkData& rMark = GetViewData().GetMarkData();
        if (rMark.IsMultiMarked())
        {
            // Source data is single-range but destination is multi-range.
            return PasteFromClipToMultiRanges(
                nFlags, pClipDoc, nFunction, bSkipEmpty, bTranspose, bAsLink, bAllowDialogs,
                eMoveMode, nUndoFlags);
        }

        bool bCutMode = pClipDoc->IsCutMode();      // if transposing, take from original clipdoc
        bool bIncludeFiltered = bCutMode;

        // paste drawing: also if InsertDeleteFlags::NOTE is set (to create drawing layer for note captions)
        bool bPasteDraw = ( pClipDoc->GetDrawLayer() && ( nFlags & (InsertDeleteFlags::OBJECTS|InsertDeleteFlags::NOTE) ) );

        ScDocShellRef aTransShellRef;   // for objects in xTransClip - must remain valid as long as xTransClip
        ScDocument* pOrigClipDoc = nullptr;
        ScDocumentUniquePtr xTransClip;
        if ( bTranspose )
        {
            SCCOL nX;
            SCROW nY;
            // include filtered rows until TransposeClip can skip them
            bIncludeFiltered = true;
            pClipDoc->GetClipArea( nX, nY, true );
            if ( nY > static_cast<sal_Int32>(MAXCOL) )                      // too many lines for transpose
            {
                ErrorMessage(STR_PASTE_FULL);
                return false;
            }
            pOrigClipDoc = pClipDoc;        // refs

            if ( bPasteDraw )
            {
                aTransShellRef = new ScDocShell;        // DocShell needs a Ref immediately
                aTransShellRef->DoInitNew();
            }
            ScDrawLayer::SetGlobalDrawPersist( aTransShellRef.get() );

            xTransClip.reset( new ScDocument( SCDOCMODE_CLIP ));
            pClipDoc->TransposeClip( xTransClip.get(), nFlags, bAsLink );
            pClipDoc = xTransClip.get();

            ScDrawLayer::SetGlobalDrawPersist(nullptr);
        }

        SCCOL nStartCol;
        SCROW nStartRow;
        SCTAB nStartTab;
        SCCOL nEndCol;
        SCROW nEndRow;
        SCTAB nEndTab;
        SCCOL nClipSizeX;
        SCROW nClipSizeY;
        pClipDoc->GetClipArea( nClipSizeX, nClipSizeY, true );      // size in clipboard doc

        //  size in target doc: include filtered rows only if CutMode is set
        SCCOL nDestSizeX;
        SCROW nDestSizeY;
        pClipDoc->GetClipArea( nDestSizeX, nDestSizeY, bIncludeFiltered );

        ScDocument* pDoc = GetViewData().GetDocument();
        ScDocShell* pDocSh = GetViewData().GetDocShell();
        SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
        const bool bRecord(pDoc->IsUndoEnabled());

        ScDocShellModificator aModificator( *pDocSh );

        ScRange aMarkRange;
        ScMarkData aFilteredMark( rMark);   // local copy for all modifications
        ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange, aFilteredMark);
        bool bMarkIsFiltered = (eMarkType == SC_MARK_SIMPLE_FILTERED);
        bool bNoPaste = ((eMarkType != SC_MARK_SIMPLE && !bMarkIsFiltered) ||
                (bMarkIsFiltered && (eMoveMode != INS_NONE || bAsLink)));

        if (!bNoPaste)
        {
            if (!rMark.IsMarked())
            {
                // Create a selection with clipboard row count and check that for
                // filtered.
                nStartCol = GetViewData().GetCurX();
                nStartRow = GetViewData().GetCurY();
                nStartTab = GetViewData().GetTabNo();
                nEndCol = nStartCol + nDestSizeX;
                nEndRow = nStartRow + nDestSizeY;
                nEndTab = nStartTab;
                aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
                if (ScViewUtil::HasFiltered( aMarkRange, pDoc))
                {
                    bMarkIsFiltered = true;
                    // Fit to clipboard's row count unfiltered rows. If there is no
                    // fit assume that pasting is not possible. Note that nDestSizeY is
                    // size-1 (difference).
                    if (!ScViewUtil::FitToUnfilteredRows( aMarkRange, pDoc, nDestSizeY+1))
                        bNoPaste = true;
                }
                aFilteredMark.SetMarkArea( aMarkRange);
            }
            else
            {
                // Expand the marked area when the destination area is larger than the
                // current selection, to get the undo do the right thing. (i#106711)
                ScRange aRange;
                aFilteredMark.GetMarkArea( aRange );
                if( (aRange.aEnd.Col() - aRange.aStart.Col()) < nDestSizeX )
                {
                    aRange.aEnd.SetCol(aRange.aStart.Col() + nDestSizeX);
                    aFilteredMark.SetMarkArea(aRange);
                }
            }
        }

        if (bNoPaste)
        {
            ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
            return false;
        }

        SCROW nUnfilteredRows = aMarkRange.aEnd.Row() - aMarkRange.aStart.Row() + 1;
        ScRangeList aRangeList;
        if (bMarkIsFiltered)
        {
            ScViewUtil::UnmarkFiltered( aFilteredMark, pDoc);
            aFilteredMark.FillRangeListWithMarks( &aRangeList, false);
            nUnfilteredRows = 0;
            size_t ListSize = aRangeList.size();
            for ( size_t i = 0; i < ListSize; ++i )
            {
                ScRange & r = aRangeList[i];
                nUnfilteredRows += r.aEnd.Row() - r.aStart.Row() + 1;
            }
    #if 0
            /* This isn't needed but could be a desired restriction. */
            // For filtered, destination rows have to be an exact multiple of
            // source rows. Note that nDestSizeY is size-1 (difference), so
            // nDestSizeY==0 fits always.
            if ((nUnfilteredRows % (nDestSizeY+1)) != 0)
            {
                /* FIXME: this should be a more descriptive error message then. */
                ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
                return false;
            }
    #endif
        }

        // Also for a filtered selection the area is used, for undo et al.
        if ( aFilteredMark.IsMarked() || bMarkIsFiltered )
        {
            aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
            SCCOL nBlockAddX = nEndCol-nStartCol;
            SCROW nBlockAddY = nEndRow-nStartRow;

            // request, if the selection is greater than one row/column, but smaller
            // as the Clipboard (then inserting is done beyond the selection)

            //  ClipSize is not size, but difference
            if ( ( nBlockAddX != 0 && nBlockAddX < nDestSizeX ) ||
                 ( nBlockAddY != 0 && nBlockAddY < nDestSizeY ) ||
                 ( bMarkIsFiltered && nUnfilteredRows < nDestSizeY+1 ) )
            {
                ScWaitCursorOff aWaitOff( GetFrameWin() );
                OUString aMessage = ScResId( STR_PASTE_BIGGER );

                vcl::Window* pWin = GetViewData().GetDialogParent();
                std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
                                                               VclMessageType::Question, VclButtonsType::YesNo,
                                                               aMessage));
                xQueryBox->set_default_response(RET_NO);
                if (xQueryBox->run() != RET_YES)
                {
                    return false;
                }
            }

            if (nBlockAddX <= nDestSizeX)
                nEndCol = nStartCol + nDestSizeX;

            if (nBlockAddY <= nDestSizeY)
            {
                nEndRow = nStartRow + nDestSizeY;
                if (bMarkIsFiltered || nEndRow > aMarkRange.aEnd.Row())
                {
                    // Same as above if nothing was marked: re-fit selection to
                    // unfiltered rows. Extending the selection actually may
                    // introduce filtered rows where there weren't any before, so
                    // we also need to test for that.
                    aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
                    if (bMarkIsFiltered || ScViewUtil::HasFiltered( aMarkRange, pDoc))
                    {
                        bMarkIsFiltered = true;
                        // Worst case: all rows up to the end of the sheet are filtered.
                        if (!ScViewUtil::FitToUnfilteredRows( aMarkRange, pDoc, nDestSizeY+1))
                        {
                            ErrorMessage(STR_PASTE_FULL);
                            return false;
                        }
                    }
                    aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
                    aFilteredMark.SetMarkArea( aMarkRange);
                    if (bMarkIsFiltered)
                    {
                        ScViewUtil::UnmarkFiltered( aFilteredMark, pDoc);
                        aFilteredMark.FillRangeListWithMarks( &aRangeList, true);
                    }
                }
            }
        }
        else
        {
            nStartCol = GetViewData().GetCurX();
            nStartRow = GetViewData().GetCurY();
            nStartTab = GetViewData().GetTabNo();
            nEndCol = nStartCol + nDestSizeX;
            nEndRow = nStartRow + nDestSizeY;
            nEndTab = nStartTab;
        }

        bool bOffLimits = !ValidCol(nEndCol) || !ValidRow(nEndRow);

        //  target-range, as displayed:
        ScRange aUserRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab );

        //  should lines be inserted?
        //  ( too large nEndCol/nEndRow are detected below)
        bool bInsertCells = ( eMoveMode != INS_NONE && !bOffLimits );
        if ( bInsertCells )
        {
            //  Instead of EnterListAction, the paste undo action is merged into the
            //  insert action, so Repeat can insert the right cells

            MarkRange( aUserRange );            // set through CopyFromClip

            // CutMode is reset on insertion of cols/rows but needed again on cell move
            bool bCut = pClipDoc->IsCutMode();
            if (!InsertCells( eMoveMode, bRecord, true ))   // is inserting possible?
            {
                return false;
                //  #i21036# EnterListAction isn't used, and InsertCells doesn't insert
                //  its undo action on failure, so no undo handling is needed here
            }
            if ( bCut )
                pClipDoc->SetCutMode( bCut );
        }
        else if (!bOffLimits)
        {
            bool bAskIfNotEmpty = bAllowDialogs &&
                                    ( nFlags & InsertDeleteFlags::CONTENTS ) &&
                                    nFunction == ScPasteFunc::NONE &&
                                    SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
            if ( bAskIfNotEmpty )
            {
                ScRangeList aTestRanges(aUserRange);
                vcl::Window* pWin = GetViewData().GetDialogParent();
                if (!checkDestRangeForOverwrite(aTestRanges, pDoc, aFilteredMark, pWin ? pWin->GetFrameWeld() : nullptr))
                    return false;
            }
        }

        SCCOL nClipStartX;                      // enlarge clipboard-range
        SCROW nClipStartY;
        pClipDoc->GetClipStart( nClipStartX, nClipStartY );
        SCCOL nUndoEndCol = nClipStartX + nClipSizeX;
        SCROW nUndoEndRow = nClipStartY + nClipSizeY;   // end of source area in clipboard document
        bool bClipOver = false;
        // #i68690# ExtendMerge for the clip doc must be called with the clipboard's sheet numbers.
        // The same end column/row can be used for all calls because the clip doc doesn't contain
        // content outside the clip area.
        for (SCTAB nClipTab=0; nClipTab < pClipDoc->GetTableCount(); nClipTab++)
            if ( pClipDoc->HasTable(nClipTab) )
                if ( pClipDoc->ExtendMerge( nClipStartX,nClipStartY, nUndoEndCol,nUndoEndRow, nClipTab ) )
                    bClipOver = true;
        nUndoEndCol -= nClipStartX + nClipSizeX;
        nUndoEndRow -= nClipStartY + nClipSizeY;        // now contains only the difference added by ExtendMerge
        nUndoEndCol = sal::static_int_cast<SCCOL>( nUndoEndCol + nEndCol );
        nUndoEndRow = sal::static_int_cast<SCROW>( nUndoEndRow + nEndRow ); // destination area, expanded for merged cells

        if (nUndoEndCol>MAXCOL || nUndoEndRow>MAXROW)
        {
            ErrorMessage(STR_PASTE_FULL);
            return false;
        }

        pDoc->ExtendMergeSel( nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark );

            //  check cell-protection

        ScEditableTester aTester( pDoc, nStartTab, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow );
        if (!aTester.IsEditable())
        {
            ErrorMessage(aTester.GetMessageId());
            return false;
        }

            //! check overlapping
            //! just check truly intersection !!!!!!!

        ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
        if ( bRecord )
        {
            OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY );
            pUndoMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
        }

        if (bClipOver)
            if (lcl_SelHasAttrib( pDoc, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark, HasAttrFlags::Overlapped ))
            {       // "Cell merge not possible if cells already merged"
                ScDocAttrIterator aIter( pDoc, nStartTab, nStartCol, nStartRow, nUndoEndCol, nUndoEndRow );
                const ScPatternAttr* pPattern = nullptr;
                SCCOL nCol = -1;
                SCROW nRow1 = -1;
                SCROW nRow2 = -1;
                while ( ( pPattern = aIter.GetNext( nCol, nRow1, nRow2 ) ) != nullptr )
                {
                    const ScMergeAttr* pMergeFlag = &pPattern->GetItem(ATTR_MERGE);
                    const ScMergeFlagAttr* pMergeFlagAttr = &pPattern->GetItem(ATTR_MERGE_FLAG);
                    if( ( pMergeFlag && pMergeFlag->IsMerged() ) || ( pMergeFlagAttr && pMergeFlagAttr->IsOverlapped() ) )
                    {
                        ScRange aRange(nCol, nRow1, nStartTab);
                        pDoc->ExtendOverlapped(aRange);
                        pDoc->ExtendMerge(aRange, true);
                        rDocFunc.UnmergeCells(aRange, bRecord, nullptr /*TODO: should pass combined UndoDoc if bRecord*/);
                    }
                }
            }

        if ( !bCutMode )
        {
            ScChangeTrack* pChangeTrack = pDoc->GetChangeTrack();
            if ( pChangeTrack )
                pChangeTrack->ResetLastCut();   // no more cut-mode
        }

        bool bColInfo = ( nStartRow==0 && nEndRow==MAXROW );
        bool bRowInfo = ( nStartCol==0 && nEndCol==MAXCOL );

        ScDocumentUniquePtr pUndoDoc;
        ScDocument* pRefUndoDoc = nullptr;
        std::unique_ptr<ScRefUndoData> pUndoData;

        if ( bRecord )
        {
            pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
            pUndoDoc->InitUndoSelected( pDoc, aFilteredMark, bColInfo, bRowInfo );

            // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
            SCTAB nTabCount = pDoc->GetTableCount();
            pDoc->CopyToDocument( nStartCol, nStartRow, 0, nUndoEndCol, nUndoEndRow, nTabCount-1,
                                  nUndoFlags, false, *pUndoDoc );

            if ( bCutMode )
            {
                pRefUndoDoc = new ScDocument( SCDOCMODE_UNDO );
                pRefUndoDoc->InitUndo( pDoc, 0, nTabCount-1 );

                pUndoData.reset(new ScRefUndoData( pDoc ));
            }
        }

        sal_uInt16 nExtFlags = 0;
        pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
                                           nEndCol,   nEndRow,   nEndTab );     // content before the change

        if (GetViewData().IsActive())
        {
            DoneBlockMode();
            InitOwnBlockMode();
        }
        rMark.SetMarkArea( aUserRange );
        MarkDataChanged();

            //  copy from clipboard
            //  save original data in case of calculation

        ScDocumentUniquePtr pMixDoc;
        if (nFunction != ScPasteFunc::NONE)
        {
            bSkipEmpty = false;
            if ( nFlags & InsertDeleteFlags::CONTENTS )
            {
                pMixDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
                pMixDoc->InitUndo( pDoc, nStartTab, nEndTab );
                pDoc->CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
                                     InsertDeleteFlags::CONTENTS, false, *pMixDoc);
            }
        }

        /*  Make draw layer and start drawing undo.
            - Needed before AdjustBlockHeight to track moved drawing objects.
            - Needed before pDoc->CopyFromClip to track inserted note caption objects.
         */
        if ( bPasteDraw )
            pDocSh->MakeDrawLayer();
        if ( bRecord )
            pDoc->BeginDrawUndo();

        InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
        if (!bAsLink)
        {
            //  copy normally (original range)
            pDoc->CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags,
                    pRefUndoDoc, pClipDoc, true, false, bIncludeFiltered,
                    bSkipEmpty, (bMarkIsFiltered ? &aRangeList : nullptr) );

            // adapt refs manually in case of transpose
            if ( bTranspose && bCutMode && (nFlags & InsertDeleteFlags::CONTENTS) )
                pDoc->UpdateTranspose( aUserRange.aStart, pOrigClipDoc, aFilteredMark, pRefUndoDoc );
        }
        else if (!bTranspose)
        {
            //  copy with bAsLink=TRUE
            pDoc->CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc, pClipDoc,
                                    true, true, bIncludeFiltered, bSkipEmpty );
        }
        else
        {
            //  copy all content (TransClipDoc contains only formula)
            pDoc->CopyFromClip( aUserRange, aFilteredMark, nContFlags, pRefUndoDoc, pClipDoc );
        }

        // skipped rows and merged cells don't mix
        if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
            rDocFunc.UnmergeCells( aUserRange, false, nullptr );

        pDoc->ExtendMergeSel( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, true );    // refresh
                                                                                        // new range

        if ( pMixDoc )              // calculate with original data?
        {
            pDoc->MixDocument( aUserRange, nFunction, bSkipEmpty, pMixDoc.get() );
        }
        pMixDoc.reset();

        AdjustBlockHeight();            // update row heights before pasting objects

        ::std::vector< OUString > aExcludedChartNames;
        SdrPage* pPage = nullptr;

        if ( nFlags & InsertDeleteFlags::OBJECTS )
        {
            ScDrawView* pScDrawView = GetScDrawView();
            SdrModel* pModel = ( pScDrawView ? pScDrawView->GetModel() : nullptr );
            pPage = ( pModel ? pModel->GetPage( static_cast< sal_uInt16 >( nStartTab ) ) : nullptr );
            if ( pPage )
            {
                ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
            }

            //  Paste the drawing objects after the row heights have been updated.

            pDoc->CopyFromClip( aUserRange, aFilteredMark, InsertDeleteFlags::OBJECTS, pRefUndoDoc, pClipDoc,
                                    true, false, bIncludeFiltered );
        }

        pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
                                           nEndCol,   nEndRow,   nEndTab );     // content after the change

            //  if necessary, delete autofilter-heads
        if (bCutMode)
            if (pDoc->RefreshAutoFilter( nClipStartX,nClipStartY, nClipStartX+nClipSizeX,
                                            nClipStartY+nClipSizeY, nStartTab ))
            {
                pDocSh->PostPaint(
                    ScRange(nClipStartX, nClipStartY, nStartTab, nClipStartX+nClipSizeX, nClipStartY, nStartTab),
                    PaintPartFlags::Grid );
            }

        //!     remove block-range on RefUndoDoc !!!

        if ( bRecord )
        {
            ScDocumentUniquePtr pRedoDoc;
            // copy redo data after appearance of the first undo
            // don't create Redo-Doc without RefUndoDoc

            if (pRefUndoDoc)
            {
                pRedoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
                pRedoDoc->InitUndo( pDoc, nStartTab, nEndTab, bColInfo, bRowInfo );

                //      move adapted refs to Redo-Doc

                SCTAB nTabCount = pDoc->GetTableCount();
                pRedoDoc->AddUndoTab( 0, nTabCount-1 );
                pDoc->CopyUpdated( pRefUndoDoc, pRedoDoc.get() );

                //      move old refs to Undo-Doc

                //      not charts?
                pUndoDoc->AddUndoTab( 0, nTabCount-1 );
                pRefUndoDoc->DeleteArea( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, InsertDeleteFlags::ALL );
                pRefUndoDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
                                                InsertDeleteFlags::FORMULA, false, *pUndoDoc );
                delete pRefUndoDoc;
            }

            //  DeleteUnchanged for pUndoData is in ScUndoPaste ctor,
            //  UndoData for redo is made during first undo

            ScUndoPasteOptions aOptions;            // store options for repeat
            aOptions.nFunction  = nFunction;
            aOptions.bSkipEmpty = bSkipEmpty;
            aOptions.bTranspose = bTranspose;
            aOptions.bAsLink    = bAsLink;
            aOptions.eMoveMode  = eMoveMode;

            std::unique_ptr<SfxUndoAction> pUndo(new ScUndoPaste(
                pDocSh, ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
                aFilteredMark, std::move(pUndoDoc), std::move(pRedoDoc), nFlags | nUndoFlags, std::move(pUndoData),
                false, &aOptions ));     // false = Redo data not yet copied

            if ( bInsertCells )
            {
                //  Merge the paste undo action into the insert action.
                //  Use ScUndoWrapper so the ScUndoPaste pointer can be stored in the insert action.

                pUndoMgr->AddUndoAction( new ScUndoWrapper( std::move(pUndo) ), true );
            }
            else
                pUndoMgr->AddUndoAction( pUndo.release() );
            pUndoMgr->LeaveListAction();
        }

        PaintPartFlags nPaint = PaintPartFlags::Grid;
        if (bColInfo)
        {
            nPaint |= PaintPartFlags::Top;
            nUndoEndCol = MAXCOL;               // just for drawing !
        }
        if (bRowInfo)
        {
            nPaint |= PaintPartFlags::Left;
            nUndoEndRow = MAXROW;               // just for drawing !
        }
        pDocSh->PostPaint(
            ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
            nPaint, nExtFlags);
        // AdjustBlockHeight has already been called above

        ResetAutoSpell();
        aModificator.SetDocumentModified();
        PostPasteFromClip(aUserRange, rMark);

        if ( nFlags & InsertDeleteFlags::OBJECTS )
        {
            ScModelObj* pModelObj = ScModelObj::getImplementation( pDocSh->GetModel() );
            if ( pPage && pModelObj )
            {
                bool bSameDoc = ( rClipParam.getSourceDocID() == pDoc->GetDocumentID() );
                const ScRangeListVector& rProtectedChartRangesVector( rClipParam.maProtectedChartRangesVector );
                ScChartHelper::CreateProtectedChartListenersAndNotify( pDoc, pPage, pModelObj, nStartTab,
                    rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
            }
        }

        return true;
    }

    bool ScViewFunc::PasteMultiRangesFromClip(
        InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction,
        bool bSkipEmpty, bool bTranspose, bool bAsLink, bool bAllowDialogs,
        InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags)
    {
        ScViewData& rViewData = GetViewData();
        ScDocument* pDoc = rViewData.GetDocument();
        ScDocShell* pDocSh = rViewData.GetDocShell();
        ScMarkData aMark(rViewData.GetMarkData());
        const ScAddress& rCurPos = rViewData.GetCurPos();
        ScClipParam& rClipParam = pClipDoc->GetClipParam();
        SCCOL nColSize = rClipParam.getPasteColSize();
        SCROW nRowSize = rClipParam.getPasteRowSize();

        if (bTranspose)
        {
            if (static_cast<SCROW>(rCurPos.Col()) + nRowSize-1 > static_cast<SCROW>(MAXCOL))
            {
                ErrorMessage(STR_PASTE_FULL);
                return false;
            }

            ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
            pClipDoc->TransposeClip(pTransClip.get(), nFlags, bAsLink);
            pClipDoc = pTransClip.release();
            SCCOL nTempColSize = nColSize;
            nColSize = static_cast<SCCOL>(nRowSize);
            nRowSize = static_cast<SCROW>(nTempColSize);
        }

        if (!ValidCol(rCurPos.Col()+nColSize-1) || !ValidRow(rCurPos.Row()+nRowSize-1))
        {
            ErrorMessage(STR_PASTE_FULL);
            return false;
        }

        // Determine the first and last selected sheet numbers.
        SCTAB nTab1 = aMark.GetFirstSelected();
        SCTAB nTab2 = aMark.GetLastSelected();

        ScDocShellModificator aModificator(*pDocSh);

        // For multi-selection paste, we don't support cell duplication for larger
        // destination range.  In case the destination is marked, we reset it to
        // the clip size.
        ScRange aMarkedRange(rCurPos.Col(), rCurPos.Row(), nTab1,
                             rCurPos.Col()+nColSize-1, rCurPos.Row()+nRowSize-1, nTab2);

        // Extend the marked range to account for filtered rows in the destination
        // area.
        if (ScViewUtil::HasFiltered(aMarkedRange, pDoc))
        {
            if (!ScViewUtil::FitToUnfilteredRows(aMarkedRange, pDoc, nRowSize))
                return false;
        }

        bool bAskIfNotEmpty =
            bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
            nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();

        if (bAskIfNotEmpty)
        {
            ScRangeList aTestRanges(aMarkedRange);
            vcl::Window* pWin = GetViewData().GetDialogParent();
            if (!checkDestRangeForOverwrite(aTestRanges, pDoc, aMark, pWin ? pWin->GetFrameWeld() : nullptr))
                return false;
        }

        aMark.SetMarkArea(aMarkedRange);
        MarkRange(aMarkedRange);

        bool bInsertCells = (eMoveMode != INS_NONE);
        if (bInsertCells)
        {
            if (!InsertCells(eMoveMode, pDoc->IsUndoEnabled(), true))
                return false;
        }

        bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==MAXCOL );
        ScDocumentUniquePtr pUndoDoc;
        if (pDoc->IsUndoEnabled())
        {
            pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
            pUndoDoc->InitUndoSelected(pDoc, aMark, false, bRowInfo);
            pDoc->CopyToDocument(aMarkedRange, nUndoFlags, false, *pUndoDoc, &aMark);
        }

        ScDocumentUniquePtr pMixDoc;
        if ( bSkipEmpty || nFunction != ScPasteFunc::NONE)
        {
            if ( nFlags & InsertDeleteFlags::CONTENTS )
            {
                pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
                pMixDoc->InitUndoSelected(pDoc, aMark);
                pDoc->CopyToDocument(aMarkedRange, InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
            }
        }

        /*  Make draw layer and start drawing undo.
            - Needed before AdjustBlockHeight to track moved drawing objects.
            - Needed before pDoc->CopyFromClip to track inserted note caption objects.
         */
        if (nFlags & InsertDeleteFlags::OBJECTS)
            pDocSh->MakeDrawLayer();
        if (pDoc->IsUndoEnabled())
            pDoc->BeginDrawUndo();

        InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
        pDoc->CopyMultiRangeFromClip(rCurPos, aMark, nNoObjFlags, pClipDoc,
                                     true, bAsLink, false, bSkipEmpty);

        if (pMixDoc.get())
            pDoc->MixDocument(aMarkedRange, nFunction, bSkipEmpty, pMixDoc.get());

        AdjustBlockHeight();            // update row heights before pasting objects

        if (nFlags & InsertDeleteFlags::OBJECTS)
        {
            //  Paste the drawing objects after the row heights have been updated.
            pDoc->CopyMultiRangeFromClip(rCurPos, aMark, InsertDeleteFlags::OBJECTS, pClipDoc,
                                         true, false, false, true);
        }

        if (bRowInfo)
            pDocSh->PostPaint(aMarkedRange.aStart.Col(), aMarkedRange.aStart.Row(), nTab1, MAXCOL, MAXROW, nTab1, PaintPartFlags::Grid|PaintPartFlags::Left);
        else
        {
            ScRange aTmp = aMarkedRange;
            aTmp.aStart.SetTab(nTab1);
            aTmp.aEnd.SetTab(nTab1);
            pDocSh->PostPaint(aTmp, PaintPartFlags::Grid);
        }

        if (pDoc->IsUndoEnabled())
        {
            SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
            OUString aUndo = ScResId(
                pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
            pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());

            ScUndoPasteOptions aOptions;            // store options for repeat
            aOptions.nFunction  = nFunction;
            aOptions.bSkipEmpty = bSkipEmpty;
            aOptions.bTranspose = bTranspose;
            aOptions.bAsLink    = bAsLink;
            aOptions.eMoveMode  = eMoveMode;

            std::unique_ptr<ScUndoPaste> pUndo(new ScUndoPaste(pDocSh,
                aMarkedRange, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions));

            if (bInsertCells)
                pUndoMgr->AddUndoAction(new ScUndoWrapper(std::move(pUndo)), true);
            else
                pUndoMgr->AddUndoAction(pUndo.release());

            pUndoMgr->LeaveListAction();
        }

        ResetAutoSpell();
        aModificator.SetDocumentModified();
        PostPasteFromClip(aMarkedRange, aMark);
        return true;
    }

    bool ScViewFunc::PasteFromClipToMultiRanges(
        InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction,
        bool bSkipEmpty, bool bTranspose, bool bAsLink, bool bAllowDialogs,
        InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags )
    {
        if (bTranspose)
        {
            // We don't allow transpose for this yet.
            ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
            return false;
        }

        if (eMoveMode != INS_NONE)
        {
            // We don't allow insertion mode either.  Too complicated.
            ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
            return false;
        }

        ScViewData& rViewData = GetViewData();
        ScClipParam& rClipParam = pClipDoc->GetClipParam();
        if (rClipParam.mbCutMode)
        {
            // No cut and paste with this, please.
            ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
            return false;
        }

        const ScAddress& rCurPos = rViewData.GetCurPos();
        ScDocument* pDoc = rViewData.GetDocument();

        ScRange aSrcRange = rClipParam.getWholeRange();
        SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
        SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;

        if (!ValidCol(rCurPos.Col()+nColSize-1) || !ValidRow(rCurPos.Row()+nRowSize-1))
        {
            ErrorMessage(STR_PASTE_FULL);
            return false;
        }

        ScMarkData aMark(rViewData.GetMarkData());

        ScRangeList aRanges;
        aMark.MarkToSimple();
        aMark.FillRangeListWithMarks(&aRanges, false);
        if (!ScClipUtil::CheckDestRanges(pDoc, nColSize, nRowSize, aMark, aRanges))
        {
            ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
            return false;
        }

        ScDocShell* pDocSh = rViewData.GetDocShell();

        ScDocShellModificator aModificator(*pDocSh);

        bool bAskIfNotEmpty =
            bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
            nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();

        if (bAskIfNotEmpty)
        {
            vcl::Window* pWin = GetViewData().GetDialogParent();
            if (!checkDestRangeForOverwrite(aRanges, pDoc, aMark, pWin ? pWin->GetFrameWeld() : nullptr))
                return false;
        }

        ScDocumentUniquePtr pUndoDoc;
        if (pDoc->IsUndoEnabled())
        {
            pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
            pUndoDoc->InitUndoSelected(pDoc, aMark);
            for (size_t i = 0, n = aRanges.size(); i < n; ++i)
            {
                pDoc->CopyToDocument(
                    aRanges[i], nUndoFlags, false, *pUndoDoc, &aMark);
            }
        }

        ScDocumentUniquePtr pMixDoc;
        if (bSkipEmpty || nFunction != ScPasteFunc::NONE)
        {
            if (nFlags & InsertDeleteFlags::CONTENTS)
            {
                pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
                pMixDoc->InitUndoSelected(pDoc, aMark);
                for (size_t i = 0, n = aRanges.size(); i < n; ++i)
                {
                    pDoc->CopyToDocument(
                        aRanges[i], InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
                }
            }
        }

        if (nFlags & InsertDeleteFlags::OBJECTS)
            pDocSh->MakeDrawLayer();
        if (pDoc->IsUndoEnabled())
            pDoc->BeginDrawUndo();

        // First, paste everything but the drawing objects.
        for (size_t i = 0, n = aRanges.size(); i < n; ++i)
        {
            pDoc->CopyFromClip(
                aRanges[i], aMark, (nFlags & ~InsertDeleteFlags::OBJECTS), nullptr, pClipDoc,
                false, false, true, bSkipEmpty);
        }

        if (pMixDoc.get())
        {
            for (size_t i = 0, n = aRanges.size(); i < n; ++i)
                pDoc->MixDocument(aRanges[i], nFunction, bSkipEmpty, pMixDoc.get());
        }

        AdjustBlockHeight();            // update row heights before pasting objects

        // Then paste the objects.
        if (nFlags & InsertDeleteFlags::OBJECTS)
        {
            for (size_t i = 0, n = aRanges.size(); i < n; ++i)
            {
                pDoc->CopyFromClip(
                    aRanges[i], aMark, InsertDeleteFlags::OBJECTS, nullptr, pClipDoc,
                    false, false, true, bSkipEmpty);
            }
        }

        // Refresh the range that includes all pasted ranges.  We only need to
        // refresh the current sheet.
        PaintPartFlags nPaint = PaintPartFlags::Grid;
        bool bRowInfo = (aSrcRange.aStart.Col()==0 &&  aSrcRange.aEnd.Col()==MAXCOL);
        if (bRowInfo)
            nPaint |= PaintPartFlags::Left;
        pDocSh->PostPaint(aRanges, nPaint);

        if (pDoc->IsUndoEnabled())
        {
            SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
            OUString aUndo = ScResId(
                pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
            pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());

            ScUndoPasteOptions aOptions;            // store options for repeat
            aOptions.nFunction  = nFunction;
            aOptions.bSkipEmpty = bSkipEmpty;
            aOptions.bTranspose = bTranspose;
            aOptions.bAsLink    = bAsLink;
            aOptions.eMoveMode  = eMoveMode;

            ScUndoPaste* pUndo = new ScUndoPaste(
                pDocSh, aRanges, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions);

            pUndoMgr->AddUndoAction(pUndo);
            pUndoMgr->LeaveListAction();
        }

        ResetAutoSpell();
        aModificator.SetDocumentModified();
        PostPasteFromClip(aRanges, aMark);

        return false;
    }

    void ScViewFunc::PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark)
    {
        ScViewData& rViewData = GetViewData();
        ScDocShell* pDocSh = rViewData.GetDocShell();
        pDocSh->UpdateOle(&rViewData);

        SelectionChanged();

        ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh);
        if (!pModelObj)
            return;

        ScRangeList aChangeRanges;
        for (size_t i = 0, n = rPasteRanges.size(); i < n; ++i)
        {
            const ScRange& r = rPasteRanges[i];
            ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
            for (; itr != itrEnd; ++itr)
            {
                ScRange aChangeRange(r);
                aChangeRange.aStart.SetTab(*itr);
                aChangeRange.aEnd.SetTab(*itr);
                aChangeRanges.push_back(aChangeRange);
            }
        }
        HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
    }

    //      D R A G   A N D   D R O P

    //  inside the doc

    bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
                                    bool bCut )
    {
        ScDocShell* pDocSh = GetViewData().GetDocShell();
        HideAllCursors();

        bool bSuccess = true;
        SCTAB nDestTab = rDestPos.Tab();
        const ScMarkData& rMark = GetViewData().GetMarkData();
        if ( rSource.aStart.Tab() == nDestTab && rSource.aEnd.Tab() == nDestTab && rMark.GetSelectCount() > 1 )
        {
            //  moving within one table and several tables selected -> apply to all selected tables

            OUString aUndo = ScResId( bCut ? STR_UNDO_MOVE : STR_UNDO_COPY );
            pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );

            //  collect ranges of consecutive selected tables

            ScRange aLocalSource = rSource;
            ScAddress aLocalDest = rDestPos;
            SCTAB nTabCount = pDocSh->GetDocument().GetTableCount();
            SCTAB nStartTab = 0;
            while ( nStartTab < nTabCount && bSuccess )
            {
                while ( nStartTab < nTabCount && !rMark.GetTableSelect(nStartTab) )
                    ++nStartTab;
                if ( nStartTab < nTabCount )
                {
                    SCTAB nEndTab = nStartTab;
                    while ( nEndTab+1 < nTabCount && rMark.GetTableSelect(nEndTab+1) )
                        ++nEndTab;

                    aLocalSource.aStart.SetTab( nStartTab );
                    aLocalSource.aEnd.SetTab( nEndTab );
                    aLocalDest.SetTab( nStartTab );

                    bSuccess = pDocSh->GetDocFunc().MoveBlock(
                                    aLocalSource, aLocalDest, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );

                    nStartTab = nEndTab + 1;
                }
            }

            pDocSh->GetUndoManager()->LeaveListAction();
        }
        else
        {
            //  move the block as specified
            bSuccess = pDocSh->GetDocFunc().MoveBlock(
                                    rSource, rDestPos, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );
        }

        ShowAllCursors();
        if (bSuccess)
        {
            //   mark destination range
            ScAddress aDestEnd(
                        rDestPos.Col() + rSource.aEnd.Col() - rSource.aStart.Col(),
                        rDestPos.Row() + rSource.aEnd.Row() - rSource.aStart.Row(),
                        nDestTab );

            bool bIncludeFiltered = bCut;
            if ( !bIncludeFiltered )
            {
                // find number of non-filtered rows
                SCROW nPastedCount = pDocSh->GetDocument().CountNonFilteredRows(
                    rSource.aStart.Row(), rSource.aEnd.Row(), rSource.aStart.Tab());

                if ( nPastedCount == 0 )
                    nPastedCount = 1;
                aDestEnd.SetRow( rDestPos.Row() + nPastedCount - 1 );
            }

            MarkRange( ScRange( rDestPos, aDestEnd ), false );          //! sal_False ???

            pDocSh->UpdateOle(&GetViewData());
            SelectionChanged();
            ResetAutoSpell();
        }
        return bSuccess;
    }

    //  link inside the doc

    bool ScViewFunc::LinkBlock( const ScRange& rSource, const ScAddress& rDestPos )
    {
        //  check overlapping

        if ( rSource.aStart.Tab() == rDestPos.Tab() )
        {
            SCCOL nDestEndCol = rDestPos.Col() + ( rSource.aEnd.Col() - rSource.aStart.Col() );
            SCROW nDestEndRow = rDestPos.Row() + ( rSource.aEnd.Row() - rSource.aStart.Row() );

            if ( rSource.aStart.Col() <= nDestEndCol && rDestPos.Col() <= rSource.aEnd.Col() &&
                 rSource.aStart.Row() <= nDestEndRow && rDestPos.Row() <= rSource.aEnd.Row() )
            {
                return false;
            }
        }

        //  run with paste

        ScDocument* pDoc = GetViewData().GetDocument();
        ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
        pDoc->CopyTabToClip( rSource.aStart.Col(), rSource.aStart.Row(),
                                rSource.aEnd.Col(), rSource.aEnd.Row(),
                             rSource.aStart.Tab(), pClipDoc.get() );

        //  mark destination area (set cursor, no marks)

        if ( GetViewData().GetTabNo() != rDestPos.Tab() )
            SetTabNo( rDestPos.Tab() );

        MoveCursorAbs( rDestPos.Col(), rDestPos.Row(), SC_FOLLOW_NONE, false, false );

        //  Paste

        PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, true );       // as a link

        return true;
    }

    void ScViewFunc::DataFormPutData( SCROW nCurrentRow ,
                                      SCROW nStartRow , SCCOL nStartCol ,
                                      SCROW nEndRow , SCCOL nEndCol ,
                                      std::vector<VclPtr<Edit> >& aEdits,
                                      sal_uInt16 aColLength )
    {
        ScDocument* pDoc = GetViewData().GetDocument();
        ScDocShell* pDocSh = GetViewData().GetDocShell();
        ScMarkData& rMark = GetViewData().GetMarkData();
        ScDocShellModificator aModificator( *pDocSh );
        SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
        if ( pDoc )
        {
            const bool bRecord( pDoc->IsUndoEnabled());
            ScDocumentUniquePtr pUndoDoc;
            ScDocumentUniquePtr pRedoDoc;
            std::unique_ptr<ScRefUndoData> pUndoData;
            SCTAB nTab = GetViewData().GetTabNo();
            SCTAB nStartTab = nTab;
            SCTAB nEndTab = nTab;

            {
                    ScChangeTrack* pChangeTrack = pDoc->GetChangeTrack();
                    if ( pChangeTrack )
                            pChangeTrack->ResetLastCut();   // no more cut-mode
            }
            ScRange aUserRange( nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab );
            bool bColInfo = ( nStartRow==0 && nEndRow==MAXROW );
            bool bRowInfo = ( nStartCol==0 && nEndCol==MAXCOL );
            SCCOL nUndoEndCol = nStartCol+aColLength-1;
            SCROW nUndoEndRow = nCurrentRow;

            if ( bRecord )
            {
                pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
                pUndoDoc->InitUndoSelected( pDoc , rMark , bColInfo , bRowInfo );
                pDoc->CopyToDocument( aUserRange , InsertDeleteFlags::VALUE , false, *pUndoDoc );
            }
            sal_uInt16 nExtFlags = 0;
            pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab , nEndCol, nEndRow, nEndTab ); // content before the change
            pDoc->BeginDrawUndo();

            for(sal_uInt16 i = 0; i < aColLength; i++)
            {
                if (aEdits[i] != nullptr)
                {
                    OUString  aFieldName=aEdits[i]->GetText();
                    pDoc->SetString( nStartCol + i, nCurrentRow, nTab, aFieldName );
                }
            }
            pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab );  // content after the change
            std::unique_ptr<SfxUndoAction> pUndo( new ScUndoDataForm( pDocSh,
                                                       nStartCol, nCurrentRow, nStartTab,
                                                       nUndoEndCol, nUndoEndRow, nEndTab, rMark,
                                                       std::move(pUndoDoc), std::move(pRedoDoc),
                                                       std::move(pUndoData) ) );
            pUndoMgr->AddUndoAction( new ScUndoWrapper( std::move(pUndo) ), true );

            PaintPartFlags nPaint = PaintPartFlags::Grid;
            if (bColInfo)
            {
                    nPaint |= PaintPartFlags::Top;
                    nUndoEndCol = MAXCOL;                           // just for drawing !
            }
            if (bRowInfo)
            {
                    nPaint |= PaintPartFlags::Left;
                    nUndoEndRow = MAXROW;                           // just for drawing !
            }

            pDocSh->PostPaint(
                ScRange(nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
                nPaint, nExtFlags);
            pDocSh->UpdateOle(&GetViewData());
        }
    }

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

↑ V575 The null pointer is passed into 'move' function. Inspect the first argument.

↑ V575 The null pointer is passed into 'move' function. Inspect the first argument.

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

↑ V668 There is no sense in testing the 'pClipDoc' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

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

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