tdf#140813: Use GetUpdatedClipboardFormats to enumerate clipboard formats

We really don't have to provide plain text formats other than Unicode, so
we may avoid checking CF_LOCALE data when initializing flavor list. Let's
pretend that any textual format in the clipboard is Unicode, and ensure
that we only actually access system clipboard when we paste.

Change-Id: Ife30f57605a42d59233bfcb97f8bc297b3ace463
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112044
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/vcl/win/dtrans/DOTransferable.cxx b/vcl/win/dtrans/DOTransferable.cxx
index 0c61ddc..7a911a0 100644
--- a/vcl/win/dtrans/DOTransferable.cxx
+++ b/vcl/win/dtrans/DOTransferable.cxx
@@ -25,6 +25,7 @@
#include "DOTransferable.hxx"
#include "ImplHelper.hxx"
#include "WinClip.hxx"
#include "WinClipboard.hxx"
#include "DTransHelper.hxx"
#include "TxtCnvtHlp.hxx"
#include "MimeAttrib.hxx"
@@ -208,19 +209,6 @@

} // end namespace

Reference< XTransferable > CDOTransferable::create( const Reference< XComponentContext >& rxContext,
                                                                                     IDataObjectPtr pIDataObject )
{
    CDOTransferable* pTransf = new CDOTransferable(rxContext, pIDataObject);
    Reference<XTransferable> refDOTransf(pTransf);

    pTransf->acquire();
    pTransf->initFlavorList();
    pTransf->release();

    return refDOTransf;
}

CDOTransferable::CDOTransferable(
    const Reference< XComponentContext >& rxContext, IDataObjectPtr rDataObject ) :
    m_rDataObject( rDataObject ),
@@ -229,6 +217,20 @@
    m_bUnicodeRegistered( false ),
    m_TxtFormatOnClipboard( CF_INVALID )
{
    initFlavorList();
}

CDOTransferable::CDOTransferable(
    const Reference<XComponentContext>& rxContext,
    const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard,
    const std::vector<sal_uInt32>& rFormats)
    : m_xClipboard(xClipboard)
    , m_xContext(rxContext)
    , m_DataFormatTranslator(rxContext)
    , m_bUnicodeRegistered(false)
    , m_TxtFormatOnClipboard(CF_INVALID)
{
    initFlavorListFromFormatList(rFormats);
}

Any SAL_CALL CDOTransferable::getTransferData( const DataFlavor& aFlavor )
@@ -312,6 +314,7 @@

void CDOTransferable::initFlavorList( )
{
    std::vector<sal_uInt32> aFormats;
    sal::systools::COMReference<IEnumFORMATETC> pEnumFormatEtc;
    HRESULT hr = m_rDataObject->EnumFormatEtc( DATADIR_GET, &pEnumFormatEtc );
    if ( SUCCEEDED( hr ) )
@@ -321,39 +324,38 @@
        FORMATETC fetc;
        while ( S_OK == pEnumFormatEtc->Next( 1, &fetc, nullptr ) )
        {
            // we use locales only to determine the
            // charset if there is text on the cliboard
            // we don't offer this format
            if ( CF_LOCALE == fetc.cfFormat )
                continue;

            DataFlavor aFlavor = formatEtcToDataFlavor( fetc );

            // if text or oemtext is offered we also pretend to have unicode text
            if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) &&
                 !m_bUnicodeRegistered )
            {
                addSupportedFlavor( aFlavor );

                m_TxtFormatOnClipboard = fetc.cfFormat;
                m_bUnicodeRegistered   = true;

                // register unicode text as accompany format
                aFlavor = formatEtcToDataFlavor(
                    CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT ) );
                addSupportedFlavor( aFlavor );
            }
            else if ( (CF_UNICODETEXT == fetc.cfFormat) && !m_bUnicodeRegistered )
            {
                addSupportedFlavor( aFlavor );
                m_bUnicodeRegistered = true;
            }
            else
                addSupportedFlavor( aFlavor );

            aFormats.push_back(fetc.cfFormat);
            // see MSDN IEnumFORMATETC
            CoTaskMemFree( fetc.ptd );
        }
        initFlavorListFromFormatList(aFormats);
    }
}

void CDOTransferable::initFlavorListFromFormatList(const std::vector<sal_uInt32>& rFormats)
{
    for (sal_uInt32 cfFormat : rFormats)
    {
        // we use locales only to determine the
        // charset if there is text on the cliboard
        // we don't offer this format
        if (CF_LOCALE == cfFormat)
            continue;

        // if text or oemtext is offered we pretend to have unicode text
        if (CDataFormatTranslator::isTextFormat(cfFormat))
        {
            if (!m_bUnicodeRegistered)
            {
                m_TxtFormatOnClipboard = cfFormat;
                m_bUnicodeRegistered   = true;

                // register unicode text as format
                addSupportedFlavor(formatEtcToDataFlavor(CF_UNICODETEXT));
            }
        }
        else
            addSupportedFlavor(formatEtcToDataFlavor(cfFormat));
    }
}

@@ -370,18 +372,9 @@
    }
}

DataFlavor CDOTransferable::formatEtcToDataFlavor( const FORMATETC& aFormatEtc )
DataFlavor CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat)
{
    LCID lcid = 0;

    // for non-unicode text format we must provide a locale to get
    // the character-set of the text, if there is no locale on the
    // clipboard we assume the text is in a charset appropriate for
    // the current thread locale
    if ( (CF_TEXT == aFormatEtc.cfFormat) || (CF_OEMTEXT == aFormatEtc.cfFormat) )
        lcid = getLocaleFromClipboard( );

    return m_DataFormatTranslator.getDataFlavorFromFormatEtc( aFormatEtc, lcid );
    return m_DataFormatTranslator.getDataFlavorFromFormatEtc(cfFormat);
}

// returns the current locale on clipboard; if there is no locale on
@@ -411,6 +404,18 @@
    return lcid;
}

void CDOTransferable::tryToGetIDataObjectIfAbsent()
{
    if (!m_rDataObject.is())
    {
        auto xClipboard = m_xClipboard.get(); // holding the reference while we get the object
        if (CWinClipboard* pWinClipboard = dynamic_cast<CWinClipboard*>(xClipboard.get()))
        {
            m_rDataObject = pWinClipboard->getIDataObject();
        }
    }
}

// I think it's not necessary to call ReleaseStgMedium
// in case of failures because nothing should have been
// allocated etc.
@@ -418,6 +423,9 @@
CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& aFormatEtc )
{
    STGMEDIUM stgmedium;
    tryToGetIDataObjectIfAbsent();
    if (!m_rDataObject.is()) // Maybe we are shutting down, and clipboard is already destroyed?
        throw RuntimeException();
    HRESULT hr = m_rDataObject->GetData( aFormatEtc, &stgmedium );

    // in case of failure to get a WMF metafile handle, try to get a memory block
diff --git a/vcl/win/dtrans/DOTransferable.hxx b/vcl/win/dtrans/DOTransferable.hxx
index af2d200..0e652f9 100644
--- a/vcl/win/dtrans/DOTransferable.hxx
+++ b/vcl/win/dtrans/DOTransferable.hxx
@@ -23,12 +23,16 @@

#include <cppuhelper/implbase.hxx>
#include "DataFmtTransl.hxx"
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
#include <com/sun/star/datatransfer/XMimeContentType.hpp>
#include <com/sun/star/datatransfer/XSystemTransferable.hpp>
#include <cppuhelper/weakref.hxx>

#include <systools/win32/comtools.hxx>

#include <vector>

// forward
class CFormatEtc;

@@ -39,9 +43,6 @@
public:
    typedef css::uno::Sequence< sal_Int8 > ByteSequence_t;

    static css::uno::Reference< css::datatransfer::XTransferable > create(
        const css::uno::Reference< css::uno::XComponentContext >& rxContext, IDataObjectPtr pIDataObject );

    // XTransferable

    virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override;
@@ -54,18 +55,25 @@

    virtual css::uno::Any SAL_CALL getData( const css::uno::Sequence<sal_Int8>& aProcessId  ) override;

private:
    explicit CDOTransferable(
        const css::uno::Reference< css::uno::XComponentContext >& rxContext,
        const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard,
        const std::vector<sal_uInt32>& rFormats);

    explicit CDOTransferable(
        const css::uno::Reference< css::uno::XComponentContext >& rxContext,
        IDataObjectPtr rDataObject );

private:
    // some helper functions

    void initFlavorList( );
    void initFlavorListFromFormatList(const std::vector<sal_uInt32>& rFormats);

    void addSupportedFlavor( const css::datatransfer::DataFlavor& aFlavor );
    css::datatransfer::DataFlavor formatEtcToDataFlavor( const FORMATETC& aFormatEtc );
    css::datatransfer::DataFlavor formatEtcToDataFlavor(sal_uInt32 cfFormat);

    void tryToGetIDataObjectIfAbsent();
    ByteSequence_t getClipboardData( CFormatEtc& aFormatEtc );
    OUString synthesizeUnicodeText( );

@@ -75,6 +83,7 @@
                                          const css::datatransfer::DataFlavor& rhs );

private:
    css::uno::WeakReference<css::datatransfer::clipboard::XClipboard> m_xClipboard;
    IDataObjectPtr                                                                          m_rDataObject;
    css::uno::Sequence< css::datatransfer::DataFlavor >               m_FlavorList;
    const css::uno::Reference< css::uno::XComponentContext >          m_xContext;
diff --git a/vcl/win/dtrans/DataFmtTransl.cxx b/vcl/win/dtrans/DataFmtTransl.cxx
index f666f8c..5e23822 100644
--- a/vcl/win/dtrans/DataFmtTransl.cxx
+++ b/vcl/win/dtrans/DataFmtTransl.cxx
@@ -92,13 +92,13 @@
    return sal::static_int_cast<CFormatEtc>(getFormatEtcForClipformat( sal::static_int_cast<CLIPFORMAT>(cf) ));
}

DataFlavor CDataFormatTranslator::getDataFlavorFromFormatEtc( const FORMATETC& aFormatEtc, LCID lcid ) const
DataFlavor CDataFormatTranslator::getDataFlavorFromFormatEtc(sal_uInt32 cfFormat, LCID lcid) const
{
    DataFlavor aFlavor;

    try
    {
        CLIPFORMAT aClipformat = aFormatEtc.cfFormat;
        CLIPFORMAT aClipformat = cfFormat;

        Any aAny;
        aAny <<= static_cast< sal_Int32 >( aClipformat );
diff --git a/vcl/win/dtrans/DataFmtTransl.hxx b/vcl/win/dtrans/DataFmtTransl.hxx
index 3d74875..e003f25 100644
--- a/vcl/win/dtrans/DataFmtTransl.hxx
+++ b/vcl/win/dtrans/DataFmtTransl.hxx
@@ -42,7 +42,7 @@

    CFormatEtc getFormatEtcFromDataFlavor( const css::datatransfer::DataFlavor& aDataFlavor ) const;
    css::datatransfer::DataFlavor getDataFlavorFromFormatEtc(
        const FORMATETC& aFormatEtc, LCID lcid = GetThreadLocale( ) ) const;
        sal_uInt32 cfFormat, LCID lcid = GetThreadLocale()) const;

    static CFormatEtc getFormatEtcForClipformat( CLIPFORMAT cf );
    static CFormatEtc getFormatEtcForClipformatName( const OUString& aClipFmtName );
diff --git a/vcl/win/dtrans/FetcList.cxx b/vcl/win/dtrans/FetcList.cxx
index d8fc6a5..e9a1c0d 100644
--- a/vcl/win/dtrans/FetcList.cxx
+++ b/vcl/win/dtrans/FetcList.cxx
@@ -285,10 +285,7 @@

bool CFormatRegistrar::hasUnicodeFlavor( const Reference< XTransferable >& aXTransferable ) const
{
    CFormatEtc fetc( CF_UNICODETEXT );

    DataFlavor aFlavor =
        m_DataFormatTranslator.getDataFlavorFromFormatEtc( fetc );
    DataFlavor aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(CF_UNICODETEXT);

    return aXTransferable->isDataFlavorSupported( aFlavor );
}
diff --git a/vcl/win/dtrans/WinClipboard.cxx b/vcl/win/dtrans/WinClipboard.cxx
index 6ed9e96..2100510 100644
--- a/vcl/win/dtrans/WinClipboard.cxx
+++ b/vcl/win/dtrans/WinClipboard.cxx
@@ -112,6 +112,32 @@

    uno::Reference<datatransfer::XTransferable> rClipContent;

    // get the current format list from clipboard
    if (UINT nFormats; !GetUpdatedClipboardFormats(nullptr, 0, &nFormats)
                       && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        std::vector<UINT> aUINTFormats(nFormats);
        if (GetUpdatedClipboardFormats(aUINTFormats.data(), nFormats, &nFormats))
        {
            std::vector<sal_uInt32> aFormats(aUINTFormats.begin(), aUINTFormats.end());
            rClipContent = new CDOTransferable(m_xContext, this, aFormats);

            osl::MutexGuard aGuard2(m_ClipContentMutex);
            m_foreignContent = rClipContent;
        }
    }

    return rClipContent;
}

IDataObjectPtr CWinClipboard::getIDataObject()
{
    osl::MutexGuard aGuard(m_aMutex);

    if (rBHelper.bDisposed)
        throw lang::DisposedException("object is already disposed",
                                      static_cast<XClipboardEx*>(this));

    // get the current dataobject from clipboard
    IDataObjectPtr pIDataObject;
    HRESULT hr = m_MtaOleClipboard.getClipboard(&pIDataObject);
@@ -120,16 +146,10 @@
    {
        // create an apartment neutral dataobject and initialize it with a
        // com smart pointer to the IDataObject from clipboard
        IDataObjectPtr pIDo(new CAPNDataObject(pIDataObject));

        // remember pIDo destroys itself due to the smart pointer
        rClipContent = CDOTransferable::create(m_xContext, pIDo);

        osl::MutexGuard aGuard2(m_ClipContentMutex);
        m_foreignContent = rClipContent;
        pIDataObject = new CAPNDataObject(pIDataObject);
    }

    return rClipContent;
    return pIDataObject;
}

void SAL_CALL CWinClipboard::setContents(
diff --git a/vcl/win/dtrans/WinClipboard.hxx b/vcl/win/dtrans/WinClipboard.hxx
index 1b0a05a..8e64029 100644
--- a/vcl/win/dtrans/WinClipboard.hxx
+++ b/vcl/win/dtrans/WinClipboard.hxx
@@ -32,10 +32,10 @@
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <osl/conditn.hxx>
#include <systools/win32/comtools.hxx>

#include "MtaOleClipb.hxx"

class CXNotifyingDataObject;
#include "XNotifyingDataObject.hxx"

// implements the XClipboard[Ex] ... interfaces
// for the clipboard viewer mechanism we need a static callback function
@@ -113,6 +113,8 @@
    virtual OUString SAL_CALL getImplementationName() override;
    virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;

    IDataObjectPtr getIDataObject();
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/XTDataObject.cxx b/vcl/win/dtrans/XTDataObject.cxx
index 9300d29..adbed6b 100644
--- a/vcl/win/dtrans/XTDataObject.cxx
+++ b/vcl/win/dtrans/XTDataObject.cxx
@@ -636,10 +636,10 @@
    DataFlavor aFlavor;

    if ( m_FormatRegistrar.hasSynthesizedLocale( ) )
        aFlavor =
            m_DataFormatTranslator.getDataFlavorFromFormatEtc( aFormatEtc, CFormatRegistrar::getSynthesizedLocale( ) );
        aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(
            aFormatEtc.cfFormat, CFormatRegistrar::getSynthesizedLocale());
    else
        aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc( aFormatEtc );
        aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(aFormatEtc.cfFormat);

    if ( !aFlavor.MimeType.getLength( ) )
        throw UnsupportedFlavorException( );
diff --git a/vcl/win/dtrans/target.cxx b/vcl/win/dtrans/target.cxx
index 2492c8a..32cf95f 100644
--- a/vcl/win/dtrans/target.cxx
+++ b/vcl/win/dtrans/target.cxx
@@ -333,8 +333,7 @@
        else
        {
            // Convert the IDataObject to a XTransferable
            m_currentData= CDOTransferable::create(
                                            m_xContext, IDataObjectPtr(pDataObj));
            m_currentData = new CDOTransferable(m_xContext, IDataObjectPtr(pDataObj));
        }

        //<-- TRA