1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
21 #include <rtl/process.h>
22 #include <osl/diagnose.h>
23 #include <sal/log.hxx>
25 #include "DOTransferable.hxx"
26 #include "ImplHelper.hxx"
27 #include "WinClip.hxx"
28 #include "WinClipboard.hxx"
29 #include "DTransHelper.hxx"
30 #include "TxtCnvtHlp.hxx"
31 #include "MimeAttrib.hxx"
32 #include "FmtFilter.hxx"
34 #include <com/sun/star/container/NoSuchElementException.hpp>
35 #include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
36 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
37 #include <com/sun/star/io/IOException.hpp>
38 #include <com/sun/star/lang/IllegalArgumentException.hpp>
43 using namespace com::sun::star::uno
;
44 using namespace com::sun::star::datatransfer
;
45 using namespace com::sun::star::io
;
46 using namespace com::sun::star::lang
;
47 using namespace com::sun::star::container
;
51 const Type CPPUTYPE_SEQINT8
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
52 const Type CPPUTYPE_OUSTRING
= cppu::UnoType
<OUString
>::get();
54 bool isValidFlavor( const DataFlavor
& aFlavor
)
56 return ( aFlavor
.MimeType
.getLength( ) &&
57 ( ( aFlavor
.DataType
== CPPUTYPE_SEQINT8
) ||
58 ( aFlavor
.DataType
== CPPUTYPE_OUSTRING
) ) );
61 void clipDataToByteStream( CLIPFORMAT cf
, STGMEDIUM stgmedium
, CDOTransferable::ByteSequence_t
& aByteSequence
)
63 CStgTransferHelper memTransferHelper
;
64 LPSTREAM pStream
= nullptr;
66 switch( stgmedium
.tymed
)
69 memTransferHelper
.init( stgmedium
.hGlobal
);
73 memTransferHelper
.init( stgmedium
.hMetaFilePict
);
77 memTransferHelper
.init( stgmedium
.hEnhMetaFile
);
81 pStream
= stgmedium
.pstm
;
85 throw UnsupportedFlavorException( );
91 // We have a stream, read from it.
93 HRESULT hr
= pStream
->Stat(&aStat
, STATFLAG_NONAME
);
96 SAL_WARN("dtrans", "clipDataToByteStream: Stat() failed");
100 size_t nMemSize
= aStat
.cbSize
.QuadPart
;
101 aByteSequence
.realloc(nMemSize
);
104 hr
= pStream
->Seek(li
, STREAM_SEEK_SET
, nullptr);
107 SAL_WARN("dtrans", "clipDataToByteStream: Seek() failed");
111 hr
= pStream
->Read(aByteSequence
.getArray(), nMemSize
, &nRead
);
114 SAL_WARN("dtrans", "clipDataToByteStream: Read() failed");
116 if (nRead
< nMemSize
)
118 SAL_WARN("dtrans", "clipDataToByteStream: Read() was partial");
124 int nMemSize
= memTransferHelper
.memSize( cf
);
125 aByteSequence
.realloc( nMemSize
);
126 memTransferHelper
.read( aByteSequence
.getArray( ), nMemSize
);
129 OUString
byteStreamToOUString( CDOTransferable::ByteSequence_t
& aByteStream
)
132 sal_Int32 nMemSize
= aByteStream
.getLength( );
134 // if there is a trailing L"\0" subtract 1 from length
135 // for unknown reason, the sequence may sometimes arrive empty
136 if ( aByteStream
.getLength( ) > 1 &&
137 0 == aByteStream
[ aByteStream
.getLength( ) - 2 ] &&
138 0 == aByteStream
[ aByteStream
.getLength( ) - 1 ] )
139 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) ) - 1;
141 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) );
143 return OUString( reinterpret_cast< sal_Unicode
* >( aByteStream
.getArray( ) ), nWChars
);
146 Any
byteStreamToAny( CDOTransferable::ByteSequence_t
& aByteStream
, const Type
& aRequestedDataType
)
150 if ( aRequestedDataType
== CPPUTYPE_OUSTRING
)
152 OUString str
= byteStreamToOUString( aByteStream
);
154 throw RuntimeException();
158 aAny
<<= aByteStream
;
163 bool cmpFullMediaType(
164 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
166 return xLhs
->getFullMediaType().equalsIgnoreAsciiCase( xRhs
->getFullMediaType( ) );
169 bool cmpAllContentTypeParameter(
170 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
172 Sequence
< OUString
> xLhsFlavors
= xLhs
->getParameters( );
173 Sequence
< OUString
> xRhsFlavors
= xRhs
->getParameters( );
178 if ( xLhsFlavors
.getLength( ) == xRhsFlavors
.getLength( ) )
183 for ( sal_Int32 i
= 0; i
< xLhsFlavors
.getLength( ); i
++ )
185 pLhs
= xLhs
->getParameterValue( xLhsFlavors
[i
] );
186 pRhs
= xRhs
->getParameterValue( xLhsFlavors
[i
] );
188 if ( !pLhs
.equalsIgnoreAsciiCase( pRhs
) )
198 catch( NoSuchElementException
& )
202 catch( IllegalArgumentException
& )
212 CDOTransferable::CDOTransferable(
213 const Reference
< XComponentContext
>& rxContext
, IDataObjectPtr rDataObject
) :
214 m_rDataObject( rDataObject
),
215 m_xContext( rxContext
),
216 m_DataFormatTranslator( rxContext
),
217 m_bUnicodeRegistered( false ),
218 m_TxtFormatOnClipboard( CF_INVALID
)
223 CDOTransferable::CDOTransferable(
224 const Reference
<XComponentContext
>& rxContext
,
225 const css::uno::Reference
<css::datatransfer::clipboard::XClipboard
>& xClipboard
,
226 const std::vector
<sal_uInt32
>& rFormats
)
227 : m_xClipboard(xClipboard
)
228 , m_xContext(rxContext
)
229 , m_DataFormatTranslator(rxContext
)
230 , m_bUnicodeRegistered(false)
231 , m_TxtFormatOnClipboard(CF_INVALID
)
233 initFlavorListFromFormatList(rFormats
);
236 Any SAL_CALL
CDOTransferable::getTransferData( const DataFlavor
& aFlavor
)
238 OSL_ASSERT( isValidFlavor( aFlavor
) );
240 MutexGuard
aGuard( m_aMutex
);
242 // convert dataflavor to formatetc
244 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcFromDataFlavor( aFlavor
);
245 OSL_ASSERT( CF_INVALID
!= fetc
.getClipformat() );
247 // get the data from clipboard in a byte stream
249 ByteSequence_t clipDataStream
;
253 clipDataStream
= getClipboardData( fetc
);
255 catch( UnsupportedFlavorException
& )
257 if ( CDataFormatTranslator::isUnicodeTextFormat( fetc
.getClipformat( ) ) &&
258 m_bUnicodeRegistered
)
260 OUString aUnicodeText
= synthesizeUnicodeText( );
261 Any aAny
= makeAny( aUnicodeText
);
264 // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
265 // clipboard for being on the safe side
266 else if(CF_DIBV5
== fetc
.getClipformat())
268 // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
269 // check CF_DIB availability as an alternative
270 fetc
.setClipformat(CF_DIB
);
272 clipDataStream
= getClipboardData( fetc
);
273 // pass UnsupportedFlavorException out, tried all possibilities
276 throw; // pass through exception
279 // return the data as any
281 return byteStreamToAny( clipDataStream
, aFlavor
.DataType
);
284 // getTransferDataFlavors
286 Sequence
< DataFlavor
> SAL_CALL
CDOTransferable::getTransferDataFlavors( )
291 // isDataFlavorSupported
292 // returns true if we find a DataFlavor with the same MimeType and
295 sal_Bool SAL_CALL
CDOTransferable::isDataFlavorSupported( const DataFlavor
& aFlavor
)
297 OSL_ASSERT( isValidFlavor( aFlavor
) );
299 for ( DataFlavor
const & df
: std::as_const(m_FlavorList
) )
300 if ( compareDataFlavors( aFlavor
, df
) )
306 // the list of dataflavors currently on the clipboard will be initialized
307 // only once; if the client of this Transferable will hold a reference
308 // to it and the underlying clipboard content changes, the client does
309 // possible operate on an invalid list
310 // if there is only text on the clipboard we will also offer unicode text
311 // an synthesize this format on the fly if requested, to accomplish this
312 // we save the first offered text format which we will later use for the
315 void CDOTransferable::initFlavorList( )
317 std::vector
<sal_uInt32
> aFormats
;
318 sal::systools::COMReference
<IEnumFORMATETC
> pEnumFormatEtc
;
319 HRESULT hr
= m_rDataObject
->EnumFormatEtc( DATADIR_GET
, &pEnumFormatEtc
);
320 if ( SUCCEEDED( hr
) )
322 pEnumFormatEtc
->Reset( );
325 while ( S_OK
== pEnumFormatEtc
->Next( 1, &fetc
, nullptr ) )
327 aFormats
.push_back(fetc
.cfFormat
);
328 // see MSDN IEnumFORMATETC
329 CoTaskMemFree( fetc
.ptd
);
331 initFlavorListFromFormatList(aFormats
);
335 void CDOTransferable::initFlavorListFromFormatList(const std::vector
<sal_uInt32
>& rFormats
)
337 for (sal_uInt32 cfFormat
: rFormats
)
339 // we use locales only to determine the
340 // charset if there is text on the cliboard
341 // we don't offer this format
342 if (CF_LOCALE
== cfFormat
)
345 // if text or oemtext is offered we pretend to have unicode text
346 if (CDataFormatTranslator::isTextFormat(cfFormat
))
348 if (!m_bUnicodeRegistered
)
350 m_TxtFormatOnClipboard
= cfFormat
;
351 m_bUnicodeRegistered
= true;
353 // register unicode text as format
354 addSupportedFlavor(formatEtcToDataFlavor(CF_UNICODETEXT
));
358 addSupportedFlavor(formatEtcToDataFlavor(cfFormat
));
363 void CDOTransferable::addSupportedFlavor( const DataFlavor
& aFlavor
)
365 // we ignore all formats that couldn't be translated
366 if ( aFlavor
.MimeType
.getLength( ) )
368 OSL_ASSERT( isValidFlavor( aFlavor
) );
370 m_FlavorList
.realloc( m_FlavorList
.getLength( ) + 1 );
371 m_FlavorList
[m_FlavorList
.getLength( ) - 1] = aFlavor
;
375 DataFlavor
CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat
)
377 return m_DataFormatTranslator
.getDataFlavorFromFormatEtc(cfFormat
);
380 // returns the current locale on clipboard; if there is no locale on
381 // clipboard the function returns the current thread locale
383 LCID
CDOTransferable::getLocaleFromClipboard( )
385 LCID lcid
= GetThreadLocale( );
389 CFormatEtc fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE
);
390 ByteSequence_t aLCIDSeq
= getClipboardData( fetc
);
391 lcid
= *reinterpret_cast<LCID
*>( aLCIDSeq
.getArray( ) );
393 // because of a Win95/98 Bug; there the high word
394 // of a locale has the same value as the
395 // low word e.g. 0x07040704 that's not right
396 // correct is 0x00000704
401 // we take the default locale
407 void CDOTransferable::tryToGetIDataObjectIfAbsent()
409 if (!m_rDataObject
.is())
411 auto xClipboard
= m_xClipboard
.get(); // holding the reference while we get the object
412 if (CWinClipboard
* pWinClipboard
= dynamic_cast<CWinClipboard
*>(xClipboard
.get()))
414 m_rDataObject
= pWinClipboard
->getIDataObject();
419 // I think it's not necessary to call ReleaseStgMedium
420 // in case of failures because nothing should have been
423 CDOTransferable::ByteSequence_t
CDOTransferable::getClipboardData( CFormatEtc
& aFormatEtc
)
426 tryToGetIDataObjectIfAbsent();
427 if (!m_rDataObject
.is()) // Maybe we are shutting down, and clipboard is already destroyed?
428 throw RuntimeException();
429 HRESULT hr
= m_rDataObject
->GetData( aFormatEtc
, &stgmedium
);
431 // in case of failure to get a WMF metafile handle, try to get a memory block
433 ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() ) &&
434 ( TYMED_MFPICT
== aFormatEtc
.getTymed() ) )
436 CFormatEtc
aTempFormat( aFormatEtc
);
437 aTempFormat
.setTymed( TYMED_HGLOBAL
);
438 hr
= m_rDataObject
->GetData( aTempFormat
, &stgmedium
);
441 if (FAILED(hr
) && aFormatEtc
.getTymed() == TYMED_HGLOBAL
)
443 // Handle type is not memory, try stream.
444 CFormatEtc
aTempFormat(aFormatEtc
);
445 aTempFormat
.setTymed(TYMED_ISTREAM
);
446 hr
= m_rDataObject
->GetData(aTempFormat
, &stgmedium
);
451 OSL_ASSERT( (hr
!= E_INVALIDARG
) &&
452 (hr
!= DV_E_DVASPECT
) &&
453 (hr
!= DV_E_LINDEX
) &&
454 (hr
!= DV_E_TYMED
) );
456 if ( DV_E_FORMATETC
== hr
)
457 throw UnsupportedFlavorException( );
458 else if ( STG_E_MEDIUMFULL
== hr
)
459 throw IOException( );
461 throw RuntimeException( );
464 ByteSequence_t byteStream
;
468 if ( CF_ENHMETAFILE
== aFormatEtc
.getClipformat() )
469 byteStream
= WinENHMFPictToOOMFPict( stgmedium
.hEnhMetaFile
);
470 else if (CF_HDROP
== aFormatEtc
.getClipformat())
471 byteStream
= CF_HDROPToFileList(stgmedium
.hGlobal
);
472 else if ( CF_BITMAP
== aFormatEtc
.getClipformat() )
474 byteStream
= WinBITMAPToOOBMP(stgmedium
.hBitmap
);
475 if( aFormatEtc
.getTymed() == TYMED_GDI
&&
476 ! stgmedium
.pUnkForRelease
)
478 DeleteObject(stgmedium
.hBitmap
);
483 clipDataToByteStream( aFormatEtc
.getClipformat( ), stgmedium
, byteStream
);
485 // format conversion if necessary
486 // #i124085# DIBV5 should not happen currently, but keep as a hint here
487 if(CF_DIBV5
== aFormatEtc
.getClipformat() || CF_DIB
== aFormatEtc
.getClipformat())
489 byteStream
= WinDIBToOOBMP(byteStream
);
491 else if(CF_METAFILEPICT
== aFormatEtc
.getClipformat())
493 byteStream
= WinMFPictToOOMFPict(byteStream
);
497 ReleaseStgMedium( &stgmedium
);
499 catch( CStgTransferHelper::CStgTransferException
& )
501 ReleaseStgMedium( &stgmedium
);
502 throw IOException( );
508 OUString
CDOTransferable::synthesizeUnicodeText( )
510 ByteSequence_t aTextSequence
;
512 LCID lcid
= getLocaleFromClipboard( );
513 sal_uInt32 cpForTxtCnvt
= 0;
515 if ( CF_TEXT
== m_TxtFormatOnClipboard
)
517 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT
);
518 aTextSequence
= getClipboardData( fetc
);
520 // determine the codepage used for text conversion
521 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTANSICODEPAGE
).toInt32( );
523 else if ( CF_OEMTEXT
== m_TxtFormatOnClipboard
)
525 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT
);
526 aTextSequence
= getClipboardData( fetc
);
528 // determine the codepage used for text conversion
529 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTCODEPAGE
).toInt32( );
534 CStgTransferHelper stgTransferHelper
;
537 MultiByteToWideCharEx( cpForTxtCnvt
,
538 reinterpret_cast<char*>( aTextSequence
.getArray( ) ),
539 sal::static_int_cast
<sal_uInt32
>(-1), // Huh ?
543 CRawHGlobalPtr
ptrHGlob(stgTransferHelper
);
544 sal_Unicode
* pWChar
= static_cast<sal_Unicode
*>(ptrHGlob
.GetMemPtr());
546 return OUString(pWChar
);
549 bool CDOTransferable::compareDataFlavors(
550 const DataFlavor
& lhs
, const DataFlavor
& rhs
)
552 if ( !m_rXMimeCntFactory
.is( ) )
554 m_rXMimeCntFactory
= MimeContentTypeFactory::create( m_xContext
);
561 Reference
< XMimeContentType
> xLhs( m_rXMimeCntFactory
->createMimeContentType( lhs
.MimeType
) );
562 Reference
< XMimeContentType
> xRhs( m_rXMimeCntFactory
->createMimeContentType( rhs
.MimeType
) );
564 if ( cmpFullMediaType( xLhs
, xRhs
) )
566 bRet
= cmpAllContentTypeParameter( xLhs
, xRhs
);
569 catch( IllegalArgumentException
& )
571 OSL_FAIL( "Invalid content type detected" );
578 css::uno::Any SAL_CALL
CDOTransferable::getData( const Sequence
< sal_Int8
>& aProcessId
)
582 sal_Int8
const * arProcCaller
= aProcessId
.getConstArray();
584 rtl_getGlobalProcessId(arId
);
585 if( ! memcmp( arId
, arProcCaller
,16))
587 if (m_rDataObject
.is())
589 IDataObject
* pObj
= m_rDataObject
.get();
591 retVal
.setValue( &pObj
, cppu::UnoType
<sal_uInt32
>::get());
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */