diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/win/dtrans | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/win/dtrans')
48 files changed, 9088 insertions, 0 deletions
diff --git a/vcl/win/dtrans/APNDataObject.cxx b/vcl/win/dtrans/APNDataObject.cxx new file mode 100644 index 0000000000..2492647f26 --- /dev/null +++ b/vcl/win/dtrans/APNDataObject.cxx @@ -0,0 +1,320 @@ +/* -*- 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 "APNDataObject.hxx" +#include <osl/diagnose.h> + +#include <systools/win32/comtools.hxx> + +#define FREE_HGLOB_ON_RELEASE TRUE +#define KEEP_HGLOB_ON_RELEASE FALSE + +// ctor + +CAPNDataObject::CAPNDataObject( IDataObjectPtr rIDataObject ) : + m_rIDataObjectOrg( rIDataObject ), + m_hGlobal( nullptr ), + m_nRefCnt( 0 ) +{ + + OSL_ENSURE( m_rIDataObjectOrg.get( ), "constructing CAPNDataObject with empty data object" ); + + // we marshal the IDataObject interface pointer here so + // that it can be unmarshalled multiple times when this + // class will be used from another apartment + sal::systools::COMReference<IStream> pStm; + HRESULT hr = CreateStreamOnHGlobal( nullptr, KEEP_HGLOB_ON_RELEASE, &pStm ); + + OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" ); + + if ( SUCCEEDED( hr ) ) + { + HRESULT hr_marshal = CoMarshalInterface( + pStm, + __uuidof(IDataObject), + m_rIDataObjectOrg, + MSHCTX_LOCAL, + nullptr, + MSHLFLAGS_TABLEWEAK ); + + OSL_ENSURE( CO_E_NOTINITIALIZED != hr_marshal, "COM is not initialized" ); + + // marshalling may fail if COM is not initialized + // for the calling thread which is a program time + // error or because of stream errors which are runtime + // errors for instance E_OUTOFMEMORY etc. + + hr = GetHGlobalFromStream(pStm, &m_hGlobal ); + + OSL_ENSURE( E_INVALIDARG != hr, "invalid stream passed to GetHGlobalFromStream" ); + + // if the marshalling failed we free the + // global memory again and set m_hGlobal + // to a defined value + if (FAILED(hr_marshal)) + { + OSL_FAIL("marshalling failed"); + + HGLOBAL hGlobal = + GlobalFree(m_hGlobal); + OSL_ENSURE(nullptr == hGlobal, "GlobalFree failed"); + m_hGlobal = nullptr; + } + } +} + +CAPNDataObject::~CAPNDataObject( ) +{ + if (m_hGlobal) + { + sal::systools::COMReference<IStream> pStm; + HRESULT hr = CreateStreamOnHGlobal(m_hGlobal, FREE_HGLOB_ON_RELEASE, &pStm); + + OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" ); + + if (SUCCEEDED(hr)) + { + hr = CoReleaseMarshalData(pStm); + OSL_ENSURE(SUCCEEDED(hr), "CoReleaseMarshalData failed"); + } + } +} + +// IUnknown->QueryInterface + +STDMETHODIMP CAPNDataObject::QueryInterface( REFIID iid, void** ppvObject ) +{ + OSL_ASSERT( nullptr != ppvObject ); + + if ( nullptr == ppvObject ) + return E_INVALIDARG; + + HRESULT hr = E_NOINTERFACE; + *ppvObject = nullptr; + + if ( ( __uuidof( IUnknown ) == iid ) || ( __uuidof( IDataObject ) == iid ) ) + { + *ppvObject = static_cast< IUnknown* >( this ); + AddRef( ); + hr = S_OK; + } + + return hr; +} + +// IUnknown->AddRef + +STDMETHODIMP_(ULONG) CAPNDataObject::AddRef( ) +{ + return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) ); +} + +// IUnknown->Release + +STDMETHODIMP_(ULONG) CAPNDataObject::Release( ) +{ + // we need a helper variable because it's not allowed to access + // a member variable after an object is destroyed + ULONG nRefCnt = static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) ); + + if ( 0 == nRefCnt ) + delete this; + + return nRefCnt; +} + +// IDataObject->GetData + +STDMETHODIMP CAPNDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) +{ + HRESULT hr = m_rIDataObjectOrg->GetData( pFormatetc, pmedium ); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->GetData(pFormatetc, pmedium); + } + return hr; +} + +// IDataObject->EnumFormatEtc + +STDMETHODIMP CAPNDataObject::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) +{ + HRESULT hr = m_rIDataObjectOrg->EnumFormatEtc(dwDirection, ppenumFormatetc); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->EnumFormatEtc(dwDirection, ppenumFormatetc); + } + return hr; +} + +// IDataObject->QueryGetData + +STDMETHODIMP CAPNDataObject::QueryGetData( FORMATETC * pFormatetc ) +{ + HRESULT hr = m_rIDataObjectOrg->QueryGetData( pFormatetc ); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment( &pIDOTmp ); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->QueryGetData(pFormatetc); + } + return hr; +} + +// IDataObject->GetDataHere + +STDMETHODIMP CAPNDataObject::GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) +{ + HRESULT hr = m_rIDataObjectOrg->GetDataHere(pFormatetc, pmedium); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->GetDataHere(pFormatetc, pmedium); + } + return hr; +} + +// IDataObject->GetCanonicalFormatEtc + +STDMETHODIMP CAPNDataObject::GetCanonicalFormatEtc(FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut) +{ + HRESULT hr = m_rIDataObjectOrg->GetCanonicalFormatEtc( pFormatectIn, pFormatetcOut ); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->GetCanonicalFormatEtc(pFormatectIn, pFormatetcOut); + } + return hr; +} + +// IDataObject->SetData + +STDMETHODIMP CAPNDataObject::SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) +{ + HRESULT hr = m_rIDataObjectOrg->SetData( pFormatetc, pmedium, fRelease ); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->SetData(pFormatetc, pmedium, fRelease); + } + return hr; +} + +// IDataObject->DAdvise + +STDMETHODIMP CAPNDataObject::DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection ) +{ + HRESULT hr = m_rIDataObjectOrg->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection); + } + return hr; +} + +// IDataObject->DUnadvise + +STDMETHODIMP CAPNDataObject::DUnadvise( DWORD dwConnection ) +{ + HRESULT hr = m_rIDataObjectOrg->DUnadvise( dwConnection ); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->DUnadvise(dwConnection); + } + return hr; +} + +// IDataObject->EnumDAdvise + +STDMETHODIMP CAPNDataObject::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise ) +{ + HRESULT hr = m_rIDataObjectOrg->EnumDAdvise(ppenumAdvise); + + if (RPC_E_WRONG_THREAD == hr) + { + IDataObjectPtr pIDOTmp; + hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp); + + if (SUCCEEDED(hr)) + hr = pIDOTmp->EnumDAdvise(ppenumAdvise); + } + return hr; +} + +// helper function + +HRESULT CAPNDataObject::MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj ) +{ + OSL_ASSERT(nullptr != ppIDataObj); + + *ppIDataObj = nullptr; + HRESULT hr = E_FAIL; + + if (m_hGlobal) + { + sal::systools::COMReference<IStream> pStm; + hr = CreateStreamOnHGlobal(m_hGlobal, KEEP_HGLOB_ON_RELEASE, &pStm); + + OSL_ENSURE(E_INVALIDARG != hr, "CreateStreamOnHGlobal with invalid args called"); + + if (SUCCEEDED(hr)) + { + hr = CoUnmarshalInterface(pStm, IID_PPV_ARGS(ppIDataObj)); + OSL_ENSURE(CO_E_NOTINITIALIZED != hr, "COM is not initialized"); + } + } + return hr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/APNDataObject.hxx b/vcl/win/dtrans/APNDataObject.hxx new file mode 100644 index 0000000000..5de8ccfcc8 --- /dev/null +++ b/vcl/win/dtrans/APNDataObject.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <objidl.h> + +#include <systools/win32/comtools.hxx> + +/* + an APartment Neutral dataobject wrapper; this wrapper of an IDataObject + pointer can be used from any apartment without RPC_E_WRONG_THREAD + which normally occurs if an apartment tries to use an interface + pointer of another apartment; we use containment to hold the original + DataObject +*/ +class CAPNDataObject : public IDataObject +{ +public: + explicit CAPNDataObject(IDataObjectPtr rIDataObject); + virtual ~CAPNDataObject(); + + //IUnknown interface methods + + STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; + STDMETHODIMP_( ULONG ) AddRef( ) override; + STDMETHODIMP_( ULONG ) Release( ) override; + + // IDataObject interface methods + + STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override; + STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override; + STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override; + STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override; + STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override; + STDMETHODIMP DUnadvise( DWORD dwConnection ) override; + STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override; + +private: + HRESULT MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj ); + +private: + IDataObjectPtr m_rIDataObjectOrg; + HGLOBAL m_hGlobal; + LONG m_nRefCnt; + +// prevent copy and assignment +private: + CAPNDataObject( const CAPNDataObject& theOther ) = delete; + CAPNDataObject& operator=( const CAPNDataObject& theOther ) = delete; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DOTransferable.cxx b/vcl/win/dtrans/DOTransferable.cxx new file mode 100644 index 0000000000..c2b54bf83a --- /dev/null +++ b/vcl/win/dtrans/DOTransferable.cxx @@ -0,0 +1,577 @@ +/* -*- 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 <sal/types.h> +#include <rtl/process.h> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include "DOTransferable.hxx" +#include "ImplHelper.hxx" +#include "WinClip.hxx" +#include "WinClipboard.hxx" +#include "DTransHelper.hxx" +#include "TxtCnvtHlp.hxx" +#include "MimeAttrib.hxx" +#include "FmtFilter.hxx" +#include "Fetc.hxx" +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp> +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +using namespace osl; +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::io; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; + +namespace +{ + const Type CPPUTYPE_SEQINT8 = cppu::UnoType<Sequence< sal_Int8 >>::get(); + const Type CPPUTYPE_OUSTRING = cppu::UnoType<OUString>::get(); + + bool isValidFlavor( const DataFlavor& aFlavor ) + { + return ( aFlavor.MimeType.getLength( ) && + ( ( aFlavor.DataType == CPPUTYPE_SEQINT8 ) || + ( aFlavor.DataType == CPPUTYPE_OUSTRING ) ) ); + } + +void clipDataToByteStream( CLIPFORMAT cf, STGMEDIUM stgmedium, CDOTransferable::ByteSequence_t& aByteSequence ) +{ + CStgTransferHelper memTransferHelper; + LPSTREAM pStream = nullptr; + + switch( stgmedium.tymed ) + { + case TYMED_HGLOBAL: + memTransferHelper.init( stgmedium.hGlobal ); + break; + + case TYMED_MFPICT: + memTransferHelper.init( stgmedium.hMetaFilePict ); + break; + + case TYMED_ENHMF: + memTransferHelper.init( stgmedium.hEnhMetaFile ); + break; + + case TYMED_ISTREAM: + pStream = stgmedium.pstm; + break; + + default: + throw UnsupportedFlavorException( ); + break; + } + + if (pStream) + { + // We have a stream, read from it. + STATSTG aStat; + HRESULT hr = pStream->Stat(&aStat, STATFLAG_NONAME); + if (FAILED(hr)) + { + SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Stat() failed"); + return; + } + + size_t nMemSize = aStat.cbSize.QuadPart; + aByteSequence.realloc(nMemSize); + LARGE_INTEGER li; + li.QuadPart = 0; + hr = pStream->Seek(li, STREAM_SEEK_SET, nullptr); + if (FAILED(hr)) + { + SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Seek() failed"); + } + + ULONG nRead = 0; + hr = pStream->Read(aByteSequence.getArray(), nMemSize, &nRead); + if (FAILED(hr)) + { + SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() failed"); + } + if (nRead < nMemSize) + { + SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() was partial"); + } + + return; + } + + int nMemSize = memTransferHelper.memSize( cf ); + aByteSequence.realloc( nMemSize ); + memTransferHelper.read( aByteSequence.getArray( ), nMemSize ); +} + +OUString byteStreamToOUString( CDOTransferable::ByteSequence_t& aByteStream ) +{ + sal_Int32 nWChars; + sal_Int32 nMemSize = aByteStream.getLength( ); + + // if there is a trailing L"\0" subtract 1 from length + // for unknown reason, the sequence may sometimes arrive empty + if ( aByteStream.getLength( ) > 1 && + 0 == aByteStream[ aByteStream.getLength( ) - 2 ] && + 0 == aByteStream[ aByteStream.getLength( ) - 1 ] ) + nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) ) - 1; + else + nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) ); + + return OUString( reinterpret_cast< sal_Unicode* >( aByteStream.getArray( ) ), nWChars ); +} + +Any byteStreamToAny( CDOTransferable::ByteSequence_t& aByteStream, const Type& aRequestedDataType ) +{ + Any aAny; + + if ( aRequestedDataType == CPPUTYPE_OUSTRING ) + { + OUString str = byteStreamToOUString( aByteStream ); + if (str.isEmpty()) + throw RuntimeException(); + aAny <<= str; + } + else + aAny <<= aByteStream; + + return aAny; +} + +bool cmpFullMediaType( + const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs ) +{ + return xLhs->getFullMediaType().equalsIgnoreAsciiCase( xRhs->getFullMediaType( ) ); +} + +bool cmpAllContentTypeParameter( + const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs ) +{ + Sequence< OUString > xLhsFlavors = xLhs->getParameters( ); + Sequence< OUString > xRhsFlavors = xRhs->getParameters( ); + bool bRet = true; + + try + { + if ( xLhsFlavors.getLength( ) == xRhsFlavors.getLength( ) ) + { + OUString pLhs; + OUString pRhs; + + for ( sal_Int32 i = 0; i < xLhsFlavors.getLength( ); i++ ) + { + pLhs = xLhs->getParameterValue( xLhsFlavors[i] ); + pRhs = xRhs->getParameterValue( xLhsFlavors[i] ); + + if ( !pLhs.equalsIgnoreAsciiCase( pRhs ) ) + { + bRet = false; + break; + } + } + } + else + bRet = false; + } + catch( NoSuchElementException& ) + { + bRet = false; + } + catch( IllegalArgumentException& ) + { + bRet = false; + } + + return bRet; +} + +} // end namespace + +CDOTransferable::CDOTransferable( + const Reference< XComponentContext >& rxContext, IDataObjectPtr rDataObject ) : + m_rDataObject( rDataObject ), + m_xContext( rxContext ), + m_DataFormatTranslator( rxContext ), + 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 ) +{ + OSL_ASSERT( isValidFlavor( aFlavor ) ); + + std::unique_lock aGuard( m_aMutex ); + + // convert dataflavor to formatetc + + CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor ); + OSL_ASSERT( CF_INVALID != fetc.getClipformat() ); + + // get the data from clipboard in a byte stream + + ByteSequence_t clipDataStream; + + try + { + clipDataStream = getClipboardData( fetc ); + } + catch( UnsupportedFlavorException& ) + { + if ( CDataFormatTranslator::isUnicodeTextFormat( fetc.getClipformat( ) ) && + m_bUnicodeRegistered ) + { + OUString aUnicodeText = synthesizeUnicodeText( ); + Any aAny( aUnicodeText ); + return aAny; + } + // #i124085# CF_DIBV5 should not be possible, but keep for reading from the + // clipboard for being on the safe side + else if(CF_DIBV5 == fetc.getClipformat()) + { + // #i123407# CF_DIBV5 has priority; if the try to fetch this failed, + // check CF_DIB availability as an alternative + fetc.setClipformat(CF_DIB); + + clipDataStream = getClipboardData( fetc ); + // pass UnsupportedFlavorException out, tried all possibilities + } + else + throw; // pass through exception + } + + // return the data as any + + return byteStreamToAny( clipDataStream, aFlavor.DataType ); +} + +// getTransferDataFlavors + +Sequence< DataFlavor > SAL_CALL CDOTransferable::getTransferDataFlavors( ) +{ + return m_FlavorList; +} + +// isDataFlavorSupported +// returns true if we find a DataFlavor with the same MimeType and +// DataType + +sal_Bool SAL_CALL CDOTransferable::isDataFlavorSupported( const DataFlavor& aFlavor ) +{ + OSL_ASSERT( isValidFlavor( aFlavor ) ); + + for ( DataFlavor const & df : std::as_const(m_FlavorList) ) + if ( compareDataFlavors( aFlavor, df ) ) + return true; + + return false; +} + +// the list of dataflavors currently on the clipboard will be initialized +// only once; if the client of this Transferable will hold a reference +// to it and the underlying clipboard content changes, the client does +// possible operate on an invalid list +// if there is only text on the clipboard we will also offer unicode text +// an synthesize this format on the fly if requested, to accomplish this +// we save the first offered text format which we will later use for the +// conversion + +void CDOTransferable::initFlavorList( ) +{ + std::vector<sal_uInt32> aFormats; + sal::systools::COMReference<IEnumFORMATETC> pEnumFormatEtc; + HRESULT hr = m_rDataObject->EnumFormatEtc( DATADIR_GET, &pEnumFormatEtc ); + if ( SUCCEEDED( hr ) ) + { + pEnumFormatEtc->Reset( ); + + FORMATETC fetc; + while ( S_OK == pEnumFormatEtc->Next( 1, &fetc, nullptr ) ) + { + 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 clipboard + // 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)); + } +} + +inline +void CDOTransferable::addSupportedFlavor( const DataFlavor& aFlavor ) +{ + // we ignore all formats that couldn't be translated + if ( aFlavor.MimeType.getLength( ) ) + { + OSL_ASSERT( isValidFlavor( aFlavor ) ); + + m_FlavorList.realloc( m_FlavorList.getLength( ) + 1 ); + m_FlavorList.getArray()[m_FlavorList.getLength( ) - 1] = aFlavor; + } +} + +DataFlavor CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat) +{ + return m_DataFormatTranslator.getDataFlavorFromFormatEtc(cfFormat); +} + +// returns the current locale on clipboard; if there is no locale on +// clipboard the function returns the current thread locale + +LCID CDOTransferable::getLocaleFromClipboard( ) +{ + LCID lcid = GetThreadLocale( ); + + try + { + CFormatEtc fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE ); + ByteSequence_t aLCIDSeq = getClipboardData( fetc ); + lcid = *reinterpret_cast<LCID*>( aLCIDSeq.getArray( ) ); + + // because of a Win95/98 Bug; there the high word + // of a locale has the same value as the + // low word e.g. 0x07040704 that's not right + // correct is 0x00000704 + lcid &= 0x0000FFFF; + } + catch(...) + { + // we take the default locale + } + + 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. + +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 + if( FAILED( hr ) && + ( CF_METAFILEPICT == aFormatEtc.getClipformat() ) && + ( TYMED_MFPICT == aFormatEtc.getTymed() ) ) + { + CFormatEtc aTempFormat( aFormatEtc ); + aTempFormat.setTymed( TYMED_HGLOBAL ); + hr = m_rDataObject->GetData( aTempFormat, &stgmedium ); + } + + if (FAILED(hr) && aFormatEtc.getTymed() == TYMED_HGLOBAL) + { + // Handle type is not memory, try stream. + CFormatEtc aTempFormat(aFormatEtc); + aTempFormat.setTymed(TYMED_ISTREAM); + hr = m_rDataObject->GetData(aTempFormat, &stgmedium); + } + + if ( FAILED( hr ) ) + { + OSL_ASSERT( (hr != E_INVALIDARG) && + (hr != DV_E_DVASPECT) && + (hr != DV_E_LINDEX) && + (hr != DV_E_TYMED) ); + + if ( DV_E_FORMATETC == hr ) + throw UnsupportedFlavorException( ); + else if ( STG_E_MEDIUMFULL == hr ) + throw IOException( ); + else + throw RuntimeException( ); + } + + ByteSequence_t byteStream; + + try + { + if ( CF_ENHMETAFILE == aFormatEtc.getClipformat() ) + byteStream = WinENHMFPictToOOMFPict( stgmedium.hEnhMetaFile ); + else if (CF_HDROP == aFormatEtc.getClipformat()) + byteStream = CF_HDROPToFileList(stgmedium.hGlobal); + else if ( CF_BITMAP == aFormatEtc.getClipformat() ) + { + byteStream = WinBITMAPToOOBMP(stgmedium.hBitmap); + if( aFormatEtc.getTymed() == TYMED_GDI && + ! stgmedium.pUnkForRelease ) + { + DeleteObject(stgmedium.hBitmap); + } + } + else + { + clipDataToByteStream( aFormatEtc.getClipformat( ), stgmedium, byteStream ); + + // format conversion if necessary + // #i124085# DIBV5 should not happen currently, but keep as a hint here + if(CF_DIBV5 == aFormatEtc.getClipformat() || CF_DIB == aFormatEtc.getClipformat()) + { + byteStream = WinDIBToOOBMP(byteStream); + } + else if(CF_METAFILEPICT == aFormatEtc.getClipformat()) + { + byteStream = WinMFPictToOOMFPict(byteStream); + } + } + + ReleaseStgMedium( &stgmedium ); + } + catch( CStgTransferHelper::CStgTransferException& ) + { + ReleaseStgMedium( &stgmedium ); + throw IOException( ); + } + + return byteStream; +} + +OUString CDOTransferable::synthesizeUnicodeText( ) +{ + ByteSequence_t aTextSequence; + CFormatEtc fetc; + LCID lcid = getLocaleFromClipboard( ); + sal_uInt32 cpForTxtCnvt = 0; + + if ( CF_TEXT == m_TxtFormatOnClipboard ) + { + fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT ); + aTextSequence = getClipboardData( fetc ); + + // determine the codepage used for text conversion + cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTANSICODEPAGE ).toInt32( ); + } + else if ( CF_OEMTEXT == m_TxtFormatOnClipboard ) + { + fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT ); + aTextSequence = getClipboardData( fetc ); + + // determine the codepage used for text conversion + cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTCODEPAGE ).toInt32( ); + } + else + OSL_ASSERT( false ); + + CStgTransferHelper stgTransferHelper; + + // convert the text + MultiByteToWideCharEx( cpForTxtCnvt, + reinterpret_cast<char*>( aTextSequence.getArray( ) ), + -1, + stgTransferHelper, + false); + + CRawHGlobalPtr ptrHGlob(stgTransferHelper); + sal_Unicode* pWChar = static_cast<sal_Unicode*>(ptrHGlob.GetMemPtr()); + + return OUString(pWChar); +} + +bool CDOTransferable::compareDataFlavors( + const DataFlavor& lhs, const DataFlavor& rhs ) +{ + if ( !m_rXMimeCntFactory.is( ) ) + { + m_rXMimeCntFactory = MimeContentTypeFactory::create( m_xContext ); + } + + bool bRet = false; + + try + { + Reference< XMimeContentType > xLhs( m_rXMimeCntFactory->createMimeContentType( lhs.MimeType ) ); + Reference< XMimeContentType > xRhs( m_rXMimeCntFactory->createMimeContentType( rhs.MimeType ) ); + + if ( cmpFullMediaType( xLhs, xRhs ) ) + { + bRet = cmpAllContentTypeParameter( xLhs, xRhs ); + } + } + catch( IllegalArgumentException& ) + { + OSL_FAIL( "Invalid content type detected" ); + bRet = false; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DOTransferable.hxx b/vcl/win/dtrans/DOTransferable.hxx new file mode 100644 index 0000000000..1824c7b448 --- /dev/null +++ b/vcl/win/dtrans/DOTransferable.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/XTransferable.hpp> + +#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 <mutex> +#include <vector> + +// forward +class CFormatEtc; + +class CDOTransferable : public ::cppu::WeakImplHelper< + css::datatransfer::XTransferable> +{ +public: + typedef css::uno::Sequence< sal_Int8 > ByteSequence_t; + + // XTransferable + + virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override; + + virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override; + + virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override; + + 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(sal_uInt32 cfFormat); + + void tryToGetIDataObjectIfAbsent(); + ByteSequence_t getClipboardData( CFormatEtc& aFormatEtc ); + OUString synthesizeUnicodeText( ); + + LCID getLocaleFromClipboard( ); + + bool compareDataFlavors( const css::datatransfer::DataFlavor& lhs, + 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; + CDataFormatTranslator m_DataFormatTranslator; + css::uno::Reference< css::datatransfer::XMimeContentTypeFactory > m_rXMimeCntFactory; + std::mutex m_aMutex; + bool m_bUnicodeRegistered; + CLIPFORMAT m_TxtFormatOnClipboard; + +// non supported operations +private: + CDOTransferable( const CDOTransferable& ); + CDOTransferable& operator=( const CDOTransferable& ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DTransHelper.cxx b/vcl/win/dtrans/DTransHelper.cxx new file mode 100644 index 0000000000..66d18f9fb5 --- /dev/null +++ b/vcl/win/dtrans/DTransHelper.cxx @@ -0,0 +1,205 @@ +/* -*- 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 <rtl/ustring.h> +#include <osl/diagnose.h> +#include "DTransHelper.hxx" + +// implementation + +CStgTransferHelper::CStgTransferHelper( bool bAutoInit, + HGLOBAL hGlob, + bool bDelStgOnRelease ) : + m_lpStream( nullptr ), + m_bDelStgOnRelease( bDelStgOnRelease ) +{ + if ( bAutoInit ) + init( hGlob, m_bDelStgOnRelease ); +} + +// dtor + +CStgTransferHelper::~CStgTransferHelper( ) +{ + if ( m_lpStream ) + m_lpStream->Release( ); +} + +// TransferData into the + +void CStgTransferHelper::write( const void* lpData, ULONG cb, ULONG* cbWritten ) +{ + HRESULT hr = E_FAIL; + + if ( m_lpStream ) + hr = m_lpStream->Write( lpData, cb, cbWritten ); + + if ( FAILED( hr ) ) + throw CStgTransferException( hr ); + +#if OSL_DEBUG_LEVEL > 0 + HGLOBAL hGlob; + hr = GetHGlobalFromStream( m_lpStream, &hGlob ); + OSL_ASSERT( SUCCEEDED( hr ) ); + + /*DWORD dwSize =*/ GlobalSize( hGlob ); + /*LPVOID lpdbgData =*/ GlobalLock( hGlob ); + GlobalUnlock( hGlob ); +#endif +} + +// read + +void CStgTransferHelper::read( LPVOID pv, ULONG cb, ULONG* pcbRead ) +{ + HRESULT hr = E_FAIL; + + if ( m_lpStream ) + hr = m_lpStream->Read( pv, cb , pcbRead ); + + if ( FAILED( hr ) ) + throw CStgTransferException( hr ); +} + +// GetHGlobal + +HGLOBAL CStgTransferHelper::getHGlobal( ) const +{ + OSL_ASSERT( m_lpStream ); + + HGLOBAL hGlob = nullptr; + + if ( m_lpStream ) + { + HRESULT hr = GetHGlobalFromStream( m_lpStream, &hGlob ); + if ( FAILED( hr ) ) + hGlob = nullptr; + } + + return hGlob; +} + +// getIStream + +void CStgTransferHelper::getIStream( LPSTREAM* ppStream ) +{ + OSL_ASSERT( ppStream ); + *ppStream = m_lpStream; + if ( *ppStream ) + static_cast< LPUNKNOWN >( *ppStream )->AddRef( ); +} + +// Init + +void CStgTransferHelper::init( SIZE_T newSize, + sal_uInt32 uiFlags, + bool bDelStgOnRelease ) +{ + cleanup( ); + + m_bDelStgOnRelease = bDelStgOnRelease; + + HGLOBAL hGlob = GlobalAlloc( uiFlags, newSize ); + if ( nullptr == hGlob ) + throw CStgTransferException( STG_E_MEDIUMFULL ); + + HRESULT hr = CreateStreamOnHGlobal( hGlob, m_bDelStgOnRelease, &m_lpStream ); + if ( FAILED( hr ) ) + { + GlobalFree( hGlob ); + m_lpStream = nullptr; + throw CStgTransferException( hr ); + } + +#if OSL_DEBUG_LEVEL > 0 + STATSTG statstg; + hr = m_lpStream->Stat( &statstg, STATFLAG_DEFAULT ); + OSL_ASSERT( SUCCEEDED( hr ) ); +#endif +} + +// Init + +void CStgTransferHelper::init( HGLOBAL hGlob, + bool bDelStgOnRelease ) +{ + cleanup( ); + + m_bDelStgOnRelease = bDelStgOnRelease; + + HRESULT hr = CreateStreamOnHGlobal( hGlob, m_bDelStgOnRelease, &m_lpStream ); + if ( FAILED( hr ) ) + throw CStgTransferException( hr ); +} + +// free the global memory and invalidate the stream pointer + +void CStgTransferHelper::cleanup( ) +{ + if ( m_lpStream && !m_bDelStgOnRelease ) + { + HGLOBAL hGlob; + GetHGlobalFromStream( m_lpStream, &hGlob ); + GlobalFree( hGlob ); + } + + if ( m_lpStream ) + { + m_lpStream->Release( ); + m_lpStream = nullptr; + } +} + +// return the size of memory we point to + +sal_uInt32 CStgTransferHelper::memSize( CLIPFORMAT cf ) const +{ + DWORD dwSize = 0; + + if ( nullptr != m_lpStream ) + { + HGLOBAL hGlob; + GetHGlobalFromStream( m_lpStream, &hGlob ); + + if ( CF_TEXT == cf || RegisterClipboardFormatW( L"HTML Format" ) == cf ) + { + char* pText = static_cast< char* >( GlobalLock( hGlob ) ); + if ( pText ) + { + dwSize = strlen(pText) + 1; // strlen + trailing '\0' + GlobalUnlock( hGlob ); + } + } + else if ( CF_UNICODETEXT == cf ) + { + sal_Unicode* pText = static_cast< sal_Unicode* >( GlobalLock( hGlob ) ); + if ( pText ) + { + dwSize = rtl_ustr_getLength( pText ) * sizeof( sal_Unicode ); + GlobalUnlock( hGlob ); + } + } + else + dwSize = GlobalSize( hGlob ); + } + + return dwSize; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DTransHelper.hxx b/vcl/win/dtrans/DTransHelper.hxx new file mode 100644 index 0000000000..d8ab3349cb --- /dev/null +++ b/vcl/win/dtrans/DTransHelper.hxx @@ -0,0 +1,167 @@ +/* -*- 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 . + */ + +#pragma once + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> +#include "WinClip.hxx" + +#define AUTO_INIT true + +// a helper class to manage a global memory area, the clients can write +// into the global memory area and extract the handle to the global mem +// note: not thread-safe + +class CStgTransferHelper +{ +public: + // will be thrown in case of failures + class CStgTransferException + { + public: + HRESULT m_hr; + explicit CStgTransferException( HRESULT hr ) : m_hr( hr ) {}; + }; + +public: + CStgTransferHelper( + bool bAutoInit = false, + HGLOBAL hGlob = nullptr, + bool bDelStgOnRelease = false ); + + ~CStgTransferHelper( ); + + void write( const void* lpData, ULONG cb, ULONG* cbWritten = nullptr ); + void read( LPVOID pv, ULONG cb, ULONG* pcbRead = nullptr ); + + HGLOBAL getHGlobal( ) const; + void getIStream( LPSTREAM* ppStream ); + + void init( + SIZE_T newSize, + sal_uInt32 uiFlags = GHND, + bool bDelStgOnRelease = false ); + + void init( + HGLOBAL hGlob, + bool bDelStgOnRelease = false ); + + // returns the size of the managed memory + sal_uInt32 memSize( CLIPFORMAT cf = CF_INVALID ) const; + + // free the global memory and necessary + // release the internal stream pointer + void cleanup( ); + +private: + LPSTREAM m_lpStream; + bool m_bDelStgOnRelease; + +private: + CStgTransferHelper( const CStgTransferHelper& ); + CStgTransferHelper& operator=( const CStgTransferHelper& ); +}; + +// something like an auto-pointer - allows access to the memory belonging +// to a HGLOBAL and automatically unlocks a global memory at destruction +// time + +class CRawHGlobalPtr +{ +public: + + // ctor + + explicit CRawHGlobalPtr( HGLOBAL hGlob ) : + m_hGlob( hGlob ), + m_bIsLocked( false ), + m_pGlobMem( nullptr ) + { + } + + // ctor + + explicit CRawHGlobalPtr( const CStgTransferHelper& theHGlobalHelper ) : + m_hGlob( theHGlobalHelper.getHGlobal( ) ), + m_bIsLocked( false ), + m_pGlobMem( nullptr ) + { + } + + // dtor + + ~CRawHGlobalPtr( ) + { + if ( m_bIsLocked ) + GlobalUnlock( m_hGlob ); + } + + // lock the global memory (initializes a + // pointer to this memory) + + BOOL Lock( ) + { + if ( !m_bIsLocked && ( nullptr != m_hGlob ) ) + { + m_pGlobMem = GlobalLock( m_hGlob ); + m_bIsLocked = ( nullptr != m_pGlobMem ); + } + + return m_bIsLocked; + } + + // unlock the global memory (invalidates the + // pointer to this memory) + + BOOL Unlock( ) + { + GlobalUnlock( m_hGlob ); + m_bIsLocked = false; + m_pGlobMem = nullptr; + + return ( NO_ERROR == GetLastError( ) ); + } + + // locks the global memory and returns a + // pointer to this memory + + LPVOID GetMemPtr( ) + { + Lock( ); + return m_pGlobMem; + } + + // size of mem we point to + + int MemSize( ) const + { + return GlobalSize( m_hGlob ); + } + +private: + HGLOBAL m_hGlob; + bool m_bIsLocked; + LPVOID m_pGlobMem; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DataFmtTransl.cxx b/vcl/win/dtrans/DataFmtTransl.cxx new file mode 100644 index 0000000000..5daf0b453f --- /dev/null +++ b/vcl/win/dtrans/DataFmtTransl.cxx @@ -0,0 +1,252 @@ +/* -*- 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 "DataFmtTransl.hxx" +#include <rtl/string.hxx> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> +#include "ImplHelper.hxx" +#include "WinClip.hxx" +#include "MimeAttrib.hxx" +#include "DTransHelper.hxx" +#include <rtl/string.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include "Fetc.hxx" +#include <com/sun/star/datatransfer/DataFormatTranslator.hpp> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <shlobj.h> + +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::lang; + +const Type CPPUTYPE_SALINT32 = cppu::UnoType<sal_Int32>::get(); +const Type CPPUTYPE_SALINT8 = cppu::UnoType<sal_Int8>::get(); +const Type CPPUTYPE_OUSTRING = cppu::UnoType<OUString>::get(); +const Type CPPUTYPE_SEQSALINT8 = cppu::UnoType<Sequence< sal_Int8>>::get(); +const sal_Int32 MAX_CLIPFORMAT_NAME = 256; + +CDataFormatTranslator::CDataFormatTranslator( const Reference< XComponentContext >& rxContext ) +{ + m_XDataFormatTranslator = DataFormatTranslator::create( rxContext ); +} + +CFormatEtc CDataFormatTranslator::getFormatEtcFromDataFlavor( const DataFlavor& aDataFlavor ) const +{ + sal_Int32 cf = CF_INVALID; + + try + { + if( m_XDataFormatTranslator.is( ) ) + { + Any aFormat = m_XDataFormatTranslator->getSystemDataTypeFromDataFlavor( aDataFlavor ); + + if ( aFormat.hasValue( ) ) + { + if ( aFormat.getValueType( ) == CPPUTYPE_SALINT32 ) + { + aFormat >>= cf; + OSL_ENSURE( CF_INVALID != cf, "Invalid Clipboard format delivered" ); + } + else if ( aFormat.getValueType( ) == CPPUTYPE_OUSTRING ) + { + OUString aClipFmtName; + aFormat >>= aClipFmtName; + + OSL_ASSERT( aClipFmtName.getLength( ) ); + cf = RegisterClipboardFormatW( o3tl::toW(aClipFmtName.getStr( )) ); + + OSL_ENSURE( CF_INVALID != cf, "RegisterClipboardFormat failed" ); + } + else + OSL_FAIL( "Wrong Any-Type detected" ); + } + } + } + catch( ... ) + { + OSL_FAIL( "Unexpected error" ); + } + + return sal::static_int_cast<CFormatEtc>(getFormatEtcForClipformat( sal::static_int_cast<CLIPFORMAT>(cf) )); +} + +DataFlavor CDataFormatTranslator::getDataFlavorFromFormatEtc(sal_uInt32 cfFormat, LCID lcid) const +{ + DataFlavor aFlavor; + + try + { + CLIPFORMAT aClipformat = cfFormat; + + Any aAny; + aAny <<= static_cast< sal_Int32 >( aClipformat ); + + if ( isOemOrAnsiTextFormat( aClipformat ) ) + { + aFlavor.MimeType = "text/plain;charset="; + aFlavor.MimeType += getTextCharsetFromLCID( lcid, aClipformat ); + + aFlavor.HumanPresentableName = "OEM/ANSI Text"; + aFlavor.DataType = CPPUTYPE_SEQSALINT8; + } + else if ( CF_INVALID != aClipformat ) + { + if ( m_XDataFormatTranslator.is( ) ) + { + aFlavor = m_XDataFormatTranslator->getDataFlavorFromSystemDataType( aAny ); + + if ( !aFlavor.MimeType.getLength( ) ) + { + // lookup of DataFlavor from clipboard format id + // failed, so we try to resolve via clipboard + // format name + OUString clipFormatName = getClipboardFormatName( aClipformat ); + + // if we could not get a clipboard format name an + // error must have occurred or it is a standard + // clipboard format that we don't translate, e.g. + // CF_BITMAP (the office only uses CF_DIB) + if ( clipFormatName.getLength( ) ) + { + aAny <<= clipFormatName; + aFlavor = m_XDataFormatTranslator->getDataFlavorFromSystemDataType( aAny ); + } + } + } + } + } + catch( ... ) + { + OSL_FAIL( "Unexpected error" ); + } + + return aFlavor; +} + +CFormatEtc CDataFormatTranslator::getFormatEtcForClipformatName( const OUString& aClipFmtName ) +{ + // check parameter + if ( !aClipFmtName.getLength( ) ) + return CFormatEtc( CF_INVALID ); + + CLIPFORMAT cf = sal::static_int_cast<CLIPFORMAT>(RegisterClipboardFormatW( o3tl::toW(aClipFmtName.getStr( )) )); + return getFormatEtcForClipformat( cf ); +} + +OUString CDataFormatTranslator::getClipboardFormatName( CLIPFORMAT aClipformat ) +{ + OSL_PRECOND( CF_INVALID != aClipformat, "Invalid clipboard format" ); + + sal_Unicode wBuff[ MAX_CLIPFORMAT_NAME + 1 ]; // Null terminator isn't counted, apparently. + sal_Int32 nLen = GetClipboardFormatNameW( aClipformat, o3tl::toW(wBuff), MAX_CLIPFORMAT_NAME ); + + return OUString( wBuff, nLen ); +} + +CFormatEtc CDataFormatTranslator::getFormatEtcForClipformat( CLIPFORMAT cf ) +{ + CFormatEtc fetc( cf, TYMED_NULL, nullptr, DVASPECT_CONTENT ); + + switch( cf ) + { + case CF_METAFILEPICT: + fetc.setTymed( TYMED_MFPICT ); + break; + + case CF_ENHMETAFILE: + fetc.setTymed( TYMED_ENHMF ); + break; + + default: + fetc.setTymed( TYMED_HGLOBAL /*| TYMED_ISTREAM*/ ); + } + + /* + hack: in order to paste urls copied by Internet Explorer + with "copy link" we set the lindex member to 0 + but if we really want to support CFSTR_FILECONTENT and + the accompany format CFSTR_FILEDESCRIPTOR (FileGroupDescriptor) + the client of the clipboard service has to provide a id + of which FileContents it wants to paste + see MSDN: "Handling Shell Data Transfer Scenarios" + */ + if ( cf == RegisterClipboardFormat( CFSTR_FILECONTENTS ) ) + fetc.setLindex( 0 ); + + return fetc; +} + +bool CDataFormatTranslator::isOemOrAnsiTextFormat( CLIPFORMAT cf ) +{ + return ( (cf == CF_TEXT) || (cf == CF_OEMTEXT) ); +} + +bool CDataFormatTranslator::isUnicodeTextFormat( CLIPFORMAT cf ) +{ + return ( cf == CF_UNICODETEXT ); +} + +bool CDataFormatTranslator::isTextFormat( CLIPFORMAT cf ) +{ + return ( isOemOrAnsiTextFormat( cf ) || isUnicodeTextFormat( cf ) ); +} + +bool CDataFormatTranslator::isHTMLFormat( CLIPFORMAT cf ) +{ + OUString clipFormatName = getClipboardFormatName( cf ); + return ( clipFormatName == "HTML Format" ); +} + +bool CDataFormatTranslator::isTextHtmlFormat( CLIPFORMAT cf ) +{ + OUString clipFormatName = getClipboardFormatName( cf ); + return clipFormatName.equalsIgnoreAsciiCase( "HTML (HyperText Markup Language)" ); +} + +OUString CDataFormatTranslator::getTextCharsetFromLCID( LCID lcid, CLIPFORMAT aClipformat ) +{ + OSL_ASSERT( isOemOrAnsiTextFormat( aClipformat ) ); + + OUString charset; + if ( CF_TEXT == aClipformat ) + { + charset = getMimeCharsetFromLocaleId( + lcid, + LOCALE_IDEFAULTANSICODEPAGE, + PRE_WINDOWS_CODEPAGE ); + } + else if ( CF_OEMTEXT == aClipformat ) + { + charset = getMimeCharsetFromLocaleId( + lcid, + LOCALE_IDEFAULTCODEPAGE, + PRE_OEM_CODEPAGE ); + } + else // CF_UNICODE + OSL_ASSERT( false ); + + return charset; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DataFmtTransl.hxx b/vcl/win/dtrans/DataFmtTransl.hxx new file mode 100644 index 0000000000..e003f2538b --- /dev/null +++ b/vcl/win/dtrans/DataFmtTransl.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/XDataFormatTranslator.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <sal/types.h> +#include <rtl/ustring.hxx> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> + +// declaration + +class CFormatEtc; + +class CDataFormatTranslator +{ +public: + explicit CDataFormatTranslator( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + CFormatEtc getFormatEtcFromDataFlavor( const css::datatransfer::DataFlavor& aDataFlavor ) const; + css::datatransfer::DataFlavor getDataFlavorFromFormatEtc( + sal_uInt32 cfFormat, LCID lcid = GetThreadLocale()) const; + + static CFormatEtc getFormatEtcForClipformat( CLIPFORMAT cf ); + static CFormatEtc getFormatEtcForClipformatName( const OUString& aClipFmtName ); + static OUString getClipboardFormatName( CLIPFORMAT aClipformat ); + + static bool isHTMLFormat( CLIPFORMAT cf ); + static bool isTextHtmlFormat( CLIPFORMAT cf ); + static bool isOemOrAnsiTextFormat( CLIPFORMAT cf ); + static bool isUnicodeTextFormat( CLIPFORMAT cf ); + static bool isTextFormat( CLIPFORMAT cf ); + +private: + static OUString getTextCharsetFromLCID( LCID lcid, CLIPFORMAT aClipformat ); + +private: + css::uno::Reference< css::datatransfer::XDataFormatTranslator > m_XDataFormatTranslator; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DtObjFactory.cxx b/vcl/win/dtrans/DtObjFactory.cxx new file mode 100644 index 0000000000..8fe03789b1 --- /dev/null +++ b/vcl/win/dtrans/DtObjFactory.cxx @@ -0,0 +1,34 @@ +/* -*- 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 "DtObjFactory.hxx" +#include "XTDataObject.hxx" + +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::lang; + +IDataObjectPtr +CDTransObjFactory::createDataObjFromTransferable(const Reference<XComponentContext>& rxContext, + const Reference<XTransferable>& refXTransferable) +{ + return (IDataObjectPtr(new CXTDataObject(rxContext, refXTransferable))); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/DtObjFactory.hxx b/vcl/win/dtrans/DtObjFactory.hxx new file mode 100644 index 0000000000..b5d5f50075 --- /dev/null +++ b/vcl/win/dtrans/DtObjFactory.hxx @@ -0,0 +1,33 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <systools/win32/comtools.hxx> + +namespace CDTransObjFactory +{ + IDataObjectPtr createDataObjFromTransferable( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::datatransfer::XTransferable >& refXTransferable ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/Fetc.cxx b/vcl/win/dtrans/Fetc.cxx new file mode 100644 index 0000000000..9bcb2f609f --- /dev/null +++ b/vcl/win/dtrans/Fetc.cxx @@ -0,0 +1,166 @@ +/* -*- 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 <osl/diagnose.h> +#include "Fetc.hxx" +#include "ImplHelper.hxx" + +CFormatEtc::CFormatEtc( ) +{ + m_FormatEtc.cfFormat = 0; + m_FormatEtc.ptd = nullptr; + m_FormatEtc.dwAspect = 0; + m_FormatEtc.lindex = -1; + m_FormatEtc.tymed = TYMED_NULL; +} + +// transfer of ownership + +CFormatEtc::CFormatEtc( const FORMATETC& aFormatEtc ) +{ + CopyFormatEtc( &m_FormatEtc, &const_cast< FORMATETC& >( aFormatEtc ) ); +} + +CFormatEtc::~CFormatEtc( ) +{ + DeleteTargetDevice( m_FormatEtc.ptd ); +} + +CFormatEtc::CFormatEtc( CLIPFORMAT cf, DWORD tymed, DVTARGETDEVICE* ptd, DWORD dwAspect, LONG lindex ) +{ + m_FormatEtc.cfFormat = cf; + m_FormatEtc.ptd = CopyTargetDevice( ptd ); + m_FormatEtc.dwAspect = dwAspect; + m_FormatEtc.lindex = lindex; + m_FormatEtc.tymed = tymed; +} + +CFormatEtc::CFormatEtc( const CFormatEtc& theOther ) +{ + m_FormatEtc.cfFormat = theOther.m_FormatEtc.cfFormat; + m_FormatEtc.ptd = CopyTargetDevice( theOther.m_FormatEtc.ptd ); + m_FormatEtc.dwAspect = theOther.m_FormatEtc.dwAspect; + m_FormatEtc.lindex = theOther.m_FormatEtc.lindex; + m_FormatEtc.tymed = theOther.m_FormatEtc.tymed; +} + +CFormatEtc& CFormatEtc::operator=( const CFormatEtc& theOther ) +{ + if ( this != &theOther ) + { + DeleteTargetDevice( m_FormatEtc.ptd ); + + m_FormatEtc.cfFormat = theOther.m_FormatEtc.cfFormat; + m_FormatEtc.ptd = CopyTargetDevice( theOther.m_FormatEtc.ptd ); + m_FormatEtc.dwAspect = theOther.m_FormatEtc.dwAspect; + m_FormatEtc.lindex = theOther.m_FormatEtc.lindex; + m_FormatEtc.tymed = theOther.m_FormatEtc.tymed; + } + + return *this; +} + +CFormatEtc::operator FORMATETC*( ) +{ + return &m_FormatEtc; +} + +CFormatEtc::operator FORMATETC( ) +{ + return m_FormatEtc; +} + +void CFormatEtc::getFORMATETC( LPFORMATETC lpFormatEtc ) +{ + OSL_ASSERT( lpFormatEtc ); + OSL_ASSERT( !IsBadWritePtr( lpFormatEtc, sizeof( FORMATETC ) ) ); + + CopyFormatEtc( lpFormatEtc, &m_FormatEtc ); +} + +CLIPFORMAT CFormatEtc::getClipformat( ) const +{ + return m_FormatEtc.cfFormat; +} + +DWORD CFormatEtc::getTymed( ) const +{ + return m_FormatEtc.tymed; +} + +void CFormatEtc::getTargetDevice( DVTARGETDEVICE** lpDvTargetDevice ) const +{ + OSL_ASSERT( lpDvTargetDevice ); + OSL_ASSERT( !IsBadWritePtr( lpDvTargetDevice, sizeof( DVTARGETDEVICE ) ) ); + + *lpDvTargetDevice = nullptr; + + if ( m_FormatEtc.ptd ) + *lpDvTargetDevice = CopyTargetDevice( m_FormatEtc.ptd ); +} + +DWORD CFormatEtc::getDvAspect( ) const +{ + return m_FormatEtc.dwAspect; +} + +LONG CFormatEtc::getLindex( ) const +{ + return m_FormatEtc.lindex; +} + +void CFormatEtc::setClipformat( CLIPFORMAT cf ) +{ + m_FormatEtc.cfFormat = cf; +} + +void CFormatEtc::setTymed( DWORD tymed ) +{ + m_FormatEtc.tymed = tymed; +} + +// transfer of ownership! + +void CFormatEtc::setTargetDevice( DVTARGETDEVICE* ptd ) +{ + DeleteTargetDevice( m_FormatEtc.ptd ); + m_FormatEtc.ptd = ptd; +} + +void CFormatEtc::setDvAspect( DWORD dwAspect ) +{ + m_FormatEtc.dwAspect = dwAspect; +} + +void CFormatEtc::setLindex( LONG lindex ) +{ + m_FormatEtc.lindex = lindex; +} + +bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs ) +{ + return CompareFormatEtc( &lhs.m_FormatEtc, &rhs.m_FormatEtc ); +} + +bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs ) +{ + return !( lhs == rhs ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/Fetc.hxx b/vcl/win/dtrans/Fetc.hxx new file mode 100644 index 0000000000..e0880acd64 --- /dev/null +++ b/vcl/win/dtrans/Fetc.hxx @@ -0,0 +1,76 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> + +/********************************************************************** + stl container elements must fulfill the following requirements: + 1. they need a copy ctor and assignment operator(?) + 2. they must be comparable + because the FORMATETC structure has a pointer to a TARGETDEVICE + structure we need a simple wrapper class to fulfill these needs +***********************************************************************/ + +class CFormatEtc +{ +public: + CFormatEtc( ); + explicit CFormatEtc( const FORMATETC& aFormatEtc ); + CFormatEtc( CLIPFORMAT cf, DWORD tymed = TYMED_HGLOBAL, DVTARGETDEVICE* ptd = nullptr, DWORD dwAspect = DVASPECT_CONTENT, LONG lindex = -1 ); + CFormatEtc( const CFormatEtc& theOther ); + + ~CFormatEtc( ); + + CFormatEtc& operator=( const CFormatEtc& theOther ); + operator FORMATETC*( ); + operator FORMATETC( ); + + void getFORMATETC( LPFORMATETC lpFormatEtc ); + + CLIPFORMAT getClipformat( ) const; + DWORD getTymed( ) const; + void getTargetDevice( DVTARGETDEVICE** ptd ) const; + DWORD getDvAspect( ) const; + LONG getLindex( ) const; + + void setClipformat( CLIPFORMAT cf ); + void setTymed( DWORD tymed ); + void setTargetDevice( DVTARGETDEVICE* ptd ); + void setDvAspect( DWORD dwAspect ); + void setLindex( LONG lindex ); + +private: + FORMATETC m_FormatEtc; + + friend bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs ); + friend bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs ); +}; + +bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs ); +bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/FetcList.cxx b/vcl/win/dtrans/FetcList.cxx new file mode 100644 index 0000000000..5ace3cdca6 --- /dev/null +++ b/vcl/win/dtrans/FetcList.cxx @@ -0,0 +1,340 @@ +/* -*- 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 <osl/diagnose.h> +#include "FetcList.hxx" +#include "Fetc.hxx" +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp> +#include <com/sun/star/datatransfer/XMimeContentType.hpp> + +#include "DataFmtTransl.hxx" +#include "ImplHelper.hxx" +#include "WinClip.hxx" + +#include <algorithm> + +#include "MimeAttrib.hxx" + +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; + +LCID CFormatRegistrar::m_TxtLocale = 0; +sal_uInt32 CFormatRegistrar::m_TxtCodePage = GetACP( ); + +CFormatEtcContainer::CFormatEtcContainer( ) +{ + m_EnumIterator = m_FormatMap.begin( ); +} + +void CFormatEtcContainer::addFormatEtc( const CFormatEtc& fetc ) +{ + m_FormatMap.push_back( fetc ); +} + +void CFormatEtcContainer::removeFormatEtc( const CFormatEtc& fetc ) +{ + FormatEtcMap_t::iterator iter = + find( m_FormatMap.begin(), m_FormatMap.end(), fetc ); + + if ( iter != m_FormatMap.end( ) ) + m_FormatMap.erase( iter ); +} + +void CFormatEtcContainer::removeAllFormatEtc( ) +{ + m_FormatMap.clear( ); +} + +bool CFormatEtcContainer::hasFormatEtc( const CFormatEtc& fetc ) const +{ + FormatEtcMap_t::const_iterator iter = + find( m_FormatMap.begin(), m_FormatMap.end(), fetc ); + + return ( iter != m_FormatMap.end( ) ); +} + +bool CFormatEtcContainer::hasElements( ) const +{ + return !m_FormatMap.empty(); +} + +void CFormatEtcContainer::beginEnumFormatEtc( ) +{ + m_EnumIterator = m_FormatMap.begin( ); +} + +sal_uInt32 CFormatEtcContainer::nextFormatEtc( LPFORMATETC lpFetc, + sal_uInt32 aNum ) +{ + OSL_ASSERT( lpFetc ); + OSL_ASSERT( !IsBadWritePtr( lpFetc, sizeof( FORMATETC ) * aNum ) ); + + sal_uInt32 nFetched = 0; + + for ( sal_uInt32 i = 0; i < aNum; i++, nFetched++, lpFetc++, ++m_EnumIterator ) + { + if ( m_EnumIterator == m_FormatMap.end() ) + break; + CopyFormatEtc( lpFetc, *m_EnumIterator ); + } + + return nFetched; +} + +bool CFormatEtcContainer::skipFormatEtc( sal_uInt32 aNum ) +{ + FormatEtcMap_t::const_iterator iter_end = m_FormatMap.end( ); + for ( sal_uInt32 i = 0; + (i < aNum) && (m_EnumIterator != iter_end); + i++, ++m_EnumIterator ) + ;/* intentionally left empty */ + + return ( m_EnumIterator != m_FormatMap.end( ) ); +} + +CFormatRegistrar::CFormatRegistrar( const Reference< XComponentContext >& rxContext, + const CDataFormatTranslator& aDataFormatTranslator ) : + m_DataFormatTranslator( aDataFormatTranslator ), + m_bHasSynthesizedLocale( false ), + m_xContext( rxContext ) +{ +} + +// this function converts all DataFlavors of the given FlavorList into +// an appropriate FORMATETC structure, for some formats like unicodetext, +// text and text/html we will offer an accompany format e.g.: +// +// DataFlavor | Registered Clipformat | Registered accompany clipformat +// -------------------------|---------------------------|----------------------------------- +// text/plain;charset=ansi | CF_TEXT | CF_UNICODETEXT +// | | CF_LOCALE (if charset != GetACP() +// | | +// text/plain;charset=oem | CF_OEMTEXT | CF_UNICODETEXT +// | | CF_LOCALE (if charset != GetOEMCP() +// | | +// text/plain;charset=utf-16| CF_UNICODETEXT | CF_TEXT +// | | +// text/html | HTML (Hypertext ...) | HTML Format +// | | +// +// if some tries to register different text formats with different charsets the last +// registered wins and the others are ignored + +void CFormatRegistrar::RegisterFormats( + const Reference< XTransferable >& aXTransferable, CFormatEtcContainer& aFormatEtcContainer ) +{ + Sequence< DataFlavor > aFlavorList = aXTransferable->getTransferDataFlavors( ); + sal_Int32 nFlavors = aFlavorList.getLength( ); + bool bUnicodeRegistered = false; + DataFlavor aFlavor; + + for( sal_Int32 i = 0; i < nFlavors; i++ ) + { + aFlavor = aFlavorList[i]; + CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor ); + + // maybe an internal format so we ignore it + if ( CF_INVALID == fetc.getClipformat( ) ) + continue; + + if ( !needsToSynthesizeAccompanyFormats( fetc ) ) + aFormatEtcContainer.addFormatEtc( fetc ); + else + { + // if we haven't registered any text format up to now + if ( CDataFormatTranslator::isTextFormat( fetc.getClipformat() ) && !bUnicodeRegistered ) + { + // if the transferable supports unicode text we ignore + // any further text format the transferable offers + // because we can create it from Unicode text in addition + // we register CF_TEXT for non unicode clients + if ( CDataFormatTranslator::isUnicodeTextFormat( fetc.getClipformat() ) ) + { + aFormatEtcContainer.addFormatEtc( fetc ); // add CF_UNICODE + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT ) ); // add CF_TEXT + bUnicodeRegistered = true; + } + else if ( !hasUnicodeFlavor( aXTransferable ) ) + { + // we try to investigate the charset and make a valid + // windows codepage from this charset the default + // return value is the result of GetACP( ) + OUString charset = getCharsetFromDataFlavor( aFlavor ); + sal_uInt32 txtCP = getWinCPFromMimeCharset( charset ); + + // we try to get a Locale appropriate for this codepage + if ( findLocaleForTextCodePage( ) ) + { + m_TxtCodePage = txtCP; + + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT ) ); + + if ( !IsOEMCP( m_TxtCodePage ) ) + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT ) ); + else + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT ) ); + + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE ) ); + + // we save the flavor so it's easier when + // queried for it in XTDataObject::GetData(...) + m_RegisteredTextFlavor = aFlavor; + m_bHasSynthesizedLocale = true; + } + } + } + else if ( CDataFormatTranslator::isTextHtmlFormat( fetc.getClipformat( ) ) ) // Html (Hyper Text...) + { + // we add text/html ( HTML (HyperText Markup Language) ) + aFormatEtcContainer.addFormatEtc( fetc ); + + // and HTML Format + aFormatEtcContainer.addFormatEtc( + CDataFormatTranslator::getFormatEtcForClipformatName( "HTML Format" ) ); + } + } + } +} + +bool CFormatRegistrar::hasSynthesizedLocale( ) const +{ + return m_bHasSynthesizedLocale; +} + +LCID CFormatRegistrar::getSynthesizedLocale( ) +{ + return m_TxtLocale; +} + +sal_uInt32 CFormatRegistrar::getRegisteredTextCodePage( ) +{ + return m_TxtCodePage; +} + +DataFlavor CFormatRegistrar::getRegisteredTextFlavor( ) const +{ + return m_RegisteredTextFlavor; +} + +bool CFormatRegistrar::isSynthesizeableFormat( const CFormatEtc& aFormatEtc ) +{ + return ( CDataFormatTranslator::isOemOrAnsiTextFormat( aFormatEtc.getClipformat() ) || + CDataFormatTranslator::isUnicodeTextFormat( aFormatEtc.getClipformat() ) || + CDataFormatTranslator::isHTMLFormat( aFormatEtc.getClipformat() ) ); +} + +inline +bool CFormatRegistrar::needsToSynthesizeAccompanyFormats( const CFormatEtc& aFormatEtc ) +{ + return ( CDataFormatTranslator::isOemOrAnsiTextFormat( aFormatEtc.getClipformat() ) || + CDataFormatTranslator::isUnicodeTextFormat( aFormatEtc.getClipformat() ) || + CDataFormatTranslator::isTextHtmlFormat( aFormatEtc.getClipformat( ) ) ); +} + +OUString CFormatRegistrar::getCharsetFromDataFlavor( const DataFlavor& aFlavor ) +{ + OUString charset; + + try + { + Reference< XMimeContentTypeFactory > xMimeFac = + MimeContentTypeFactory::create(m_xContext); + + Reference< XMimeContentType > xMimeType( xMimeFac->createMimeContentType( aFlavor.MimeType ) ); + if ( xMimeType->hasParameter( TEXTPLAIN_PARAM_CHARSET ) ) + charset = xMimeType->getParameterValue( TEXTPLAIN_PARAM_CHARSET ); + else + charset = getMimeCharsetFromWinCP( GetACP( ), PRE_WINDOWS_CODEPAGE ); + } + catch(NoSuchElementException&) + { + OSL_FAIL( "Unexpected" ); + } + catch(...) + { + OSL_FAIL( "Invalid data flavor" ); + } + + return charset; +} + +bool CFormatRegistrar::hasUnicodeFlavor( const Reference< XTransferable >& aXTransferable ) const +{ + DataFlavor aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(CF_UNICODETEXT); + + return aXTransferable->isDataFlavorSupported( aFlavor ); +} + +bool CFormatRegistrar::findLocaleForTextCodePage( ) +{ + m_TxtLocale = 0; + EnumSystemLocalesA( CFormatRegistrar::EnumLocalesProc, LCID_INSTALLED ); + return IsValidLocale( m_TxtLocale, LCID_INSTALLED ); +} + +bool CFormatRegistrar::isLocaleCodePage( LCID lcid, LCTYPE lctype, sal_uInt32 codepage ) +{ + char buff[6]; + sal_uInt32 localeCodePage; + + OSL_ASSERT( IsValidLocale( lcid, LCID_INSTALLED ) ); + + // get the ansi codepage of the current locale + GetLocaleInfoA( lcid, lctype, buff, sizeof( buff ) ); + localeCodePage = atol( buff ); + + return ( localeCodePage == codepage ); +} + +inline +bool CFormatRegistrar::isLocaleOemCodePage( LCID lcid, sal_uInt32 codepage ) +{ + return isLocaleCodePage( lcid, LOCALE_IDEFAULTCODEPAGE, codepage ); +} + +inline +bool CFormatRegistrar::isLocaleAnsiCodePage( LCID lcid, sal_uInt32 codepage ) +{ + return isLocaleCodePage( lcid, LOCALE_IDEFAULTANSICODEPAGE, codepage ); +} + +BOOL CALLBACK CFormatRegistrar::EnumLocalesProc( LPSTR lpLocaleStr ) +{ + // the lpLocaleStr parameter is hexadecimal + LCID lcid = strtol( lpLocaleStr, nullptr, 16 ); + + if ( isLocaleAnsiCodePage( lcid, CFormatRegistrar::m_TxtCodePage ) || + isLocaleOemCodePage( lcid, CFormatRegistrar::m_TxtCodePage ) ) + { + CFormatRegistrar::m_TxtLocale = lcid; + return false; // stop enumerating + } + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/FetcList.hxx b/vcl/win/dtrans/FetcList.hxx new file mode 100644 index 0000000000..2df28ff575 --- /dev/null +++ b/vcl/win/dtrans/FetcList.hxx @@ -0,0 +1,139 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include "Fetc.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <vector> + +/***************************************************************** + a simple container for FORMATECT structures + instances of this class are not thread-safe +*****************************************************************/ + +class CFormatEtcContainer +{ +public: + CFormatEtcContainer( ); + + // duplicates not allowed + void addFormatEtc( const CFormatEtc& fetc ); + + // removes the specified formatetc + void removeFormatEtc( const CFormatEtc& fetc ); + + // removes the formatetc at pos + void removeAllFormatEtc( ); + + bool hasFormatEtc( const CFormatEtc& fetc ) const; + + bool hasElements( ) const; + + // begin enumeration + void beginEnumFormatEtc( ); + + // copies the specified number of formatetc structures starting + // at the current enum position + // the return value is the number of copied elements; if the + // current enum position is at the end the return value is 0 + sal_uInt32 nextFormatEtc( LPFORMATETC lpFetc, sal_uInt32 aNum = 1 ); + + // skips the specified number of elements in the container + bool skipFormatEtc( sal_uInt32 aNum ); + +protected: + typedef std::vector< CFormatEtc > FormatEtcMap_t; + +private: + FormatEtcMap_t m_FormatMap; + FormatEtcMap_t::iterator m_EnumIterator; +}; + +/***************************************************************** + a helper class which converts data flavors to clipformats, + creates an appropriate formatetc structures and if possible + synthesizes clipboard formats if necessary, e.g. if text + is provided a locale will also be provided; + the class registers the formatetc within a CFormatEtcContainer + + instances of this class are not thread-safe and multiple + instances of this class would use the same static variables + that's why this class should not be used by multiple threads, + only one thread of a process should use it +*****************************************************************/ + +// forward +class CDataFormatTranslator; + +class CFormatRegistrar +{ +public: + CFormatRegistrar( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const CDataFormatTranslator& aDataFormatTranslator ); + + void RegisterFormats( const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable, + CFormatEtcContainer& aFormatEtcContainer ); + + bool hasSynthesizedLocale( ) const; + static LCID getSynthesizedLocale( ); + static sal_uInt32 getRegisteredTextCodePage( ); + css::datatransfer::DataFlavor getRegisteredTextFlavor( ) const; + + static bool isSynthesizeableFormat( const CFormatEtc& aFormatEtc ); + static bool needsToSynthesizeAccompanyFormats( const CFormatEtc& aFormatEtc ); + +private: + OUString getCharsetFromDataFlavor( const css::datatransfer::DataFlavor& aFlavor ); + + bool hasUnicodeFlavor( + const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable ) const; + + static bool findLocaleForTextCodePage( ); + + static bool isLocaleOemCodePage( LCID lcid, sal_uInt32 codepage ); + static bool isLocaleAnsiCodePage( LCID lcid, sal_uInt32 codepage ); + static bool isLocaleCodePage( LCID lcid, LCTYPE lctype, sal_uInt32 codepage ); + + static BOOL CALLBACK EnumLocalesProc( LPSTR lpLocaleStr ); + +private: + const CDataFormatTranslator& m_DataFormatTranslator; + bool m_bHasSynthesizedLocale; + css::datatransfer::DataFlavor m_RegisteredTextFlavor; + + const css::uno::Reference< css::uno::XComponentContext > m_xContext; + + static LCID m_TxtLocale; + static sal_uInt32 m_TxtCodePage; + +private: + CFormatRegistrar( const CFormatRegistrar& ); + CFormatRegistrar& operator=( const CFormatRegistrar& ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/FmtFilter.cxx b/vcl/win/dtrans/FmtFilter.cxx new file mode 100644 index 0000000000..27d051f7e8 --- /dev/null +++ b/vcl/win/dtrans/FmtFilter.cxx @@ -0,0 +1,435 @@ +/* -*- 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 <string.h> + +#include "FmtFilter.hxx" + +#include <o3tl/safeint.hxx> +#include <o3tl/unit_conversion.hxx> +#include <osl/diagnose.h> + +#include <shobjidl.h> +#include <shlguid.h> +#include <objidl.h> +#include <shellapi.h> + +#include <string> +#include <sstream> +#include <vector> +#include <iomanip> + +#include <systools/win32/comtools.hxx> + +using namespace com::sun::star::uno; + +namespace { + +#pragma pack(2) +struct METAFILEHEADER +{ + DWORD key; + short hmf; + SMALL_RECT bbox; + WORD inch; + DWORD reserved; + WORD checksum; +}; +#pragma pack() + +} + +// convert a windows metafile picture to a LibreOffice metafile picture + +Sequence< sal_Int8 > WinMFPictToOOMFPict( Sequence< sal_Int8 >& aMetaFilePict ) +{ + OSL_ASSERT( aMetaFilePict.getLength( ) == sizeof( METAFILEPICT ) ); + + Sequence< sal_Int8 > mfpictStream; + METAFILEPICT* pMFPict = reinterpret_cast< METAFILEPICT* >( aMetaFilePict.getArray( ) ); + HMETAFILE hMf = pMFPict->hMF; + sal_uInt32 nCount = GetMetaFileBitsEx( hMf, 0, nullptr ); + + if ( nCount > 0 ) + { + mfpictStream.realloc( nCount + sizeof( METAFILEHEADER ) ); + + METAFILEHEADER* pMFHeader = reinterpret_cast< METAFILEHEADER* >( mfpictStream.getArray( ) ); + SMALL_RECT aRect = { 0, + 0, + static_cast< short >( pMFPict->xExt ), + static_cast< short >( pMFPict->yExt ) }; + USHORT nInch; + + switch( pMFPict->mm ) + { + case MM_TEXT: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::pt); + break; + + case MM_LOMETRIC: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm10); + break; + + case MM_HIMETRIC: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100); + break; + + case MM_LOENGLISH: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::in100); + break; + + case MM_HIENGLISH: + case MM_ISOTROPIC: + case MM_ANISOTROPIC: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::in1000); + break; + + case MM_TWIPS: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::twip); + break; + + default: + nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::master); + } + + pMFHeader->key = 0x9AC6CDD7L; + pMFHeader->hmf = 0; + pMFHeader->bbox = aRect; + pMFHeader->inch = nInch; + pMFHeader->reserved = 0; + pMFHeader->checksum = 0; + + char* pMFBuff = reinterpret_cast< char* >( mfpictStream.getArray( ) ); + + nCount = GetMetaFileBitsEx( pMFPict->hMF, nCount, pMFBuff + sizeof( METAFILEHEADER ) ); + OSL_ASSERT( nCount > 0 ); + } + + return mfpictStream; +} + +// convert a windows enhanced metafile to a LibreOffice metafile + +Sequence< sal_Int8 > WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile ) +{ + Sequence< sal_Int8 > aRet; + UINT nSize = 0; + + if( hEnhMetaFile && + ( ( nSize = GetEnhMetaFileBits( hEnhMetaFile, 0, nullptr ) ) != 0 ) ) + { + aRet.realloc( nSize ); + + if( GetEnhMetaFileBits( hEnhMetaFile, nSize, reinterpret_cast<unsigned char*>(aRet.getArray()) ) != nSize ) + aRet.realloc( 0 ); + } + + return aRet; +} + +// convert a LibreOffice metafile picture to a windows metafile picture + +HMETAFILEPICT OOMFPictToWinMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict ) +{ + HMETAFILEPICT hPict = nullptr; + HMETAFILE hMtf = SetMetaFileBitsEx( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) ); + + if( hMtf ) + { + METAFILEPICT* pPict = static_cast<METAFILEPICT*>(GlobalLock( hPict = GlobalAlloc( GHND, sizeof( METAFILEPICT ) ) )); + + pPict->mm = 8; + pPict->xExt = 0; + pPict->yExt = 0; + pPict->hMF = hMtf; + + GlobalUnlock( hPict ); + } + + return hPict; +} + +// convert a LibreOffice metafile picture to a windows enhanced metafile picture + +HENHMETAFILE OOMFPictToWinENHMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict ) +{ + HENHMETAFILE hEnhMtf = SetEnhMetaFileBits( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) ); + + return hEnhMtf; +} + +// convert a windows device independent bitmap into a LibreOffice bitmap + +Sequence< sal_Int8 > WinDIBToOOBMP( const Sequence< sal_Int8 >& aWinDIB ) +{ + OSL_ENSURE(o3tl::make_unsigned(aWinDIB.getLength()) > sizeof(BITMAPINFOHEADER), "CF_DIBV5/CF_DIB too small (!)"); + Sequence< sal_Int8 > ooBmpStream; + + ooBmpStream.realloc(aWinDIB.getLength( ) + sizeof(BITMAPFILEHEADER)); + const BITMAPINFOHEADER* pBmpInfoHdr = reinterpret_cast< const BITMAPINFOHEADER* >(aWinDIB.getConstArray()); + BITMAPFILEHEADER* pBmpFileHdr = reinterpret_cast< BITMAPFILEHEADER* >(ooBmpStream.getArray()); + const DWORD nSizeInfoOrV5(pBmpInfoHdr->biSize > sizeof(BITMAPINFOHEADER) ? sizeof(BITMAPV5HEADER) : sizeof(BITMAPINFOHEADER)); + DWORD nOffset(sizeof(BITMAPFILEHEADER) + nSizeInfoOrV5); + + memcpy(pBmpFileHdr + 1, pBmpInfoHdr, aWinDIB.getLength()); + + if(pBmpInfoHdr->biBitCount <= 8) + { + nOffset += (pBmpInfoHdr->biClrUsed ? pBmpInfoHdr->biClrUsed : (1 << pBmpInfoHdr->biBitCount)) << 2; + } + else if((BI_BITFIELDS == pBmpInfoHdr->biCompression ) && ((16 == pBmpInfoHdr->biBitCount ) || (32 == pBmpInfoHdr->biBitCount ))) + { + nOffset += 12; + } + + pBmpFileHdr->bfType = ('M' << 8) | 'B'; + pBmpFileHdr->bfSize = 0; // maybe: nMemSize + sizeof(BITMAPFILEHEADER) + pBmpFileHdr->bfReserved1 = 0; + pBmpFileHdr->bfReserved2 = 0; + pBmpFileHdr->bfOffBits = nOffset; + + return ooBmpStream; +} + +// convert a LibreOffice bitmap into a windows device independent bitmap + +Sequence< sal_Int8 > OOBmpToWinDIB( Sequence< sal_Int8 >& aOOBmp ) +{ + Sequence< sal_Int8 > winDIBStream( aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) ); + + memcpy( winDIBStream.getArray( ), + aOOBmp.getArray( ) + sizeof( BITMAPFILEHEADER ), + aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) ); + + return winDIBStream; +} + +static std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment) +{ + std::ostringstream htmlHeader; + htmlHeader << "Version:1.0" << '\r' << '\n'; + htmlHeader << "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec << startHtml << '\r' << '\n'; + htmlHeader << "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec << endHtml << '\r' << '\n'; + htmlHeader << "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec << startFragment << '\r' << '\n'; + htmlHeader << "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec << endFragment << '\r' << '\n'; + return htmlHeader.str(); +} + +// the case of these tags has to match what we output in our filters +// both tags don't allow parameters +const std::string TAG_HTML("<html>"); +const std::string TAG_END_HTML("</html>"); + +// The body tag may have parameters so we need to search for the +// closing '>' manually e.g. <body param> #92840# +const std::string TAG_BODY("<body"); +const std::string TAG_END_BODY("</body"); + +Sequence<sal_Int8> TextHtmlToHTMLFormat(Sequence<sal_Int8> const & aTextHtml) +{ + OSL_ASSERT(aTextHtml.getLength() > 0); + + if (aTextHtml.getLength() <= 0) + return Sequence<sal_Int8>(); + + // fill the buffer with dummy values to calc the exact length + std::string dummyHtmlHeader = GetHtmlFormatHeader(0, 0, 0, 0); + size_t lHtmlFormatHeader = dummyHtmlHeader.length(); + + std::string textHtml( + reinterpret_cast<const char*>(aTextHtml.getConstArray()), + reinterpret_cast<const char*>(aTextHtml.getConstArray()) + aTextHtml.getLength()); + + std::string::size_type nStartHtml = textHtml.find(TAG_HTML) + lHtmlFormatHeader - 1; // we start one before '<HTML>' Word 2000 does also so + std::string::size_type nEndHtml = textHtml.find(TAG_END_HTML) + lHtmlFormatHeader + TAG_END_HTML.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>? + + // The body tag may have parameters so we need to search for the + // closing '>' manually e.g. <BODY param> #92840# + std::string::size_type nStartFragment = textHtml.find(">", textHtml.find(TAG_BODY)) + lHtmlFormatHeader + 1; + std::string::size_type nEndFragment = textHtml.find(TAG_END_BODY) + lHtmlFormatHeader; + + std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment); + htmlFormat += textHtml; + + Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0' + memset(byteSequence.getArray(), 0, byteSequence.getLength()); + + memcpy( + static_cast<void*>(byteSequence.getArray()), + static_cast<const void*>(htmlFormat.c_str()), + htmlFormat.length()); + + return byteSequence; +} + +static std::wstring getFileExtension(const std::wstring& aFilename) +{ + std::wstring::size_type idx = aFilename.rfind(L"."); + if (idx != std::wstring::npos) + { + return std::wstring(aFilename, idx); + } + return std::wstring(); +} + +const std::wstring SHELL_LINK_FILE_EXTENSION = L".lnk"; + +static bool isShellLink(const std::wstring& aFilename) +{ + std::wstring ext = getFileExtension(aFilename); + return (_wcsicmp(ext.c_str(), SHELL_LINK_FILE_EXTENSION.c_str()) == 0); +} + +/** Resolve a Windows Shell Link (lnk) file. If a resolution + is not possible simply return the provided name of the + lnk file. */ +static std::wstring getShellLinkTarget(const std::wstring& aLnkFile) +{ + OSL_ASSERT(isShellLink(aLnkFile)); + + std::wstring target = aLnkFile; + + try + { + sal::systools::COMReference<IShellLinkW> pIShellLink; + pIShellLink.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER); + + sal::systools::COMReference<IPersistFile> pIPersistFile(pIShellLink, + sal::systools::COM_QUERY_THROW); + + HRESULT hr = pIPersistFile->Load(aLnkFile.c_str(), STGM_READ); + if (FAILED(hr)) + return target; + + hr = pIShellLink->Resolve(nullptr, SLR_UPDATE | SLR_NO_UI); + if (FAILED(hr)) + return target; + + wchar_t pathW[MAX_PATH]; + WIN32_FIND_DATAW wfd; + hr = pIShellLink->GetPath(pathW, MAX_PATH, &wfd, SLGP_RAWPATH); + if (FAILED(hr)) + return target; + + target = pathW; + } + catch(sal::systools::ComError& ex) + { + OSL_FAIL(ex.what()); + } + return target; +} + +typedef Sequence<sal_Int8> ByteSequence_t; + +/* Calculate the size required for turning a string list into + a double '\0' terminated string buffer */ +static size_t CalcSizeForStringListBuffer(const std::vector<std::wstring>& fileList) +{ + if ( fileList.empty() ) + return 0; + + size_t size = 1; // one for the very final '\0' + for (auto const& elem : fileList) + { + size += elem.length() + 1; // length including terminating '\0' + } + return (size * sizeof(std::vector<std::wstring>::value_type::value_type)); +} + +static ByteSequence_t FileListToByteSequence(const std::vector<std::wstring>& fileList) +{ + ByteSequence_t bseq; + size_t size = CalcSizeForStringListBuffer(fileList); + + if (size > 0) + { + bseq.realloc(size); + wchar_t* p = reinterpret_cast<wchar_t*>(bseq.getArray()); + ZeroMemory(p, size); + + for (auto const& elem : fileList) + { + wcsncpy(p, elem.c_str(), elem.length()); + p += (elem.length() + 1); + } + } + return bseq; +} + +css::uno::Sequence<sal_Int8> CF_HDROPToFileList(HGLOBAL hGlobal) +{ + UINT nFiles = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0); + std::vector<std::wstring> files; + + for (UINT i = 0; i < nFiles; i++) + { + wchar_t buff[MAX_PATH]; + /*UINT size =*/ DragQueryFileW(static_cast<HDROP>(hGlobal), i, buff, MAX_PATH); + std::wstring filename = buff; + if (isShellLink(filename)) + filename = getShellLinkTarget(filename); + files.push_back(filename); + } + return FileListToByteSequence(files); +} + +// convert a windows bitmap handle into a LibreOffice bitmap + +Sequence< sal_Int8 > WinBITMAPToOOBMP( HBITMAP aHBMP ) +{ + Sequence< sal_Int8 > ooBmpStream; + + SIZE aBmpSize; + if( GetBitmapDimensionEx( aHBMP, &aBmpSize ) ) + { + // fill bitmap info header + size_t nDataBytes = 4 * aBmpSize.cy * aBmpSize.cy; + Sequence< sal_Int8 > aBitmapStream( + sizeof(BITMAPINFO) + + nDataBytes + ); + PBITMAPINFOHEADER pBmp = reinterpret_cast<PBITMAPINFOHEADER>(aBitmapStream.getArray()); + pBmp->biSize = sizeof( BITMAPINFOHEADER ); + pBmp->biWidth = aBmpSize.cx; + pBmp->biHeight = aBmpSize.cy; + pBmp->biPlanes = 1; + pBmp->biBitCount = 32; + pBmp->biCompression = BI_RGB; + pBmp->biSizeImage = static_cast<DWORD>(nDataBytes); + pBmp->biXPelsPerMeter = 1000; + pBmp->biYPelsPerMeter = 1000; + pBmp->biClrUsed = 0; + pBmp->biClrImportant = 0; + if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of the desktop + aHBMP, + 0, aBmpSize.cy, + aBitmapStream.getArray() + sizeof(BITMAPINFO), + reinterpret_cast<LPBITMAPINFO>(pBmp), + DIB_RGB_COLORS ) ) + { + ooBmpStream = WinDIBToOOBMP( aBitmapStream ); + } + } + + return ooBmpStream; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/FmtFilter.hxx b/vcl/win/dtrans/FmtFilter.hxx new file mode 100644 index 0000000000..b4fb1e1fc3 --- /dev/null +++ b/vcl/win/dtrans/FmtFilter.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +#include <com/sun/star/uno/Sequence.hxx> + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> + +/*------------------------------------------------------------------------ + input: + aMetaFilePict - a sequence of bytes containing a METAFILEPICT struct +------------------------------------------------------------------------*/ +css::uno::Sequence<sal_Int8> WinMFPictToOOMFPict(css::uno::Sequence<sal_Int8>& aMetaFilePict); +css::uno::Sequence<sal_Int8> WinENHMFPictToOOMFPict(HENHMETAFILE hEnhMetaFile); + +/*------------------------------------------------------------------------ + input: + aByteStream - a sequence of bytes containing a LibreOffice metafile + picture with a leading METAFILEHEADER +------------------------------------------------------------------------*/ +HMETAFILEPICT OOMFPictToWinMFPict(css::uno::Sequence<sal_Int8> const& aOOMetaFilePict); +HENHMETAFILE OOMFPictToWinENHMFPict(css::uno::Sequence<sal_Int8> const& aOOMetaFilePict); + +/*------------------------------------------------------------------------ + input: + aWinDIB - sequence of bytes containing a windows device independent + bitmap +------------------------------------------------------------------------*/ +css::uno::Sequence<sal_Int8> WinDIBToOOBMP(const css::uno::Sequence<sal_Int8>& aWinDIB); + +/*------------------------------------------------------------------------ + input: + aWinDIB - sequence of bytes containing a windows bitmap handle +------------------------------------------------------------------------*/ +css::uno::Sequence<sal_Int8> WinBITMAPToOOBMP(HBITMAP); + +/*------------------------------------------------------------------------ + input: + aOOBmp - sequence of bytes containing a LibreOffice bitmap + May contain CF_DIBV5 or CF_DIB, but removing the BITMAPFILEHEADER + is always the same size +------------------------------------------------------------------------*/ +css::uno::Sequence<sal_Int8> OOBmpToWinDIB(css::uno::Sequence<sal_Int8>& aOOBmp); + +/*------------------------------------------------------------------------ + input: + aTextHtml - a sequence of text/html which will be converted to the + HTML Format; the HTML Format has header before the real html data + the Format is described in the MSDN Library under HTML Clipboard + Format +------------------------------------------------------------------------*/ +css::uno::Sequence<sal_Int8> TextHtmlToHTMLFormat(css::uno::Sequence<sal_Int8> const& aTextHtml); + +/** + Return a FileList in which Windows Shell Links (lnk) are resolved. + If for whatever reason a resolution is not possible leave the + original lnk file. +*/ +css::uno::Sequence<sal_Int8> CF_HDROPToFileList(HGLOBAL hGlobal); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/ImplHelper.cxx b/vcl/win/dtrans/ImplHelper.cxx new file mode 100644 index 0000000000..84c7383b2c --- /dev/null +++ b/vcl/win/dtrans/ImplHelper.cxx @@ -0,0 +1,352 @@ +/* -*- 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 <osl/diagnose.h> +#include "ImplHelper.hxx" +#include <rtl/tencinfo.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <string.h> +#include <memory> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <vector> + +#define FORMATETC_EXACT_MATCH 1 +#define FORMATETC_PARTIAL_MATCH -1 +#define FORMATETC_NO_MATCH 0 + +// returns a windows codepage appropriate to the +// given mime charset parameter value + +sal_uInt32 getWinCPFromMimeCharset( const OUString& charset ) +{ + sal_uInt32 winCP = GetACP( ); + + if ( charset.getLength( ) ) + { + OString osCharset( + charset.getStr( ), charset.getLength( ), RTL_TEXTENCODING_ASCII_US ); + + rtl_TextEncoding txtEnc = + rtl_getTextEncodingFromMimeCharset( osCharset.getStr( ) ); + + sal_uIntPtr winChrs = rtl_getBestWindowsCharsetFromTextEncoding( txtEnc ); + + CHARSETINFO chrsInf; + bool bRet = TranslateCharsetInfo( reinterpret_cast<DWORD*>(winChrs), &chrsInf, TCI_SRCCHARSET ); + + // if one of the above functions fails + // we will return the current ANSI codepage + // of this thread + if ( bRet ) + winCP = chrsInf.ciACP; + } + + return winCP; +} + +// returns a windows codepage appropriate to the +// given locale and locale type + +OUString getWinCPFromLocaleId( LCID lcid, LCTYPE lctype ) +{ + OSL_ASSERT( IsValidLocale( lcid, LCID_SUPPORTED ) ); + + // we set a default value + OUString winCP; + + // set a default value + if ( LOCALE_IDEFAULTCODEPAGE == lctype ) + { + winCP = OUString::number( static_cast<sal_Int32>(GetOEMCP( )) ); + } + else if ( LOCALE_IDEFAULTANSICODEPAGE == lctype ) + { + winCP = OUString::number( static_cast<sal_Int32>(GetACP( )) ); + } + else + OSL_ASSERT( false ); + + // First, get required buffer size, in characters + int nResult = GetLocaleInfoW( + lcid, lctype, nullptr, 0 ); + + OSL_ASSERT( nResult ); + + if ( nResult ) + { + std::unique_ptr<wchar_t[]> buff( new wchar_t[nResult] ); + // Now get the actual data + nResult = GetLocaleInfoW( lcid, lctype, buff.get(), nResult ); + + OSL_ASSERT(nResult); + + if (nResult) + winCP = o3tl::toU( buff.get() ); + + } + + return winCP; +} + +// returns a mime charset parameter value appropriate +// to the given codepage, optional a prefix can be +// given, e.g. "windows-" or "cp" + +OUString getMimeCharsetFromWinCP( sal_uInt32 cp, std::u16string_view aPrefix ) +{ + return aPrefix + cptostr( cp ); +} + +// returns a mime charset parameter value appropriate +// to the given locale id and locale type, optional a +// prefix can be given, e.g. "windows-" or "cp" + +OUString getMimeCharsetFromLocaleId( LCID lcid, LCTYPE lctype, std::u16string_view aPrefix ) +{ + OUString charset = getWinCPFromLocaleId( lcid, lctype ); + return aPrefix + charset; +} + +// IsOEMCP + +bool IsOEMCP( sal_uInt32 codepage ) +{ + OSL_ASSERT( IsValidCodePage( codepage ) ); + + sal_uInt32 arrOEMCP[] = { 437, 708, 709, 710, 720, 737, + 775, 850, 852, 855, 857, 860, + 861, 862, 863, 864, 865, 866, + 869, 874, 932, 936, 949, 950, 1361 }; + + for ( size_t i = 0; i < SAL_N_ELEMENTS( arrOEMCP ); ++i ) + if ( arrOEMCP[i] == codepage ) + return true; + + return false; +} + +// converts a codepage into its string representation + +OUString cptostr( sal_uInt32 codepage ) +{ + OSL_ASSERT( IsValidCodePage( codepage ) ); + + return OUString::number( static_cast<sal_Int64>( codepage ) ); +} + +// OleStdDeleteTargetDevice() +// +// Purpose: +// +// Parameters: +// +// Return Value: +// SCODE - S_OK if successful +void DeleteTargetDevice( DVTARGETDEVICE* ptd ) +{ + __try + { + CoTaskMemFree( ptd ); + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + OSL_FAIL( "Error DeleteTargetDevice" ); + } +} + +// OleStdCopyTargetDevice() +// +// Purpose: +// duplicate a TARGETDEVICE struct. this function allocates memory for +// the copy. the caller MUST free the allocated copy when done with it +// using the standard allocator returned from CoGetMalloc. +// (OleStdFree can be used to free the copy). +// +// Parameters: +// ptdSrc pointer to source TARGETDEVICE +// +// Return Value: +// pointer to allocated copy of ptdSrc +// if ptdSrc==NULL then returns NULL is returned. +// if ptdSrc!=NULL and memory allocation fails, then NULL is returned +DVTARGETDEVICE* CopyTargetDevice( DVTARGETDEVICE* ptdSrc ) +{ + DVTARGETDEVICE* ptdDest = nullptr; + + __try + { + if ( nullptr != ptdSrc ) + { + ptdDest = static_cast< DVTARGETDEVICE* >( CoTaskMemAlloc( ptdSrc->tdSize ) ); + memcpy( ptdDest, ptdSrc, static_cast< size_t >( ptdSrc->tdSize ) ); + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + return ptdDest; +} + +// OleStdCopyFormatEtc() +// +// Purpose: +// Copies the contents of a FORMATETC structure. this function takes +// special care to copy correctly copying the pointer to the TARGETDEVICE +// contained within the source FORMATETC structure. +// if the source FORMATETC has a non-NULL TARGETDEVICE, then a copy +// of the TARGETDEVICE will be allocated for the destination of the +// FORMATETC (petcDest). +// +// NOTE: the caller MUST free the allocated copy of the TARGETDEVICE +// within the destination FORMATETC when done with it +// using the standard allocator returned from CoGetMalloc. +// (OleStdFree can be used to free the copy). +// +// Parameters: +// petcDest pointer to destination FORMATETC +// petcSrc pointer to source FORMATETC +// +// Return Value: +// returns TRUE if copy was successful; +// returns FALSE if not successful, e.g. one or both of the pointers +// were invalid or the pointers were equal +bool CopyFormatEtc( LPFORMATETC petcDest, LPFORMATETC petcSrc ) +{ + bool bRet = false; + + __try + { + if ( petcDest != petcSrc ) + { + + petcDest->cfFormat = petcSrc->cfFormat; + + petcDest->ptd = nullptr; + if ( nullptr != petcSrc->ptd ) + petcDest->ptd = CopyTargetDevice(petcSrc->ptd); + + petcDest->dwAspect = petcSrc->dwAspect; + petcDest->lindex = petcSrc->lindex; + petcDest->tymed = petcSrc->tymed; + + bRet = true; + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + OSL_FAIL( "Error CopyFormatEtc" ); + } + + return bRet; +} + +// returns: +// 1 for exact match, +// 0 for no match, +// -1 for partial match (which is defined to mean the left is a subset +// of the right: fewer aspects, null target device, fewer medium). + +sal_Int32 CompareFormatEtc( const FORMATETC* pFetcLhs, const FORMATETC* pFetcRhs ) +{ + sal_Int32 nMatch = FORMATETC_EXACT_MATCH; + + __try + { + if ( pFetcLhs != pFetcRhs ) + { + if ( ( pFetcLhs->cfFormat != pFetcRhs->cfFormat ) || + ( pFetcLhs->lindex != pFetcRhs->lindex ) || + !CompareTargetDevice( pFetcLhs->ptd, pFetcRhs->ptd ) ) + { + nMatch = FORMATETC_NO_MATCH; + } + + else if ( pFetcLhs->dwAspect == pFetcRhs->dwAspect ) + // same aspects; equal + ; + else if ( ( pFetcLhs->dwAspect & ~pFetcRhs->dwAspect ) != 0 ) + { + // left not subset of aspects of right; not equal + nMatch = FORMATETC_NO_MATCH; + } + else + // left subset of right + nMatch = FORMATETC_PARTIAL_MATCH; + + if ( nMatch == FORMATETC_EXACT_MATCH || nMatch == FORMATETC_PARTIAL_MATCH ) + { + if ( pFetcLhs->tymed == pFetcRhs->tymed ) + // same medium flags; equal + ; + else if ( ( pFetcLhs->tymed & ~pFetcRhs->tymed ) != 0 ) + { + // left not subset of medium flags of right; not equal + nMatch = FORMATETC_NO_MATCH; + } + else + // left subset of right + nMatch = FORMATETC_PARTIAL_MATCH; + } + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + OSL_FAIL( "Error CompareFormatEtc" ); + nMatch = FORMATETC_NO_MATCH; + } + + return nMatch; +} + +bool CompareTargetDevice( DVTARGETDEVICE* ptdLeft, DVTARGETDEVICE const * ptdRight ) +{ + bool bRet = false; + + __try + { + if ( ptdLeft == ptdRight ) + { + // same address of td; must be same (handles NULL case) + bRet = true; + } + + // one of the two is NULL + else if ( ( nullptr != ptdRight ) && ( nullptr != ptdLeft ) ) + + if ( ptdLeft->tdSize == ptdRight->tdSize ) + + if ( memcmp( ptdLeft, ptdRight, ptdLeft->tdSize ) == 0 ) + bRet = true; + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + OSL_FAIL( "Error CompareTargetDevice" ); + bRet = false; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/ImplHelper.hxx b/vcl/win/dtrans/ImplHelper.hxx new file mode 100644 index 0000000000..df6731ec7a --- /dev/null +++ b/vcl/win/dtrans/ImplHelper.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <sal/types.h> +#include <rtl/ustring.hxx> + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> + +// target device and formatetc helper +void DeleteTargetDevice(DVTARGETDEVICE* ptd); +bool CopyFormatEtc(LPFORMATETC petcDest, LPFORMATETC petcSrc); +sal_Int32 CompareFormatEtc(const FORMATETC* pFetcLeft, const FORMATETC* pFetcRight); +bool CompareTargetDevice(DVTARGETDEVICE* ptdLeft, DVTARGETDEVICE const* ptdRight); +DVTARGETDEVICE* CopyTargetDevice(DVTARGETDEVICE* ptdSrc); + +// some codepage helper functions + +// returns a windows codepage appropriate to the +// given mime charset parameter value + +sal_uInt32 getWinCPFromMimeCharset(const OUString& charset); + +// returns a windows codepage appropriate to the +// given locale and locale type + +OUString getWinCPFromLocaleId(LCID lcid, LCTYPE lctype); + +// returns a mime charset parameter value appropriate +// to the given codepage, optional a prefix can be +// given, e.g. "windows-" or "cp" + +OUString getMimeCharsetFromWinCP(sal_uInt32 cp, std::u16string_view aPrefix); + +// returns a mime charset parameter value appropriate +// to the given locale id and locale type, optional a +// prefix can be given, e.g. "windows-" or "cp" + +OUString getMimeCharsetFromLocaleId(LCID lcid, LCTYPE lctype, std::u16string_view aPrefix); + +// returns true, if a given codepage is an oem codepage + +bool IsOEMCP(sal_uInt32 codepage); + +// converts a codepage into a string representation + +OUString cptostr(sal_uInt32 codepage); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/MimeAttrib.hxx b/vcl/win/dtrans/MimeAttrib.hxx new file mode 100644 index 0000000000..48dd5fc11e --- /dev/null +++ b/vcl/win/dtrans/MimeAttrib.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> + +constexpr OUString TEXTPLAIN_PARAM_CHARSET(u"charset"_ustr); + +constexpr OUString PRE_WINDOWS_CODEPAGE(u"windows"_ustr); +constexpr OUString PRE_OEM_CODEPAGE(u"cp"_ustr); +constexpr OUString CHARSET_UTF16(u"utf-16"_ustr); +constexpr OUString CHARSET_UNICODE(u"unicode"_ustr); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/MtaOleClipb.cxx b/vcl/win/dtrans/MtaOleClipb.cxx new file mode 100644 index 0000000000..fcf16df1ad --- /dev/null +++ b/vcl/win/dtrans/MtaOleClipb.cxx @@ -0,0 +1,748 @@ +/* -*- 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 . + */ + +/* + MtaOleClipb.cxx - documentation + + This class setup a single threaded apartment (sta) thread to deal with + the ole clipboard, which runs only in an sta thread. + The consequence is that callback from the ole clipboard are in the + context of this sta thread. In the soffice applications this may lead + to problems because they all use the one and only mutex called + SolarMutex. + In order to transfer clipboard requests to our sta thread we use a + hidden window and forward these requests via window messages. +*/ + +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include "MtaOleClipb.hxx" + +#include <svsys.h> +#include <win/saldata.hxx> + +#include <osl/thread.h> + +#include <wchar.h> +#include <process.h> + +#include <systools/win32/comtools.hxx> +#include <systools/win32/retry_if_failed.hxx> + +#include <comphelper/windowserrorstring.hxx> + +namespace /* private */ +{ + const wchar_t g_szWndClsName[] = L"MtaOleReqWnd###"; + + // messages constants + + const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001; + const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002; + const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003; + const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004; + const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005; + + const sal_uInt32 MAX_WAITTIME = 10000; // msec + const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec + + const bool MANUAL_RESET = true; + const bool INIT_NONSIGNALED = false; + + /* Cannot use osl conditions because they are blocking + without waking up on messages sent by another thread + this leads to deadlocks because we are blocking the + communication between inter-thread marshalled COM + pointers. + COM Proxy-Stub communication uses SendMessages for + synchronization purposes. + */ + class Win32Condition + { + public: + Win32Condition() = default; + + ~Win32Condition() { CloseHandle(m_hEvent); } + + // wait infinite for own event (or abort event) be signaled + // leave messages sent through + bool wait(HANDLE hEvtAbort) + { + const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; + while (true) + { + DWORD dwResult + = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE); + + switch (dwResult) + { + case WAIT_OBJECT_0: // wait successful + return true; + + case WAIT_OBJECT_0 + 1: // wait aborted + return false; + + case WAIT_OBJECT_0 + 2: + { + /* PeekMessage processes all messages in the SendMessage + queue that's what we want, messages from the PostMessage + queue stay untouched */ + MSG msg; + PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE); + + break; + } + + default: // WAIT_FAILED? + return false; + } + } + } + + // set the event + void set() { SetEvent(m_hEvent); } + + private: + HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr); + + // prevent copy/assignment + Win32Condition(const Win32Condition&) = delete; + Win32Condition& operator=(const Win32Condition&) = delete; + }; + + // we use one condition for every request + + struct MsgCtx + { + Win32Condition aCondition; + HRESULT hr; + }; + +} /* namespace private */ + +// static member initialization + +CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr; + +// marshal an IDataObject + +//inline +static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream ) +{ + OSL_ASSERT( nullptr != pIDataObject ); + OSL_ASSERT( nullptr != ppStream ); + + *ppStream = nullptr; + return CoMarshalInterThreadInterfaceInStream( + __uuidof(IDataObject), //The IID of interface to be marshalled + pIDataObject, //The interface pointer + ppStream //IStream pointer + ); +} + +// unmarshal an IDataObject + +//inline +static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject ) +{ + OSL_ASSERT( nullptr != lpStream ); + OSL_ASSERT( nullptr != ppIDataObject ); + + *ppIDataObject = nullptr; + return CoGetInterfaceAndReleaseStream( + lpStream, + __uuidof(IDataObject), + reinterpret_cast<LPVOID*>(ppIDataObject)); +} + +// helper class to ensure that the calling thread has com initialized + +namespace { + +class CAutoComInit +{ +public: + /* + to be safe we call CoInitializeEx + although it is not necessary if + the calling thread was created + using osl_CreateThread because + this function calls CoInitializeEx + for every thread it creates + */ + CAutoComInit( ) : m_hResult( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) ) + { + if ( S_OK == m_hResult ) + OSL_FAIL( + "com was not yet initialized, the thread was not created using osl_createThread" ); + else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) ) + OSL_FAIL( + "com could not be initialized, maybe the thread was not created using osl_createThread" ); + } + + ~CAutoComInit( ) + { + /* + we only call CoUninitialize when + CoInitializeEx returned S_FALSE, what + means that com was already initialize + for that thread so we keep the balance + if CoInitializeEx returned S_OK what means + com was not yet initialized we better + let com initialized or we may run into + the realm of undefined behaviour + */ + if ( m_hResult == S_FALSE ) + CoUninitialize( ); + } + +private: + HRESULT m_hResult; +}; + +} + +// ctor + +CMtaOleClipboard::CMtaOleClipboard( ) : + m_hOleThread( nullptr ), + m_uOleThreadId( 0 ), + // signals that the thread was successfully setup + m_hEvtThrdReady(CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr )), + m_hwndMtaOleReqWnd( nullptr ), + // signals that the window is destroyed - to stop waiting any winproc result + m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)), + m_MtaOleReqWndClassAtom( 0 ), + m_pfncClipViewerCallback( nullptr ), + m_bRunClipboardNotifierThread( true ), + m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ), + m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ), + m_ClipboardChangedEventCount( 0 ) +{ + OSL_ASSERT( nullptr != m_hEvtThrdReady ); + SAL_WARN_IF(!m_hEvtWndDisposed, "vcl.win.dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr"); + + s_theMtaOleClipboardInst = this; + + m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex( + nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId )); + OSL_ASSERT( nullptr != m_hOleThread ); + + // setup the clipboard changed notifier thread + + m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr ); + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] ); + + m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr ); + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] ); + + m_hClipboardChangedNotifierThread = reinterpret_cast<HANDLE>(_beginthreadex( + nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, nullptr )); + + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread ); +} + +// dtor + +CMtaOleClipboard::~CMtaOleClipboard( ) +{ + // block calling threads out + if ( nullptr != m_hEvtThrdReady ) + ResetEvent( m_hEvtThrdReady ); + + // terminate the clipboard changed notifier thread + m_bRunClipboardNotifierThread = false; + SetEvent( m_hTerminateClipboardChangedNotifierEvent ); + + // unblock whoever could still wait for event processing + if (m_hEvtWndDisposed) + SetEvent(m_hEvtWndDisposed); + + sal_uInt32 dwResult = WaitForSingleObject( + m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN ); + + OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" ); + + if ( nullptr != m_hClipboardChangedNotifierThread ) + CloseHandle( m_hClipboardChangedNotifierThread ); + + if ( nullptr != m_hClipboardChangedNotifierEvents[0] ) + CloseHandle( m_hClipboardChangedNotifierEvents[0] ); + + if ( nullptr != m_hClipboardChangedNotifierEvents[1] ) + CloseHandle( m_hClipboardChangedNotifierEvents[1] ); + + // end the thread + // because DestroyWindow can only be called + // from within the thread that created the window + sendMessage( MSG_SHUTDOWN ); + + // wait for thread shutdown + dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN ); + OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" ); + + if ( nullptr != m_hOleThread ) + CloseHandle( m_hOleThread ); + + if ( nullptr != m_hEvtThrdReady ) + CloseHandle( m_hEvtThrdReady ); + + if (m_hEvtWndDisposed) + CloseHandle(m_hEvtWndDisposed); + + if ( m_MtaOleReqWndClassAtom ) + UnregisterClassW( g_szWndClsName, nullptr ); + + OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ), + "Clipboard viewer not properly unregistered" ); +} + +HRESULT CMtaOleClipboard::flushClipboard( ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, + "flushClipboard from within clipboard sta thread called" ); + + MsgCtx aMsgCtx; + + const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx)) + && aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + return bWaitSuccess ? aMsgCtx.hr : E_ABORT; +} + +HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject ) +{ + OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" ); + OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" ); + + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + CAutoComInit comAutoInit; + + LPSTREAM lpStream; + + *ppIDataObject = nullptr; + + MsgCtx aMsgCtx; + + const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream), + reinterpret_cast<LPARAM>(&aMsgCtx)) + && aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT; + + if ( SUCCEEDED( hr ) ) + { + hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject ); + OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" ); + } + + return hr; +} + +// this is an asynchronous method that's why we don't wait until the +// request is completed + +HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + CAutoComInit comAutoInit; + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" ); + + // because we marshall this request + // into the sta thread we better + // acquire the interface here so + // that the object will not be + // destroyed before the ole clipboard + // can acquire it + // remember: pIDataObject may be NULL + // which is a request to clear the + // current clipboard content + if ( pIDataObject ) + pIDataObject->AddRef( ); + + postMessage( + MSG_SETCLIPBOARD, + reinterpret_cast< WPARAM >( pIDataObject ) ); + + // because this is an asynchronous function + // the return value is useless + return S_OK; +} + +// register a clipboard viewer + +bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return false; + } + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" ); + + MsgCtx aMsgCtx; + + if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback), + reinterpret_cast<LPARAM>(&aMsgCtx))) + aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + return false; +} + +// register a clipboard viewer + +bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ) +{ + bool bRet = false; + + // we need exclusive access because the clipboard changed notifier + // thread also accesses this variable + std::unique_lock aGuard( m_pfncClipViewerCallbackMutex ); + + // register if not yet done + if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) ) + { + // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore + // this message if we register ourself as clip viewer + m_bInRegisterClipViewer = true; + bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd); + m_bInRegisterClipViewer = false; + + // save the new callback function + m_pfncClipViewerCallback = pfncClipViewerCallback; + } + else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) ) + { + m_pfncClipViewerCallback = nullptr; + + // unregister if input parameter is NULL and we previously registered + // as clipboard viewer + bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd); + } + + return bRet; +} + +HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject ) +{ + return sal::systools::RetryIfFailed(10, 100, + [pIDataObject] { return OleSetClipboard(pIDataObject); }); +} + +HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream ) +{ + OSL_ASSERT(nullptr != ppStream); + + IDataObjectPtr pIDataObject; + + // forward the request to the OleClipboard + HRESULT hr + = sal::systools::RetryIfFailed(10, 100, [p = &pIDataObject] { return OleGetClipboard(p); }); + if ( SUCCEEDED( hr ) ) + { + hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream); + OSL_ENSURE(SUCCEEDED(hr), "marshalling clipboard data object failed"); + } + return hr; +} + +// flush the ole-clipboard + +HRESULT CMtaOleClipboard::onFlushClipboard( ) +{ + return sal::systools::RetryIfFailed(10, 100, [] { return OleFlushClipboard(); }); +} + +// handle clipboard update event + +LRESULT CMtaOleClipboard::onClipboardUpdate() +{ + // we don't send a notification if we are + // registering ourself as clipboard + if ( !m_bInRegisterClipViewer ) + { + std::unique_lock aGuard( m_ClipboardChangedEventCountMutex ); + + m_ClipboardChangedEventCount++; + SetEvent( m_hClipboardChangedEvent ); + } + + return 0; +} + +// SendMessage so we don't need to supply the HWND if we send +// something to our wrapped window + +LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam ) +{ + return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam ); +} + +// PostMessage so we don't need to supply the HWND if we send +// something to our wrapped window + +bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam ) +{ + bool const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam); + SAL_WARN_IF(!ret, "vcl.win.dtrans", "ERROR: PostMessage() failed!"); + return ret; +} + +// the window proc + +LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + LRESULT lResult = 0; + + // get a connection to the class-instance via the static member + CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst; + OSL_ASSERT( nullptr != pImpl ); + + switch( uMsg ) + { + case MSG_SETCLIPBOARD: + { + IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam ); + CMtaOleClipboard::onSetClipboard( pIDataObject ); + + // in setClipboard we did acquire the + // interface pointer in order to prevent + // destruction of the object before the + // ole clipboard can acquire the interface + // now we release the interface so that + // our lostOwnership mechanism works + // remember: pIDataObject may be NULL + if ( pIDataObject ) + pIDataObject->Release( ); + } + break; + + case MSG_GETCLIPBOARD: + { + MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam ); + OSL_ASSERT( aMsgCtx ); + + aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) ); + aMsgCtx->aCondition.set( ); + } + break; + + case MSG_FLUSHCLIPBOARD: + { + MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam ); + OSL_ASSERT( aMsgCtx ); + + aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( ); + aMsgCtx->aCondition.set( ); + } + break; + + case MSG_REGCLIPVIEWER: + { + MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam); + SAL_WARN_IF(!pMsgCtx, "vcl.win.dtrans", "pMsgCtx is nullptr"); + + pImpl->onRegisterClipViewer( + reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam)); + pMsgCtx->aCondition.set(); + } + break; + + case WM_CLIPBOARDUPDATE: + lResult = pImpl->onClipboardUpdate(); + break; + + case MSG_SHUTDOWN: + DestroyWindow( pImpl->m_hwndMtaOleReqWnd ); + break; + + // force the sta thread to end + case WM_DESTROY: + SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc + PostQuitMessage( 0 ); + break; + + default: + lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam ); + break; + } + + return lResult; +} + +void CMtaOleClipboard::createMtaOleReqWnd( ) +{ + WNDCLASSEXW wcex; + + SalData* pSalData = GetSalData(); + OSL_ASSERT(nullptr != pSalData->mhInst); + + ZeroMemory( &wcex, sizeof(wcex) ); + + wcex.cbSize = sizeof(wcex); + wcex.style = 0; + wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = pSalData->mhInst; + wcex.hIcon = nullptr; + wcex.hCursor = nullptr; + wcex.hbrBackground = nullptr; + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = g_szWndClsName; + wcex.hIconSm = nullptr; + + m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex ); + + if ( 0 != m_MtaOleReqWndClassAtom ) + m_hwndMtaOleReqWnd = CreateWindowW( + g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, pSalData->mhInst, nullptr ); +} + +unsigned int CMtaOleClipboard::run( ) +{ + HRESULT hr = OleInitialize( nullptr ); + OSL_ASSERT( SUCCEEDED( hr ) ); + + createMtaOleReqWnd( ); + + unsigned int nRet = ~0U; // = error + + if ( IsWindow( m_hwndMtaOleReqWnd ) ) + { + if ( nullptr != m_hEvtThrdReady ) + SetEvent( m_hEvtThrdReady ); + + nRet = 0; + + // pumping messages + for (;;) + { + MSG msg; + int const bRet = GetMessageW(&msg, nullptr, 0, 0); + if (bRet == 0) + { + break; + } + if (-1 == bRet) + { + SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError())); + nRet = ~0U; + break; + } + DispatchMessageW(&msg); + } + } + + OleUninitialize( ); + + return nRet; +} + +unsigned __stdcall CMtaOleClipboard::oleThreadProc( void* pParam ) +{ + osl_setThreadName("CMtaOleClipboard::run()"); + + CMtaOleClipboard* pInst = + static_cast<CMtaOleClipboard*>( pParam ); + OSL_ASSERT( nullptr != pInst ); + + return pInst->run( ); +} + +unsigned __stdcall CMtaOleClipboard::clipboardChangedNotifierThreadProc(void* pParam) +{ + osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()"); + CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam ); + OSL_ASSERT( nullptr != pInst ); + + sal::systools::CoInitializeGuard aGuard(COINIT_APARTMENTTHREADED, false, + sal::systools::CoInitializeGuard::WhenFailed::NoThrow); + + // assuming we don't need a lock for + // a boolean variable like m_bRun... + while ( pInst->m_bRunClipboardNotifierThread ) + { + // process window messages because of CoInitializeEx + MSG Msg; + while (PeekMessageW(&Msg, nullptr, 0, 0, PM_REMOVE)) + DispatchMessageW(&Msg); + + // wait for clipboard changed or terminate event + MsgWaitForMultipleObjects(2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE, + QS_ALLINPUT | QS_ALLPOSTMESSAGE); + + std::unique_lock aGuard2( pInst->m_ClipboardChangedEventCountMutex ); + + if ( pInst->m_ClipboardChangedEventCount > 0 ) + { + pInst->m_ClipboardChangedEventCount--; + if ( 0 == pInst->m_ClipboardChangedEventCount ) + ResetEvent( pInst->m_hClipboardChangedEvent ); + + aGuard2.unlock( ); + + // nobody should touch m_pfncClipViewerCallback while we do + std::unique_lock aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex ); + + // notify all clipboard listener + if ( pInst->m_pfncClipViewerCallback ) + pInst->m_pfncClipViewerCallback( ); + } + else + aGuard2.unlock( ); + } + + return 0; +} + +bool CMtaOleClipboard::WaitForThreadReady( ) const +{ + bool bRet = false; + + if ( nullptr != m_hEvtThrdReady ) + { + DWORD dwResult = WaitForSingleObject( + m_hEvtThrdReady, MAX_WAITTIME ); + bRet = ( dwResult == WAIT_OBJECT_0 ); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/MtaOleClipb.hxx b/vcl/win/dtrans/MtaOleClipb.hxx new file mode 100644 index 0000000000..f76becb50c --- /dev/null +++ b/vcl/win/dtrans/MtaOleClipb.hxx @@ -0,0 +1,110 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <mutex> + +#include <objidl.h> + +// the Mta-Ole clipboard class is for internal use only! +// only one instance of this class should be created, the +// user has to ensure this! +// the class is not thread-safe because it will be used +// only from within the clipboard service and the methods +// of the clipboard service are already synchronized + +class CMtaOleClipboard +{ +public: + typedef void ( WINAPI *LPFNC_CLIPVIEWER_CALLBACK_t )( void ); + +public: + CMtaOleClipboard( ); + ~CMtaOleClipboard( ); + + // clipboard functions + HRESULT setClipboard( IDataObject* pIDataObject ); + HRESULT getClipboard( IDataObject** ppIDataObject ); + HRESULT flushClipboard( ); + + // register/unregister a clipboard viewer; there can only + // be one at a time; parameter NULL means unregister + // a clipboard viewer + // returns true on success else false; use GetLastError( ) in + // false case + bool registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ); + +private: + unsigned int run( ); + + // create a hidden window which serves as a request target; so we + // guarantee synchronization + void createMtaOleReqWnd( ); + + // message support + bool postMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 ); + LRESULT sendMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 ); + + // message handler functions; remember these functions are called + // from a different thread context! + + static HRESULT onSetClipboard( IDataObject* pIDataObject ); + static HRESULT onGetClipboard( LPSTREAM* ppStream ); + static HRESULT onFlushClipboard( ); + bool onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ); + + // win32 clipboard listener support + LRESULT onClipboardUpdate(); + + static LRESULT CALLBACK mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static unsigned __stdcall oleThreadProc(void* pParam); + + static unsigned __stdcall clipboardChangedNotifierThreadProc(void* pParam); + + bool WaitForThreadReady( ) const; + +private: + HANDLE m_hOleThread; + unsigned m_uOleThreadId; + HANDLE m_hEvtThrdReady; + HWND m_hwndMtaOleReqWnd; + HANDLE m_hEvtWndDisposed; + ATOM m_MtaOleReqWndClassAtom; + LPFNC_CLIPVIEWER_CALLBACK_t m_pfncClipViewerCallback; + bool m_bInRegisterClipViewer; + + bool m_bRunClipboardNotifierThread; + HANDLE m_hClipboardChangedNotifierThread; + HANDLE m_hClipboardChangedNotifierEvents[2]; + HANDLE& m_hClipboardChangedEvent; + HANDLE& m_hTerminateClipboardChangedNotifierEvent; + std::mutex m_ClipboardChangedEventCountMutex; + sal_Int32 m_ClipboardChangedEventCount; + + std::mutex m_pfncClipViewerCallbackMutex; + + static CMtaOleClipboard* s_theMtaOleClipboardInst; + + CMtaOleClipboard( const CMtaOleClipboard& ) = delete; + CMtaOleClipboard& operator=( const CMtaOleClipboard& ) = delete; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/TxtCnvtHlp.cxx b/vcl/win/dtrans/TxtCnvtHlp.cxx new file mode 100644 index 0000000000..d7ab386fc1 --- /dev/null +++ b/vcl/win/dtrans/TxtCnvtHlp.cxx @@ -0,0 +1,125 @@ +/* -*- 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 <osl/diagnose.h> + +#include "TxtCnvtHlp.hxx" +#include "DTransHelper.hxx" +#include "ImplHelper.hxx" + +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::uno; + +// assuming a '\0' terminated string if no length specified + +static int CalcBuffSizeForTextConversion( UINT code_page, LPCSTR lpMultiByteString, int nLen = -1 ) +{ + return ( MultiByteToWideChar( code_page, + 0, + lpMultiByteString, + nLen, + nullptr, + 0 ) * sizeof( sal_Unicode ) ); +} + +// assuming a '\0' terminated string if no length specified + +static int CalcBuffSizeForTextConversion( UINT code_page, LPCWSTR lpWideCharString, int nLen = -1 ) +{ + return WideCharToMultiByte( code_page, + 0, + lpWideCharString, + nLen, + nullptr, + 0, + nullptr, + nullptr ); +} + +// converts text in one code page into unicode text +// automatically calculates the necessary buffer size and allocates +// the buffer + +int MultiByteToWideCharEx( UINT cp_src, + LPCSTR lpMultiByteString, + int lenStr, + CStgTransferHelper& refDTransHelper, + BOOL bEnsureTrailingZero ) +{ + OSL_ASSERT( IsValidCodePage( cp_src ) ); + OSL_ASSERT( nullptr != lpMultiByteString ); + + // calculate the required buff size + int reqSize = CalcBuffSizeForTextConversion( cp_src, lpMultiByteString, lenStr ); + + if ( bEnsureTrailingZero ) + reqSize += sizeof( sal_Unicode ); + + // initialize the data-transfer helper + refDTransHelper.init( reqSize ); + + // setup a global memory pointer + CRawHGlobalPtr ptrHGlob( refDTransHelper ); + + // do the conversion and return + return MultiByteToWideChar( cp_src, + 0, + lpMultiByteString, + lenStr, + static_cast< LPWSTR >( ptrHGlob.GetMemPtr( ) ), + ptrHGlob.MemSize( ) ); +} + +// converts unicode text into text of the specified code page +// automatically calculates the necessary buffer size and allocates +// the buffer + +int WideCharToMultiByteEx( UINT cp_dest, + LPCWSTR lpWideCharString, + int lenStr, + CStgTransferHelper& refDTransHelper, + BOOL bEnsureTrailingZero ) +{ + OSL_ASSERT( IsValidCodePage( cp_dest ) ); + OSL_ASSERT( nullptr != lpWideCharString ); + + // calculate the required buff size + int reqSize = CalcBuffSizeForTextConversion( cp_dest, lpWideCharString, lenStr ); + + if ( bEnsureTrailingZero ) + reqSize += sizeof( sal_Int8 ); + + // initialize the data-transfer helper + refDTransHelper.init( reqSize ); + + // setup a global memory pointer + CRawHGlobalPtr ptrHGlob( refDTransHelper ); + + // do the conversion and return + return WideCharToMultiByte( cp_dest, + 0, + lpWideCharString, + lenStr, + static_cast< LPSTR >( ptrHGlob.GetMemPtr( ) ), + ptrHGlob.MemSize( ), + nullptr, + nullptr ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/TxtCnvtHlp.hxx b/vcl/win/dtrans/TxtCnvtHlp.hxx new file mode 100644 index 0000000000..5294879337 --- /dev/null +++ b/vcl/win/dtrans/TxtCnvtHlp.hxx @@ -0,0 +1,41 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/DataFlavor.hpp> + +#include "DTransHelper.hxx" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +int MultiByteToWideCharEx( UINT cp_src, + LPCSTR lpMultiByteString, + int lenStr, + CStgTransferHelper& refDTransHelper, + BOOL bEnsureTrailingZero = TRUE ); + +int WideCharToMultiByteEx( UINT cp_dest, + LPCWSTR lpWideCharString, + int lenStr, + CStgTransferHelper& refDTransHelper, + BOOL bEnsureTrailingZero = TRUE ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/WinClip.hxx b/vcl/win/dtrans/WinClip.hxx new file mode 100644 index 0000000000..f90d5eea06 --- /dev/null +++ b/vcl/win/dtrans/WinClip.hxx @@ -0,0 +1,26 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +const sal_Int32 CF_INVALID = 0; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/WinClipboard.cxx b/vcl/win/dtrans/WinClipboard.cxx new file mode 100644 index 0000000000..1a8eaea151 --- /dev/null +++ b/vcl/win/dtrans/WinClipboard.cxx @@ -0,0 +1,383 @@ +/* -*- 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 <sal/config.h> + +#include <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> +#include <com/sun/star/datatransfer/clipboard/ClipboardEvent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <vcl/svapp.hxx> +#include <svdata.hxx> +#include <salinst.hxx> + +#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp> +#include "XNotifyingDataObject.hxx" + +#include <systools/win32/comtools.hxx> +#include "DtObjFactory.hxx" +#include "APNDataObject.hxx" +#include "DOTransferable.hxx" +#include "WinClipboard.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> +#include <objidl.h> + +using namespace com::sun::star; + +namespace +{ +CWinClipboard* s_pCWinClipbImpl = nullptr; +osl::Mutex s_aClipboardSingletonMutex; +} + +/*XEventListener,*/ +CWinClipboard::CWinClipboard(const uno::Reference<uno::XComponentContext>& rxContext, + const OUString& aClipboardName) + : m_xContext(rxContext) + , m_itsName(aClipboardName) + , m_pCurrentClipContent(nullptr) +{ + // necessary to reassociate from + // the static callback function + { + osl::MutexGuard aGuard(s_aClipboardSingletonMutex); + s_pCWinClipbImpl = this; + } + + registerClipboardViewer(); +} + +CWinClipboard::~CWinClipboard() +{ + { + osl::MutexGuard aGuard(s_aClipboardSingletonMutex); + s_pCWinClipbImpl = nullptr; + } + + unregisterClipboardViewer(); +} + +void CWinClipboard::disposing(std::unique_lock<std::mutex>& mutex) +{ + { + osl::MutexGuard aGuard(s_aClipboardSingletonMutex); + s_pCWinClipbImpl = nullptr; + } + + unregisterClipboardViewer(); + + WeakComponentImplHelper::disposing(mutex); +} + +// XClipboard + +// to avoid unnecessary traffic we check first if there is a clipboard +// content which was set via setContent, in this case we don't need +// to query the content from the clipboard, create a new wrapper object +// and so on, we simply return the original XTransferable instead of our +// DOTransferable + +uno::Reference<datatransfer::XTransferable> SAL_CALL CWinClipboard::getContents() +{ + osl::MutexGuard aGuard(m_aContentMutex); + + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + // use the shortcut or create a transferable from + // system clipboard + { + osl::MutexGuard aGuard2(m_aContentCacheMutex); + + if (nullptr != m_pCurrentClipContent) + return m_pCurrentClipContent->m_XTransferable; + + // Content cached? + if (m_foreignContent.is()) + return m_foreignContent; + + // release the mutex, so that the variable may be + // changed by other threads + } + + 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_aContentCacheMutex); + m_foreignContent = rClipContent; + } + } + + return rClipContent; +} + +IDataObjectPtr CWinClipboard::getIDataObject() +{ + osl::MutexGuard aGuard(m_aContentMutex); + + if (m_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); + + if (SUCCEEDED(hr)) + { + // create an apartment neutral dataobject and initialize it with a + // com smart pointer to the IDataObject from clipboard + pIDataObject = new CAPNDataObject(pIDataObject); + } + + return pIDataObject; +} + +void SAL_CALL CWinClipboard::setContents( + const uno::Reference<datatransfer::XTransferable>& xTransferable, + const uno::Reference<datatransfer::clipboard::XClipboardOwner>& xClipboardOwner) +{ + osl::MutexGuard aGuard(m_aContentMutex); + + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + IDataObjectPtr pIDataObj; + + if (xTransferable.is()) + { + { + osl::MutexGuard aGuard2(m_aContentCacheMutex); + + m_foreignContent.clear(); + + m_pCurrentClipContent = new CXNotifyingDataObject( + CDTransObjFactory::createDataObjFromTransferable(m_xContext, xTransferable), + xTransferable, xClipboardOwner, this); + } + + pIDataObj = IDataObjectPtr(m_pCurrentClipContent); + } + + m_MtaOleClipboard.setClipboard(pIDataObj.get()); +} + +OUString SAL_CALL CWinClipboard::getName() +{ + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + return m_itsName; +} + +// XFlushableClipboard + +void SAL_CALL CWinClipboard::flushClipboard() +{ + osl::MutexGuard aGuard(m_aContentMutex); + + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + // actually it should be ClearableMutexGuard aGuard( m_aContentCacheMutex ); + // but it does not work since FlushClipboard does a callback and frees DataObject + // which results in a deadlock in onReleaseDataObject. + // FlushClipboard had to be synchron in order to prevent shutdown until all + // clipboard-formats are rendered. + // The request is needed to prevent flushing if we are not clipboard owner (it is + // not known what happens if we flush but aren't clipboard owner). + // It may be possible to move the request to the clipboard STA thread by saving the + // DataObject and call OleIsCurrentClipboard before flushing. + + if (nullptr != m_pCurrentClipContent) + m_MtaOleClipboard.flushClipboard(); +} + +// XClipboardEx + +sal_Int8 SAL_CALL CWinClipboard::getRenderingCapabilities() +{ + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + using namespace datatransfer::clipboard::RenderingCapabilities; + return (Delayed | Persistent); +} + +// XClipboardNotifier + +void SAL_CALL CWinClipboard::addClipboardListener( + const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener) +{ + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + // check input parameter + if (!listener.is()) + throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this), + 1); + + std::unique_lock aGuard(m_aMutex); + maClipboardListeners.addInterface(aGuard, listener); +} + +void SAL_CALL CWinClipboard::removeClipboardListener( + const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener) +{ + if (m_bDisposed) + throw lang::DisposedException("object is already disposed", + static_cast<XClipboardEx*>(this)); + + // check input parameter + if (!listener.is()) + throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this), + 1); + + std::unique_lock aGuard(m_aMutex); + maClipboardListeners.removeInterface(aGuard, listener); +} + +void CWinClipboard::notifyAllClipboardListener() +{ + if (m_bDisposed) + return; + + std::unique_lock aGuard(m_aMutex); + if (m_bDisposed) + return; + + if (!maClipboardListeners.getLength(aGuard)) + return; + + try + { + uno::Reference<datatransfer::XTransferable> rXTransf(getContents()); + datatransfer::clipboard::ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this), + rXTransf); + maClipboardListeners.notifyEach( + aGuard, &datatransfer::clipboard::XClipboardListener::changedContents, aClipbEvent); + } + catch (const lang::DisposedException&) + { + OSL_FAIL("Service Manager disposed"); + + aGuard.unlock(); + // no further clipboard changed notifications + unregisterClipboardViewer(); + } +} + +// XServiceInfo + +OUString SAL_CALL CWinClipboard::getImplementationName() +{ + return "com.sun.star.datatransfer.clipboard.ClipboardW32"; +} + +sal_Bool SAL_CALL CWinClipboard::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL CWinClipboard::getSupportedServiceNames() +{ + return { "com.sun.star.datatransfer.clipboard.SystemClipboard" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_CWinClipboard_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const& args) +{ + // We run unit tests in parallel, which is a problem when touching a shared resource + // like the system clipboard, so rather use the dummy GenericClipboard. + static const bool bRunningUnitTest = getenv("LO_TESTNAME"); + + if (bRunningUnitTest) + { + SolarMutexGuard aGuard; + auto xClipboard = ImplGetSVData()->mpDefInst->CreateClipboard(args); + if (xClipboard.is()) + xClipboard->acquire(); + return xClipboard.get(); + } + else + { + return cppu::acquire(new CWinClipboard(context, "")); + } +} + +void CWinClipboard::onReleaseDataObject(CXNotifyingDataObject* theCaller) +{ + OSL_ASSERT(nullptr != theCaller); + + if (theCaller) + theCaller->lostOwnership(); + + // if the current caller is the one we currently hold, then set it to NULL + // because an external source must be the clipboardowner now + osl::MutexGuard aGuard(m_aContentCacheMutex); + + if (m_pCurrentClipContent == theCaller) + m_pCurrentClipContent = nullptr; +} + +void CWinClipboard::registerClipboardViewer() +{ + m_MtaOleClipboard.registerClipViewer(CWinClipboard::onClipboardContentChanged); +} + +void CWinClipboard::unregisterClipboardViewer() { m_MtaOleClipboard.registerClipViewer(nullptr); } + +void WINAPI CWinClipboard::onClipboardContentChanged() +{ + osl::MutexGuard aGuard(s_aClipboardSingletonMutex); + + // reassociation to instance through static member + if (nullptr != s_pCWinClipbImpl) + { + s_pCWinClipbImpl->m_foreignContent.clear(); + s_pCWinClipbImpl->notifyAllClipboardListener(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/WinClipboard.hxx b/vcl/win/dtrans/WinClipboard.hxx new file mode 100644 index 0000000000..fbaa1b2062 --- /dev/null +++ b/vcl/win/dtrans/WinClipboard.hxx @@ -0,0 +1,112 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#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" +#include "XNotifyingDataObject.hxx" + +// implements the XClipboard[Ex] ... interfaces +// for the clipboard viewer mechanism we need a static callback function +// and a static member to reassociate from this static function to the +// class instance +// watch out: we are using only one static member variable and not a list +// because we assume to be instantiated only once +// this will be assured by a OneInstanceFactory of the service and not +// by this class! + +class CWinClipboard final + : public comphelper::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard, + css::datatransfer::clipboard::XFlushableClipboard, + css::lang::XServiceInfo> +{ + friend STDMETHODIMP_(ULONG) CXNotifyingDataObject::Release(); + + css::uno::Reference<css::uno::XComponentContext> m_xContext; + const OUString m_itsName; + CMtaOleClipboard m_MtaOleClipboard; + CXNotifyingDataObject* m_pCurrentClipContent; + com::sun::star::uno::Reference<com::sun::star::datatransfer::XTransferable> m_foreignContent; + osl::Mutex m_aContentMutex; + osl::Mutex m_aContentCacheMutex; + comphelper::OInterfaceContainerHelper4<css::datatransfer::clipboard::XClipboardListener> + maClipboardListeners; + + void notifyAllClipboardListener(); + void onReleaseDataObject(CXNotifyingDataObject* theCaller); + + void registerClipboardViewer(); + void unregisterClipboardViewer(); + + static void WINAPI onClipboardContentChanged(); + +public: + CWinClipboard(const css::uno::Reference<css::uno::XComponentContext>& rxContext, + const OUString& aClipboardName); + virtual ~CWinClipboard() override; + + // XClipboard + virtual css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override; + virtual void SAL_CALL setContents( + const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable, + const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner) + override; + virtual OUString SAL_CALL getName() override; + + // XFlushableClipboard + virtual void SAL_CALL flushClipboard() override; + + // XClipboardEx + virtual sal_Int8 SAL_CALL getRenderingCapabilities() override; + + // XClipboardNotifier + virtual void SAL_CALL addClipboardListener( + const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) + override; + virtual void SAL_CALL removeClipboardListener( + const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) + override; + + // XServiceInfo + 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(); + + virtual void disposing(std::unique_lock<std::mutex>&) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/XNotifyingDataObject.cxx b/vcl/win/dtrans/XNotifyingDataObject.cxx new file mode 100644 index 0000000000..aab168f043 --- /dev/null +++ b/vcl/win/dtrans/XNotifyingDataObject.cxx @@ -0,0 +1,149 @@ +/* -*- 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 <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> +#include "XNotifyingDataObject.hxx" +#include "WinClipboard.hxx" + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using com::sun::star::uno::RuntimeException; +using com::sun::star::uno::Reference; + +CXNotifyingDataObject::CXNotifyingDataObject( + const IDataObjectPtr& aIDataObject, + const Reference< XTransferable >& aXTransferable, + const Reference< XClipboardOwner >& aXClipOwner, + CWinClipboard* const theWinClipoard) : + m_nRefCnt( 0 ), + m_aIDataObject( aIDataObject ), + m_XTransferable( aXTransferable ), + m_XClipboardOwner( aXClipOwner ), + m_pWinClipImpl( theWinClipoard ) +{ +} + +STDMETHODIMP CXNotifyingDataObject::QueryInterface( REFIID iid, void** ppvObject ) +{ + if ( nullptr == ppvObject ) + return E_INVALIDARG; + + HRESULT hr = E_NOINTERFACE; + + *ppvObject = nullptr; + if ( ( __uuidof( IUnknown ) == iid ) || + ( __uuidof( IDataObject ) == iid ) ) + { + *ppvObject = static_cast< IUnknown* >( this ); + static_cast<LPUNKNOWN>(*ppvObject)->AddRef( ); + hr = S_OK; + } + + return hr; +} + +STDMETHODIMP_(ULONG) CXNotifyingDataObject::AddRef( ) +{ + return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) ); +} + +STDMETHODIMP_(ULONG) CXNotifyingDataObject::Release( ) +{ + ULONG nRefCnt = + static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) ); + + if ( 0 == nRefCnt ) + { + if ( m_pWinClipImpl ) + m_pWinClipImpl->onReleaseDataObject( this ); + + delete this; + } + + return nRefCnt; +} + +STDMETHODIMP CXNotifyingDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) +{ + return m_aIDataObject->GetData(pFormatetc, pmedium); +} + +STDMETHODIMP CXNotifyingDataObject::EnumFormatEtc( + DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) +{ + return m_aIDataObject->EnumFormatEtc(dwDirection, ppenumFormatetc); +} + +STDMETHODIMP CXNotifyingDataObject::QueryGetData( FORMATETC * pFormatetc ) +{ + return m_aIDataObject->QueryGetData(pFormatetc); +} + +STDMETHODIMP CXNotifyingDataObject::GetDataHere( FORMATETC * lpFetc, STGMEDIUM * lpStgMedium ) +{ + return m_aIDataObject->GetDataHere(lpFetc, lpStgMedium); +} + +STDMETHODIMP CXNotifyingDataObject::GetCanonicalFormatEtc( FORMATETC * lpFetc, FORMATETC * lpCanonicalFetc ) +{ + return m_aIDataObject->GetCanonicalFormatEtc(lpFetc, lpCanonicalFetc); +} + +STDMETHODIMP CXNotifyingDataObject::SetData( FORMATETC * lpFetc, STGMEDIUM * lpStgMedium, BOOL bRelease ) +{ + return m_aIDataObject->SetData( lpFetc, lpStgMedium, bRelease ); +} + +STDMETHODIMP CXNotifyingDataObject::DAdvise( + FORMATETC * lpFetc, DWORD advf, IAdviseSink * lpAdvSink, DWORD* pdwConnection ) +{ + return m_aIDataObject->DAdvise( lpFetc, advf, lpAdvSink, pdwConnection ); +} + +STDMETHODIMP CXNotifyingDataObject::DUnadvise( DWORD dwConnection ) +{ + return m_aIDataObject->DUnadvise( dwConnection ); +} + +STDMETHODIMP CXNotifyingDataObject::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise ) +{ + return m_aIDataObject->EnumDAdvise( ppenumAdvise ); +} + +CXNotifyingDataObject::operator IDataObject*( ) +{ + return static_cast< IDataObject* >( this ); +} + +void CXNotifyingDataObject::lostOwnership( ) +{ + try + { + if (m_XClipboardOwner.is()) + m_XClipboardOwner->lostOwnership( + static_cast<XClipboardEx*>(m_pWinClipImpl), m_XTransferable); + } + catch(RuntimeException&) + { + TOOLS_WARN_EXCEPTION( "vcl", "" ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/XNotifyingDataObject.hxx b/vcl/win/dtrans/XNotifyingDataObject.hxx new file mode 100644 index 0000000000..408413a5de --- /dev/null +++ b/vcl/win/dtrans/XNotifyingDataObject.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <objidl.h> + +#include <systools/win32/comtools.hxx> + +/*-------------------------------------------------------------------------- + To implement the lostOwnership mechanism cleanly we need this wrapper + object +----------------------------------------------------------------------------*/ + +// forward +class CWinClipboard; + +class CXNotifyingDataObject final : public IDataObject +{ +public: + CXNotifyingDataObject( + const IDataObjectPtr& aIDataObject, + const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable, + const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& aXClipOwner, + CWinClipboard* const theWinClipoard); + + virtual ~CXNotifyingDataObject() {} + + // ole interface implementation + + //IUnknown interface methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; + STDMETHODIMP_( ULONG ) AddRef( ) override; + STDMETHODIMP_( ULONG ) Release( ) override; + + // IDataObject interface methods + STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override; + STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override; + STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override; + STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override; + STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override; + STDMETHODIMP DUnadvise( DWORD dwConnection ) override; + STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override; + + operator IDataObject*( ); + +private: + void lostOwnership( ); + + sal_Int32 m_nRefCnt; + IDataObjectPtr m_aIDataObject; + const css::uno::Reference< css::datatransfer::XTransferable > m_XTransferable; + const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner > m_XClipboardOwner; + CWinClipboard* const m_pWinClipImpl; + + friend class CWinClipboard; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/XTDataObject.cxx b/vcl/win/dtrans/XTDataObject.cxx new file mode 100644 index 0000000000..b3f5ccb303 --- /dev/null +++ b/vcl/win/dtrans/XTDataObject.cxx @@ -0,0 +1,751 @@ +/* -*- 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 <osl/diagnose.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/safeint.hxx> + +#include "XTDataObject.hxx" +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include "ImplHelper.hxx" +#include "DTransHelper.hxx" +#include "TxtCnvtHlp.hxx" +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> +#include <com/sun/star/awt/AsyncCallback.hpp> +#include <com/sun/star/awt/XCallback.hpp> +#include "FmtFilter.hxx" +#include <cppuhelper/implbase.hxx> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <shlobj.h> + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +namespace { + +void setupStgMedium( const FORMATETC& fetc, + CStgTransferHelper& stgTransHlp, + STGMEDIUM& stgmedium ) +{ + stgmedium.pUnkForRelease = nullptr; + + if ( fetc.cfFormat == CF_METAFILEPICT ) + { + stgmedium.tymed = TYMED_MFPICT; + stgmedium.hMetaFilePict = static_cast< HMETAFILEPICT >( stgTransHlp.getHGlobal( ) ); + } + else if ( fetc.cfFormat == CF_ENHMETAFILE ) + { + stgmedium.tymed = TYMED_ENHMF; + stgmedium.hEnhMetaFile = static_cast< HENHMETAFILE >( stgTransHlp.getHGlobal( ) ); + } + else if ( fetc.tymed & TYMED_HGLOBAL ) + { + stgmedium.tymed = TYMED_HGLOBAL; + stgmedium.hGlobal = stgTransHlp.getHGlobal( ); + } + else if ( fetc.tymed & TYMED_ISTREAM ) + { + stgmedium.tymed = TYMED_ISTREAM; + stgTransHlp.getIStream( &stgmedium.pstm ); + } + else + OSL_ASSERT( false ); +} + +/** + We need to destroy XTransferable in the main thread to avoid dead lock + when locking in the clipboard thread. So we transfer the ownership of the + XTransferable reference to this object and release it when the callback + is executed in main thread. +*/ +class AsyncDereference : public cppu::WeakImplHelper<css::awt::XCallback> +{ + Reference<XTransferable> maTransferable; + +public: + AsyncDereference(css::uno::Reference<css::datatransfer::XTransferable> const & rTransferable) + : maTransferable(rTransferable) + {} + + virtual void SAL_CALL notify(css::uno::Any const &) override + { + maTransferable.clear(); + } +}; + +// a helper class that will be thrown by the function validateFormatEtc + +class CInvalidFormatEtcException +{ +public: + HRESULT m_hr; + explicit CInvalidFormatEtcException( HRESULT hr ) : m_hr( hr ) {}; +}; + +void validateFormatEtc( LPFORMATETC lpFormatEtc ) +{ + OSL_ASSERT( lpFormatEtc ); + + if ( lpFormatEtc->lindex != -1 ) + throw CInvalidFormatEtcException( DV_E_LINDEX ); + + if ( !(lpFormatEtc->dwAspect & DVASPECT_CONTENT) && + !(lpFormatEtc->dwAspect & DVASPECT_SHORTNAME) ) + throw CInvalidFormatEtcException( DV_E_DVASPECT ); + + if ( !(lpFormatEtc->tymed & TYMED_HGLOBAL) && + !(lpFormatEtc->tymed & TYMED_ISTREAM) && + !(lpFormatEtc->tymed & TYMED_MFPICT) && + !(lpFormatEtc->tymed & TYMED_ENHMF) ) + throw CInvalidFormatEtcException( DV_E_TYMED ); + + if ( lpFormatEtc->cfFormat == CF_METAFILEPICT && + !(lpFormatEtc->tymed & TYMED_MFPICT) ) + throw CInvalidFormatEtcException( DV_E_TYMED ); + + if ( lpFormatEtc->cfFormat == CF_ENHMETAFILE && + !(lpFormatEtc->tymed & TYMED_ENHMF) ) + throw CInvalidFormatEtcException( DV_E_TYMED ); +} + +void invalidateStgMedium( STGMEDIUM& stgmedium ) +{ + stgmedium.tymed = TYMED_NULL; +} + +HRESULT translateStgExceptionCode( HRESULT hr ) +{ + HRESULT hrTransl; + + switch( hr ) + { + case STG_E_MEDIUMFULL: + hrTransl = hr; + break; + + default: + hrTransl = E_UNEXPECTED; + break; + } + + return hrTransl; +} + +// inline +void renderDataAndSetupStgMedium( + const sal_Int8* lpStorage, const FORMATETC& fetc, sal_uInt32 nInitStgSize, + sal_uInt32 nBytesToTransfer, STGMEDIUM& stgmedium ) +{ + OSL_PRECOND( !nInitStgSize || (nInitStgSize >= nBytesToTransfer), + "Memory size less than number of bytes to transfer" ); + + CStgTransferHelper stgTransfHelper( AUTO_INIT ); + + // setup storage size + if ( nInitStgSize > 0 ) + stgTransfHelper.init( nInitStgSize ); + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nBytesWritten = 0; + stgTransfHelper.write( lpStorage, nBytesToTransfer, &nBytesWritten ); + OSL_ASSERT( nBytesWritten == nBytesToTransfer ); +#else + stgTransfHelper.write( lpStorage, nBytesToTransfer ); +#endif + + setupStgMedium( fetc, stgTransfHelper, stgmedium ); +} + +} + +CXTDataObject::CXTDataObject( const Reference< XComponentContext >& rxContext, + const Reference< XTransferable >& aXTransferable ) + : m_nRefCnt( 0 ) + , m_XTransferable( aXTransferable ) + , m_XComponentContext( rxContext ) + , m_bFormatEtcContainerInitialized( false ) + , m_DataFormatTranslator( rxContext ) + , m_FormatRegistrar( rxContext, m_DataFormatTranslator ) +{ +} + +CXTDataObject::~CXTDataObject() +{ + css::awt::AsyncCallback::create(m_XComponentContext)->addCallback( + new AsyncDereference(m_XTransferable), + css::uno::Any()); +} + +// IUnknown->QueryInterface + +STDMETHODIMP CXTDataObject::QueryInterface( REFIID iid, void** ppvObject ) +{ + if ( nullptr == ppvObject ) + return E_INVALIDARG; + + HRESULT hr = E_NOINTERFACE; + + *ppvObject = nullptr; + if ( ( __uuidof( IUnknown ) == iid ) || + ( __uuidof( IDataObject ) == iid ) ) + { + *ppvObject = static_cast< IUnknown* >( this ); + static_cast<LPUNKNOWN>(*ppvObject)->AddRef( ); + hr = S_OK; + } + + return hr; +} + +// IUnknown->AddRef + +STDMETHODIMP_(ULONG) CXTDataObject::AddRef( ) +{ + return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) ); +} + +// IUnknown->Release + +STDMETHODIMP_(ULONG) CXTDataObject::Release( ) +{ + ULONG nRefCnt = + static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) ); + + if ( 0 == nRefCnt ) + delete this; + + return nRefCnt; +} + +STDMETHODIMP CXTDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) +{ + if ( !(pFormatetc && pmedium) ) + return E_INVALIDARG; + + try + { + // prepare data transfer + invalidateStgMedium( *pmedium ); + validateFormatEtc( pFormatetc ); + + // handle locale request, because locale is an artificial format for us + if ( CF_LOCALE == pFormatetc->cfFormat ) + renderLocaleAndSetupStgMedium( *pFormatetc, *pmedium ); + else if ( CF_UNICODETEXT == pFormatetc->cfFormat ) + renderUnicodeAndSetupStgMedium( *pFormatetc, *pmedium ); + else + renderAnyDataAndSetupStgMedium( *pFormatetc, *pmedium ); + } + catch(UnsupportedFlavorException&) + { + HRESULT hr = DV_E_FORMATETC; + + CFormatEtc aFormatetc(*pFormatetc); + if (CFormatRegistrar::isSynthesizeableFormat(aFormatetc)) + hr = renderSynthesizedFormatAndSetupStgMedium( *pFormatetc, *pmedium ); + + return hr; + } + catch( CInvalidFormatEtcException& ex ) + { + return ex.m_hr; + } + catch( CStgTransferHelper::CStgTransferException& ex ) + { + return translateStgExceptionCode( ex.m_hr ); + } + catch(...) + { + return E_UNEXPECTED; + } + + return S_OK; +} + +//inline +void CXTDataObject::renderLocaleAndSetupStgMedium( + FORMATETC const & fetc, STGMEDIUM& stgmedium ) +{ + if ( !m_FormatRegistrar.hasSynthesizedLocale( ) ) + throw CInvalidFormatEtcException( DV_E_FORMATETC ); + LCID lcid = CFormatRegistrar::getSynthesizedLocale( ); + renderDataAndSetupStgMedium( + reinterpret_cast< sal_Int8* >( &lcid ), + fetc, + 0, + sizeof( LCID ), + stgmedium ); +} + +void CXTDataObject::renderUnicodeAndSetupStgMedium( + FORMATETC const & fetc, STGMEDIUM& stgmedium ) +{ + DataFlavor aFlavor = formatEtcToDataFlavor( fetc ); + + Any aAny = m_XTransferable->getTransferData( aFlavor ); + + // unfortunately not all transferables fulfill the + // spec. and do throw an UnsupportedFlavorException + // so we must check the any + if ( !aAny.hasValue( ) ) + { + OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" ); + throw UnsupportedFlavorException( ); + } + + OUString aText; + aAny >>= aText; + + sal_uInt32 nBytesToTransfer = aText.getLength( ) * sizeof( sal_Unicode ); + + // to be sure there is an ending 0 + sal_uInt32 nRequiredMemSize = nBytesToTransfer + sizeof( sal_Unicode ); + + renderDataAndSetupStgMedium( + reinterpret_cast< const sal_Int8* >( aText.getStr( ) ), + fetc, + nRequiredMemSize, + nBytesToTransfer, + stgmedium ); +} + +void CXTDataObject::renderAnyDataAndSetupStgMedium( + FORMATETC& fetc, STGMEDIUM& stgmedium ) +{ + DataFlavor aFlavor = formatEtcToDataFlavor( fetc ); + + Any aAny = m_XTransferable->getTransferData( aFlavor ); + + // unfortunately not all transferables fulfill the + // spec. and do throw an UnsupportedFlavorException + // so we must check the any + if ( !aAny.hasValue( ) ) + { + OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" ); + throw UnsupportedFlavorException( ); + } + + Sequence< sal_Int8 > clipDataStream; + aAny >>= clipDataStream; + + sal_uInt32 nRequiredMemSize = 0; + if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) ) + nRequiredMemSize = sizeof( sal_Int8 ) * clipDataStream.getLength( ) + 1; + + // prepare data for transmission + // #i124085# DIBV5 should not happen for now, but keep as hint here + if ( CF_DIBV5 == fetc.cfFormat || CF_DIB == fetc.cfFormat ) + { +#ifdef DBG_UTIL + if(CF_DIBV5 == fetc.cfFormat) + { + OSL_ENSURE(o3tl::make_unsigned(clipDataStream.getLength()) > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV5HEADER)), "Wrong size on CF_DIBV5 data (!)"); + } + else // CF_DIB == fetc.cfFormat + { + OSL_ENSURE(o3tl::make_unsigned(clipDataStream.getLength()) > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)), "Wrong size on CF_DIB data (!)"); + } +#endif + + // remove BITMAPFILEHEADER + clipDataStream = OOBmpToWinDIB( clipDataStream ); + } + + if ( CF_METAFILEPICT == fetc.cfFormat ) + { + stgmedium.tymed = TYMED_MFPICT; + stgmedium.hMetaFilePict = OOMFPictToWinMFPict( clipDataStream ); + stgmedium.pUnkForRelease = nullptr; + } + else if( CF_ENHMETAFILE == fetc.cfFormat ) + { + stgmedium.tymed = TYMED_ENHMF; + stgmedium.hMetaFilePict = OOMFPictToWinENHMFPict( clipDataStream ); + stgmedium.pUnkForRelease = nullptr; + } + else + renderDataAndSetupStgMedium( + clipDataStream.getArray( ), + fetc, + nRequiredMemSize, + clipDataStream.getLength( ), + stgmedium ); +} + +HRESULT CXTDataObject::renderSynthesizedFormatAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ) +{ + HRESULT hr = S_OK; + + try + { + if ( CF_UNICODETEXT == fetc.cfFormat ) + // the transferable seems to have only text + renderSynthesizedUnicodeAndSetupStgMedium( fetc, stgmedium ); + else if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) ) + // the transferable seems to have only unicode text + renderSynthesizedTextAndSetupStgMedium( fetc, stgmedium ); + else + // the transferable seems to have only text/html + renderSynthesizedHtmlAndSetupStgMedium( fetc, stgmedium ); + } + catch(UnsupportedFlavorException&) + { + hr = DV_E_FORMATETC; + } + catch( CInvalidFormatEtcException& ) + { + OSL_FAIL( "Unexpected exception" ); + } + catch( CStgTransferHelper::CStgTransferException& ex ) + { + return translateStgExceptionCode( ex.m_hr ); + } + catch(...) + { + hr = E_UNEXPECTED; + } + + return hr; +} + +// the transferable must have only text, so we will synthesize unicode text + +void CXTDataObject::renderSynthesizedUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium ) +{ + OSL_ASSERT( CF_UNICODETEXT == fetc.cfFormat ); + + Any aAny = m_XTransferable->getTransferData( m_FormatRegistrar.getRegisteredTextFlavor( ) ); + + // unfortunately not all transferables fulfill the + // spec. and do throw an UnsupportedFlavorException + // so we must check the any + if ( !aAny.hasValue( ) ) + { + OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" ); + throw UnsupportedFlavorException( ); + } + + Sequence< sal_Int8 > aText; + aAny >>= aText; + + CStgTransferHelper stgTransfHelper; + + MultiByteToWideCharEx( + CFormatRegistrar::getRegisteredTextCodePage( ), + reinterpret_cast< char* >( aText.getArray( ) ), + aText.getLength( ), + stgTransfHelper ); + + setupStgMedium( fetc, stgTransfHelper, stgmedium ); +} + +// the transferable must have only unicode text so we will synthesize text + +void CXTDataObject::renderSynthesizedTextAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ) +{ + OSL_ASSERT( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) ); + + DataFlavor aFlavor = formatEtcToDataFlavor( + CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT ) ); + + Any aAny = m_XTransferable->getTransferData( aFlavor ); + + // unfortunately not all transferables fulfill the + // spec. and do throw an UnsupportedFlavorException + // so we must check the any + if ( !aAny.hasValue( ) ) + { + OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" ); + throw UnsupportedFlavorException( ); + } + + OUString aUnicodeText; + aAny >>= aUnicodeText; + + CStgTransferHelper stgTransfHelper; + + WideCharToMultiByteEx( + GetACP( ), + o3tl::toW( aUnicodeText.getStr( ) ), + aUnicodeText.getLength( ), + stgTransfHelper ); + + setupStgMedium( fetc, stgTransfHelper, stgmedium ); +} + +void CXTDataObject::renderSynthesizedHtmlAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ) +{ + OSL_ASSERT( CDataFormatTranslator::isHTMLFormat( fetc.cfFormat ) ); + + DataFlavor aFlavor; + + // creating a DataFlavor on the fly + aFlavor.MimeType = "text/html"; + aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get(); + + Any aAny = m_XTransferable->getTransferData( aFlavor ); + + // unfortunately not all transferables fulfill the + // spec. and do throw an UnsupportedFlavorException + // so we must check the any + if ( !aAny.hasValue( ) ) + { + OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" ); + throw UnsupportedFlavorException( ); + } + + Sequence< sal_Int8 > aTextHtmlSequence; + aAny >>= aTextHtmlSequence; + + Sequence< sal_Int8 > aHTMLFormatSequence = TextHtmlToHTMLFormat( aTextHtmlSequence ); + + sal_uInt32 nBytesToTransfer = aHTMLFormatSequence.getLength( ); + + renderDataAndSetupStgMedium( + reinterpret_cast< const sal_Int8* >( aHTMLFormatSequence.getArray( ) ), + fetc, + 0, + nBytesToTransfer, + stgmedium ); +} + +// IDataObject->EnumFormatEtc + +STDMETHODIMP CXTDataObject::EnumFormatEtc( + DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) +{ + if ( nullptr == ppenumFormatetc ) + return E_INVALIDARG; + + if ( DATADIR_SET == dwDirection ) + return E_NOTIMPL; + + *ppenumFormatetc = nullptr; + + InitializeFormatEtcContainer( ); + + HRESULT hr; + if ( DATADIR_GET == dwDirection ) + { + *ppenumFormatetc = new CEnumFormatEtc( this, m_FormatEtcContainer ); + static_cast< LPUNKNOWN >( *ppenumFormatetc )->AddRef( ); + + hr = S_OK; + } + else + hr = E_INVALIDARG; + + return hr; +} + +// IDataObject->QueryGetData + +STDMETHODIMP CXTDataObject::QueryGetData( FORMATETC * pFormatetc ) +{ + if ( (nullptr == pFormatetc) || IsBadReadPtr( pFormatetc, sizeof( FORMATETC ) ) ) + return E_INVALIDARG; + + InitializeFormatEtcContainer( ); + + CFormatEtc aFormatetc(*pFormatetc); + return m_FormatEtcContainer.hasFormatEtc(aFormatetc) ? S_OK : S_FALSE; +} + +// IDataObject->GetDataHere + +STDMETHODIMP CXTDataObject::GetDataHere( FORMATETC *, STGMEDIUM * ) +{ + return E_NOTIMPL; +} + +// IDataObject->GetCanonicalFormatEtc + +STDMETHODIMP CXTDataObject::GetCanonicalFormatEtc( FORMATETC *, FORMATETC * ) +{ + return E_NOTIMPL; +} + +// IDataObject->SetData + +STDMETHODIMP CXTDataObject::SetData( FORMATETC *, STGMEDIUM *, BOOL ) +{ + return E_NOTIMPL; +} + +// IDataObject->DAdvise + +STDMETHODIMP CXTDataObject::DAdvise( FORMATETC *, DWORD, IAdviseSink *, DWORD * ) +{ + return E_NOTIMPL; +} + +// IDataObject->DUnadvise + +STDMETHODIMP CXTDataObject::DUnadvise( DWORD ) +{ + return E_NOTIMPL; +} + +// IDataObject->EnumDAdvise + +STDMETHODIMP CXTDataObject::EnumDAdvise( IEnumSTATDATA ** ) +{ + return E_NOTIMPL; +} + +// for our convenience + +CXTDataObject::operator IDataObject*( ) +{ + return static_cast< IDataObject* >( this ); +} + +inline +DataFlavor CXTDataObject::formatEtcToDataFlavor( const FORMATETC& aFormatEtc ) const +{ + DataFlavor aFlavor; + + if ( m_FormatRegistrar.hasSynthesizedLocale( ) ) + aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc( + aFormatEtc.cfFormat, CFormatRegistrar::getSynthesizedLocale()); + else + aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(aFormatEtc.cfFormat); + + if ( !aFlavor.MimeType.getLength( ) ) + throw UnsupportedFlavorException( ); + + return aFlavor; +} + +inline void CXTDataObject::InitializeFormatEtcContainer( ) +{ + if ( !m_bFormatEtcContainerInitialized ) + { + m_FormatRegistrar.RegisterFormats( m_XTransferable, m_FormatEtcContainer ); + m_bFormatEtcContainerInitialized = true; + } +} + +CEnumFormatEtc::CEnumFormatEtc( LPUNKNOWN lpUnkOuter, const CFormatEtcContainer& aFormatEtcContainer ) : + m_nRefCnt( 0 ), + m_lpUnkOuter( lpUnkOuter ), + m_FormatEtcContainer( aFormatEtcContainer ) +{ + Reset( ); +} + +// IUnknown->QueryInterface + +STDMETHODIMP CEnumFormatEtc::QueryInterface( REFIID iid, void** ppvObject ) +{ + if ( nullptr == ppvObject ) + return E_INVALIDARG; + + HRESULT hr = E_NOINTERFACE; + + *ppvObject = nullptr; + + if ( ( __uuidof( IUnknown ) == iid ) || + ( __uuidof( IEnumFORMATETC ) == iid ) ) + { + *ppvObject = static_cast< IUnknown* >( this ); + static_cast< LPUNKNOWN >( *ppvObject )->AddRef( ); + hr = S_OK; + } + + return hr; +} + +// IUnknown->AddRef + +STDMETHODIMP_(ULONG) CEnumFormatEtc::AddRef( ) +{ + // keep the dataobject alive + m_lpUnkOuter->AddRef( ); + return InterlockedIncrement( &m_nRefCnt ); +} + +// IUnknown->Release + +STDMETHODIMP_(ULONG) CEnumFormatEtc::Release( ) +{ + // release the outer dataobject + m_lpUnkOuter->Release( ); + + ULONG nRefCnt = InterlockedDecrement( &m_nRefCnt ); + if ( 0 == nRefCnt ) + delete this; + + return nRefCnt; +} + +// IEnumFORMATETC->Next + +STDMETHODIMP CEnumFormatEtc::Next( ULONG nRequested, FORMATETC * lpDest, ULONG* lpFetched ) +{ + if ( ( nRequested < 1 ) || + (( nRequested > 1 ) && ( nullptr == lpFetched )) || + IsBadWritePtr( lpDest, sizeof( FORMATETC ) * nRequested ) ) + return E_INVALIDARG; + + sal_uInt32 nFetched = m_FormatEtcContainer.nextFormatEtc( lpDest, nRequested ); + + if ( nullptr != lpFetched ) + *lpFetched = nFetched; + + return (nFetched == nRequested) ? S_OK : S_FALSE; +} + +// IEnumFORMATETC->Skip + +STDMETHODIMP CEnumFormatEtc::Skip( ULONG celt ) +{ + return m_FormatEtcContainer.skipFormatEtc( celt ) ? S_OK : S_FALSE; +} + +// IEnumFORMATETC->Reset + +STDMETHODIMP CEnumFormatEtc::Reset( ) +{ + m_FormatEtcContainer.beginEnumFormatEtc( ); + return S_OK; +} + +// IEnumFORMATETC->Clone + +STDMETHODIMP CEnumFormatEtc::Clone( IEnumFORMATETC** ppenum ) +{ + if ( nullptr == ppenum ) + return E_INVALIDARG; + + *ppenum = new CEnumFormatEtc( m_lpUnkOuter, m_FormatEtcContainer ); + static_cast< LPUNKNOWN >( *ppenum )->AddRef( ); + + return S_OK; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/XTDataObject.hxx b/vcl/win/dtrans/XTDataObject.hxx new file mode 100644 index 0000000000..77f8c53f29 --- /dev/null +++ b/vcl/win/dtrans/XTDataObject.hxx @@ -0,0 +1,134 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> + +#include "DataFmtTransl.hxx" + +#include "FetcList.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> +#include <objidl.h> + +/*-------------------------------------------------------------------------- + - the function principle of the windows clipboard: + a data provider offers all formats he can deliver on the clipboard + a clipboard client ask for the available formats on the clipboard + and decides if there is a format he can use + if there is one, he requests the data in this format + + - This class inherits from IDataObject and so can be placed on the + OleClipboard. The class wraps a transferable object which is the + original DataSource + - DataFlavors offered by this transferable will be translated into + appropriate clipboard formats + - if the transferable contains text data always text and unicodetext + will be offered or vice versa + - text data will be automatically converted between text and unicode text + - although the transferable may support text in different charsets + (codepages) only text in one codepage can be offered by the clipboard + +----------------------------------------------------------------------------*/ + +class CStgTransferHelper; + +class CXTDataObject : public IDataObject +{ +public: + CXTDataObject( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable ); + virtual ~CXTDataObject(); + + // ole interface implementation + + //IUnknown interface methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override; + STDMETHODIMP_( ULONG ) AddRef( ) override; + STDMETHODIMP_( ULONG ) Release( ) override; + + // IDataObject interface methods + STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override; + STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override; + STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override; + STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override; + STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override; + STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override; + STDMETHODIMP DUnadvise( DWORD dwConnection ) override; + STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override; + + operator IDataObject*( ); + +private: + css::datatransfer::DataFlavor formatEtcToDataFlavor( const FORMATETC& aFormatEtc ) const; + + void renderLocaleAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium ); + void renderUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium ); + void renderAnyDataAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ); + + HRESULT renderSynthesizedFormatAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ); + void renderSynthesizedUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium ); + void renderSynthesizedTextAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ); + void renderSynthesizedHtmlAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium ); + + inline void InitializeFormatEtcContainer( ); + +private: + LONG m_nRefCnt; + css::uno::Reference< css::datatransfer::XTransferable > m_XTransferable; + css::uno::Reference< css::uno::XComponentContext> m_XComponentContext; + CFormatEtcContainer m_FormatEtcContainer; + bool m_bFormatEtcContainerInitialized; + CDataFormatTranslator m_DataFormatTranslator; + CFormatRegistrar m_FormatRegistrar; +}; + +class CEnumFormatEtc : public IEnumFORMATETC +{ +public: + CEnumFormatEtc( LPUNKNOWN lpUnkOuter, const CFormatEtcContainer& aFormatEtcContainer ); + virtual ~CEnumFormatEtc() {} + + // IUnknown + STDMETHODIMP QueryInterface( REFIID iid, void** ppvObject ) override; + STDMETHODIMP_( ULONG ) AddRef( ) override; + STDMETHODIMP_( ULONG ) Release( ) override; + + //IEnumFORMATETC + STDMETHODIMP Next( ULONG nRequested, FORMATETC * lpDest, ULONG* lpFetched ) override; + STDMETHODIMP Skip( ULONG celt ) override; + STDMETHODIMP Reset( ) override; + STDMETHODIMP Clone( IEnumFORMATETC** ppenum ) override; + +private: + LONG m_nRefCnt; + LPUNKNOWN m_lpUnkOuter; + CFormatEtcContainer m_FormatEtcContainer; +}; + +typedef CEnumFormatEtc *PCEnumFormatEtc; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/clipboardmanager.cxx b/vcl/win/dtrans/clipboardmanager.cxx new file mode 100644 index 0000000000..bff5aec49f --- /dev/null +++ b/vcl/win/dtrans/clipboardmanager.cxx @@ -0,0 +1,198 @@ +/* -*- 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 "clipboardmanager.hxx" +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <rtl/ref.hxx> + +using namespace com::sun::star::container; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace cppu; +using namespace osl; + +using ::dtrans::ClipboardManager; + +static std::mutex g_InstanceGuard; +static rtl::Reference<ClipboardManager> g_Instance; +static bool g_Disposed = false; + + +ClipboardManager::ClipboardManager(): + m_aDefaultName(OUString("default")) +{ +} + +ClipboardManager::~ClipboardManager() +{ +} + +OUString SAL_CALL ClipboardManager::getImplementationName( ) +{ + return "com.sun.star.comp.datatransfer.ClipboardManager"; +} + +sal_Bool SAL_CALL ClipboardManager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL ClipboardManager::getSupportedServiceNames( ) +{ + return { "com.sun.star.datatransfer.clipboard.ClipboardManager" }; +} + +Reference< XClipboard > SAL_CALL ClipboardManager::getClipboard( const OUString& aName ) +{ + std::unique_lock aGuard(m_aMutex); + + // object is disposed already + if (m_bDisposed) + throw DisposedException("object is disposed.", + static_cast < XClipboardManager * > (this)); + + ClipboardMap::iterator iter = + m_aClipboardMap.find(aName.getLength() ? aName : m_aDefaultName); + + if (iter != m_aClipboardMap.end()) + return iter->second; + + throw NoSuchElementException(aName, static_cast < XClipboardManager * > (this)); +} + +void SAL_CALL ClipboardManager::addClipboard( const Reference< XClipboard >& xClipboard ) +{ + OSL_ASSERT(xClipboard.is()); + + // check parameter + if (!xClipboard.is()) + throw IllegalArgumentException("empty reference", + static_cast < XClipboardManager * > (this), 1); + + // the name "default" is reserved for internal use + OUString aName = xClipboard->getName(); + if ( m_aDefaultName == aName ) + throw IllegalArgumentException("name reserved", + static_cast < XClipboardManager * > (this), 1); + + // try to add new clipboard to the list + std::unique_lock aGuard(m_aMutex); + if (!m_bDisposed) + { + std::pair< const OUString, Reference< XClipboard > > value ( + aName.getLength() ? aName : m_aDefaultName, + xClipboard ); + + std::pair< ClipboardMap::iterator, bool > p = m_aClipboardMap.insert(value); + aGuard.unlock(); + + // insert failed, element must exist already + if (!p.second) + throw ElementExistException(aName, static_cast < XClipboardManager * > (this)); + + // request disposing notifications + Reference< XComponent > xComponent(xClipboard, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast < XEventListener * > (this)); + } +} + +void SAL_CALL ClipboardManager::removeClipboard( const OUString& aName ) +{ + std::unique_lock aGuard(m_aMutex); + if (!m_bDisposed) + m_aClipboardMap.erase(aName.getLength() ? aName : m_aDefaultName ); +} + +Sequence< OUString > SAL_CALL ClipboardManager::listClipboardNames() +{ + std::unique_lock aGuard(m_aMutex); + + if (m_bDisposed) + throw DisposedException("object is disposed.", + static_cast < XClipboardManager * > (this)); + + return comphelper::mapKeysToSequence(m_aClipboardMap); +} + +void ClipboardManager::disposing(std::unique_lock<std::mutex>& rGuard) +{ + rGuard.unlock(); + + { + std::unique_lock aGuard(g_InstanceGuard); + g_Instance.clear(); + g_Disposed = true; + } + + // removeClipboard is still allowed here, so make a copy of the + // list (to ensure integrity) and clear the original. + rGuard.lock(); + ClipboardMap aCopy; + std::swap(aCopy, m_aClipboardMap); + rGuard.unlock(); + + // dispose all clipboards still in list + for (auto const& elem : aCopy) + { + Reference< XComponent > xComponent(elem.second, UNO_QUERY); + if (xComponent.is()) + { + try + { + xComponent->removeEventListener(static_cast < XEventListener * > (this)); + xComponent->dispose(); + } + catch (const Exception&) + { + // exceptions can be safely ignored here. + } + } + } +} + +void SAL_CALL ClipboardManager::disposing( const EventObject& event ) +{ + Reference < XClipboard > xClipboard(event.Source, UNO_QUERY); + + if (xClipboard.is()) + removeClipboard(xClipboard->getName()); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_ClipboardManager_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + std::unique_lock aGuard(g_InstanceGuard); + if (g_Disposed) + return nullptr; + if (!g_Instance) + g_Instance.set(new ClipboardManager()); + return cppu::acquire(g_Instance.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/clipboardmanager.hxx b/vcl/win/dtrans/clipboardmanager.hxx new file mode 100644 index 0000000000..27f9ddbdae --- /dev/null +++ b/vcl/win/dtrans/clipboardmanager.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ + +#pragma once + +#include <comphelper/compbase.hxx> + +#include <com/sun/star/datatransfer/clipboard/XClipboardManager.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <map> + +typedef std::map< OUString, css::uno::Reference< css::datatransfer::clipboard::XClipboard > > ClipboardMap; + +namespace dtrans +{ + + class ClipboardManager : public ::comphelper::WeakComponentImplHelper < + css::datatransfer::clipboard::XClipboardManager, + css::lang::XEventListener, + css::lang::XServiceInfo > + { + ClipboardMap m_aClipboardMap; + + const OUString m_aDefaultName; + + virtual ~ClipboardManager() override; + protected: + using WeakComponentImplHelperBase::disposing; + public: + + ClipboardManager(); + + /* + * XServiceInfo + */ + + 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; + + /* + * XComponent + */ + + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + /* + * XEventListener + */ + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + /* + * XClipboardManager + */ + + virtual css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL getClipboard( const OUString& aName ) override; + + virtual void SAL_CALL addClipboard( const css::uno::Reference< css::datatransfer::clipboard::XClipboard >& xClipboard ) override; + + virtual void SAL_CALL removeClipboard( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL listClipboardNames( ) override; + + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/ftransl.cxx b/vcl/win/dtrans/ftransl.cxx new file mode 100644 index 0000000000..20056bba0e --- /dev/null +++ b/vcl/win/dtrans/ftransl.cxx @@ -0,0 +1,550 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> + +#include "ftransl.hxx" +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/datatransfer/XMimeContentType.hpp> +#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include "ImplHelper.hxx" + +#include <shlobj.h> + +#define CPPUTYPE_SEQSALINT8 cppu::UnoType<Sequence< sal_Int8 >>::get() +#define CPPUTYPE_DEFAULT CPPUTYPE_SEQSALINT8 + +constexpr OUString Windows_FormatName = u"windows_formatname"_ustr; +const css::uno::Type CppuType_ByteSequence = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(); +const css::uno::Type CppuType_String = ::cppu::UnoType<OUString>::get(); + +using namespace osl; +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::container; + +namespace +{ + +struct FormatEntry +{ + FormatEntry( + const char *mime_content_type, + const char *human_presentable_name, + const char *native_format_name, + CLIPFORMAT std_clipboard_format_id, + css::uno::Type const & cppu_type + ); + + css::datatransfer::DataFlavor aDataFlavor; + OUString aNativeFormatName; + sal_Int32 aStandardFormatId; +}; + +} + +FormatEntry::FormatEntry( + const char *mime_content_type, + const char *human_presentable_name, + const char *native_format_name, + CLIPFORMAT std_clipboard_format_id, + css::uno::Type const & cppu_type) + : aDataFlavor( OUString::createFromAscii(mime_content_type), OUString::createFromAscii(human_presentable_name), cppu_type) +{ + if (native_format_name) + aNativeFormatName = OUString::createFromAscii(native_format_name); + else + aNativeFormatName = OUString::createFromAscii(human_presentable_name); + + aStandardFormatId = std_clipboard_format_id; +} + +// to optimize searching we add all entries with a +// standard clipboard format number first, in the +// table before the entries with CF_INVALID +// if we are searching for a standard clipboard +// format number we can stop if we find the first +// CF_INVALID + +const std::vector< FormatEntry > g_TranslTable { + //SotClipboardFormatId::DIF + FormatEntry("application/x-openoffice-dif;windows_formatname=\"DIF\"", "DIF", "DIF", CF_DIF, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::BITMAP + + // #i124085# CF_DIBV5 disabled, leads to problems at export. To fully support, using + // an own mime-type may be necessary. I have tried that, but saw no real advantages + // with different apps when exchanging bitmap-based data. So, disabled for now. At + // the same time increased png format exchange for better interoperability + // FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_DIBV5, CPPUTYPE_DEFAULT), + + FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_DIB, CPPUTYPE_DEFAULT), + FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_BITMAP, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STRING + FormatEntry("text/plain;charset=utf-16", "Unicode-Text", "", CF_UNICODETEXT, CppuType_String), + // Format Locale - for internal use + FormatEntry("application/x-openoffice-locale;windows_formatname=\"Locale\"", "Locale", "Locale", CF_LOCALE, CPPUTYPE_DEFAULT), + // SOT_FORMAT_WMF + FormatEntry("application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", "Windows MetaFile", "Image WMF", CF_METAFILEPICT, CPPUTYPE_DEFAULT), + // SOT_FORMAT_EMF + FormatEntry("application/x-openoffice-emf;windows_formatname=\"Image EMF\"", "Windows Enhanced MetaFile", "Image EMF", CF_ENHMETAFILE, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::FILE_LIST + FormatEntry("application/x-openoffice-filelist;windows_formatname=\"FileList\"", "FileList", "FileList", CF_HDROP, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SYLK + FormatEntry("application/x-openoffice-sylk;windows_formatname=\"Sylk\"", "Sylk", "Sylk", CF_SYLK, CPPUTYPE_DEFAULT ), + // SotClipboardFormatId::GDIMETAFILE + FormatEntry("application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", "GDIMetaFile", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::PRIVATE + FormatEntry("application/x-openoffice-private;windows_formatname=\"Private\"", "Private", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::SIMPLE_FILE + FormatEntry("application/x-openoffice-file;windows_formatname=\"FileNameW\"", "FileName", nullptr, CF_INVALID, CppuType_String), + // SotClipboardFormatId::RTF + FormatEntry("text/rtf", "Rich Text Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::DRAWING + FormatEntry("application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"", "Drawing Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::SVXB + FormatEntry("application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"", "SVXB (StarView Bitmap/Animation)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::SVIM + FormatEntry("application/x-openoffice-svim;windows_formatname=\"SVIM (StarView ImageMap)\"", "SVIM (StarView ImageMap)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::XFA + FormatEntry("application/x-libreoffice-xfa;windows_formatname=\"XFA (XOutDev FillAttr Any)\"", "XFA (XOutDev FillAttr Any)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT + FormatEntry("application/vnd.oasis.opendocument.text-flat-xml", "EditEngine ODF", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::INTERNALLINK_STATE + FormatEntry("application/x-openoffice-internallink-state;windows_formatname=\"StatusInfo of SvxInternalLink\"", "StatusInfo of SvxInternalLink", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::SOLK + FormatEntry("application/x-openoffice-solk;windows_formatname=\"SOLK (StarOffice Link)\"", "SOLK (StarOffice Link)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::NETSCAPE_BOOKMARK + FormatEntry("application/x-openoffice-netscape-bookmark;windows_formatname=\"Netscape Bookmark\"", "Netscape Bookmark", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::TREELISTBOX + FormatEntry("application/x-openoffice-treelistbox;windows_formatname=\"SV_LBOX_DD_FORMAT\"", "SV_LBOX_DD_FORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::NATIVE + FormatEntry("application/x-openoffice-native;windows_formatname=\"Native\"", "Native", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::OWNERLINK + FormatEntry("application/x-openoffice-ownerlink;windows_formatname=\"OwnerLink\"", "OwnerLink", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARSERVER + FormatEntry("application/x-openoffice-starserver;windows_formatname=\"StarServerFormat\"", "StarServerFormat", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STAROBJECT + FormatEntry("application/x-openoffice-starobject;windows_formatname=\"StarObjectFormat\"", "StarObjectFormat", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::APPLETOBJECT + FormatEntry("application/x-openoffice-appletobject;windows_formatname=\"Applet Object\"", "Applet Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::PLUGIN_OBJECT + FormatEntry("application/x-openoffice-plugin-object;windows_formatname=\"PlugIn Object\"", "PlugIn Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARWRITER_30 + FormatEntry("application/x-openoffice-starwriter-30;windows_formatname=\"StarWriter 3.0\"", "StarWriter 3.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITER_40 + FormatEntry("application/x-openoffice-starwriter-40;windows_formatname=\"StarWriter 4.0\"", "StarWriter 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITER_50 + FormatEntry("application/x-openoffice-starwriter-50;windows_formatname=\"StarWriter 5.0\"", "StarWriter 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITERWEB_40 + FormatEntry("application/x-openoffice-starwriterweb-40;windows_formatname=\"StarWriter/Web 4.0\"", "StarWriter/Web 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITERWEB_50 + FormatEntry("application/x-openoffice-starwriterweb-50;windows_formatname=\"StarWriter/Web 5.0\"", "StarWriter/Web 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITERGLOB_40 + FormatEntry("application/x-openoffice-starwriterglob-40;windows_formatname=\"StarWriter/Global 4.0\"", "StarWriter/Global 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARWRITERGLOB_50 + FormatEntry("application/x-openoffice-starwriterglob-50;windows_formatname=\"StarWriter/Global 5.0\"", "StarWriter/Global 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARDRAW + FormatEntry("application/x-openoffice-stardraw;windows_formatname=\"StarDrawDocument\"", "StarDrawDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARDRAW_40 + FormatEntry("application/x-openoffice-stardraw-40;windows_formatname=\"StarDrawDocument 4.0\"", "StarDrawDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARIMPRESS_50 + FormatEntry("application/x-openoffice-starimpress-50;windows_formatname=\"StarImpress 5.0\"", "StarImpress 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARDRAW_50 + FormatEntry("application/x-openoffice-stardraw-50;windows_formatname=\"StarDraw 5.0\"", "StarDraw 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARCALC + FormatEntry("application/x-openoffice-starcalc;windows_formatname=\"StarCalcDocument\"", "StarCalcDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARCALC_40 + FormatEntry("application/x-openoffice-starcalc-40;windows_formatname=\"StarCalc 4.0\"", "StarCalc 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARCALC_50 + FormatEntry("application/x-openoffice-starcalc-50;windows_formatname=\"StarCalc 5.0\"", "StarCalc 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARCHART + FormatEntry("application/x-openoffice-starchart;windows_formatname=\"StarChartDocument\"", "StarChartDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARCHART_40 + FormatEntry("application/x-openoffice-starchart-40;windows_formatname=\"StarChartDocument 4.0\"", "StarChartDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::STARCHART_50 + FormatEntry("application/x-openoffice-starchart-50;windows_formatname=\"StarChart 5.0\"", "StarChart 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARIMAGE + FormatEntry("application/x-openoffice-starimage;windows_formatname=\"StarImageDocument\"", "StarImageDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARIMAGE_40 + FormatEntry("application/x-openoffice-starimage-40;windows_formatname=\"StarImageDocument 4.0\"", "StarImageDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARIMAGE_50 + FormatEntry("application/x-openoffice-starimage-50;windows_formatname=\"StarImage 5.0\"", "StarImage 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARMATH + FormatEntry("application/x-openoffice-starmath;windows_formatname=\"StarMath\"", "StarMath", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARMATH_40 + FormatEntry("application/x-openoffice-starmath-40;windows_formatname=\"StarMathDocument 4.0\"", "StarMathDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARMATH_50 + FormatEntry("application/x-openoffice-starmath-50;windows_formatname=\"StarMath 5.0\"", "StarMath 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STAROBJECT_PAINTDOC + FormatEntry("application/x-openoffice-starobject-paintdoc;windows_formatname=\"StarObjectPaintDocument\"", "StarObjectPaintDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::FILLED_AREA + FormatEntry("application/x-openoffice-filled-area;windows_formatname=\"FilledArea\"", "FilledArea", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::HTML + FormatEntry("text/html", "HTML (HyperText Markup Language)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::HTML_SIMPLE + FormatEntry("application/x-openoffice-html-simple;windows_formatname=\"HTML Format\"", "HTML Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::CHAOS + FormatEntry("application/x-openoffice-chaos;windows_formatname=\"FORMAT_CHAOS\"", "FORMAT_CHAOS", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::CNT_MSGATTACHFILE + FormatEntry("application/x-openoffice-msgattachfile;windows_formatname=\"CNT_MSGATTACHFILE_FORMAT\"", "CNT_MSGATTACHFILE_FORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::BIFF_5 + FormatEntry("application/x-openoffice-biff5;windows_formatname=\"Biff5\"", "Biff5", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::BIFF__5 + FormatEntry("application/x-openoffice-biff-5;windows_formatname=\"Biff 5\"", "Biff 5", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::BIFF_8 + FormatEntry("application/x-openoffice-biff-8;windows_formatname=\"Biff8\"", "Biff8", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SYLK_BIGCAPS + FormatEntry("application/x-openoffice-sylk-bigcaps;windows_formatname=\"SYLK\"", "SYLK", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::LINK + FormatEntry("application/x-openoffice-link;windows_formatname=\"Link\"", "Link", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARDRAW_TABBAR + FormatEntry("application/x-openoffice-stardraw-tabbar;windows_formatname=\"StarDraw TabBar\"", "StarDraw TabBar", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SONLK + FormatEntry("application/x-openoffice-sonlk;windows_formatname=\"SONLK (StarOffice Navi Link)\"", "SONLK (StarOffice Navi Link)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::MSWORD_DOC + FormatEntry("application/msword", "MSWordDoc", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STAR_FRAMESET_DOC + FormatEntry("application/x-openoffice-star-frameset-doc;windows_formatname=\"StarFrameSetDocument\"", "StarFrameSetDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::OFFICE_DOC + FormatEntry("application/x-openoffice-office-doc;windows_formatname=\"OfficeDocument\"", "OfficeDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::NOTES_DOCINFO + FormatEntry("application/x-openoffice-notes-docinfo;windows_formatname=\"NotesDocInfo\"", "NotesDocInfo", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::NOTES_HNOTE + FormatEntry("application/x-openoffice-notes-hnote;windows_formatname=\"NoteshNote\"", "NoteshNote", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::NOTES_NATIVE + FormatEntry("application/x-openoffice-notes-native;windows_formatname=\"Native\"", "Native", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SFX_DOC + FormatEntry("application/x-openoffice-sfx-doc;windows_formatname=\"SfxDocument\"", "SfxDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EVDF + FormatEntry("application/x-openoffice-evdf;windows_formatname=\"EVDF (Explorer View Dummy Format)\"", "EVDF (Explorer View Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::ESDF + FormatEntry("application/x-openoffice-esdf;windows_formatname=\"ESDF (Explorer Search Dummy Format)\"", "ESDF (Explorer Search Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::IDF + FormatEntry("application/x-openoffice-idf;windows_formatname=\"IDF (Iconview Dummy Format)\"", "IDF (Iconview Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EFTP + FormatEntry("application/x-openoffice-eftp;windows_formatname=\"EFTP (Explorer Ftp File)\"", "EFTP (Explorer Ftp File)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EFD + FormatEntry("application/x-openoffice-efd;windows_formatname=\"EFD (Explorer Ftp Dir)\"", "EFD (Explorer Ftp Dir)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SVX_FORMFIELDEXCH + FormatEntry("application/x-openoffice-svx-formfieldexch;windows_formatname=\"SvxFormFieldExch\"", "SvxFormFieldExch", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EXTENDED_TABBAR + FormatEntry("application/x-openoffice-extended-tabbar;windows_formatname=\"ExtendedTabBar\"", "ExtendedTabBar", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_DATAEXCHANGE + FormatEntry("application/x-openoffice-sba-dataexchange;windows_formatname=\"SBA-DATAFORMAT\"", "SBA-DATAFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_FIELDDATAEXCHANGE + FormatEntry("application/x-openoffice-sba-fielddataexchange;windows_formatname=\"SBA-FIELDFORMAT\"", "SBA-FIELDFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_PRIVATE_URL + FormatEntry("application/x-openoffice-sba-private-url;windows_formatname=\"SBA-PRIVATEURLFORMAT\"", "SBA-PRIVATEURLFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_TABED + FormatEntry("application/x-openoffice-sba-tabed;windows_formatname=\"Tabed\"", "Tabed", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_TABID + FormatEntry("application/x-openoffice-sba-tabid;windows_formatname=\"Tabid\"", "Tabid", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_JOIN + FormatEntry("application/x-openoffice-sba-join;windows_formatname=\"SBA-JOINFORMAT\"", "SBA-JOINFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::OBJECTDESCRIPTOR + FormatEntry("application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"", "Star Object Descriptor (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::LINKSRCDESCRIPTOR + FormatEntry("application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"", "Star Link Source Descriptor (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EMBED_SOURCE + FormatEntry("application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"", "Star Embed Source (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::LINK_SOURCE + FormatEntry("application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"", "Star Link Source (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EMBEDDED_OBJ + FormatEntry("application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"", "Star Embedded Object (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::FILECONTENT + FormatEntry("application/x-openoffice-filecontent;windows_formatname=\"" CFSTR_FILECONTENTS "\"", CFSTR_FILECONTENTS, nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::FILEGRPDESCRIPTOR + FormatEntry("application/x-openoffice-filegrpdescriptor;windows_formatname=\"" CFSTR_FILEDESCRIPTOR "\"", CFSTR_FILEDESCRIPTOR, nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::FILENAME + FormatEntry("application/x-openoffice-filename;windows_formatname=\"" CFSTR_FILENAME "\"", CFSTR_FILENAME, nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SD_OLE + FormatEntry("application/x-openoffice-sd-ole;windows_formatname=\"SD-OLE\"", "SD-OLE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EMBEDDED_OBJ_OLE + FormatEntry("application/x-openoffice-embedded-obj-ole;windows_formatname=\"Embedded Object\"", "Embedded Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::EMBED_SOURCE_OLE + FormatEntry("application/x-openoffice-embed-source-ole;windows_formatname=\"Embed Source\"", "Embed Source", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::OBJECTDESCRIPTOR_OLE + FormatEntry("application/x-openoffice-objectdescriptor-ole;windows_formatname=\"Object Descriptor\"", "Object Descriptor", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::LINKSRCDESCRIPTOR_OLE + FormatEntry("application/x-openoffice-linkdescriptor-ole;windows_formatname=\"Link Source Descriptor\"", "Link Source Descriptor", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::LINK_SOURCE_OLE + FormatEntry("application/x-openoffice-link-source-ole;windows_formatname=\"Link Source\"", "Link Source", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_CTRLDATAEXCHANGE + FormatEntry("application/x-openoffice-sba-ctrldataexchange;windows_formatname=\"SBA-CTRLFORMAT\"", "SBA-CTRLFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::OUTPLACE_OBJ + FormatEntry("application/x-openoffice-outplace-obj;windows_formatname=\"OutPlace Object\"", "OutPlace Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::CNT_OWN_CLIP + FormatEntry("application/x-openoffice-cnt-own-clip;windows_formatname=\"CntOwnClipboard\"", "CntOwnClipboard", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::INET_IMAGE + FormatEntry("application/x-openoffice-inet-image;windows_formatname=\"SO-INet-Image\"", "SO-INet-Image", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::NETSCAPE_IMAGE + FormatEntry("application/x-openoffice-netscape-image;windows_formatname=\"Netscape Image Format\"", "Netscape Image Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_FORMEXCHANGE + FormatEntry("application/x-openoffice-sba-formexchange;windows_formatname=\"SBA_FORMEXCHANGE\"", "SBA_FORMEXCHANGE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::SBA_REPORTEXCHANGE + FormatEntry("application/x-openoffice-sba-reportexchange;windows_formatname=\"SBA_REPORTEXCHANGE\"", "SBA_REPORTEXCHANGE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::UNIFORMRESOURCELOCATOR + FormatEntry("application/x-openoffice-uniformresourcelocator;windows_formatname=\"UniformResourceLocatorW\"", "UniformResourceLocator", nullptr, CF_INVALID, CppuType_String), + //SotClipboardFormatId::STARCHARTDOCUMENT_50 + FormatEntry("application/x-openoffice-starchartdocument-50;windows_formatname=\"StarChartDocument 5.0\"", "StarChartDocument 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::GRAPHOBJ + FormatEntry("application/x-openoffice-graphobj;windows_formatname=\"Graphic Object\"", "Graphic Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITER_60 + FormatEntry("application/vnd.sun.xml.writer", "Writer 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITERWEB_60 + FormatEntry("application/vnd.sun.xml.writer.web", "Writer/Web 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWRITERGLOB_60 + FormatEntry("application/vnd.sun.xml.writer.global", "Writer/Global 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARWDRAW_60 + FormatEntry("application/vnd.sun.xml.draw", "Draw 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARIMPRESS_60 + FormatEntry("application/vnd.sun.xml.impress", "Impress 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARCALC_60 + FormatEntry("application/vnd.sun.xml.calc", "Calc 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARCHART_60 + FormatEntry("application/vnd.sun.xml.chart", "Chart 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::STARMATH_60 + FormatEntry("application/vnd.sun.xml.math", "Math 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::DIALOG_60 + FormatEntry("application/vnd.sun.xml.dialog", "Dialog 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::BMP + FormatEntry("image/bmp", "Windows Bitmap", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::PNG + FormatEntry("image/png", "PNG", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::MATHML + FormatEntry("application/mathml+xml", "MathML", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::DUMMY3 + FormatEntry("application/x-openoffice-dummy3;windows_formatname=\"SO_DUMMYFORMAT_3\"", "SO_DUMMYFORMAT_3", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + //SotClipboardFormatId::DUMMY4 + FormatEntry("application/x-openoffice-dummy4;windows_formatname=\"SO_DUMMYFORMAT_4\"", "SO_DUMMYFORMAT_4", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + // SotClipboardFormatId::RICHTEXT + FormatEntry("text/richtext", "Richtext Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT), + }; + +namespace { + +void findDataFlavorForStandardFormatId( sal_Int32 aStandardFormatId, DataFlavor& aDataFlavor ) +{ + /* + we stop search if we find the first CF_INVALID + because in the translation table the entries with a + standard clipboard format id appear before the other + entries with CF_INVALID + */ + std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(), + [&aStandardFormatId](const FormatEntry& rEntry) { + return rEntry.aStandardFormatId == aStandardFormatId + || rEntry.aStandardFormatId == CF_INVALID; + }); + if (citer != g_TranslTable.end() && citer->aStandardFormatId == aStandardFormatId) + aDataFlavor = citer->aDataFlavor; +} + +void findDataFlavorForNativeFormatName( const OUString& aNativeFormatName, DataFlavor& aDataFlavor ) +{ + std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(), + [&aNativeFormatName](const FormatEntry& rEntry) { + return aNativeFormatName.equalsIgnoreAsciiCase(rEntry.aNativeFormatName); }); + if (citer != g_TranslTable.end()) + aDataFlavor = citer->aDataFlavor; +} + +void findStandardFormatIdForCharset( const OUString& aCharset, Any& aAny ) +{ + if ( aCharset.equalsIgnoreAsciiCase( "utf-16" ) ) + aAny <<= static_cast< sal_Int32 >( CF_UNICODETEXT ); + else + { + sal_Int32 wincp = getWinCPFromMimeCharset( aCharset ); + if ( IsOEMCP ( wincp ) ) + aAny <<= static_cast< sal_Int32 >( CF_OEMTEXT ); + } +} + +void setStandardFormatIdForNativeFormatName( const OUString& aNativeFormatName, Any& aAny ) +{ + std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(), + [&aNativeFormatName](const FormatEntry& rEntry) { + return aNativeFormatName.equalsIgnoreAsciiCase(rEntry.aNativeFormatName) + && (CF_INVALID != rEntry.aStandardFormatId); + }); + if (citer != g_TranslTable.end()) + aAny <<= citer->aStandardFormatId; +} + +void findStdFormatIdOrNativeFormatNameForFullMediaType( + const Reference< XMimeContentTypeFactory >& aRefXMimeFactory, + const OUString& aFullMediaType, + Any& aAny ) +{ + std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(), + [&aRefXMimeFactory, &aFullMediaType](const FormatEntry& rEntry) { + Reference<XMimeContentType> refXMime( aRefXMimeFactory->createMimeContentType(rEntry.aDataFlavor.MimeType) ); + return aFullMediaType.equalsIgnoreAsciiCase(refXMime->getFullMediaType()); + }); + if (citer != g_TranslTable.end()) + { + sal_Int32 cf = citer->aStandardFormatId; + if ( CF_INVALID != cf ) + aAny <<= cf; + else + { + OSL_ENSURE( citer->aNativeFormatName.getLength( ), + "Invalid standard format id and empty native format name in translation table" ); + aAny <<= citer->aNativeFormatName; + } + } +} + +bool isTextPlainMediaType( std::u16string_view fullMediaType ) +{ + return o3tl::equalsIgnoreAsciiCase(fullMediaType, u"text/plain"); +} + +DataFlavor mkDataFlv(const OUString& cnttype, const OUString& hpname, Type dtype) +{ + DataFlavor dflv; + dflv.MimeType = cnttype; + dflv.HumanPresentableName = hpname; + dflv.DataType = dtype; + return dflv; +} + +} + +CDataFormatTranslatorUNO::CDataFormatTranslatorUNO( const Reference< XComponentContext >& rxContext ) : + m_xContext( rxContext ) +{ +} + +Any SAL_CALL CDataFormatTranslatorUNO::getSystemDataTypeFromDataFlavor( const DataFlavor& aDataFlavor ) +{ + Any aAny; + + try + { + Reference< XMimeContentTypeFactory > refXMimeCntFactory = MimeContentTypeFactory::create( m_xContext ); + + Reference< XMimeContentType > + refXMimeCntType( refXMimeCntFactory->createMimeContentType( aDataFlavor.MimeType ) ); + + OUString fullMediaType = refXMimeCntType->getFullMediaType( ); + if ( isTextPlainMediaType( fullMediaType ) ) + { + // default is CF_TEXT + aAny <<= static_cast< sal_Int32 >( CF_TEXT ); + + if ( refXMimeCntType->hasParameter( "charset" ) ) + { + // but maybe it is unicode text or oem text + OUString charset = refXMimeCntType->getParameterValue( "charset" ); + findStandardFormatIdForCharset( charset, aAny ); + } + } + else + { + if (refXMimeCntType->hasParameter(Windows_FormatName)) + { + OUString winfmtname = refXMimeCntType->getParameterValue(Windows_FormatName); + aAny <<= winfmtname; + + setStandardFormatIdForNativeFormatName( winfmtname, aAny ); + } + else + findStdFormatIdOrNativeFormatNameForFullMediaType( refXMimeCntFactory, fullMediaType, aAny ); + } + } + catch( IllegalArgumentException& ) + { + OSL_FAIL( "Invalid content-type detected!" ); + } + catch( NoSuchElementException& ) + { + OSL_FAIL( "Illegal content-type parameter" ); + } + catch( ... ) + { + OSL_FAIL( "Unexpected error" ); + throw; + } + + return aAny; +} + +DataFlavor SAL_CALL CDataFormatTranslatorUNO::getDataFlavorFromSystemDataType( const Any& aSysDataType ) +{ + OSL_PRECOND( aSysDataType.hasValue( ), "Empty system data type delivered" ); + + DataFlavor aFlavor = mkDataFlv( OUString(), OUString(), CPPUTYPE_SEQSALINT8 ); + + if ( aSysDataType.getValueType( ) == cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 clipformat = CF_INVALID; + aSysDataType >>= clipformat; + if ( CF_INVALID != clipformat ) + findDataFlavorForStandardFormatId( clipformat, aFlavor ); + } + else if ( aSysDataType.getValueType( ) == cppu::UnoType<OUString>::get() ) + { + OUString nativeFormatName; + aSysDataType >>= nativeFormatName; + + findDataFlavorForNativeFormatName( nativeFormatName, aFlavor ); + } + else + OSL_FAIL( "Invalid data type received" ); + + return aFlavor; +} + +// XServiceInfo + +OUString SAL_CALL CDataFormatTranslatorUNO::getImplementationName( ) +{ + return "com.sun.star.datatransfer.DataFormatTranslator"; +} + +sal_Bool SAL_CALL CDataFormatTranslatorUNO::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL CDataFormatTranslatorUNO::getSupportedServiceNames( ) +{ + return { "com.sun.star.datatransfer.DataFormatTranslator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_CDataFormatTranslatorUNO_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CDataFormatTranslatorUNO(context)); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/ftransl.hxx b/vcl/win/dtrans/ftransl.hxx new file mode 100644 index 0000000000..7a5041be5f --- /dev/null +++ b/vcl/win/dtrans/ftransl.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/datatransfer/XDataFormatTranslator.hpp> +#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include "WinClip.hxx" + +#include <vector> + +class CDataFormatTranslatorUNO : public + cppu::WeakImplHelper< css::datatransfer::XDataFormatTranslator, + css::lang::XServiceInfo > +{ + +public: + explicit CDataFormatTranslatorUNO( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XDataFormatTranslator + + virtual css::uno::Any SAL_CALL getSystemDataTypeFromDataFlavor( const css::datatransfer::DataFlavor& aDataFlavor ) override; + + virtual css::datatransfer::DataFlavor SAL_CALL getDataFlavorFromSystemDataType( const css::uno::Any& aSysDataType ) override; + + // XServiceInfo + + 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; + +private: + const css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/generic_clipboard.cxx b/vcl/win/dtrans/generic_clipboard.cxx new file mode 100644 index 0000000000..fd822f091e --- /dev/null +++ b/vcl/win/dtrans/generic_clipboard.cxx @@ -0,0 +1,136 @@ +/* -*- 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 "generic_clipboard.hxx" +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace cppu; +using namespace osl; + +using ::dtrans::GenericClipboard; + +GenericClipboard::GenericClipboard() : + m_bInitialized(false) +{ +} + +GenericClipboard::~GenericClipboard() +{ +} + +void SAL_CALL GenericClipboard::initialize( const Sequence< Any >& aArguments ) +{ + if (!m_bInitialized) + { + for (Any const & arg : aArguments) + if (arg.getValueType() == cppu::UnoType<OUString>::get()) + { + arg >>= m_aName; + break; + } + } +} + +OUString SAL_CALL GenericClipboard::getImplementationName( ) +{ + return "com.sun.star.comp.datatransfer.clipboard.GenericClipboard"; +} + +sal_Bool SAL_CALL GenericClipboard::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL GenericClipboard::getSupportedServiceNames( ) +{ + return { "com.sun.star.datatransfer.clipboard.GenericClipboard" }; +} + +Reference< XTransferable > SAL_CALL GenericClipboard::getContents() +{ + std::unique_lock aGuard(m_aMutex); + return m_aContents; +} + +void SAL_CALL GenericClipboard::setContents(const Reference< XTransferable >& xTrans, + const Reference< XClipboardOwner >& xClipboardOwner ) +{ + // remember old values for callbacks before setting the new ones. + std::unique_lock aGuard(m_aMutex); + + Reference< XClipboardOwner > oldOwner(m_aOwner); + m_aOwner = xClipboardOwner; + + Reference< XTransferable > oldContents(m_aContents); + m_aContents = xTrans; + + aGuard.unlock(); + + // notify old owner on loss of ownership + if( oldOwner.is() ) + oldOwner->lostOwnership(static_cast < XClipboard * > (this), oldContents); + + // notify all listeners on content changes + aGuard.lock(); + ClipboardEvent aEvent(static_cast < XClipboard * > (this), m_aContents); + maClipboardListeners.notifyEach(aGuard, &XClipboardListener::changedContents, aEvent); +} + +OUString SAL_CALL GenericClipboard::getName() +{ + return m_aName; +} + +sal_Int8 SAL_CALL GenericClipboard::getRenderingCapabilities() +{ + return RenderingCapabilities::Delayed; +} + +void SAL_CALL GenericClipboard::addClipboardListener( const Reference< XClipboardListener >& listener ) +{ + std::unique_lock aGuard( m_aMutex ); + OSL_ENSURE( !m_bDisposed, "object is disposed" ); + if (!m_bDisposed) + maClipboardListeners.addInterface( aGuard, listener ); +} + +void SAL_CALL GenericClipboard::removeClipboardListener( const Reference< XClipboardListener >& listener ) +{ + std::unique_lock aGuard( m_aMutex ); + OSL_ENSURE( !m_bDisposed, "object is disposed" ); + if (!m_bDisposed) + maClipboardListeners.removeInterface( aGuard, listener ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_GenericClipboard_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new GenericClipboard()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/generic_clipboard.hxx b/vcl/win/dtrans/generic_clipboard.hxx new file mode 100644 index 0000000000..cc1ad976b3 --- /dev/null +++ b/vcl/win/dtrans/generic_clipboard.hxx @@ -0,0 +1,101 @@ +/* -*- 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 . + */ + +#pragma once + +#include <comphelper/compbase.hxx> + +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> + +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +namespace dtrans +{ + + class GenericClipboard : public ::comphelper::WeakComponentImplHelper < + css::datatransfer::clipboard::XClipboardEx, + css::datatransfer::clipboard::XClipboardNotifier, + css::lang::XServiceInfo, + css::lang::XInitialization > + { + OUString m_aName; + + css::uno::Reference< css::datatransfer::XTransferable > m_aContents; + css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner > m_aOwner; + comphelper::OInterfaceContainerHelper4<css::datatransfer::clipboard::XClipboardListener> maClipboardListeners; + + bool m_bInitialized; + virtual ~GenericClipboard() override; + + public: + + GenericClipboard(); + + /* + * XInitialization + */ + + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + /* + * XServiceInfo + */ + + 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; + + /* + * XClipboard + */ + + virtual css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL getContents() override; + + virtual void SAL_CALL setContents( + const css::uno::Reference< css::datatransfer::XTransferable >& xTrans, + const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) override; + + virtual OUString SAL_CALL getName() override; + + /* + * XClipboardEx + */ + + virtual sal_Int8 SAL_CALL getRenderingCapabilities() override; + + /* + * XClipboardNotifier + */ + + virtual void SAL_CALL addClipboardListener( + const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override; + + virtual void SAL_CALL removeClipboardListener( + const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override; + + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/globals.cxx b/vcl/win/dtrans/globals.cxx new file mode 100644 index 0000000000..fcddef22e6 --- /dev/null +++ b/vcl/win/dtrans/globals.cxx @@ -0,0 +1,130 @@ +/* -*- 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 <com/sun/star/datatransfer/dnd/DNDConstants.hpp> + +#include "globals.hxx" + +//--> TRA +#include <com/sun/star/datatransfer/XTransferable.hpp> + +// used as shortcut when drag-source and drop-target are the same +css::uno::Reference< css::datatransfer::XTransferable > g_XTransferable; + +//<-- TRA + +using namespace com::sun::star::datatransfer::dnd::DNDConstants; + +sal_Int8 dndOleKeysToAction( DWORD grfKeyState, sal_Int8 nSourceActions) +{ + sal_Int8 ret= 0; + + // no MK_ALT, MK_CONTROL, MK_SHIFT + if( !(grfKeyState & MK_CONTROL) && + !(grfKeyState & MK_ALT) && + !(grfKeyState & MK_RBUTTON) && + !(grfKeyState & MK_SHIFT)) + { + if( nSourceActions & ACTION_MOVE ) + { + ret= ACTION_DEFAULT | ACTION_MOVE; + } + + else if( nSourceActions & ACTION_COPY ) + { + ret= ACTION_COPY; + } + + else if( nSourceActions & ACTION_LINK ) + { + ret= ACTION_LINK; + } + + else + ret = 0; + } + else if( grfKeyState & MK_SHIFT && + !(grfKeyState & MK_CONTROL)) + { + ret= ACTION_MOVE; + } + else if ( grfKeyState & MK_CONTROL && + !(grfKeyState & MK_SHIFT) ) + { + ret= ACTION_COPY; + } + else if ( grfKeyState & MK_CONTROL && + grfKeyState & MK_SHIFT) + { + ret= ACTION_LINK; + } + else if ( grfKeyState & MK_RBUTTON || + grfKeyState & MK_ALT) + { + ret= ACTION_COPY_OR_MOVE | ACTION_LINK; + } + return ret; +} + +sal_Int8 dndOleDropEffectsToActions( DWORD dwEffect) +{ + sal_Int8 ret= ACTION_NONE; + if( dwEffect & DROPEFFECT_COPY) + ret |= ACTION_COPY; + if( dwEffect & DROPEFFECT_MOVE) + ret |= ACTION_MOVE; + if( dwEffect & DROPEFFECT_LINK) + ret |= ACTION_LINK; + + return ret; +} + +DWORD dndActionsToDropEffects( sal_Int8 actions) +{ + DWORD ret= DROPEFFECT_NONE; + if( actions & ACTION_MOVE) + ret |= DROPEFFECT_MOVE; + if( actions & ACTION_COPY) + ret |= DROPEFFECT_COPY; + if( actions & ACTION_LINK) + ret |= DROPEFFECT_LINK; + if( actions & ACTION_DEFAULT) + ret |= DROPEFFECT_COPY; + return ret; +} + +DWORD dndActionsToSingleDropEffect( sal_Int8 actions) +{ + DWORD effects= dndActionsToDropEffects( actions); + + sal_Int8 countEffect= 0; + + if( effects & DROPEFFECT_MOVE) + countEffect++; + if( effects & DROPEFFECT_COPY) + countEffect++; + if( effects & DROPEFFECT_LINK) + countEffect++; + + // DROPEFFECT_MOVE is the default effect + DWORD retVal= countEffect > 1 ? DROPEFFECT_MOVE : effects; + return retVal; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/globals.hxx b/vcl/win/dtrans/globals.hxx new file mode 100644 index 0000000000..9bb174d0b6 --- /dev/null +++ b/vcl/win/dtrans/globals.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ +#pragma once + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <osl/mutex.hxx> + +namespace com::sun::star::datatransfer +{ +class XTransferable; +} + +#include <wtypes.h> +#include <sal/types.h> + +// This maps key states as occur as parameter, e.g. in IDropTarget::DragEnter, +// IDropSource::QueryContinueDrag, to actions as are declared in +// css::datatransfer::dnd::DNDConstants ( ACTION_MOVE etc). +// If the grfKeyState indicates the ALt or right mousebutton then the returned +// values combines all possible actions. This is because those keys and buttons are +// used when the user expect a menu to appear when he drops. The menu then +// contains entries, such as move, copy, link, cancel. +// An odd fact is that the argument grfKeyState in IDropTarget::Drop does not +// contain mouse buttons (winnt 4 SP6). That indicates that the right mouse button +// is not considered relevant in a drag operation. Contrarily the file explorer +// gives that button a special meaning: the user has to select the effect from +// a context menu on drop. +sal_Int8 dndOleKeysToAction(DWORD grfKeyState, sal_Int8 sourceActions); + +// The function maps a windows DROPEFFECTs to actions +// ( css::datatransfer::dnd::DNDConstants). +// The argument can be a combination of different DROPEFFECTS, +// In that case the return value is also a combination of the +// appropriate actions. +sal_Int8 dndOleDropEffectsToActions(DWORD dwEffect); + +// The function maps actions ( css::datatransfer::dnd::DNDConstants) +// to window DROPEFFECTs. +// The argument can be a combination of different actions +// In that case the return value is also a combination of the +// appropriate DROPEFFECTS. +DWORD dndActionsToDropEffects(sal_Int8 actions); + +// If the argument constitutes only one action then it is mapped to the +// corresponding DROPEFFECT otherwise DROPEFFECT_MOVE is returned. This is +// why move is the default effect (no modifiers pressed, or right mouse button +// or Alt). +DWORD dndActionsToSingleDropEffect(sal_Int8 actions); + +extern css::uno::Reference<css::datatransfer::XTransferable> g_XTransferable; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/idroptarget.cxx b/vcl/win/dtrans/idroptarget.cxx new file mode 100644 index 0000000000..8b403eb7a3 --- /dev/null +++ b/vcl/win/dtrans/idroptarget.cxx @@ -0,0 +1,96 @@ +/* -*- 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 "idroptarget.hxx" + +IDropTargetImpl::IDropTargetImpl( DropTarget& pTarget): m_nRefCount( 0), + m_rDropTarget( pTarget) +{ +} + +IDropTargetImpl::~IDropTargetImpl() +{ +} + +//IDropTarget +HRESULT STDMETHODCALLTYPE IDropTargetImpl::QueryInterface( REFIID riid, void **ppvObject) +{ + if( !ppvObject) + return E_POINTER; + *ppvObject= nullptr; + + if( riid == __uuidof( IUnknown)) + *ppvObject= static_cast<IUnknown*>( this); + else if ( riid == __uuidof( IDropTarget)) + *ppvObject= static_cast<IDropTarget*>( this); + + if(*ppvObject) + { + AddRef(); + return S_OK; + } + else + return E_NOINTERFACE; + +} + +ULONG STDMETHODCALLTYPE IDropTargetImpl::AddRef() +{ + return InterlockedIncrement( &m_nRefCount); +} + +ULONG STDMETHODCALLTYPE IDropTargetImpl::Release() +{ + LONG count= InterlockedDecrement( &m_nRefCount); + if( m_nRefCount == 0 ) + delete this; + return count; +} + +STDMETHODIMP IDropTargetImpl::DragEnter( IDataObject __RPC_FAR *pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD *pdwEffect) +{ + return m_rDropTarget.DragEnter( pDataObj, grfKeyState, + pt, pdwEffect); +} + +STDMETHODIMP IDropTargetImpl::DragOver( DWORD grfKeyState, + POINTL pt, + DWORD *pdwEffect) +{ + return m_rDropTarget.DragOver( grfKeyState, pt, pdwEffect); +} + +STDMETHODIMP IDropTargetImpl::DragLeave() +{ + return m_rDropTarget.DragLeave(); +} + +STDMETHODIMP IDropTargetImpl::Drop( IDataObject *pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD __RPC_FAR *pdwEffect) +{ + return m_rDropTarget.Drop( pDataObj, grfKeyState, + pt, pdwEffect); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/idroptarget.hxx b/vcl/win/dtrans/idroptarget.hxx new file mode 100644 index 0000000000..5871373c6c --- /dev/null +++ b/vcl/win/dtrans/idroptarget.hxx @@ -0,0 +1,65 @@ +/* -*- 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 . + */ +#pragma once + +#include <win/dnd_target.hxx> + +class IDropTargetImpl: public IDropTarget +{ + LONG m_nRefCount; + // Calls to IDropTarget functions are delegated to a DropTarget. + DropTarget& m_rDropTarget; + + virtual ~IDropTargetImpl(); // delete is only called by IUnknown::Release + IDropTargetImpl( const IDropTargetImpl& ); + IDropTargetImpl& operator=( const IDropTargetImpl& ); +public: + explicit IDropTargetImpl(DropTarget& rTarget); + + // IDropTarget + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) override; + + virtual ULONG STDMETHODCALLTYPE AddRef( ) override; + + virtual ULONG STDMETHODCALLTYPE Release( ) override; + + virtual HRESULT STDMETHODCALLTYPE DragEnter( + /* [unique][in] */ IDataObject __RPC_FAR *pDataObj, + /* [in] */ DWORD grfKeyState, + /* [in] */ POINTL pt, + /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override; + + virtual HRESULT STDMETHODCALLTYPE DragOver( + /* [in] */ DWORD grfKeyState, + /* [in] */ POINTL pt, + /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override; + + virtual HRESULT STDMETHODCALLTYPE DragLeave( ) override; + + virtual HRESULT STDMETHODCALLTYPE Drop( + /* [unique][in] */ IDataObject __RPC_FAR *pDataObj, + /* [in] */ DWORD grfKeyState, + /* [in] */ POINTL pt, + /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/source.cxx b/vcl/win/dtrans/source.cxx new file mode 100644 index 0000000000..94124b23dd --- /dev/null +++ b/vcl/win/dtrans/source.cxx @@ -0,0 +1,373 @@ +/* -*- 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 <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/any.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <process.h> +#include <memory> + +#include <win/dnd_source.hxx> +#include "globals.hxx" +#include "sourcecontext.hxx" +#include "DtObjFactory.hxx" + +#include <rtl/ustring.h> +#include <osl/thread.h> +#include <winuser.h> +#include <stdio.h> + +using namespace cppu; +using namespace osl; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; +using namespace com::sun::star::uno; +using namespace com::sun::star::awt::MouseButton; +using namespace com::sun::star::awt; +using namespace com::sun::star::lang; + +static unsigned __stdcall DndOleSTAFunc(void* pParams); + +DragSource::DragSource( const Reference<XComponentContext>& rxContext): + WeakComponentImplHelper< XDragSource, XInitialization, XServiceInfo >(m_aMutex), + m_xContext( rxContext ), +// m_pcurrentContext_impl(0), + m_hAppWindow(nullptr), + m_MouseButton(0), + m_RunningDndOperationCount(0) +{ +} + +DragSource::~DragSource() +{ +} + +/** First start a new drag and drop thread if + the last one has finished + + ???? + Do we really need a separate thread for + every Dnd operation or only if the source + thread is an MTA thread + ???? +*/ +void DragSource::StartDragImpl( + const DragGestureEvent& trigger, + sal_Int8 sourceActions, + sal_Int32 /*cursor*/, + sal_Int32 /*image*/, + const Reference<XTransferable >& trans, + const Reference<XDragSourceListener >& listener ) +{ + // The actions supported by the drag source + m_sourceActions= sourceActions; + // We need to know which mouse button triggered the operation. + // If it was the left one, then the drop occurs when that button + // has been released and if it was the right one then the drop + // occurs when the right button has been released. If the event is not + // set then we assume that the left button is pressed. + MouseEvent evtMouse; + trigger.Event >>= evtMouse; + m_MouseButton= evtMouse.Buttons; + + // The SourceContext class administers the XDragSourceListener s and + // fires events to them. An instance only exists in the scope of this + // function. However, the drag and drop operation causes callbacks + // to the IDropSource interface implemented in this class (but only + // while this function executes). The source context is also used + // in DragSource::QueryContinueDrag. + m_currentContext = new SourceContext(this, listener); + + // Convert the XTransferable data object into an IDataObject object; + + //--> TRA + g_XTransferable = trans; + //<-- TRA + + m_spDataObject= CDTransObjFactory::createDataObjFromTransferable( + m_xContext, trans); + + // Obtain the id of the thread that created the window + DWORD processId; + m_threadIdWindow= GetWindowThreadProcessId( m_hAppWindow, &processId); + + // hold the instance for the DnD thread, it's too late + // to acquire at the start of the thread procedure + // the thread procedure is responsible for the release + acquire(); + + // The thread accesses members of this instance but does not call acquire. + // Hopefully this instance is not destroyed before the thread has terminated. + HANDLE hThread + = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, DndOleSTAFunc, this, 0, nullptr)); + + // detach from thread + CloseHandle(hThread); +} + +// XInitialization +/** aArguments contains a machine id */ +void SAL_CALL DragSource::initialize( const Sequence< Any >& aArguments ) +{ + if( aArguments.getLength() >=2) + m_hAppWindow= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[1]))); + OSL_ASSERT( IsWindow( m_hAppWindow) ); +} + +/** XDragSource */ +sal_Bool SAL_CALL DragSource::isDragImageSupported( ) +{ + return false; +} + +sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ ) +{ + return 0; +} + +/** Notifies the XDragSourceListener by + calling dragDropEnd */ +void SAL_CALL DragSource::startDrag( + const DragGestureEvent& trigger, + sal_Int8 sourceActions, + sal_Int32 cursor, + sal_Int32 image, + const Reference<XTransferable >& trans, + const Reference<XDragSourceListener >& listener ) +{ + // Allow only one running dnd operation at a time, + // see XDragSource documentation + + LONG cnt = InterlockedIncrement(&m_RunningDndOperationCount); + + if (1 == cnt) + { + StartDragImpl(trigger, sourceActions, cursor, image, trans, listener); + } + else + { + cnt = InterlockedDecrement(&m_RunningDndOperationCount); + + DragSourceDropEvent dsde; + + dsde.DropAction = ACTION_NONE; + dsde.DropSuccess = false; + + try + { + listener->dragDropEnd(dsde); + } + catch(RuntimeException&) + { + TOOLS_WARN_EXCEPTION( "vcl", "Runtime exception during event dispatching"); + } + } +} + +/** IDropTarget */ +HRESULT STDMETHODCALLTYPE DragSource::QueryInterface( REFIID riid, void **ppvObject) +{ + if( !ppvObject) + return E_POINTER; + *ppvObject= nullptr; + + if( riid == __uuidof( IUnknown) ) + *ppvObject= static_cast<IUnknown*>( this); + else if ( riid == __uuidof( IDropSource) ) + *ppvObject= static_cast<IDropSource*>( this); + + if(*ppvObject) + { + AddRef(); + return S_OK; + } + else + return E_NOINTERFACE; + +} + +ULONG STDMETHODCALLTYPE DragSource::AddRef() +{ + acquire(); + return static_cast<ULONG>(m_refCount); +} + +ULONG STDMETHODCALLTYPE DragSource::Release() +{ + ULONG ref= m_refCount; + release(); + return --ref; +} + +/** IDropSource */ +HRESULT STDMETHODCALLTYPE DragSource::QueryContinueDrag( +/* [in] */ BOOL fEscapePressed, +/* [in] */ DWORD grfKeyState) +{ +#if defined DBG_CONSOLE_OUT + printf("\nDragSource::QueryContinueDrag"); +#endif + + HRESULT retVal= S_OK; // default continue DnD + + if (fEscapePressed) + { + retVal= DRAGDROP_S_CANCEL; + } + else + { + if( ( m_MouseButton == MouseButton::RIGHT && !(grfKeyState & MK_RBUTTON) ) || + ( m_MouseButton == MouseButton::MIDDLE && !(grfKeyState & MK_MBUTTON) ) || + ( m_MouseButton == MouseButton::LEFT && !(grfKeyState & MK_LBUTTON) ) || + ( m_MouseButton == 0 && !(grfKeyState & MK_LBUTTON) ) ) + { + retVal= DRAGDROP_S_DROP; + } + } + + // fire dropActionChanged event. + // this is actually done by the context, which also detects whether the action + // changed at all + sal_Int8 dropAction= fEscapePressed ? ACTION_NONE : + dndOleKeysToAction( grfKeyState, m_sourceActions); + + sal_Int8 userAction= fEscapePressed ? ACTION_NONE : + dndOleKeysToAction( grfKeyState, -1 ); + + static_cast<SourceContext*>(m_currentContext.get())->fire_dropActionChanged( + dropAction, userAction); + + return retVal; +} + +HRESULT STDMETHODCALLTYPE DragSource::GiveFeedback( +/* [in] */ DWORD +#if defined DBG_CONSOLE_OUT +dwEffect +#endif +) +{ +#if defined DBG_CONSOLE_OUT + printf("\nDragSource::GiveFeedback %d", dwEffect); +#endif + + return DRAGDROP_S_USEDEFAULTCURSORS; +} + +// XServiceInfo +OUString SAL_CALL DragSource::getImplementationName( ) +{ + return "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"; +} +// XServiceInfo +sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames( ) +{ + return { "com.sun.star.datatransfer.dnd.OleDragSource" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_DragSource_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new DragSource(context)); +} + +/** This function is called as extra thread from + DragSource::executeDrag. The function + carries out a drag and drop operation by calling + DoDragDrop. The thread also notifies all + XSourceListener. */ +unsigned __stdcall DndOleSTAFunc(void* pParams) +{ + osl_setThreadName("DragSource DndOleSTAFunc"); + + // The structure contains all arguments for DoDragDrop and other + DragSource *pSource= static_cast<DragSource*>(pParams); + + // Drag and drop only works in a thread in which OleInitialize is called. + HRESULT hr= OleInitialize( nullptr); + + if(SUCCEEDED(hr)) + { + // We force the creation of a thread message queue. This is necessary + // for a later call to AttachThreadInput + MSG msgtemp; + PeekMessageW( &msgtemp, nullptr, WM_USER, WM_USER, PM_NOREMOVE); + + DWORD threadId= GetCurrentThreadId(); + + // This thread is attached to the thread that created the window. Hence + // this thread also receives all mouse and keyboard messages which are + // needed by DoDragDrop + AttachThreadInput( threadId , pSource->m_threadIdWindow, TRUE ); + + DWORD dwEffect= 0; + hr= DoDragDrop( + pSource->m_spDataObject.get(), + static_cast<IDropSource*>(pSource), + dndActionsToDropEffects( pSource->m_sourceActions), + &dwEffect); + + // #105428 detach my message queue from the other threads + // message queue before calling fire_dragDropEnd else + // the office may appear to hang sometimes + AttachThreadInput( threadId, pSource->m_threadIdWindow, FALSE); + + //--> TRA + // clear the global transferable again + g_XTransferable.clear(); + //<-- TRA + + OSL_ENSURE( hr != E_INVALIDARG, "IDataObject impl does not contain valid data"); + + //Fire event + sal_Int8 action= hr == DRAGDROP_S_DROP ? dndOleDropEffectsToActions( dwEffect) : ACTION_NONE; + + static_cast<SourceContext*>(pSource->m_currentContext.get())->fire_dragDropEnd( + hr == DRAGDROP_S_DROP, action); + + // Destroy SourceContextslkfgj + pSource->m_currentContext= nullptr; + // Destroy the XTransferable wrapper + pSource->m_spDataObject=nullptr; + + OleUninitialize(); + } + + InterlockedDecrement(&pSource->m_RunningDndOperationCount); + + // the DragSource was manually acquired by + // thread starting method DelayedStartDrag + pSource->release(); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/sourcecontext.cxx b/vcl/win/dtrans/sourcecontext.cxx new file mode 100644 index 0000000000..c0e6371c5b --- /dev/null +++ b/vcl/win/dtrans/sourcecontext.cxx @@ -0,0 +1,134 @@ +/* -*- 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 <com/sun/star/datatransfer/dnd/DNDConstants.hpp> + +#include "sourcecontext.hxx" + +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; + +SourceContext::SourceContext( DragSource* pSource, + const Reference<XDragSourceListener>& listener): + WeakComponentImplHelper<XDragSourceContext>(m_aMutex), + m_pDragSource( pSource), + m_dragSource( static_cast<XDragSource*>( m_pDragSource) ) +{ +#if OSL_DEBUG_LEVEL > 1 + if( listener.is()) +#endif + rBHelper.addListener( cppu::UnoType<decltype(listener)>::get(), listener ); +} + +SourceContext::~SourceContext() +{ +} + +void SourceContext::addDragSourceListener( + const Reference<XDragSourceListener >& ) +{ +} + +void SourceContext::removeDragSourceListener( + const Reference<XDragSourceListener >& ) +{ +} + +sal_Int32 SAL_CALL SourceContext::getCurrentCursor( ) +{ + return 0; +} + +void SAL_CALL SourceContext::setCursor( sal_Int32 /*cursorId*/ ) +{ +} + +void SAL_CALL SourceContext::setImage( sal_Int32 /*imageId*/ ) +{ +} + +void SAL_CALL SourceContext::transferablesFlavorsChanged( ) +{ +} + +// non -interface functions +// Fires XDragSourceListener::dragDropEnd events. +void SourceContext::fire_dragDropEnd( bool success, sal_Int8 effect) +{ + + DragSourceDropEvent e; + + if( success ) + { + e.DropAction= effect; + e.DropSuccess= true; + } + else + { + e.DropAction= ACTION_NONE; + e.DropSuccess= false; + } + e.DragSource= m_dragSource; + e.DragSourceContext= static_cast<XDragSourceContext*>( this); + e.Source.set( static_cast<XDragSourceContext*>( this), UNO_QUERY); + + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( + cppu::UnoType<XDragSourceListener>::get()); + + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDragSourceListener> listener( + static_cast<XDragSourceListener*>( iter.next())); + listener->dragDropEnd( e); + } + } +} + +void SourceContext::fire_dropActionChanged( sal_Int8 dropAction, sal_Int8 userAction) +{ + if( m_currentAction != dropAction) + { + m_currentAction= dropAction; + DragSourceDragEvent e; + e.DropAction= dropAction; + e.UserAction= userAction; + e.DragSource= m_dragSource; + e.DragSourceContext= static_cast<XDragSourceContext*>( this); + e.Source.set( static_cast<XDragSourceContext*>( this), UNO_QUERY); + + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( + cppu::UnoType<XDragSourceListener>::get()); + + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDragSourceListener> listener( + static_cast<XDragSourceListener*>( iter.next())); + listener->dropActionChanged( e); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/sourcecontext.hxx b/vcl/win/dtrans/sourcecontext.hxx new file mode 100644 index 0000000000..4471747956 --- /dev/null +++ b/vcl/win/dtrans/sourcecontext.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <win/dnd_source.hxx> + +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::dnd; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +// This class fires events to XDragSourceListener implementations. +// Of that interface only dragDropEnd and dropActionChanged are called. +// The functions dragEnter, dragExit and dragOver are not supported +// currently. +// An instance of SourceContext only lives as long as the drag and drop +// operation lasts. +class SourceContext : public cppu::BaseMutex, public WeakComponentImplHelper<XDragSourceContext> +{ + DragSource* m_pDragSource; + Reference<XDragSource> m_dragSource; + // the action ( copy, move etc) + sal_Int8 m_currentAction; + +public: + SourceContext(DragSource* pSource, const Reference<XDragSourceListener>& listener); + ~SourceContext() override; + SourceContext(const SourceContext&) = delete; + SourceContext& operator=(const SourceContext&) = delete; + + /// @throws RuntimeException + virtual void addDragSourceListener(const Reference<XDragSourceListener>& dsl); + /// @throws RuntimeException + virtual void removeDragSourceListener(const Reference<XDragSourceListener>& dsl); + virtual sal_Int32 SAL_CALL getCurrentCursor() override; + virtual void SAL_CALL setCursor(sal_Int32 cursorId) override; + virtual void SAL_CALL setImage(sal_Int32 imageId) override; + virtual void SAL_CALL transferablesFlavorsChanged() override; + + // non - interface functions + void fire_dragDropEnd(bool success, sal_Int8 byte); + void fire_dropActionChanged(sal_Int8 dropAction, sal_Int8 userAction); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/target.cxx b/vcl/win/dtrans/target.cxx new file mode 100644 index 0000000000..abe53379ea --- /dev/null +++ b/vcl/win/dtrans/target.cxx @@ -0,0 +1,646 @@ +/* -*- 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 <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/any.hxx> + +#include <stdio.h> +#include <win/dnd_target.hxx> +#include "idroptarget.hxx" +#include "globals.hxx" +#include "targetdropcontext.hxx" +#include "targetdragcontext.hxx" +#include <rtl/ustring.h> +#include <osl/thread.h> +#include <sal/log.hxx> +#include <comphelper/windowserrorstring.hxx> + +#include "DOTransferable.hxx" + +using namespace cppu; +using namespace osl; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; + +#define WM_REGISTERDRAGDROP WM_USER + 1 +#define WM_REVOKEDRAGDROP WM_USER + 2 + +unsigned __stdcall DndTargetOleSTAFunc(void* pParams); + +DropTarget::DropTarget( const Reference<XComponentContext>& rxContext): + WeakComponentImplHelper<XInitialization,XDropTarget, XServiceInfo>(m_aMutex), + m_hWnd( nullptr), + m_threadIdWindow(0), + m_threadIdTarget(0), + m_hOleThread(nullptr), + m_oleThreadId( 0), + m_pDropTarget( nullptr), + m_xContext( rxContext ), + m_bActive(true), + m_nDefaultActions(ACTION_COPY|ACTION_MOVE|ACTION_LINK|ACTION_DEFAULT), + m_nCurrentDropAction( ACTION_NONE), + m_nLastDropAction(0), + m_bDropComplete(false) +{ +} + +DropTarget::~DropTarget() +{ +} +// called from WeakComponentImplHelperX::dispose +// WeakComponentImplHelper calls disposing before it destroys +// itself. +// NOTE: RevokeDragDrop decrements the ref count on the IDropTarget +// interface. (m_pDropTarget) +// If the HWND is invalid then it doesn't decrement and +// the IDropTarget object will live on. MEMORY LEAK +void SAL_CALL DropTarget::disposing() +{ + if( m_threadIdTarget) + { + // Call RevokeDragDrop and wait for the OLE thread to die; + PostThreadMessageW( m_threadIdTarget, WM_REVOKEDRAGDROP, reinterpret_cast<WPARAM>(this), 0); + WaitForSingleObject( m_hOleThread, INFINITE); + CloseHandle( m_hOleThread); + //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" ); + } + else + { + RevokeDragDrop( m_hWnd); + m_hWnd= nullptr; + } + if( m_pDropTarget) + { + CoLockObjectExternal( m_pDropTarget, FALSE, TRUE); + m_pDropTarget->Release(); + m_pDropTarget = nullptr; + } + + if( m_oleThreadId) + { + if( m_oleThreadId == CoGetCurrentProcess() ) + OleUninitialize(); + } + +} + +void SAL_CALL DropTarget::initialize( const Sequence< Any >& aArguments ) +{ + // The window must be registered for Dnd by RegisterDragDrop. We must ensure + // that RegisterDragDrop is called from an STA ( OleInitialize) thread. + // As long as the window is registered we need to receive OLE messages in + // an OLE thread. That is to say, if DropTarget::initialize was called from an + // MTA thread then we create an OLE thread in which the window is registered. + // The thread will stay alive until aver RevokeDragDrop has been called. + + // Additionally even if RegisterDragDrop is called from an STA thread we have + // to ensure that it is called from the same thread that created the Window + // otherwise messages sent during DND won't reach the windows message queue. + // Calling AttachThreadInput first would resolve this problem but would block + // the message queue of the calling thread. So if the current thread + // (even if it's an STA thread) and the thread that created the window are not + // identical we need to create a new thread as we do when the calling thread is + // an MTA thread. + + if( aArguments.getLength() > 0) + { + // Get the window handle from aArgument. It is needed for RegisterDragDrop. + m_hWnd= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[0]))); + OSL_ASSERT( IsWindow( m_hWnd) ); + + // Obtain the id of the thread that created the window + m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr); + + HRESULT hr= OleInitialize( nullptr); + + // Current thread is MTA or Current thread and Window thread are not identical + if( hr == RPC_E_CHANGED_MODE || GetCurrentThreadId() != m_threadIdWindow ) + { + OSL_ENSURE( ! m_threadIdTarget,"initialize was called twice"); + // create the IDropTargetImplementation + m_pDropTarget= new IDropTargetImpl( *this ); + m_pDropTarget->AddRef(); + + // Obtain the id of the thread that created the window + m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr); + // The event is set by the thread that we will create momentarily. + // It indicates that the thread is ready to receive messages. + HANDLE m_evtThreadReady= CreateEventW( nullptr, FALSE, FALSE, nullptr); + + m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, DndTargetOleSTAFunc, + &m_evtThreadReady, 0, &m_threadIdTarget)); + WaitForSingleObject( m_evtThreadReady, INFINITE); + CloseHandle( m_evtThreadReady); + PostThreadMessageW( m_threadIdTarget, WM_REGISTERDRAGDROP, reinterpret_cast<WPARAM>(this), 0); + } + else if( hr == S_OK || hr == S_FALSE) + { + // current thread is STA + // If OleInitialize has been called by the caller then we must not call + // OleUninitialize + if( hr == S_OK) + { + // caller did not call OleInitialize, so we call OleUninitialize + // remember the thread that will call OleUninitialize + m_oleThreadId= CoGetCurrentProcess(); // get a unique thread id + } + + // Get the window handle from aArgument. It is needed for RegisterDragDrop. + // create the IDropTargetImplementation + m_pDropTarget= new IDropTargetImpl( *this ); + m_pDropTarget->AddRef(); + // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count + if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget, TRUE, FALSE))) + { + if( FAILED( RegisterDragDrop( m_hWnd, m_pDropTarget) ) ) + { + // do clean up if drag and drop is not possible + CoLockObjectExternal( m_pDropTarget, FALSE, FALSE); + m_pDropTarget->Release(); + m_pDropTarget = nullptr; + m_hWnd= nullptr; + } + } + } + else + throw Exception("OleInitialize failed with " + OUString::number(hr), nullptr); + + } +} + +// This function is called as extra thread from DragSource::startDrag. +// The function carries out a drag and drop operation by calling +// DoDragDrop. The thread also notifies all XSourceListener. +unsigned __stdcall DndTargetOleSTAFunc(void* pParams) +{ + osl_setThreadName("DropTarget DndTargetOleSTAFunc"); + + HRESULT hr= OleInitialize( nullptr); + if( SUCCEEDED( hr) ) + { + MSG msg; + // force the creation of a message queue + PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE); + // Signal the creator ( DropTarget::initialize) that the thread is + // ready to receive messages. + SetEvent( *static_cast<HANDLE*>(pParams)); + // Thread id is needed for attaching this message queue to the one of the + // thread where the window was created. + DWORD threadId= GetCurrentThreadId(); + // We force the creation of a thread message queue. This is necessary + // for a later call to AttachThreadInput + for (;;) + { + int const bRet = GetMessageW(&msg, nullptr, 0, 0); + if (bRet == 0) + { + break; + } + if (-1 == bRet) + { + SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError())); + break; + } + if( msg.message == WM_REGISTERDRAGDROP) + { + DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam); + // This thread is attached to the thread that created the window. Hence + // this thread also receives all mouse and keyboard messages which are + // needed + AttachThreadInput( threadId , pTarget->m_threadIdWindow, TRUE ); + + if( SUCCEEDED( CoLockObjectExternal(pTarget-> m_pDropTarget, TRUE, FALSE))) + { + if( FAILED( RegisterDragDrop( pTarget-> m_hWnd, pTarget-> m_pDropTarget) ) ) + { + // do clean up if drag and drop is not possible + CoLockObjectExternal( pTarget->m_pDropTarget, FALSE, FALSE); + pTarget->m_pDropTarget->Release(); + pTarget->m_pDropTarget = nullptr; + pTarget->m_hWnd= nullptr; + } + } + } + else if( msg.message == WM_REVOKEDRAGDROP) + { + DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam); + RevokeDragDrop( pTarget-> m_hWnd); + // Detach this thread from the window thread + AttachThreadInput( threadId, pTarget->m_threadIdWindow, FALSE); + pTarget->m_hWnd= nullptr; + break; + } + TranslateMessage( &msg); + DispatchMessageW( &msg); + } + OleUninitialize(); + } + return 0; +} + +// XServiceInfo +OUString SAL_CALL DropTarget::getImplementationName( ) +{ + return "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"; +} +// XServiceInfo +sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( ) +{ + return { "com.sun.star.datatransfer.dnd.OleDropTarget" }; +} + +// XDropTarget +void SAL_CALL DropTarget::addDropTargetListener( const Reference< XDropTargetListener >& dtl ) +{ + rBHelper.addListener( cppu::UnoType<decltype(dtl)>::get(), dtl ); +} + +void SAL_CALL DropTarget::removeDropTargetListener( const Reference< XDropTargetListener >& dtl ) +{ + rBHelper.removeListener( cppu::UnoType<decltype(dtl)>::get(), dtl ); +} + +sal_Bool SAL_CALL DropTarget::isActive( ) +{ + return m_bActive; //m_bDropTargetRegistered; +} + +void SAL_CALL DropTarget::setActive( sal_Bool _b ) +{ + MutexGuard g(m_aMutex); + m_bActive= _b; +} + +sal_Int8 SAL_CALL DropTarget::getDefaultActions( ) +{ + return m_nDefaultActions; +} + +void SAL_CALL DropTarget::setDefaultActions( sal_Int8 actions ) +{ + OSL_ENSURE( actions < 8, "No valid default actions"); + m_nDefaultActions= actions; +} + +HRESULT DropTarget::DragEnter( IDataObject *pDataObj, + DWORD grfKeyState, + POINTL pt, + DWORD *pdwEffect) +{ +#if defined DBG_CONSOLE_OUT + printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState, *pdwEffect); +#endif + if( m_bActive ) + { + // Intersection of pdwEffect and the allowed actions ( setDefaultActions) + m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect); + // m_nLastDropAction has to be set by a listener. If no listener calls + //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect + // will be DROPEFFECT_NONE throughout + m_nLastDropAction= ACTION_DEFAULT | ACTION_MOVE; + + m_currentDragContext = new TargetDragContext(this); + + //--> TRA + + // shortcut + if ( g_XTransferable.is( ) ) + m_currentData = g_XTransferable; + else + { + // Convert the IDataObject to a XTransferable + m_currentData = new CDOTransferable(m_xContext, IDataObjectPtr(pDataObj)); + } + + //<-- TRA + + if( m_nCurrentDropAction != ACTION_NONE) + { + DropTargetDragEnterEvent e; + e.SupportedDataFlavors= m_currentData->getTransferDataFlavors(); + e.DropAction= m_nCurrentDropAction; + e.Source.set( static_cast<XDropTarget*>(this),UNO_QUERY); + e.Context= m_currentDragContext; + POINT point={ pt.x, pt.y}; + ScreenToClient( m_hWnd, &point); + e.LocationX= point.x; + e.LocationY= point.y; + e.SourceActions= dndOleDropEffectsToActions( *pdwEffect); + + fire_dragEnter( e); + // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set + // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set + // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener + // has already reacted to the notification. + // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed + // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed. + // On drop the target should present the user a dialog from which the user may change the action. + sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect); + *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions); + } + else + { + *pdwEffect= DROPEFFECT_NONE; + } + } + return S_OK; +} + +HRESULT DropTarget::DragOver( DWORD grfKeyState, + POINTL pt, + DWORD *pdwEffect) +{ + if( m_bActive) + { + m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect); + + if( m_nCurrentDropAction) + { + DropTargetDragEvent e; + e.DropAction= m_nCurrentDropAction; + e.Source.set(static_cast<XDropTarget*>(this),UNO_QUERY); + e.Context= m_currentDragContext; + POINT point={ pt.x, pt.y}; + ScreenToClient( m_hWnd, &point); + e.LocationX= point.x; + e.LocationY= point.y; + e.SourceActions= dndOleDropEffectsToActions( *pdwEffect); + + // if grfKeyState has changed since the last DragOver then fire events. + // A listener might change m_nCurrentDropAction by calling the + // XDropTargetDragContext::acceptDrag function. But this is not important + // because in the afterwards fired dragOver event the action reflects + // grgKeyState again. + if( m_nLastDropAction != m_nCurrentDropAction) + fire_dropActionChanged( e); + + // The Event contains a XDropTargetDragContext implementation. + fire_dragOver( e); + // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set + // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set + // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener + // has already reacted to the notification. + // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed + // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed. + // On drop the target should present the user a dialog from which the user may change the action. + sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect); + // set the last action to the current if listener has not changed the value yet + *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions); + } + else + { + *pdwEffect= DROPEFFECT_NONE; + } + } +#if defined DBG_CONSOLE_OUT + printf("\nDropTarget::DragOver %d", *pdwEffect ); +#endif + return S_OK; +} + +HRESULT DropTarget::DragLeave() +{ +#if defined DBG_CONSOLE_OUT + printf("\nDropTarget::DragLeave"); +#endif + if( m_bActive) + { + + m_currentData=nullptr; + m_currentDragContext= nullptr; + m_currentDropContext= nullptr; + m_nLastDropAction= 0; + + if( m_nDefaultActions != ACTION_NONE) + { + DropTargetEvent e; + e.Source= static_cast<XDropTarget*>(this); + + fire_dragExit( e); + } + } + return S_OK; +} + +HRESULT DropTarget::Drop( IDataObject * /*pDataObj*/, + DWORD grfKeyState, + POINTL pt, + DWORD *pdwEffect) +{ +#if defined DBG_CONSOLE_OUT + printf("\nDropTarget::Drop"); +#endif + if( m_bActive) + { + + m_bDropComplete= false; + + m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect); + m_currentDropContext = new TargetDropContext(this); + if( m_nCurrentDropAction) + { + DropTargetDropEvent e; + e.DropAction= m_nCurrentDropAction; + e.Source.set( static_cast<XDropTarget*>(this), UNO_QUERY); + e.Context= m_currentDropContext; + POINT point={ pt.x, pt.y}; + ScreenToClient( m_hWnd, &point); + e.LocationX= point.x; + e.LocationY= point.y; + e.SourceActions= dndOleDropEffectsToActions( *pdwEffect); + e.Transferable= m_currentData; + fire_drop( e); + + //if fire_drop returns than a listener might have modified m_nCurrentDropAction + if( m_bDropComplete ) + { + sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect); + *pdwEffect= dndActionsToSingleDropEffect( m_nCurrentDropAction & allowedActions); + } + else + *pdwEffect= DROPEFFECT_NONE; + } + else + *pdwEffect= DROPEFFECT_NONE; + + m_currentData= nullptr; + m_currentDragContext= nullptr; + m_currentDropContext= nullptr; + m_nLastDropAction= 0; + } + return S_OK; +} + +void DropTarget::fire_drop( const DropTargetDropEvent& dte) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + listener->drop( dte); + } + } +} + +void DropTarget::fire_dragEnter( const DropTargetDragEnterEvent& e ) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + listener->dragEnter( e); + } + } +} + +void DropTarget::fire_dragExit( const DropTargetEvent& dte ) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + listener->dragExit( dte); + } + } +} + +void DropTarget::fire_dragOver( const DropTargetDragEvent& dtde ) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer ); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + listener->dragOver( dtde); + } + } +} + +void DropTarget::fire_dropActionChanged( const DropTargetDragEvent& dtde ) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + listener->dropActionChanged( dtde); + } + } +} + +// Non - interface functions +// DropTarget fires events to XDropTargetListeners. The event object contains an +// XDropTargetDropContext implementation. When the listener calls on that interface +// then the calls are delegated from DropContext (XDropTargetDropContext) to these +// functions. +// Only one listener which visible area is affected is allowed to call on +// XDropTargetDropContext +// Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation +// to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed. +// return sal_False results in throwing an InvalidDNDOperationException in the caller. + +void DropTarget::_acceptDrop(sal_Int8 dropOperation, const Reference<XDropTargetDropContext>& context) +{ + if( context == m_currentDropContext) + { + m_nCurrentDropAction= dropOperation; + } +} + +void DropTarget::_rejectDrop( const Reference<XDropTargetDropContext>& context) +{ + if( context == m_currentDropContext) + { + m_nCurrentDropAction= ACTION_NONE; + } +} + +void DropTarget::_dropComplete(bool success, const Reference<XDropTargetDropContext>& context) +{ + if(context == m_currentDropContext) + { + m_bDropComplete= success; + } +} + +// DropTarget fires events to XDropTargetListeners. The event object can contains an +// XDropTargetDragContext implementation. When the listener calls on that interface +// then the calls are delegated from DragContext (XDropTargetDragContext) to these +// functions. +// Only one listener which visible area is affected is allowed to call on +// XDropTargetDragContext +void DropTarget::_acceptDrag( sal_Int8 dragOperation, const Reference<XDropTargetDragContext>& context) +{ + if( context == m_currentDragContext) + { + m_nLastDropAction= dragOperation; + } +} + +void DropTarget::_rejectDrag( const Reference<XDropTargetDragContext>& context) +{ + if(context == m_currentDragContext) + { + m_nLastDropAction= ACTION_NONE; + } +} + +// This function determines the action dependent on the pressed +// key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result +// is then checked against the allowed actions which can be set through +// XDropTarget::setDefaultActions. Only those values which are also +// default actions are returned. If setDefaultActions has not been called +// beforehand the default actions comprise all possible actions. +// params: grfKeyState - the modifier keys and mouse buttons currently pressed +inline sal_Int8 DropTarget::getFilteredActions( DWORD grfKeyState, DWORD dwEffect) +{ + sal_Int8 actions= dndOleKeysToAction( grfKeyState, dndOleDropEffectsToActions( dwEffect)); + return actions & m_nDefaultActions; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +dtrans_DropTarget_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new DropTarget(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/targetdragcontext.cxx b/vcl/win/dtrans/targetdragcontext.cxx new file mode 100644 index 0000000000..71e345b74c --- /dev/null +++ b/vcl/win/dtrans/targetdragcontext.cxx @@ -0,0 +1,39 @@ +/* -*- 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 "targetdragcontext.hxx" + +TargetDragContext::TargetDragContext(DropTarget* p) +{ + m_pDropTarget = p; + p->acquire(); +} + +TargetDragContext::~TargetDragContext() { m_pDropTarget->release(); } + +void SAL_CALL TargetDragContext::acceptDrag(sal_Int8 dragOperation) +{ + m_pDropTarget->_acceptDrag(dragOperation, static_cast<XDropTargetDragContext*>(this)); +} +void SAL_CALL TargetDragContext::rejectDrag() +{ + m_pDropTarget->_rejectDrag(static_cast<XDropTargetDragContext*>(this)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/targetdragcontext.hxx b/vcl/win/dtrans/targetdragcontext.hxx new file mode 100644 index 0000000000..a64ab6a30b --- /dev/null +++ b/vcl/win/dtrans/targetdragcontext.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> + +#include <win/dnd_target.hxx> + +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::dnd; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +class TargetDragContext : public WeakImplHelper<XDropTargetDragContext> +{ + // some calls to the functions of XDropTargetDragContext are delegated + // to non-interface functions of m_pDropTarget + DropTarget* m_pDropTarget; + +public: + explicit TargetDragContext(DropTarget* pTarget); + ~TargetDragContext() override; + TargetDragContext(const TargetDragContext&) = delete; + TargetDragContext& operator=(const TargetDragContext&) = delete; + + virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) override; + virtual void SAL_CALL rejectDrag() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/targetdropcontext.cxx b/vcl/win/dtrans/targetdropcontext.cxx new file mode 100644 index 0000000000..fe65389431 --- /dev/null +++ b/vcl/win/dtrans/targetdropcontext.cxx @@ -0,0 +1,50 @@ +/* -*- 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 "targetdropcontext.hxx" + +using namespace ::com::sun::star::datatransfer::dnd; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +TargetDropContext::TargetDropContext(DropTarget* p) +{ + m_pDropTarget = p; + p->acquire(); +} + +TargetDropContext::~TargetDropContext() { m_pDropTarget->release(); } + +void SAL_CALL TargetDropContext::acceptDrop(sal_Int8 dropOperation) +{ + m_pDropTarget->_acceptDrop(dropOperation, static_cast<XDropTargetDropContext*>(this)); +} + +void SAL_CALL TargetDropContext::rejectDrop() +{ + m_pDropTarget->_rejectDrop(static_cast<XDropTargetDropContext*>(this)); +} + +void SAL_CALL TargetDropContext::dropComplete(sal_Bool success) +{ + m_pDropTarget->_dropComplete(success, static_cast<XDropTargetDropContext*>(this)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/dtrans/targetdropcontext.hxx b/vcl/win/dtrans/targetdropcontext.hxx new file mode 100644 index 0000000000..938a23f850 --- /dev/null +++ b/vcl/win/dtrans/targetdropcontext.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/datatransfer/dnd/XDropTargetDropContext.hpp> + +#include <win/dnd_target.hxx> + +using namespace ::com::sun::star::datatransfer::dnd; +using namespace ::cppu; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +class TargetDropContext : public WeakImplHelper<XDropTargetDropContext> +{ + // calls to the functions of XDropTargetDropContext are delegated + // to non-interface functions of m_pDropTarget + DropTarget* m_pDropTarget; + +public: + explicit TargetDropContext(DropTarget* pTarget); + ~TargetDropContext() override; + TargetDropContext(const TargetDropContext&) = delete; + TargetDropContext& operator=(const TargetDropContext&) = delete; + + // XDropTargetDragContext + virtual void SAL_CALL acceptDrop(sal_Int8 dropOperation) override; + virtual void SAL_CALL rejectDrop() override; + + // XDropTargetDropContext (inherits XDropTargetDragContext) + virtual void SAL_CALL dropComplete(sal_Bool success) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |