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>
42 using namespace com::sun::star::uno
;
43 using namespace com::sun::star::datatransfer
;
44 using namespace com::sun::star::io
;
45 using namespace com::sun::star::lang
;
46 using namespace com::sun::star::container
;
50 const Type CPPUTYPE_SEQINT8
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
51 const Type CPPUTYPE_OUSTRING
= cppu::UnoType
<OUString
>::get();
53 bool isValidFlavor( const DataFlavor
& aFlavor
)
55 return ( aFlavor
.MimeType
.getLength( ) &&
56 ( ( aFlavor
.DataType
== CPPUTYPE_SEQINT8
) ||
57 ( aFlavor
.DataType
== CPPUTYPE_OUSTRING
) ) );
60 void clipDataToByteStream( CLIPFORMAT cf
, STGMEDIUM stgmedium
, CDOTransferable::ByteSequence_t
& aByteSequence
)
62 CStgTransferHelper memTransferHelper
;
63 LPSTREAM pStream
= nullptr;
65 switch( stgmedium
.tymed
)
68 memTransferHelper
.init( stgmedium
.hGlobal
);
72 memTransferHelper
.init( stgmedium
.hMetaFilePict
);
76 memTransferHelper
.init( stgmedium
.hEnhMetaFile
);
80 pStream
= stgmedium
.pstm
;
84 throw UnsupportedFlavorException( );
90 // We have a stream, read from it.
92 HRESULT hr
= pStream
->Stat(&aStat
, STATFLAG_NONAME
);
95 SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Stat() failed");
99 size_t nMemSize
= aStat
.cbSize
.QuadPart
;
100 aByteSequence
.realloc(nMemSize
);
103 hr
= pStream
->Seek(li
, STREAM_SEEK_SET
, nullptr);
106 SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Seek() failed");
110 hr
= pStream
->Read(aByteSequence
.getArray(), nMemSize
, &nRead
);
113 SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() failed");
115 if (nRead
< nMemSize
)
117 SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() was partial");
123 int nMemSize
= memTransferHelper
.memSize( cf
);
124 aByteSequence
.realloc( nMemSize
);
125 memTransferHelper
.read( aByteSequence
.getArray( ), nMemSize
);
128 OUString
byteStreamToOUString( CDOTransferable::ByteSequence_t
& aByteStream
)
131 sal_Int32 nMemSize
= aByteStream
.getLength( );
133 // if there is a trailing L"\0" subtract 1 from length
134 // for unknown reason, the sequence may sometimes arrive empty
135 if ( aByteStream
.getLength( ) > 1 &&
136 0 == aByteStream
[ aByteStream
.getLength( ) - 2 ] &&
137 0 == aByteStream
[ aByteStream
.getLength( ) - 1 ] )
138 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) ) - 1;
140 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) );
142 return OUString( reinterpret_cast< sal_Unicode
* >( aByteStream
.getArray( ) ), nWChars
);
145 Any
byteStreamToAny( CDOTransferable::ByteSequence_t
& aByteStream
, const Type
& aRequestedDataType
)
149 if ( aRequestedDataType
== CPPUTYPE_OUSTRING
)
151 OUString str
= byteStreamToOUString( aByteStream
);
153 throw RuntimeException();
157 aAny
<<= aByteStream
;
162 bool cmpFullMediaType(
163 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
165 return xLhs
->getFullMediaType().equalsIgnoreAsciiCase( xRhs
->getFullMediaType( ) );
168 bool cmpAllContentTypeParameter(
169 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
171 Sequence
< OUString
> xLhsFlavors
= xLhs
->getParameters( );
172 Sequence
< OUString
> xRhsFlavors
= xRhs
->getParameters( );
177 if ( xLhsFlavors
.getLength( ) == xRhsFlavors
.getLength( ) )
182 for ( sal_Int32 i
= 0; i
< xLhsFlavors
.getLength( ); i
++ )
184 pLhs
= xLhs
->getParameterValue( xLhsFlavors
[i
] );
185 pRhs
= xRhs
->getParameterValue( xLhsFlavors
[i
] );
187 if ( !pLhs
.equalsIgnoreAsciiCase( pRhs
) )
197 catch( NoSuchElementException
& )
201 catch( IllegalArgumentException
& )
211 CDOTransferable::CDOTransferable(
212 const Reference
< XComponentContext
>& rxContext
, IDataObjectPtr rDataObject
) :
213 m_rDataObject( rDataObject
),
214 m_xContext( rxContext
),
215 m_DataFormatTranslator( rxContext
),
216 m_bUnicodeRegistered( false ),
217 m_TxtFormatOnClipboard( CF_INVALID
)
222 CDOTransferable::CDOTransferable(
223 const Reference
<XComponentContext
>& rxContext
,
224 const css::uno::Reference
<css::datatransfer::clipboard::XClipboard
>& xClipboard
,
225 const std::vector
<sal_uInt32
>& rFormats
)
226 : m_xClipboard(xClipboard
)
227 , m_xContext(rxContext
)
228 , m_DataFormatTranslator(rxContext
)
229 , m_bUnicodeRegistered(false)
230 , m_TxtFormatOnClipboard(CF_INVALID
)
232 initFlavorListFromFormatList(rFormats
);
235 Any SAL_CALL
CDOTransferable::getTransferData( const DataFlavor
& aFlavor
)
237 OSL_ASSERT( isValidFlavor( aFlavor
) );
239 std::unique_lock
aGuard( m_aMutex
);
241 // convert dataflavor to formatetc
243 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcFromDataFlavor( aFlavor
);
244 OSL_ASSERT( CF_INVALID
!= fetc
.getClipformat() );
246 // get the data from clipboard in a byte stream
248 ByteSequence_t clipDataStream
;
252 clipDataStream
= getClipboardData( fetc
);
254 catch( UnsupportedFlavorException
& )
256 if ( CDataFormatTranslator::isUnicodeTextFormat( fetc
.getClipformat( ) ) &&
257 m_bUnicodeRegistered
)
259 OUString aUnicodeText
= synthesizeUnicodeText( );
260 Any
aAny( aUnicodeText
);
263 // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
264 // clipboard for being on the safe side
265 else if(CF_DIBV5
== fetc
.getClipformat())
267 // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
268 // check CF_DIB availability as an alternative
269 fetc
.setClipformat(CF_DIB
);
271 clipDataStream
= getClipboardData( fetc
);
272 // pass UnsupportedFlavorException out, tried all possibilities
275 throw; // pass through exception
278 // return the data as any
280 return byteStreamToAny( clipDataStream
, aFlavor
.DataType
);
283 // getTransferDataFlavors
285 Sequence
< DataFlavor
> SAL_CALL
CDOTransferable::getTransferDataFlavors( )
290 // isDataFlavorSupported
291 // returns true if we find a DataFlavor with the same MimeType and
294 sal_Bool SAL_CALL
CDOTransferable::isDataFlavorSupported( const DataFlavor
& aFlavor
)
296 OSL_ASSERT( isValidFlavor( aFlavor
) );
298 for (DataFlavor
const& df
: m_FlavorList
)
299 if ( compareDataFlavors( aFlavor
, df
) )
305 // the list of dataflavors currently on the clipboard will be initialized
306 // only once; if the client of this Transferable will hold a reference
307 // to it and the underlying clipboard content changes, the client does
308 // possible operate on an invalid list
309 // if there is only text on the clipboard we will also offer unicode text
310 // an synthesize this format on the fly if requested, to accomplish this
311 // we save the first offered text format which we will later use for the
314 void CDOTransferable::initFlavorList( )
316 std::vector
<sal_uInt32
> aFormats
;
317 sal::systools::COMReference
<IEnumFORMATETC
> pEnumFormatEtc
;
318 HRESULT hr
= m_rDataObject
->EnumFormatEtc( DATADIR_GET
, &pEnumFormatEtc
);
319 if ( SUCCEEDED( hr
) )
321 pEnumFormatEtc
->Reset( );
324 while ( S_OK
== pEnumFormatEtc
->Next( 1, &fetc
, nullptr ) )
326 aFormats
.push_back(fetc
.cfFormat
);
327 // see MSDN IEnumFORMATETC
328 CoTaskMemFree( fetc
.ptd
);
330 initFlavorListFromFormatList(aFormats
);
334 void CDOTransferable::initFlavorListFromFormatList(const std::vector
<sal_uInt32
>& rFormats
)
336 for (sal_uInt32 cfFormat
: rFormats
)
338 // we use locales only to determine the
339 // charset if there is text on the clipboard
340 // we don't offer this format
341 if (CF_LOCALE
== cfFormat
)
344 // if text or oemtext is offered we pretend to have unicode text
345 if (CDataFormatTranslator::isTextFormat(cfFormat
))
347 if (!m_bUnicodeRegistered
)
349 m_TxtFormatOnClipboard
= cfFormat
;
350 m_bUnicodeRegistered
= true;
352 // register unicode text as format
353 addSupportedFlavor(formatEtcToDataFlavor(CF_UNICODETEXT
));
357 addSupportedFlavor(formatEtcToDataFlavor(cfFormat
));
362 void CDOTransferable::addSupportedFlavor( const DataFlavor
& aFlavor
)
364 // we ignore all formats that couldn't be translated
365 if ( aFlavor
.MimeType
.getLength( ) )
367 OSL_ASSERT( isValidFlavor( aFlavor
) );
369 m_FlavorList
.realloc( m_FlavorList
.getLength( ) + 1 );
370 m_FlavorList
.getArray()[m_FlavorList
.getLength( ) - 1] = aFlavor
;
374 DataFlavor
CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat
)
376 return m_DataFormatTranslator
.getDataFlavorFromFormatEtc(cfFormat
);
379 // returns the current locale on clipboard; if there is no locale on
380 // clipboard the function returns the current thread locale
382 LCID
CDOTransferable::getLocaleFromClipboard( )
384 LCID lcid
= GetThreadLocale( );
388 CFormatEtc fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE
);
389 ByteSequence_t aLCIDSeq
= getClipboardData( fetc
);
390 lcid
= *reinterpret_cast<LCID
*>( aLCIDSeq
.getArray( ) );
392 // because of a Win95/98 Bug; there the high word
393 // of a locale has the same value as the
394 // low word e.g. 0x07040704 that's not right
395 // correct is 0x00000704
400 // we take the default locale
406 void CDOTransferable::tryToGetIDataObjectIfAbsent()
408 if (!m_rDataObject
.is())
410 auto xClipboard
= m_xClipboard
.get(); // holding the reference while we get the object
411 if (CWinClipboard
* pWinClipboard
= dynamic_cast<CWinClipboard
*>(xClipboard
.get()))
413 m_rDataObject
= pWinClipboard
->getIDataObject();
418 // I think it's not necessary to call ReleaseStgMedium
419 // in case of failures because nothing should have been
422 CDOTransferable::ByteSequence_t
CDOTransferable::getClipboardData( CFormatEtc
& aFormatEtc
)
425 tryToGetIDataObjectIfAbsent();
426 if (!m_rDataObject
.is()) // Maybe we are shutting down, and clipboard is already destroyed?
427 throw RuntimeException();
428 HRESULT hr
= m_rDataObject
->GetData( aFormatEtc
, &stgmedium
);
430 // in case of failure to get a WMF metafile handle, try to get a memory block
432 ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() ) &&
433 ( TYMED_MFPICT
== aFormatEtc
.getTymed() ) )
435 CFormatEtc
aTempFormat( aFormatEtc
);
436 aTempFormat
.setTymed( TYMED_HGLOBAL
);
437 hr
= m_rDataObject
->GetData( aTempFormat
, &stgmedium
);
440 if (FAILED(hr
) && aFormatEtc
.getTymed() == TYMED_HGLOBAL
)
442 // Handle type is not memory, try stream.
443 CFormatEtc
aTempFormat(aFormatEtc
);
444 aTempFormat
.setTymed(TYMED_ISTREAM
);
445 hr
= m_rDataObject
->GetData(aTempFormat
, &stgmedium
);
450 OSL_ASSERT( (hr
!= E_INVALIDARG
) &&
451 (hr
!= DV_E_DVASPECT
) &&
452 (hr
!= DV_E_LINDEX
) &&
453 (hr
!= DV_E_TYMED
) );
455 if ( DV_E_FORMATETC
== hr
)
456 throw UnsupportedFlavorException( );
457 else if ( STG_E_MEDIUMFULL
== hr
)
458 throw IOException( );
460 throw RuntimeException( );
463 ByteSequence_t byteStream
;
467 if ( CF_ENHMETAFILE
== aFormatEtc
.getClipformat() )
468 byteStream
= WinENHMFPictToOOMFPict( stgmedium
.hEnhMetaFile
);
469 else if (CF_HDROP
== aFormatEtc
.getClipformat())
470 byteStream
= CF_HDROPToFileList(stgmedium
.hGlobal
);
471 else if ( CF_BITMAP
== aFormatEtc
.getClipformat() )
473 byteStream
= WinBITMAPToOOBMP(stgmedium
.hBitmap
);
474 if( aFormatEtc
.getTymed() == TYMED_GDI
&&
475 ! stgmedium
.pUnkForRelease
)
477 DeleteObject(stgmedium
.hBitmap
);
482 clipDataToByteStream( aFormatEtc
.getClipformat( ), stgmedium
, byteStream
);
484 // format conversion if necessary
485 // #i124085# DIBV5 should not happen currently, but keep as a hint here
486 if(CF_DIBV5
== aFormatEtc
.getClipformat() || CF_DIB
== aFormatEtc
.getClipformat())
488 byteStream
= WinDIBToOOBMP(byteStream
);
490 else if(CF_METAFILEPICT
== aFormatEtc
.getClipformat())
492 byteStream
= WinMFPictToOOMFPict(byteStream
);
496 ReleaseStgMedium( &stgmedium
);
498 catch( CStgTransferHelper::CStgTransferException
& )
500 ReleaseStgMedium( &stgmedium
);
501 throw IOException( );
507 OUString
CDOTransferable::synthesizeUnicodeText( )
509 ByteSequence_t aTextSequence
;
511 LCID lcid
= getLocaleFromClipboard( );
512 sal_uInt32 cpForTxtCnvt
= 0;
514 if ( CF_TEXT
== m_TxtFormatOnClipboard
)
516 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT
);
517 aTextSequence
= getClipboardData( fetc
);
519 // determine the codepage used for text conversion
520 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTANSICODEPAGE
).toInt32( );
522 else if ( CF_OEMTEXT
== m_TxtFormatOnClipboard
)
524 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT
);
525 aTextSequence
= getClipboardData( fetc
);
527 // determine the codepage used for text conversion
528 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTCODEPAGE
).toInt32( );
533 CStgTransferHelper stgTransferHelper
;
536 MultiByteToWideCharEx( cpForTxtCnvt
,
537 reinterpret_cast<char*>( aTextSequence
.getArray( ) ),
542 CRawHGlobalPtr
ptrHGlob(stgTransferHelper
);
543 sal_Unicode
* pWChar
= static_cast<sal_Unicode
*>(ptrHGlob
.GetMemPtr());
545 return OUString(pWChar
);
548 bool CDOTransferable::compareDataFlavors(
549 const DataFlavor
& lhs
, const DataFlavor
& rhs
)
551 if ( !m_rXMimeCntFactory
.is( ) )
553 m_rXMimeCntFactory
= MimeContentTypeFactory::create( m_xContext
);
560 Reference
< XMimeContentType
> xLhs( m_rXMimeCntFactory
->createMimeContentType( lhs
.MimeType
) );
561 Reference
< XMimeContentType
> xRhs( m_rXMimeCntFactory
->createMimeContentType( rhs
.MimeType
) );
563 if ( cmpFullMediaType( xLhs
, xRhs
) )
565 bRet
= cmpAllContentTypeParameter( xLhs
, xRhs
);
568 catch( IllegalArgumentException
& )
570 OSL_FAIL( "Invalid content type detected" );
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */