    /* -*- 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 <editeng/eeitem.hxx>
    #include <editeng/justifyitem.hxx>

    #include <com/sun/star/uno/Sequence.hxx>
    #include <com/sun/star/embed/XTransactedObject.hpp>
    #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
    #include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>

    #include <unotools/tempfile.hxx>
    #include <unotools/ucbstreamhelper.hxx>
    #include <comphelper/fileformat.h>
    #include <comphelper/storagehelper.hxx>
    #include <comphelper/servicehelper.hxx>
    #include <sot/storage.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/virdev.hxx>
    #include <vcl/wrkwin.hxx>
    #include <sfx2/app.hxx>
    #include <sfx2/docfile.hxx>
    #include <sfx2/viewfrm.hxx>

    #include <transobj.hxx>
    #include <patattr.hxx>
    #include <cellvalue.hxx>
    #include <cellform.hxx>
    #include <document.hxx>
    #include <viewopti.hxx>
    #include <editutil.hxx>
    #include <impex.hxx>
    #include <formulacell.hxx>
    #include <printfun.hxx>
    #include <docfunc.hxx>
    #include <scmod.hxx>
    #include <dragdata.hxx>
    #include <clipparam.hxx>

    #include <editeng/paperinf.hxx>
    #include <editeng/sizeitem.hxx>
    #include <formula/errorcodes.hxx>
    #include <svx/algitem.hxx>
    #include <svl/intitem.hxx>
    #include <svl/zforlist.hxx>
    #include <docsh.hxx>
    #include <markdata.hxx>
    #include <stlpool.hxx>
    #include <viewdata.hxx>
    #include <dociter.hxx>
    #include <cellsuno.hxx>
    #include <stringutil.hxx>
    #include <formulaiter.hxx>
    #include <gridwin.hxx>

    using namespace com::sun::star;

    constexpr sal_uInt32 SCTRANS_TYPE_IMPEX              = 1;
    constexpr sal_uInt32 SCTRANS_TYPE_EDIT_RTF           = 2;
    constexpr sal_uInt32 SCTRANS_TYPE_EDIT_BIN           = 3;
    constexpr sal_uInt32 SCTRANS_TYPE_EMBOBJ             = 4;
    constexpr sal_uInt32 SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT = 5;

    void ScTransferObj::GetAreaSize( const ScDocument* pDoc, SCTAB nTab1, SCTAB nTab2, SCROW& nRow, SCCOL& nCol )
    {
        SCCOL nMaxCol = 0;
        SCROW nMaxRow = 0;
        for( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
        {
            SCCOL nLastCol = 0;
            SCROW nLastRow = 0;
            // GetPrintArea instead of GetCellArea - include drawing objects
            if( pDoc->GetPrintArea( nTab, nLastCol, nLastRow ) )
            {
                if( nLastCol > nMaxCol )
                    nMaxCol = nLastCol;
                if( nLastRow > nMaxRow  )
                    nMaxRow = nLastRow;
            }
        }
        nRow = nMaxRow;
        nCol = nMaxCol;
    }

    void ScTransferObj::PaintToDev( OutputDevice* pDev, ScDocument* pDoc, double nPrintFactor,
                                    const ScRange& rBlock )
    {
        if (!pDoc)
            return;

        tools::Rectangle aBound( Point(), pDev->GetOutputSize() );      //! use size from clip area?

        ScViewData aViewData(nullptr,nullptr);
        aViewData.InitData( pDoc );

        aViewData.SetTabNo( rBlock.aEnd.Tab() );
        aViewData.SetScreen( rBlock.aStart.Col(), rBlock.aStart.Row(),
                                rBlock.aEnd.Col(), rBlock.aEnd.Row() );

        ScPrintFunc::DrawToDev( pDoc, pDev, nPrintFactor, aBound, &aViewData, false/*bMetaFile*/ );
    }

    ScTransferObj::ScTransferObj( ScDocumentUniquePtr pClipDoc, const TransferableObjectDescriptor& rDesc ) :
        m_pDoc( std::move(pClipDoc ) ),
        m_nNonFiltered(0),
        m_aObjDesc( rDesc ),
        m_nDragHandleX( 0 ),
        m_nDragHandleY( 0 ),
        m_nSourceCursorX( MAXCOL + 1 ),
        m_nSourceCursorY( MAXROW + 1 ),
        m_nDragSourceFlags( ScDragSrc::Undefined ),
        m_bDragWasInternal( false ),
        m_bUsedForLink( false ),
        m_bUseInApi( false )
    {
        OSL_ENSURE(m_pDoc->IsClipboard(), "wrong document");

        // get aBlock from clipboard doc

        SCCOL nCol1;
        SCROW nRow1;
        SCCOL nCol2;
        SCROW nRow2;
        m_pDoc->GetClipStart( nCol1, nRow1 );
        m_pDoc->GetClipArea( nCol2, nRow2, true );    // real source area - include filtered rows
        nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nCol1 );
        nRow2 = sal::static_int_cast<SCROW>( nRow2 + nRow1 );

        SCCOL nDummy;
        m_pDoc->GetClipArea( nDummy, m_nNonFiltered, false );
        m_bHasFiltered = (m_nNonFiltered < (nRow2 - nRow1));
        ++m_nNonFiltered;     // to get count instead of diff

        SCTAB nTab1=0;
        SCTAB nTab2=0;
        bool bFirst = true;
        for (SCTAB i=0; i< m_pDoc->GetTableCount(); i++)
            if (m_pDoc->HasTable(i))
            {
                if (bFirst)
                    nTab1 = i;
                nTab2 = i;
                bFirst = false;
            }
        OSL_ENSURE(!bFirst, "no sheet selected");

        //  only limit to used cells if whole sheet was marked
        //  (so empty cell areas can be copied)
        if ( nCol2>=MAXCOL && nRow2>=MAXROW )
        {
            SCROW nMaxRow;
            SCCOL nMaxCol;
            GetAreaSize( m_pDoc.get(), nTab1, nTab2, nMaxRow, nMaxCol );
            if( nMaxRow < nRow2 )
                nRow2 = nMaxRow;
            if( nMaxCol < nCol2 )
                nCol2 = nMaxCol;
        }

        m_aBlock = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
        m_nVisibleTab = nTab1;    // valid table as default

        tools::Rectangle aMMRect = m_pDoc->GetMMRect( nCol1,nRow1, nCol2,nRow2, nTab1 );
        m_aObjDesc.maSize = aMMRect.GetSize();
        PrepareOLE( m_aObjDesc );
    }

    ScTransferObj::~ScTransferObj()
    {
        SolarMutexGuard aSolarGuard;

        ScModule* pScMod = SC_MOD();
        if ( pScMod->GetDragData().pCellTransfer == this )
        {
            OSL_FAIL("ScTransferObj wasn't released");
            pScMod->ResetDragObject();
        }

        m_pDoc.reset();        // ScTransferObj is owner of clipboard document

        m_aDocShellRef.clear();   // before releasing the mutex

        m_aDrawPersistRef.clear();                    // after the model

    }

    ScTransferObj* ScTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable)
    {
        ScTransferObj* pObj = nullptr;
        if (xTransferable.is())
        {
            uno::Reference<XUnoTunnel> xTunnel( xTransferable, uno::UNO_QUERY );
            if ( xTunnel.is() )
            {
                sal_Int64 nHandle = xTunnel->getSomething( getUnoTunnelId() );
                if ( nHandle )
                    pObj = dynamic_cast<ScTransferObj*>(reinterpret_cast<TransferableHelper*>( static_cast<sal_IntPtr>(nHandle) ));
            }
        }

        return pObj;
    }

    void ScTransferObj::AddSupportedFormats()
    {
        AddFormat( SotClipboardFormatId::EMBED_SOURCE );
        AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
        AddFormat( SotClipboardFormatId::GDIMETAFILE );
        AddFormat( SotClipboardFormatId::PNG );
        AddFormat( SotClipboardFormatId::BITMAP );

        // ScImportExport formats
        AddFormat( SotClipboardFormatId::HTML );
        AddFormat( SotClipboardFormatId::SYLK );
        AddFormat( SotClipboardFormatId::LINK );
        AddFormat( SotClipboardFormatId::DIF );
        AddFormat( SotClipboardFormatId::STRING );
        AddFormat( SotClipboardFormatId::STRING_TSVC );

        AddFormat( SotClipboardFormatId::RTF );
        AddFormat( SotClipboardFormatId::RICHTEXT );
        if ( m_aBlock.aStart == m_aBlock.aEnd )
        {
            AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT );
        }
    }

    bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
    {
        SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
        bool        bOK = false;

        if( HasFormat( nFormat ) )
        {
            ScRange aReducedBlock = m_aBlock;

            bool bReduceBlockFormat =
                nFormat == SotClipboardFormatId::HTML
                || nFormat == SotClipboardFormatId::RTF
                || nFormat == SotClipboardFormatId::RICHTEXT
                || nFormat == SotClipboardFormatId::BITMAP
                || nFormat == SotClipboardFormatId::PNG;

            if (bReduceBlockFormat && (m_aBlock.aEnd.Col() == MAXCOL || m_aBlock.aEnd.Row() == MAXROW) &&
                    m_aBlock.aStart.Tab() == m_aBlock.aEnd.Tab())
            {
                // Shrink the block here so we don't waste time creating huge
                // output when whole columns or rows are selected.

                SCCOL nPrintAreaEndCol = 0;
                SCROW nPrintAreaEndRow = 0;
                const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP ||
                        nFormat == SotClipboardFormatId::PNG);
                if (bIncludeVisual)
                    m_pDoc->GetPrintArea( m_aBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true );

                // Shrink the area to allow pasting to external applications.
                // Shrink to real data area for HTML, RTF and RICHTEXT, but include
                // all objects and top-left area for BITMAP and PNG.
                SCCOL nStartCol = aReducedBlock.aStart.Col();
                SCROW nStartRow = aReducedBlock.aStart.Row();
                SCCOL nEndCol = aReducedBlock.aEnd.Col();
                SCROW nEndRow = aReducedBlock.aEnd.Row();
                bool bShrunk = false;
                m_pDoc->ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow,
                        false, bIncludeVisual /*bStickyTopRow*/, bIncludeVisual /*bStickyLeftCol*/,
                        bIncludeVisual /*bConsiderCellNotes*/, bIncludeVisual /*bConsiderCellDrawObjects*/);

                if ( nPrintAreaEndRow > nEndRow )
                    nEndRow = nPrintAreaEndRow;

                if ( nPrintAreaEndCol > nEndCol )
                    nEndCol = nPrintAreaEndCol;

                aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab());
            }

            if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR )
            {
                bOK = SetTransferableObjectDescriptor( m_aObjDesc );
            }
            else if ( ( nFormat == SotClipboardFormatId::RTF || nFormat == SotClipboardFormatId::RICHTEXT ||
                nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) && m_aBlock.aStart == m_aBlock.aEnd )
            {
                //  RTF from a single cell is handled by EditEngine

                SCCOL nCol = m_aBlock.aStart.Col();
                SCROW nRow = m_aBlock.aStart.Row();
                SCTAB nTab = m_aBlock.aStart.Tab();
                ScAddress aPos(nCol, nRow, nTab);

                const ScPatternAttr* pPattern = m_pDoc->GetPattern( nCol, nRow, nTab );
                ScTabEditEngine aEngine( *pPattern, m_pDoc->GetEditPool() );
                ScRefCellValue aCell(*m_pDoc, aPos);
                if (aCell.meType == CELLTYPE_EDIT)
                {
                    const EditTextObject* pObj = aCell.mpEditText;
                    aEngine.SetText(*pObj);
                }
                else
                {
                    SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
                    sal_uInt32 nNumFmt = pPattern->GetNumberFormat(pFormatter);
                    OUString aText;
                    Color* pColor;
                    ScCellFormat::GetString(aCell, nNumFmt, aText, &pColor, *pFormatter, m_pDoc.get());
                    if (!aText.isEmpty())
                        aEngine.SetText(aText);
                }

                bOK = SetObject( &aEngine,
                        ((nFormat == SotClipboardFormatId::RTF) ? SCTRANS_TYPE_EDIT_RTF :
                         ((nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT) ?
                          SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT : SCTRANS_TYPE_EDIT_BIN)),
                        rFlavor );
            }
            else if ( ScImportExport::IsFormatSupported( nFormat ) || nFormat == SotClipboardFormatId::RTF
                || nFormat == SotClipboardFormatId::RICHTEXT )
            {
                //  if this transfer object was used to create a DDE link, filtered rows
                //  have to be included for subsequent calls (to be consistent with link data)
                if ( nFormat == SotClipboardFormatId::LINK )
                    m_bUsedForLink = true;

                bool bIncludeFiltered = m_pDoc->IsCutMode() || m_bUsedForLink;

                ScImportExport aObj( m_pDoc.get(), aReducedBlock );
                // Plain text ("Unformatted text") may contain embedded tabs and
                // line breaks but is not enclosed in quotes. Which makes it
                // unsuitable for multiple cells, especially if one of them is
                // multi-line, but otherwise is expected behavior for plain text.
                // For multiple cells replace embedded line breaks (and tabs) with
                // space character, otherwise pasting would yield odd results.
                /* XXX: it's debatable whether this is actually expected, but
                 * there's no way to satisfy all possible requirements when
                 * copy/pasting unformatted text. */
                const bool bPlainMulti = (nFormat == SotClipboardFormatId::STRING &&
                        aReducedBlock.aStart != aReducedBlock.aEnd);
                // Add quotes only for STRING_TSVC.
                /* TODO: a possible future STRING_TSV should not contain embedded
                 * line breaks nor tab (separator) characters and not be quoted.
                 * A possible STRING_CSV should. */
                ScExportTextOptions aTextOptions( ScExportTextOptions::None, 0,
                        (nFormat == SotClipboardFormatId::STRING_TSVC));
                if ( bPlainMulti || m_bUsedForLink )
                {
                    // For a DDE link or plain text multiple cells, convert line
                    // breaks and separators to space.
                    aTextOptions.meNewlineConversion = ScExportTextOptions::ToSpace;
                    aTextOptions.mcSeparatorConvertTo = ' ';
                    aTextOptions.mbAddQuotes = false;
                }
                aObj.SetExportTextOptions(aTextOptions);
                aObj.SetFormulas( m_pDoc->GetViewOptions().GetOption( VOPT_FORMULAS ) );
                aObj.SetIncludeFiltered( bIncludeFiltered );

                //  DataType depends on format type:

                if ( rFlavor.DataType.equals( ::cppu::UnoType<OUString>::get() ) )
                {
                    OUString aString;
                    if ( aObj.ExportString( aString, nFormat ) )
                        bOK = SetString( aString, rFlavor );
                }
                else if ( rFlavor.DataType.equals( cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) )
                {
                    //  SetObject converts a stream into a Int8-Sequence
                    bOK = SetObject( &aObj, SCTRANS_TYPE_IMPEX, rFlavor );
                }
                else
                {
                    OSL_FAIL("unknown DataType");
                }
            }
            else if ( nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG )
            {
                tools::Rectangle aMMRect = m_pDoc->GetMMRect( aReducedBlock.aStart.Col(), aReducedBlock.aStart.Row(),
                                                     aReducedBlock.aEnd.Col(), aReducedBlock.aEnd.Row(),
                                                     aReducedBlock.aStart.Tab() );
                ScopedVclPtrInstance< VirtualDevice > pVirtDev;
                pVirtDev->SetOutputSizePixel(pVirtDev->LogicToPixel(aMMRect.GetSize(), MapMode(MapUnit::Map100thMM)));

                PaintToDev( pVirtDev, m_pDoc.get(), 1.0, aReducedBlock );

                pVirtDev->SetMapMode( MapMode( MapUnit::MapPixel ) );
                BitmapEx aBmp = pVirtDev->GetBitmapEx( Point(), pVirtDev->GetOutputSize() );
                bOK = SetBitmapEx( aBmp, rFlavor );
            }
            else if ( nFormat == SotClipboardFormatId::GDIMETAFILE )
            {
                // #i123405# Do not limit visual size calculation for metafile creation.
                // It seems unlikely that removing the limitation causes problems since
                // metafile creation means that no real pixel device in the needed size is
                // created.
                InitDocShell(false);

                SfxObjectShell* pEmbObj = m_aDocShellRef.get();

                // like SvEmbeddedTransfer::GetData:
                GDIMetaFile     aMtf;
                ScopedVclPtrInstance< VirtualDevice > pVDev;
                MapMode         aMapMode( pEmbObj->GetMapUnit() );
                tools::Rectangle       aVisArea( pEmbObj->GetVisArea( ASPECT_CONTENT ) );

                pVDev->EnableOutput( false );
                pVDev->SetMapMode( aMapMode );
                aMtf.SetPrefSize( aVisArea.GetSize() );
                aMtf.SetPrefMapMode( aMapMode );
                aMtf.Record( pVDev );

                pEmbObj->DoDraw( pVDev, Point(), aVisArea.GetSize(), JobSetup() );

                aMtf.Stop();
                aMtf.WindStart();

                bOK = SetGDIMetaFile( aMtf );
            }
            else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE )
            {
                //TODO/LATER: differentiate between formats?!
                // #i123405# Do limit visual size calculation to PageSize
                InitDocShell(true);         // set aDocShellRef

                SfxObjectShell* pEmbObj = m_aDocShellRef.get();
                bOK = SetObject( pEmbObj, SCTRANS_TYPE_EMBOBJ, rFlavor );
            }
        }
        return bOK;
    }

    bool ScTransferObj::WriteObject( tools::SvRef<SotStorageStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId,
                                            const datatransfer::DataFlavor& rFlavor )
    {
        // called from SetObject, put data into stream

        bool bRet = false;
        switch (nUserObjectId)
        {
            case SCTRANS_TYPE_IMPEX:
                {
                    ScImportExport* pImpEx = static_cast<ScImportExport*>(pUserObject);

                    SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
                    // mba: no BaseURL for data exchange
                    if ( pImpEx->ExportStream( *rxOStm, OUString(), nFormat ) )
                        bRet = ( rxOStm->GetError() == ERRCODE_NONE );
                }
                break;

            case SCTRANS_TYPE_EDIT_RTF:
            case SCTRANS_TYPE_EDIT_BIN:
                {
                    ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
                    if ( nUserObjectId == SCTRANS_TYPE_EDIT_RTF )
                    {
                        pEngine->Write( *rxOStm, EETextFormat::Rtf );
                        bRet = ( rxOStm->GetError() == ERRCODE_NONE );
                    }
                    else
                    {
                        //  can't use Write for EditEngine format because that would
                        //  write old format without support for unicode characters.
                        //  Get the data from the EditEngine's transferable instead.

                        sal_Int32 nParCnt = pEngine->GetParagraphCount();
                        if ( nParCnt == 0 )
                            nParCnt = 1;
                        ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) );

                        uno::Reference<datatransfer::XTransferable> xEditTrans = pEngine->CreateTransferable( aSel );
                        TransferableDataHelper aEditHelper( xEditTrans );

                        bRet = aEditHelper.GetSotStorageStream( rFlavor, rxOStm );
                    }
                }
                break;

            case SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT:
                {
                    ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
                    if ( nUserObjectId == SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT )
                    {
                        pEngine->Write( *rxOStm, EETextFormat::Xml );
                        bRet = ( rxOStm->GetError() == ERRCODE_NONE );
                    }
                }
                break;

            case SCTRANS_TYPE_EMBOBJ:
                {
                    // TODO/MBA: testing
                    SfxObjectShell*   pEmbObj = static_cast<SfxObjectShell*>(pUserObject);
                    ::utl::TempFile     aTempFile;
                    aTempFile.EnableKillingFile();
                    uno::Reference< embed::XStorage > xWorkStore =
                        ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE );

                    // write document storage
                    pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false );

                    // mba: no relative URLs for clipboard!
                    SfxMedium aMedium( xWorkStore, OUString() );
                    pEmbObj->DoSaveObjectAs( aMedium, false );
                    pEmbObj->DoSaveCompleted();

                    uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY );
                    if ( xTransact.is() )
                        xTransact->commit();

                    std::unique_ptr<SvStream> pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ );
                    if( pSrcStm )
                    {
                        rxOStm->SetBufferSize( 0xff00 );
                        rxOStm->WriteStream( *pSrcStm );
                        pSrcStm.reset();
                    }

                    bRet = true;

                    xWorkStore->dispose();
                    xWorkStore.clear();
                    rxOStm->Commit();
                }
                break;

            default:
                OSL_FAIL("unknown object id");
        }
        return bRet;
    }

    void ScTransferObj::DragFinished( sal_Int8 nDropAction )
    {
        if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) )
        {
            //  move: delete source data
            ScDocShell* pSourceSh = GetSourceDocShell();
            if (pSourceSh)
            {
                ScMarkData aMarkData = GetSourceMarkData();
                //  external drag&drop doesn't copy objects, so they also aren't deleted:
                //  bApi=TRUE, don't show error messages from drag&drop
                pSourceSh->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, true, true );
            }
        }

        ScModule* pScMod = SC_MOD();
        if ( pScMod->GetDragData().pCellTransfer == this )
            pScMod->ResetDragObject();

        m_xDragSourceRanges = nullptr;       // don't keep source after dropping

        TransferableHelper::DragFinished( nDropAction );
    }

    void ScTransferObj::SetDragHandlePos( SCCOL nX, SCROW nY )
    {
        m_nDragHandleX = nX;
        m_nDragHandleY = nY;
    }

    void ScTransferObj::SetSourceCursorPos( SCCOL nX, SCROW nY )
    {
        m_nSourceCursorX = nX;
        m_nSourceCursorY = nY;
    }

    bool ScTransferObj::WasSourceCursorInSelection() const
    {
        return
            m_nSourceCursorX >= m_aBlock.aStart.Col() && m_nSourceCursorX <= m_aBlock.aEnd.Col() &&
            m_nSourceCursorY >= m_aBlock.aStart.Row() && m_nSourceCursorY <= m_aBlock.aEnd.Row();
    }

    void ScTransferObj::SetVisibleTab( SCTAB nNew )
    {
        m_nVisibleTab = nNew;
    }

    void ScTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef )
    {
        m_aDrawPersistRef = rRef;
    }

    void ScTransferObj::SetDragSource( ScDocShell* pSourceShell, const ScMarkData& rMark )
    {
        ScRangeList aRanges;
        rMark.FillRangeListWithMarks( &aRanges, false );
        m_xDragSourceRanges = new ScCellRangesObj( pSourceShell, aRanges );
    }

    void ScTransferObj::SetDragSourceFlags(ScDragSrc nFlags)
    {
        m_nDragSourceFlags = nFlags;
    }

    void ScTransferObj::SetDragWasInternal()
    {
        m_bDragWasInternal = true;
    }

    void ScTransferObj::SetUseInApi( bool bSet )
    {
        m_bUseInApi = bSet;
    }

    ScDocument* ScTransferObj::GetSourceDocument()
    {
        ScDocShell* pSourceDocSh = GetSourceDocShell();
        if (pSourceDocSh)
            return &pSourceDocSh->GetDocument();
        return nullptr;
    }

    ScDocShell* ScTransferObj::GetSourceDocShell()
    {
        ScCellRangesBase* pRangesObj = ScCellRangesBase::getImplementation( m_xDragSourceRanges );
        if (pRangesObj)
            return pRangesObj->GetDocShell();

        return nullptr;    // none set
    }

    ScMarkData ScTransferObj::GetSourceMarkData()
    {
        ScMarkData aMarkData;
        ScCellRangesBase* pRangesObj = ScCellRangesBase::getImplementation( m_xDragSourceRanges );
        if (pRangesObj)
        {
            const ScRangeList& rRanges = pRangesObj->GetRangeList();
            aMarkData.MarkFromRangeList( rRanges, false );
        }
        return aMarkData;
    }

    //  initialize aDocShellRef with a live document from the ClipDoc

    // #i123405# added parameter to allow size calculation without limitation
    // to PageSize, e.g. used for Metafile creation for clipboard.

    void ScTransferObj::InitDocShell(bool bLimitToPageSize)
    {
        if ( !m_aDocShellRef.is() )
        {
            ScDocShell* pDocSh = new ScDocShell;
            m_aDocShellRef = pDocSh;      // ref must be there before InitNew

            pDocSh->DoInitNew();

            ScDocument& rDestDoc = pDocSh->GetDocument();
            ScMarkData aDestMark;
            aDestMark.SelectTable( 0, true );

            rDestDoc.SetDocOptions( m_pDoc->GetDocOptions() );   // #i42666#

            OUString aTabName;
            m_pDoc->GetName( m_aBlock.aStart.Tab(), aTabName );
            rDestDoc.RenameTab( 0, aTabName );

            rDestDoc.CopyStdStylesFrom( m_pDoc.get() );

            SCCOL nStartX = m_aBlock.aStart.Col();
            SCROW nStartY = m_aBlock.aStart.Row();
            SCCOL nEndX = m_aBlock.aEnd.Col();
            SCROW nEndY = m_aBlock.aEnd.Row();

            //  widths / heights
            //  (must be copied before CopyFromClip, for drawing objects)

            SCCOL nCol;
            SCTAB nSrcTab = m_aBlock.aStart.Tab();
            rDestDoc.SetLayoutRTL(0, m_pDoc->IsLayoutRTL(nSrcTab));
            for (nCol=nStartX; nCol<=nEndX; nCol++)
                if ( m_pDoc->ColHidden(nCol, nSrcTab) )
                    rDestDoc.ShowCol( nCol, 0, false );
                else
                    rDestDoc.SetColWidth( nCol, 0, m_pDoc->GetColWidth( nCol, nSrcTab ) );

            for (SCROW nRow = nStartY; nRow <= nEndY; ++nRow)
            {
                if ( m_pDoc->RowHidden(nRow, nSrcTab) )
                    rDestDoc.ShowRow( nRow, 0, false );
                else
                {
                    rDestDoc.SetRowHeight( nRow, 0, m_pDoc->GetOriginalHeight( nRow, nSrcTab ) );

                    //  if height was set manually, that flag has to be copied, too
                    bool bManual = m_pDoc->IsManualRowHeight(nRow, nSrcTab);
                    rDestDoc.SetManualHeight(nRow, nRow, 0, bManual);
                }
            }

            if (m_pDoc->GetDrawLayer() || m_pDoc->HasNotes())
                pDocSh->MakeDrawLayer();

            //  cell range is copied to the original position, but on the first sheet
            //  -> bCutMode must be set
            //  pDoc is always a Clipboard-document

            ScRange aDestRange( nStartX,nStartY,0, nEndX,nEndY,0 );
            bool bWasCut = m_pDoc->IsCutMode();
            if (!bWasCut)
                m_pDoc->SetClipArea( aDestRange, true );          // Cut
            rDestDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL, nullptr, m_pDoc.get(), false );
            m_pDoc->SetClipArea( aDestRange, bWasCut );

            StripRefs( m_pDoc.get(), nStartX,nStartY, nEndX,nEndY, &rDestDoc );

            ScRange aMergeRange = aDestRange;
            rDestDoc.ExtendMerge( aMergeRange, true );

            m_pDoc->CopyDdeLinks( &rDestDoc );         // copy values of DDE Links

            //  page format (grid etc) and page size (maximum size for ole object)

            Size aPaperSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );       // Twips
            ScStyleSheetPool* pStylePool = m_pDoc->GetStyleSheetPool();
            OUString aStyleName = m_pDoc->GetPageStyle( m_aBlock.aStart.Tab() );
            SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
            if (pStyleSheet)
            {
                const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet();
                aPaperSize = rSourceSet.Get(ATTR_PAGE_SIZE).GetSize();

                // CopyStyleFrom copies SetItems with correct pool
                ScStyleSheetPool* pDestPool = rDestDoc.GetStyleSheetPool();
                pDestPool->CopyStyleFrom( pStylePool, aStyleName, SfxStyleFamily::Page );
            }

            ScViewData aViewData( pDocSh, nullptr );
            aViewData.SetScreen( nStartX,nStartY, nEndX,nEndY );
            aViewData.SetCurX( nStartX );
            aViewData.SetCurY( nStartY );

            rDestDoc.SetViewOptions( m_pDoc->GetViewOptions() );

            //      Size
            //! get while copying sizes

            long nPosX = 0;
            long nPosY = 0;

            for (nCol=0; nCol<nStartX; nCol++)
                nPosX += rDestDoc.GetColWidth( nCol, 0 );
            nPosY += rDestDoc.GetRowHeight( 0, nStartY-1, 0 );
            nPosX = static_cast<long>( nPosX * HMM_PER_TWIPS );
            nPosY = static_cast<long>( nPosY * HMM_PER_TWIPS );

            aPaperSize.setWidth( aPaperSize.Width() * 2 );       // limit OLE object to double of page size
            aPaperSize.setHeight( aPaperSize.Height() * 2 );

            long nSizeX = 0;
            long nSizeY = 0;
            for (nCol=nStartX; nCol<=nEndX; nCol++)
            {
                long nAdd = rDestDoc.GetColWidth( nCol, 0 );
                if ( bLimitToPageSize && nSizeX+nAdd > aPaperSize.Width() && nSizeX )   // above limit?
                    break;
                nSizeX += nAdd;
            }
            for (SCROW nRow=nStartY; nRow<=nEndY; nRow++)
            {
                long nAdd = rDestDoc.GetRowHeight( nRow, 0 );
                if ( bLimitToPageSize && nSizeY+nAdd > aPaperSize.Height() && nSizeY )  // above limit?
                    break;
                nSizeY += nAdd;
            }
            nSizeX = static_cast<long>( nSizeX * HMM_PER_TWIPS );
            nSizeY = static_cast<long>( nSizeY * HMM_PER_TWIPS );

    //      pDocSh->SetVisAreaSize( Size(nSizeX,nSizeY) );

            tools::Rectangle aNewArea( Point(nPosX,nPosY), Size(nSizeX,nSizeY) );
            //TODO/LATER: why twice?!
            //pDocSh->SvInPlaceObject::SetVisArea( aNewArea );
            pDocSh->SetVisArea( aNewArea );

            pDocSh->UpdateOle(&aViewData, true);

            //! SetDocumentModified?
            if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
                rDestDoc.UpdateChartListenerCollection();
        }
    }

    SfxObjectShell* ScTransferObj::SetDrawClipDoc( bool bAnyOle )
    {
        // update ScGlobal::xDrawClipDocShellRef

        ScGlobal::xDrawClipDocShellRef.clear();
        if (bAnyOle)
        {
            ScGlobal::xDrawClipDocShellRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS); // there must be a ref
            ScGlobal::xDrawClipDocShellRef->DoInitNew();
            return ScGlobal::xDrawClipDocShellRef.get();
        }
        else
        {
            ScGlobal::xDrawClipDocShellRef.clear();
            return nullptr;
        }
    }

    void ScTransferObj::StripRefs( ScDocument* pDoc,
                        SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY,
                        ScDocument* pDestDoc )
    {
        if (!pDestDoc)
        {
            pDestDoc = pDoc;
        }

        //  In a clipboard doc the data don't have to be on the first sheet

        SCTAB nSrcTab = 0;
        while (nSrcTab<pDoc->GetTableCount() && !pDoc->HasTable(nSrcTab))
            ++nSrcTab;
        SCTAB nDestTab = 0;
        while (nDestTab<pDestDoc->GetTableCount() && !pDestDoc->HasTable(nDestTab))
            ++nDestTab;

        if (!pDoc->HasTable(nSrcTab) || !pDestDoc->HasTable(nDestTab))
        {
            OSL_FAIL("Sheet not found in ScTransferObj::StripRefs");
            return;
        }

        ScRange aRef;

        ScCellIterator aIter( pDoc, ScRange(nStartX, nStartY, nSrcTab, nEndX, nEndY, nSrcTab) );
        for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
        {
            if (aIter.getType() != CELLTYPE_FORMULA)
                continue;

            ScFormulaCell* pFCell = aIter.getFormulaCell();
            bool bOut = false;
            ScDetectiveRefIter aRefIter( pFCell );
            while ( !bOut && aRefIter.GetNextRef( aRef ) )
            {
                if ( aRef.aStart.Tab() != nSrcTab || aRef.aEnd.Tab() != nSrcTab ||
                        aRef.aStart.Col() < nStartX || aRef.aEnd.Col() > nEndX ||
                        aRef.aStart.Row() < nStartY || aRef.aEnd.Row() > nEndY )
                    bOut = true;
            }
            if (bOut)
            {
                SCCOL nCol = aIter.GetPos().Col();
                SCROW nRow = aIter.GetPos().Row();

                FormulaError nErrCode = pFCell->GetErrCode();
                ScAddress aPos(nCol, nRow, nDestTab);
                if (nErrCode != FormulaError::NONE)
                {
                    if ( pDestDoc->GetAttr( nCol,nRow,nDestTab, ATTR_HOR_JUSTIFY)->GetValue() ==
                            SvxCellHorJustify::Standard )
                        pDestDoc->ApplyAttr( nCol,nRow,nDestTab,
                                SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY) );

                    ScSetStringParam aParam;
                    aParam.setTextInput();
                    pDestDoc->SetString(aPos, ScGlobal::GetErrorString(nErrCode), &aParam);
                }
                else if (pFCell->IsValue())
                {
                    pDestDoc->SetValue(aPos, pFCell->GetValue());
                }
                else
                {
                    OUString aStr = pFCell->GetString().getString();
                    if ( pFCell->IsMultilineResult() )
                    {
                        ScFieldEditEngine& rEngine = pDestDoc->GetEditEngine();
                        rEngine.SetText(aStr);
                        pDestDoc->SetEditText(ScAddress(nCol,nRow,nDestTab), rEngine.CreateTextObject());
                    }
                    else
                    {
                        ScSetStringParam aParam;
                        aParam.setTextInput();
                        pDestDoc->SetString(aPos, aStr, &aParam);
                    }
                }
            }
        }
    }

    namespace
    {
        class theScTransferUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theScTransferUnoTunnelId> {};
    }

    const css::uno::Sequence< sal_Int8 >& ScTransferObj::getUnoTunnelId()
    {
        return theScTransferUnoTunnelId::get().getSeq();
    }

    sal_Int64 SAL_CALL ScTransferObj::getSomething( const css::uno::Sequence< sal_Int8 >& rId )
    {
        sal_Int64 nRet;
        if( ( rId.getLength() == 16 ) &&
            ( 0 == memcmp( getUnoTunnelId().getConstArray(), rId.getConstArray(), 16 ) ) )
        {
            nRet = reinterpret_cast< sal_Int64 >( this );
        }
        else
            nRet = TransferableHelper::getSomething(rId);
        return nRet;
    }

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

↑ V547 Expression is always true.