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>
24 #include "DOTransferable.hxx"
25 #include "../misc/ImplHelper.hxx"
26 #include <WinClip.hxx>
27 #include "DTransHelper.hxx"
28 #include "TxtCnvtHlp.hxx"
29 #include "MimeAttrib.hxx"
30 #include "FmtFilter.hxx"
32 #include <com/sun/star/container/NoSuchElementException.hpp>
33 #include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
34 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
35 #include <com/sun/star/io/IOException.hpp>
36 #include <com/sun/star/lang/IllegalArgumentException.hpp>
41 using namespace com::sun::star::uno
;
42 using namespace com::sun::star::datatransfer
;
43 using namespace com::sun::star::io
;
44 using namespace com::sun::star::lang
;
45 using namespace com::sun::star::container
;
49 const Type CPPUTYPE_SEQINT8
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
50 const Type CPPUTYPE_OUSTRING
= cppu::UnoType
<OUString
>::get();
52 bool isValidFlavor( const DataFlavor
& aFlavor
)
54 return ( aFlavor
.MimeType
.getLength( ) &&
55 ( ( aFlavor
.DataType
== CPPUTYPE_SEQINT8
) ||
56 ( aFlavor
.DataType
== CPPUTYPE_OUSTRING
) ) );
59 void clipDataToByteStream( CLIPFORMAT cf
, STGMEDIUM stgmedium
, CDOTransferable::ByteSequence_t
& aByteSequence
)
61 CStgTransferHelper memTransferHelper
;
63 switch( stgmedium
.tymed
)
66 memTransferHelper
.init( stgmedium
.hGlobal
);
70 memTransferHelper
.init( stgmedium
.hMetaFilePict
);
74 memTransferHelper
.init( stgmedium
.hEnhMetaFile
);
78 //TODO: Has to be implemented
82 throw UnsupportedFlavorException( );
86 int nMemSize
= memTransferHelper
.memSize( cf
);
87 aByteSequence
.realloc( nMemSize
);
88 memTransferHelper
.read( aByteSequence
.getArray( ), nMemSize
);
91 OUString
byteStreamToOUString( CDOTransferable::ByteSequence_t
& aByteStream
)
94 sal_Int32 nMemSize
= aByteStream
.getLength( );
96 // if there is a trailing L"\0" subtract 1 from length
97 if ( 0 == aByteStream
[ aByteStream
.getLength( ) - 2 ] &&
98 0 == aByteStream
[ aByteStream
.getLength( ) - 1 ] )
99 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) ) - 1;
101 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) );
103 return OUString( reinterpret_cast< sal_Unicode
* >( aByteStream
.getArray( ) ), nWChars
);
106 Any
byteStreamToAny( CDOTransferable::ByteSequence_t
& aByteStream
, const Type
& aRequestedDataType
)
110 if ( aRequestedDataType
== CPPUTYPE_OUSTRING
)
112 OUString str
= byteStreamToOUString( aByteStream
);
116 aAny
<<= aByteStream
;
121 bool cmpFullMediaType(
122 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
124 return xLhs
->getFullMediaType().equalsIgnoreAsciiCase( xRhs
->getFullMediaType( ) );
127 bool cmpAllContentTypeParameter(
128 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
)
130 Sequence
< OUString
> xLhsFlavors
= xLhs
->getParameters( );
131 Sequence
< OUString
> xRhsFlavors
= xRhs
->getParameters( );
136 if ( xLhsFlavors
.getLength( ) == xRhsFlavors
.getLength( ) )
141 for ( sal_Int32 i
= 0; i
< xLhsFlavors
.getLength( ); i
++ )
143 pLhs
= xLhs
->getParameterValue( xLhsFlavors
[i
] );
144 pRhs
= xRhs
->getParameterValue( xLhsFlavors
[i
] );
146 if ( !pLhs
.equalsIgnoreAsciiCase( pRhs
) )
156 catch( NoSuchElementException
& )
160 catch( IllegalArgumentException
& )
170 Reference
< XTransferable
> CDOTransferable::create( const Reference
< XComponentContext
>& rxContext
,
171 IDataObjectPtr pIDataObject
)
173 CDOTransferable
* pTransf
= new CDOTransferable(rxContext
, pIDataObject
);
174 Reference
<XTransferable
> refDOTransf(pTransf
);
177 pTransf
->initFlavorList();
183 CDOTransferable::CDOTransferable(
184 const Reference
< XComponentContext
>& rxContext
, IDataObjectPtr rDataObject
) :
185 m_rDataObject( rDataObject
),
186 m_xContext( rxContext
),
187 m_DataFormatTranslator( rxContext
),
188 m_bUnicodeRegistered( false ),
189 m_TxtFormatOnClipboard( CF_INVALID
)
193 Any SAL_CALL
CDOTransferable::getTransferData( const DataFlavor
& aFlavor
)
195 OSL_ASSERT( isValidFlavor( aFlavor
) );
197 MutexGuard
aGuard( m_aMutex
);
199 // convert dataflavor to formatetc
201 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcFromDataFlavor( aFlavor
);
202 OSL_ASSERT( CF_INVALID
!= fetc
.getClipformat() );
204 // get the data from clipboard in a byte stream
206 ByteSequence_t clipDataStream
;
210 clipDataStream
= getClipboardData( fetc
);
212 catch( UnsupportedFlavorException
& )
214 if ( CDataFormatTranslator::isUnicodeTextFormat( fetc
.getClipformat( ) ) &&
215 m_bUnicodeRegistered
)
217 OUString aUnicodeText
= synthesizeUnicodeText( );
218 Any aAny
= makeAny( aUnicodeText
);
221 // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
222 // clipboard for being on the safe side
223 else if(CF_DIBV5
== fetc
.getClipformat())
225 // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
226 // check CF_DIB availability as an alternative
227 fetc
.setClipformat(CF_DIB
);
229 clipDataStream
= getClipboardData( fetc
);
230 // pass UnsupportedFlavorException out, tried all possibilities
233 throw; // pass through exception
236 // return the data as any
238 return byteStreamToAny( clipDataStream
, aFlavor
.DataType
);
241 // getTransferDataFlavors
243 Sequence
< DataFlavor
> SAL_CALL
CDOTransferable::getTransferDataFlavors( )
248 // isDataFlavorSupported
249 // returns true if we find a DataFlavor with the same MimeType and
252 sal_Bool SAL_CALL
CDOTransferable::isDataFlavorSupported( const DataFlavor
& aFlavor
)
254 OSL_ASSERT( isValidFlavor( aFlavor
) );
256 for ( sal_Int32 i
= 0; i
< m_FlavorList
.getLength( ); i
++ )
257 if ( compareDataFlavors( aFlavor
, m_FlavorList
[i
] ) )
263 // the list of dataflavors currently on the clipboard will be initialized
264 // only once; if the client of this Transferable will hold a reference
265 // to it and the underlying clipboard content changes, the client does
266 // possible operate on an invalid list
267 // if there is only text on the clipboard we will also offer unicode text
268 // an synthesize this format on the fly if requested, to accomplish this
269 // we save the first offered text format which we will later use for the
272 void CDOTransferable::initFlavorList( )
274 sal::systools::COMReference
<IEnumFORMATETC
> pEnumFormatEtc
;
275 HRESULT hr
= m_rDataObject
->EnumFormatEtc( DATADIR_GET
, &pEnumFormatEtc
);
276 if ( SUCCEEDED( hr
) )
278 pEnumFormatEtc
->Reset( );
281 while ( S_OK
== pEnumFormatEtc
->Next( 1, &fetc
, nullptr ) )
283 // we use locales only to determine the
284 // charset if there is text on the cliboard
285 // we don't offer this format
286 if ( CF_LOCALE
== fetc
.cfFormat
)
289 DataFlavor aFlavor
= formatEtcToDataFlavor( fetc
);
291 // if text or oemtext is offered we also pretend to have unicode text
292 if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc
.cfFormat
) &&
293 !m_bUnicodeRegistered
)
295 addSupportedFlavor( aFlavor
);
297 m_TxtFormatOnClipboard
= fetc
.cfFormat
;
298 m_bUnicodeRegistered
= true;
300 // register unicode text as accompany format
301 aFlavor
= formatEtcToDataFlavor(
302 CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT
) );
303 addSupportedFlavor( aFlavor
);
305 else if ( (CF_UNICODETEXT
== fetc
.cfFormat
) && !m_bUnicodeRegistered
)
307 addSupportedFlavor( aFlavor
);
308 m_bUnicodeRegistered
= true;
311 addSupportedFlavor( aFlavor
);
313 // see MSDN IEnumFORMATETC
314 CoTaskMemFree( fetc
.ptd
);
320 void CDOTransferable::addSupportedFlavor( const DataFlavor
& aFlavor
)
322 // we ignore all formats that couldn't be translated
323 if ( aFlavor
.MimeType
.getLength( ) )
325 OSL_ASSERT( isValidFlavor( aFlavor
) );
327 m_FlavorList
.realloc( m_FlavorList
.getLength( ) + 1 );
328 m_FlavorList
[m_FlavorList
.getLength( ) - 1] = aFlavor
;
332 DataFlavor
CDOTransferable::formatEtcToDataFlavor( const FORMATETC
& aFormatEtc
)
336 // for non-unicode text format we must provide a locale to get
337 // the character-set of the text, if there is no locale on the
338 // clipboard we assume the text is in a charset appropriate for
339 // the current thread locale
340 if ( (CF_TEXT
== aFormatEtc
.cfFormat
) || (CF_OEMTEXT
== aFormatEtc
.cfFormat
) )
341 lcid
= getLocaleFromClipboard( );
343 return m_DataFormatTranslator
.getDataFlavorFromFormatEtc( aFormatEtc
, lcid
);
346 // returns the current locale on clipboard; if there is no locale on
347 // clipboard the function returns the current thread locale
349 LCID
CDOTransferable::getLocaleFromClipboard( )
351 LCID lcid
= GetThreadLocale( );
355 CFormatEtc fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE
);
356 ByteSequence_t aLCIDSeq
= getClipboardData( fetc
);
357 lcid
= *reinterpret_cast<LCID
*>( aLCIDSeq
.getArray( ) );
359 // because of a Win95/98 Bug; there the high word
360 // of a locale has the same value as the
361 // low word e.g. 0x07040704 that's not right
362 // correct is 0x00000704
367 // we take the default locale
373 // I think it's not necessary to call ReleaseStgMedium
374 // in case of failures because nothing should have been
377 CDOTransferable::ByteSequence_t
CDOTransferable::getClipboardData( CFormatEtc
& aFormatEtc
)
380 HRESULT hr
= m_rDataObject
->GetData( aFormatEtc
, &stgmedium
);
382 // in case of failure to get a WMF metafile handle, try to get a memory block
384 ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() ) &&
385 ( TYMED_MFPICT
== aFormatEtc
.getTymed() ) )
387 CFormatEtc
aTempFormat( aFormatEtc
);
388 aTempFormat
.setTymed( TYMED_HGLOBAL
);
389 hr
= m_rDataObject
->GetData( aTempFormat
, &stgmedium
);
394 OSL_ASSERT( (hr
!= E_INVALIDARG
) &&
395 (hr
!= DV_E_DVASPECT
) &&
396 (hr
!= DV_E_LINDEX
) &&
397 (hr
!= DV_E_TYMED
) );
399 if ( DV_E_FORMATETC
== hr
)
400 throw UnsupportedFlavorException( );
401 else if ( STG_E_MEDIUMFULL
== hr
)
402 throw IOException( );
404 throw RuntimeException( );
407 ByteSequence_t byteStream
;
411 if ( CF_ENHMETAFILE
== aFormatEtc
.getClipformat() )
412 byteStream
= WinENHMFPictToOOMFPict( stgmedium
.hEnhMetaFile
);
413 else if (CF_HDROP
== aFormatEtc
.getClipformat())
414 byteStream
= CF_HDROPToFileList(stgmedium
.hGlobal
);
415 else if ( CF_BITMAP
== aFormatEtc
.getClipformat() )
417 byteStream
= WinBITMAPToOOBMP(stgmedium
.hBitmap
);
418 if( aFormatEtc
.getTymed() == TYMED_GDI
&&
419 ! stgmedium
.pUnkForRelease
)
421 DeleteObject(stgmedium
.hBitmap
);
426 clipDataToByteStream( aFormatEtc
.getClipformat( ), stgmedium
, byteStream
);
428 // format conversion if necessary
429 // #i124085# DIBV5 should not happen currently, but keep as a hint here
430 if(CF_DIBV5
== aFormatEtc
.getClipformat() || CF_DIB
== aFormatEtc
.getClipformat())
432 byteStream
= WinDIBToOOBMP(byteStream
);
434 else if(CF_METAFILEPICT
== aFormatEtc
.getClipformat())
436 byteStream
= WinMFPictToOOMFPict(byteStream
);
440 ReleaseStgMedium( &stgmedium
);
442 catch( CStgTransferHelper::CStgTransferException
& )
444 ReleaseStgMedium( &stgmedium
);
445 throw IOException( );
451 OUString
CDOTransferable::synthesizeUnicodeText( )
453 ByteSequence_t aTextSequence
;
455 LCID lcid
= getLocaleFromClipboard( );
456 sal_uInt32 cpForTxtCnvt
= 0;
458 if ( CF_TEXT
== m_TxtFormatOnClipboard
)
460 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT
);
461 aTextSequence
= getClipboardData( fetc
);
463 // determine the codepage used for text conversion
464 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTANSICODEPAGE
).toInt32( );
466 else if ( CF_OEMTEXT
== m_TxtFormatOnClipboard
)
468 fetc
= CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT
);
469 aTextSequence
= getClipboardData( fetc
);
471 // determine the codepage used for text conversion
472 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTCODEPAGE
).toInt32( );
477 CStgTransferHelper stgTransferHelper
;
480 MultiByteToWideCharEx( cpForTxtCnvt
,
481 reinterpret_cast<char*>( aTextSequence
.getArray( ) ),
482 sal::static_int_cast
<sal_uInt32
>(-1), // Huh ?
486 CRawHGlobalPtr
ptrHGlob(stgTransferHelper
);
487 sal_Unicode
* pWChar
= static_cast<sal_Unicode
*>(ptrHGlob
.GetMemPtr());
489 return OUString(pWChar
);
492 bool CDOTransferable::compareDataFlavors(
493 const DataFlavor
& lhs
, const DataFlavor
& rhs
)
495 if ( !m_rXMimeCntFactory
.is( ) )
497 m_rXMimeCntFactory
= MimeContentTypeFactory::create( m_xContext
);
504 Reference
< XMimeContentType
> xLhs( m_rXMimeCntFactory
->createMimeContentType( lhs
.MimeType
) );
505 Reference
< XMimeContentType
> xRhs( m_rXMimeCntFactory
->createMimeContentType( rhs
.MimeType
) );
507 if ( cmpFullMediaType( xLhs
, xRhs
) )
509 bRet
= cmpAllContentTypeParameter( xLhs
, xRhs
);
512 catch( IllegalArgumentException
& )
514 OSL_FAIL( "Invalid content type detected" );
521 css::uno::Any SAL_CALL
CDOTransferable::getData( const Sequence
< sal_Int8
>& aProcessId
)
525 sal_Int8
const * arProcCaller
= aProcessId
.getConstArray();
527 rtl_getGlobalProcessId(arId
);
528 if( ! memcmp( arId
, arProcCaller
,16))
530 if (m_rDataObject
.is())
532 IDataObject
* pObj
= m_rDataObject
.get();
534 retVal
.setValue( &pObj
, cppu::UnoType
<sal_uInt32
>::get());
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */