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 "../misc/WinClip.hxx"
27 #include "DTransHelper.hxx"
28 #include "TxtCnvtHlp.hxx"
29 #include "MimeAttrib.hxx"
30 #include "FmtFilter.hxx"
32 #include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
33 #include <comphelper/processfactory.hxx>
35 // namespace directives
40 using namespace com::sun::star::uno
;
41 using namespace com::sun::star::datatransfer
;
42 using namespace com::sun::star::io
;
43 using namespace com::sun::star::lang
;
44 using namespace com::sun::star::container
;
48 const Type CPPUTYPE_SEQINT8
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
49 const Type CPPUTYPE_OUSTRING
= cppu::UnoType
<OUString
>::get();
52 sal_Bool
isValidFlavor( const DataFlavor
& aFlavor
)
54 return ( aFlavor
.MimeType
.getLength( ) &&
55 ( ( aFlavor
.DataType
== CPPUTYPE_SEQINT8
) ||
56 ( aFlavor
.DataType
== CPPUTYPE_OUSTRING
) ) );
63 CDOTransferable::CDOTransferable(
64 const Reference
< XComponentContext
>& rxContext
, IDataObjectPtr rDataObject
) :
65 m_rDataObject( rDataObject
),
66 m_xContext( rxContext
),
67 m_DataFormatTranslator( rxContext
),
68 m_bUnicodeRegistered( sal_False
),
69 m_TxtFormatOnClipboard( CF_INVALID
)
73 Any SAL_CALL
CDOTransferable::getTransferData( const DataFlavor
& aFlavor
)
74 throw( UnsupportedFlavorException
, IOException
, RuntimeException
)
76 OSL_ASSERT( isValidFlavor( aFlavor
) );
78 MutexGuard
aGuard( m_aMutex
);
80 // convert dataflavor to formatetc
82 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcFromDataFlavor( aFlavor
);
83 OSL_ASSERT( CF_INVALID
!= fetc
.getClipformat() );
85 // get the data from clipboard in a byte stream
87 ByteSequence_t clipDataStream
;
91 clipDataStream
= getClipboardData( fetc
);
93 catch( UnsupportedFlavorException
& )
95 if ( m_DataFormatTranslator
.isUnicodeTextFormat( fetc
.getClipformat( ) ) &&
96 m_bUnicodeRegistered
)
98 OUString aUnicodeText
= synthesizeUnicodeText( );
99 Any aAny
= makeAny( aUnicodeText
);
102 // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
103 // clipboard for being on the safe side
104 else if(CF_DIBV5
== fetc
.getClipformat())
106 // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
107 // check CF_DIB availability as an alternative
108 fetc
.setClipformat(CF_DIB
);
112 clipDataStream
= getClipboardData( fetc
);
114 catch( UnsupportedFlavorException
& )
116 throw; // pass through, tried all possibilities
120 throw; // pass through exception
123 // return the data as any
125 return byteStreamToAny( clipDataStream
, aFlavor
.DataType
);
128 // getTransferDataFlavors
130 Sequence
< DataFlavor
> SAL_CALL
CDOTransferable::getTransferDataFlavors( )
131 throw( RuntimeException
)
136 // isDataFlavorSupported
137 // returns true if we find a DataFlavor with the same MimeType and
140 sal_Bool SAL_CALL
CDOTransferable::isDataFlavorSupported( const DataFlavor
& aFlavor
)
141 throw( RuntimeException
)
143 OSL_ASSERT( isValidFlavor( aFlavor
) );
145 for ( sal_Int32 i
= 0; i
< m_FlavorList
.getLength( ); i
++ )
146 if ( compareDataFlavors( aFlavor
, m_FlavorList
[i
] ) )
153 // the list of datafalvors currently on the clipboard will be initialized
154 // only once; if the client of this Transferable will hold a reference
155 // to it und the underlying clipboard content changes, the client does
156 // possible operate on a invalid list
157 // if there is only text on the clipboard we will also offer unicode text
158 // an synthesize this format on the fly if requested, to accomplish this
159 // we save the first offered text format which we will later use for the
162 void SAL_CALL
CDOTransferable::initFlavorList( )
164 IEnumFORMATETCPtr pEnumFormatEtc
;
165 HRESULT hr
= m_rDataObject
->EnumFormatEtc( DATADIR_GET
, &pEnumFormatEtc
);
166 if ( SUCCEEDED( hr
) )
168 pEnumFormatEtc
->Reset( );
171 while ( S_FALSE
!= pEnumFormatEtc
->Next( 1, &fetc
, NULL
) )
173 // we use locales only to determine the
174 // charset if there is text on the cliboard
175 // we don't offer this format
176 if ( CF_LOCALE
== fetc
.cfFormat
)
179 DataFlavor aFlavor
= formatEtcToDataFlavor( fetc
);
181 // if text or oemtext is offered we also pretend to have unicode text
182 if ( m_DataFormatTranslator
.isOemOrAnsiTextFormat( fetc
.cfFormat
) &&
183 !m_bUnicodeRegistered
)
185 addSupportedFlavor( aFlavor
);
187 m_TxtFormatOnClipboard
= fetc
.cfFormat
;
188 m_bUnicodeRegistered
= sal_True
;
190 // register unicode text as accompany format
191 aFlavor
= formatEtcToDataFlavor(
192 m_DataFormatTranslator
.getFormatEtcForClipformat( CF_UNICODETEXT
) );
193 addSupportedFlavor( aFlavor
);
195 else if ( (CF_UNICODETEXT
== fetc
.cfFormat
) && !m_bUnicodeRegistered
)
197 addSupportedFlavor( aFlavor
);
198 m_bUnicodeRegistered
= sal_True
;
201 addSupportedFlavor( aFlavor
);
203 // see MSDN IEnumFORMATETC
204 CoTaskMemFree( fetc
.ptd
);
210 void SAL_CALL
CDOTransferable::addSupportedFlavor( const DataFlavor
& aFlavor
)
212 // we ignore all formats that couldn't be translated
213 if ( aFlavor
.MimeType
.getLength( ) )
215 OSL_ASSERT( isValidFlavor( aFlavor
) );
217 m_FlavorList
.realloc( m_FlavorList
.getLength( ) + 1 );
218 m_FlavorList
[m_FlavorList
.getLength( ) - 1] = aFlavor
;
225 DataFlavor SAL_CALL
CDOTransferable::formatEtcToDataFlavor( const FORMATETC
& aFormatEtc
)
230 // for non-unicode text format we must provid a locale to get
231 // the character-set of the text, if there is no locale on the
232 // clipboard we assume the text is in a charset appropriate for
233 // the current thread locale
234 if ( (CF_TEXT
== aFormatEtc
.cfFormat
) || (CF_OEMTEXT
== aFormatEtc
.cfFormat
) )
235 lcid
= getLocaleFromClipboard( );
237 return m_DataFormatTranslator
.getDataFlavorFromFormatEtc( aFormatEtc
, lcid
);
240 // returns the current locale on clipboard; if there is no locale on
241 // clipboard the function returns the current thread locale
243 LCID SAL_CALL
CDOTransferable::getLocaleFromClipboard( )
245 LCID lcid
= GetThreadLocale( );
249 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_LOCALE
);
250 ByteSequence_t aLCIDSeq
= getClipboardData( fetc
);
251 lcid
= *(reinterpret_cast<LCID
*>( aLCIDSeq
.getArray( ) ) );
253 // because of a Win95/98 Bug; there the high word
254 // of a locale has the same value as the
255 // low word e.g. 0x07040704 that's not right
256 // correct is 0x00000704
261 // we take the default locale
267 // i think it's not necessary to call ReleaseStgMedium
268 // in case of failures because nothing should have been
271 CDOTransferable::ByteSequence_t SAL_CALL
CDOTransferable::getClipboardData( CFormatEtc
& aFormatEtc
)
274 HRESULT hr
= m_rDataObject
->GetData( aFormatEtc
, &stgmedium
);
276 // in case of failure to get a WMF metafile handle, try to get a memory block
278 ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() ) &&
279 ( TYMED_MFPICT
== aFormatEtc
.getTymed() ) )
281 CFormatEtc
aTempFormat( aFormatEtc
);
282 aTempFormat
.setTymed( TYMED_HGLOBAL
);
283 hr
= m_rDataObject
->GetData( aTempFormat
, &stgmedium
);
288 OSL_ASSERT( (hr
!= E_INVALIDARG
) &&
289 (hr
!= DV_E_DVASPECT
) &&
290 (hr
!= DV_E_LINDEX
) &&
291 (hr
!= DV_E_TYMED
) );
293 if ( DV_E_FORMATETC
== hr
)
294 throw UnsupportedFlavorException( );
295 else if ( STG_E_MEDIUMFULL
== hr
)
296 throw IOException( );
298 throw RuntimeException( );
301 ByteSequence_t byteStream
;
305 if ( CF_ENHMETAFILE
== aFormatEtc
.getClipformat() )
306 byteStream
= WinENHMFPictToOOMFPict( stgmedium
.hEnhMetaFile
);
307 else if (CF_HDROP
== aFormatEtc
.getClipformat())
308 byteStream
= CF_HDROPToFileList(stgmedium
.hGlobal
);
309 else if ( CF_BITMAP
== aFormatEtc
.getClipformat() )
311 byteStream
= WinBITMAPToOOBMP(stgmedium
.hBitmap
);
312 if( aFormatEtc
.getTymed() == TYMED_GDI
&&
313 ! stgmedium
.pUnkForRelease
)
315 DeleteObject(stgmedium
.hBitmap
);
320 clipDataToByteStream( aFormatEtc
.getClipformat( ), stgmedium
, byteStream
);
322 // format conversion if necessary
323 // #i124085# DIBV5 should not happen currently, but keep as a hint here
324 if(CF_DIBV5
== aFormatEtc
.getClipformat() || CF_DIB
== aFormatEtc
.getClipformat())
326 byteStream
= WinDIBToOOBMP(byteStream
);
328 else if(CF_METAFILEPICT
== aFormatEtc
.getClipformat())
330 byteStream
= WinMFPictToOOMFPict(byteStream
);
334 ReleaseStgMedium( &stgmedium
);
336 catch( CStgTransferHelper::CStgTransferException
& )
338 ReleaseStgMedium( &stgmedium
);
339 throw IOException( );
345 OUString SAL_CALL
CDOTransferable::synthesizeUnicodeText( )
347 ByteSequence_t aTextSequence
;
349 LCID lcid
= getLocaleFromClipboard( );
350 sal_uInt32 cpForTxtCnvt
= 0;
352 if ( CF_TEXT
== m_TxtFormatOnClipboard
)
354 fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_TEXT
);
355 aTextSequence
= getClipboardData( fetc
);
357 // determine the codepage used for text conversion
358 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTANSICODEPAGE
).toInt32( );
360 else if ( CF_OEMTEXT
== m_TxtFormatOnClipboard
)
362 fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_OEMTEXT
);
363 aTextSequence
= getClipboardData( fetc
);
365 // determine the codepage used for text conversion
366 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTCODEPAGE
).toInt32( );
369 OSL_ASSERT( sal_False
);
371 CStgTransferHelper stgTransferHelper
;
374 MultiByteToWideCharEx( cpForTxtCnvt
,
375 reinterpret_cast<char*>( aTextSequence
.getArray( ) ),
376 sal::static_int_cast
<sal_uInt32
>(-1), // Huh ?
380 CRawHGlobalPtr
ptrHGlob(stgTransferHelper
);
381 sal_Unicode
* pWChar
= reinterpret_cast<sal_Unicode
*>(ptrHGlob
.GetMemPtr());
383 return OUString(pWChar
);
386 void CDOTransferable::clipDataToByteStream( CLIPFORMAT cf
, STGMEDIUM stgmedium
, ByteSequence_t
& aByteSequence
)
388 CStgTransferHelper memTransferHelper
;
390 switch( stgmedium
.tymed
)
393 memTransferHelper
.init( stgmedium
.hGlobal
);
397 memTransferHelper
.init( stgmedium
.hMetaFilePict
);
401 memTransferHelper
.init( stgmedium
.hEnhMetaFile
);
405 //TODO: Has to be implemented
409 throw UnsupportedFlavorException( );
413 int nMemSize
= memTransferHelper
.memSize( cf
);
414 aByteSequence
.realloc( nMemSize
);
415 memTransferHelper
.read( aByteSequence
.getArray( ), nMemSize
);
419 Any
CDOTransferable::byteStreamToAny( ByteSequence_t
& aByteStream
, const Type
& aRequestedDataType
)
423 if ( aRequestedDataType
== CPPUTYPE_OUSTRING
)
425 OUString str
= byteStreamToOUString( aByteStream
);
426 aAny
= makeAny( str
);
429 aAny
= makeAny( aByteStream
);
435 OUString
CDOTransferable::byteStreamToOUString( ByteSequence_t
& aByteStream
)
438 sal_Int32 nMemSize
= aByteStream
.getLength( );
440 // if there is a trailing L"\0" subtract 1 from length
441 if ( 0 == aByteStream
[ aByteStream
.getLength( ) - 2 ] &&
442 0 == aByteStream
[ aByteStream
.getLength( ) - 1 ] )
443 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) ) - 1;
445 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) );
447 return OUString( reinterpret_cast< sal_Unicode
* >( aByteStream
.getArray( ) ), nWChars
);
450 sal_Bool SAL_CALL
CDOTransferable::compareDataFlavors(
451 const DataFlavor
& lhs
, const DataFlavor
& rhs
)
453 if ( !m_rXMimeCntFactory
.is( ) )
455 m_rXMimeCntFactory
= MimeContentTypeFactory::create( m_xContext
);
458 sal_Bool bRet
= sal_False
;
462 Reference
< XMimeContentType
> xLhs( m_rXMimeCntFactory
->createMimeContentType( lhs
.MimeType
) );
463 Reference
< XMimeContentType
> xRhs( m_rXMimeCntFactory
->createMimeContentType( rhs
.MimeType
) );
465 if ( cmpFullMediaType( xLhs
, xRhs
) )
467 bRet
= cmpAllContentTypeParameter( xLhs
, xRhs
);
470 catch( IllegalArgumentException
& )
472 OSL_FAIL( "Invalid content type detected" );
479 sal_Bool SAL_CALL
CDOTransferable::cmpFullMediaType(
480 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
) const
482 return xLhs
->getFullMediaType().equalsIgnoreAsciiCase( xRhs
->getFullMediaType( ) );
485 sal_Bool SAL_CALL
CDOTransferable::cmpAllContentTypeParameter(
486 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
) const
488 Sequence
< OUString
> xLhsFlavors
= xLhs
->getParameters( );
489 Sequence
< OUString
> xRhsFlavors
= xRhs
->getParameters( );
490 sal_Bool bRet
= sal_True
;
494 if ( xLhsFlavors
.getLength( ) == xRhsFlavors
.getLength( ) )
499 for ( sal_Int32 i
= 0; i
< xLhsFlavors
.getLength( ); i
++ )
501 pLhs
= xLhs
->getParameterValue( xLhsFlavors
[i
] );
502 pRhs
= xRhs
->getParameterValue( xLhsFlavors
[i
] );
504 if ( !pLhs
.equalsIgnoreAsciiCase( pRhs
) )
514 catch( NoSuchElementException
& )
518 catch( IllegalArgumentException
& )
526 ::com::sun::star::uno::Any SAL_CALL
CDOTransferable::getData( const Sequence
< sal_Int8
>& aProcessId
)
527 throw (::com::sun::star::uno::RuntimeException
)
531 sal_uInt8
* arProcCaller
= (sal_uInt8
*)(sal_Int8
*) aProcessId
.getConstArray();
533 rtl_getGlobalProcessId(arId
);
534 if( ! memcmp( arId
, arProcCaller
,16))
536 if (m_rDataObject
.is())
538 IDataObject
* pObj
= m_rDataObject
.get();
540 retVal
.setValue( &pObj
, cppu::UnoType
<sal_uInt32
>::get());
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */