summaryrefslogtreecommitdiffstats
path: root/vcl/win/dtrans/DOTransferable.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/win/dtrans/DOTransferable.cxx577
1 files changed, 577 insertions, 0 deletions
diff --git a/vcl/win/dtrans/DOTransferable.cxx b/vcl/win/dtrans/DOTransferable.cxx
new file mode 100644
index 000000000..7c5b55c02
--- /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 ) );
+
+ MutexGuard 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: */