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>
23 #include "DOTransferable.hxx"
24 #include "../misc/ImplHelper.hxx"
25 #include "../misc/WinClip.hxx"
26 #include "DTransHelper.hxx"
27 #include "TxtCnvtHlp.hxx"
28 #include "MimeAttrib.hxx"
29 #include "FmtFilter.hxx"
31 #include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
32 #include <comphelper/processfactory.hxx>
34 //------------------------------------------------------------------------
35 // namespace directives
36 //------------------------------------------------------------------------
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
;
48 //------------------------------------------------------------------------
50 //------------------------------------------------------------------------
54 const Type CPPUTYPE_SEQINT8
= getCppuType( ( Sequence
< sal_Int8
>* )0 );
55 const Type CPPUTYPE_OUSTRING
= getCppuType( (OUString
*)0 );
58 sal_Bool
isValidFlavor( const DataFlavor
& aFlavor
)
60 return ( aFlavor
.MimeType
.getLength( ) &&
61 ( ( aFlavor
.DataType
== CPPUTYPE_SEQINT8
) ||
62 ( aFlavor
.DataType
== CPPUTYPE_OUSTRING
) ) );
68 //------------------------------------------------------------------------
70 //------------------------------------------------------------------------
72 CDOTransferable::CDOTransferable(
73 const Reference
< XComponentContext
>& rxContext
, IDataObjectPtr rDataObject
) :
74 m_rDataObject( rDataObject
),
75 m_xContext( rxContext
),
76 m_DataFormatTranslator( rxContext
),
77 m_bUnicodeRegistered( sal_False
),
78 m_TxtFormatOnClipboard( CF_INVALID
)
82 //------------------------------------------------------------------------
84 //------------------------------------------------------------------------
86 Any SAL_CALL
CDOTransferable::getTransferData( const DataFlavor
& aFlavor
)
87 throw( UnsupportedFlavorException
, IOException
, RuntimeException
)
89 OSL_ASSERT( isValidFlavor( aFlavor
) );
91 MutexGuard
aGuard( m_aMutex
);
93 //------------------------------------------------
94 // convert dataflavor to formatetc
95 //------------------------------------------------
97 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcFromDataFlavor( aFlavor
);
98 OSL_ASSERT( CF_INVALID
!= fetc
.getClipformat() );
100 //------------------------------------------------
101 // get the data from clipboard in a byte stream
102 //------------------------------------------------
104 ByteSequence_t clipDataStream
;
108 clipDataStream
= getClipboardData( fetc
);
110 catch( UnsupportedFlavorException
& )
112 if ( m_DataFormatTranslator
.isUnicodeTextFormat( fetc
.getClipformat( ) ) &&
113 m_bUnicodeRegistered
)
115 OUString aUnicodeText
= synthesizeUnicodeText( );
116 Any aAny
= makeAny( aUnicodeText
);
120 throw; // pass through exception
123 //------------------------------------------------
124 // return the data as any
125 //------------------------------------------------
127 return byteStreamToAny( clipDataStream
, aFlavor
.DataType
);
130 //------------------------------------------------------------------------
131 // getTransferDataFlavors
132 //------------------------------------------------------------------------
134 Sequence
< DataFlavor
> SAL_CALL
CDOTransferable::getTransferDataFlavors( )
135 throw( RuntimeException
)
140 //------------------------------------------------------------------------
141 // isDataFlavorSupported
142 // returns true if we find a DataFlavor with the same MimeType and
144 //------------------------------------------------------------------------
146 sal_Bool SAL_CALL
CDOTransferable::isDataFlavorSupported( const DataFlavor
& aFlavor
)
147 throw( RuntimeException
)
149 OSL_ASSERT( isValidFlavor( aFlavor
) );
151 for ( sal_Int32 i
= 0; i
< m_FlavorList
.getLength( ); i
++ )
152 if ( compareDataFlavors( aFlavor
, m_FlavorList
[i
] ) )
158 //------------------------------------------------------------------------
160 // the list of datafalvors currently on the clipboard will be initialized
161 // only once; if the client of this Transferable will hold a reference
162 // to it und the underlying clipboard content changes, the client does
163 // possible operate on a invalid list
164 // if there is only text on the clipboard we will also offer unicode text
165 // an synthesize this format on the fly if requested, to accomplish this
166 // we save the first offered text format which we will later use for the
168 //------------------------------------------------------------------------
170 void SAL_CALL
CDOTransferable::initFlavorList( )
172 IEnumFORMATETCPtr pEnumFormatEtc
;
173 HRESULT hr
= m_rDataObject
->EnumFormatEtc( DATADIR_GET
, &pEnumFormatEtc
);
174 if ( SUCCEEDED( hr
) )
176 pEnumFormatEtc
->Reset( );
179 while ( S_FALSE
!= pEnumFormatEtc
->Next( 1, &fetc
, NULL
) )
181 // we use locales only to determine the
182 // charset if there is text on the cliboard
183 // we don't offer this format
184 if ( CF_LOCALE
== fetc
.cfFormat
)
187 DataFlavor aFlavor
= formatEtcToDataFlavor( fetc
);
189 // if text or oemtext is offered we also pretend to have unicode text
190 if ( m_DataFormatTranslator
.isOemOrAnsiTextFormat( fetc
.cfFormat
) &&
191 !m_bUnicodeRegistered
)
193 addSupportedFlavor( aFlavor
);
195 m_TxtFormatOnClipboard
= fetc
.cfFormat
;
196 m_bUnicodeRegistered
= sal_True
;
198 // register unicode text as accompany format
199 aFlavor
= formatEtcToDataFlavor(
200 m_DataFormatTranslator
.getFormatEtcForClipformat( CF_UNICODETEXT
) );
201 addSupportedFlavor( aFlavor
);
203 else if ( (CF_UNICODETEXT
== fetc
.cfFormat
) && !m_bUnicodeRegistered
)
205 addSupportedFlavor( aFlavor
);
206 m_bUnicodeRegistered
= sal_True
;
209 addSupportedFlavor( aFlavor
);
211 // see MSDN IEnumFORMATETC
212 CoTaskMemFree( fetc
.ptd
);
217 //------------------------------------------------------------------------
219 //------------------------------------------------------------------------
222 void SAL_CALL
CDOTransferable::addSupportedFlavor( const DataFlavor
& aFlavor
)
224 // we ignore all formats that couldn't be translated
225 if ( aFlavor
.MimeType
.getLength( ) )
227 OSL_ASSERT( isValidFlavor( aFlavor
) );
229 m_FlavorList
.realloc( m_FlavorList
.getLength( ) + 1 );
230 m_FlavorList
[m_FlavorList
.getLength( ) - 1] = aFlavor
;
234 //------------------------------------------------------------------------
236 //------------------------------------------------------------------------
239 DataFlavor SAL_CALL
CDOTransferable::formatEtcToDataFlavor( const FORMATETC
& aFormatEtc
)
244 // for non-unicode text format we must provid a locale to get
245 // the character-set of the text, if there is no locale on the
246 // clipboard we assume the text is in a charset appropriate for
247 // the current thread locale
248 if ( (CF_TEXT
== aFormatEtc
.cfFormat
) || (CF_OEMTEXT
== aFormatEtc
.cfFormat
) )
249 lcid
= getLocaleFromClipboard( );
251 return m_DataFormatTranslator
.getDataFlavorFromFormatEtc( aFormatEtc
, lcid
);
254 //------------------------------------------------------------------------
255 // returns the current locale on clipboard; if there is no locale on
256 // clipboard the function returns the current thread locale
257 //------------------------------------------------------------------------
259 LCID SAL_CALL
CDOTransferable::getLocaleFromClipboard( )
261 LCID lcid
= GetThreadLocale( );
265 CFormatEtc fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_LOCALE
);
266 ByteSequence_t aLCIDSeq
= getClipboardData( fetc
);
267 lcid
= *(reinterpret_cast<LCID
*>( aLCIDSeq
.getArray( ) ) );
269 // because of a Win95/98 Bug; there the high word
270 // of a locale has the same value as the
271 // low word e.g. 0x07040704 that's not right
272 // correct is 0x00000704
277 // we take the default locale
283 //------------------------------------------------------------------------
284 // i think it's not necessary to call ReleaseStgMedium
285 // in case of failures because nothing should have been
287 //------------------------------------------------------------------------
289 CDOTransferable::ByteSequence_t SAL_CALL
CDOTransferable::getClipboardData( CFormatEtc
& aFormatEtc
)
292 HRESULT hr
= m_rDataObject
->GetData( aFormatEtc
, &stgmedium
);
294 // in case of failure to get a WMF metafile handle, try to get a memory block
296 ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() ) &&
297 ( TYMED_MFPICT
== aFormatEtc
.getTymed() ) )
299 CFormatEtc
aTempFormat( aFormatEtc
);
300 aTempFormat
.setTymed( TYMED_HGLOBAL
);
301 hr
= m_rDataObject
->GetData( aTempFormat
, &stgmedium
);
306 OSL_ASSERT( (hr
!= E_INVALIDARG
) &&
307 (hr
!= DV_E_DVASPECT
) &&
308 (hr
!= DV_E_LINDEX
) &&
309 (hr
!= DV_E_TYMED
) );
311 if ( DV_E_FORMATETC
== hr
)
312 throw UnsupportedFlavorException( );
313 else if ( STG_E_MEDIUMFULL
== hr
)
314 throw IOException( );
316 throw RuntimeException( );
319 ByteSequence_t byteStream
;
323 if ( CF_ENHMETAFILE
== aFormatEtc
.getClipformat() )
324 byteStream
= WinENHMFPictToOOMFPict( stgmedium
.hEnhMetaFile
);
325 else if (CF_HDROP
== aFormatEtc
.getClipformat())
326 byteStream
= CF_HDROPToFileList(stgmedium
.hGlobal
);
327 else if ( CF_BITMAP
== aFormatEtc
.getClipformat() )
329 byteStream
= WinBITMAPToOOBMP(stgmedium
.hBitmap
);
330 if( aFormatEtc
.getTymed() == TYMED_GDI
&&
331 ! stgmedium
.pUnkForRelease
)
333 DeleteObject(stgmedium
.hBitmap
);
338 clipDataToByteStream( aFormatEtc
.getClipformat( ), stgmedium
, byteStream
);
340 // format conversion if necessary
341 if ( CF_DIB
== aFormatEtc
.getClipformat() )
342 byteStream
= WinDIBToOOBMP( byteStream
);
343 else if ( CF_METAFILEPICT
== aFormatEtc
.getClipformat() )
344 byteStream
= WinMFPictToOOMFPict( byteStream
);
347 ReleaseStgMedium( &stgmedium
);
349 catch( CStgTransferHelper::CStgTransferException
& )
351 ReleaseStgMedium( &stgmedium
);
352 throw IOException( );
358 //------------------------------------------------------------------------
360 //------------------------------------------------------------------------
362 OUString SAL_CALL
CDOTransferable::synthesizeUnicodeText( )
364 ByteSequence_t aTextSequence
;
366 LCID lcid
= getLocaleFromClipboard( );
367 sal_uInt32 cpForTxtCnvt
= 0;
369 if ( CF_TEXT
== m_TxtFormatOnClipboard
)
371 fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_TEXT
);
372 aTextSequence
= getClipboardData( fetc
);
374 // determine the codepage used for text conversion
375 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTANSICODEPAGE
).toInt32( );
377 else if ( CF_OEMTEXT
== m_TxtFormatOnClipboard
)
379 fetc
= m_DataFormatTranslator
.getFormatEtcForClipformat( CF_OEMTEXT
);
380 aTextSequence
= getClipboardData( fetc
);
382 // determine the codepage used for text conversion
383 cpForTxtCnvt
= getWinCPFromLocaleId( lcid
, LOCALE_IDEFAULTCODEPAGE
).toInt32( );
386 OSL_ASSERT( sal_False
);
388 CStgTransferHelper stgTransferHelper
;
391 MultiByteToWideCharEx( cpForTxtCnvt
,
392 reinterpret_cast<char*>( aTextSequence
.getArray( ) ),
393 sal::static_int_cast
<sal_uInt32
>(-1), // Huh ?
397 CRawHGlobalPtr
ptrHGlob(stgTransferHelper
);
398 sal_Unicode
* pWChar
= reinterpret_cast<sal_Unicode
*>(ptrHGlob
.GetMemPtr());
400 return OUString(pWChar
);
403 //------------------------------------------------------------------------
405 //------------------------------------------------------------------------
407 void CDOTransferable::clipDataToByteStream( CLIPFORMAT cf
, STGMEDIUM stgmedium
, ByteSequence_t
& aByteSequence
)
409 CStgTransferHelper memTransferHelper
;
411 switch( stgmedium
.tymed
)
414 memTransferHelper
.init( stgmedium
.hGlobal
);
418 memTransferHelper
.init( stgmedium
.hMetaFilePict
);
422 memTransferHelper
.init( stgmedium
.hEnhMetaFile
);
426 //TODO: Has to be implemented
430 throw UnsupportedFlavorException( );
434 int nMemSize
= memTransferHelper
.memSize( cf
);
435 aByteSequence
.realloc( nMemSize
);
436 memTransferHelper
.read( aByteSequence
.getArray( ), nMemSize
);
439 //------------------------------------------------------------------------
441 //------------------------------------------------------------------------
444 Any
CDOTransferable::byteStreamToAny( ByteSequence_t
& aByteStream
, const Type
& aRequestedDataType
)
448 if ( aRequestedDataType
== CPPUTYPE_OUSTRING
)
450 OUString str
= byteStreamToOUString( aByteStream
);
451 aAny
= makeAny( str
);
454 aAny
= makeAny( aByteStream
);
459 //------------------------------------------------------------------------
461 //------------------------------------------------------------------------
464 OUString
CDOTransferable::byteStreamToOUString( ByteSequence_t
& aByteStream
)
467 sal_Int32 nMemSize
= aByteStream
.getLength( );
469 // if there is a trailing L"\0" substract 1 from length
470 if ( 0 == aByteStream
[ aByteStream
.getLength( ) - 2 ] &&
471 0 == aByteStream
[ aByteStream
.getLength( ) - 1 ] )
472 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) ) - 1;
474 nWChars
= static_cast< sal_Int32
>( nMemSize
/ sizeof( sal_Unicode
) );
476 return OUString( reinterpret_cast< sal_Unicode
* >( aByteStream
.getArray( ) ), nWChars
);
479 //------------------------------------------------------------------------
481 //------------------------------------------------------------------------
483 sal_Bool SAL_CALL
CDOTransferable::compareDataFlavors(
484 const DataFlavor
& lhs
, const DataFlavor
& rhs
)
486 if ( !m_rXMimeCntFactory
.is( ) )
488 m_rXMimeCntFactory
= MimeContentTypeFactory::create( m_xContext
);
491 sal_Bool bRet
= sal_False
;
495 Reference
< XMimeContentType
> xLhs( m_rXMimeCntFactory
->createMimeContentType( lhs
.MimeType
) );
496 Reference
< XMimeContentType
> xRhs( m_rXMimeCntFactory
->createMimeContentType( rhs
.MimeType
) );
498 if ( cmpFullMediaType( xLhs
, xRhs
) )
500 bRet
= cmpAllContentTypeParameter( xLhs
, xRhs
);
503 catch( IllegalArgumentException
& )
505 OSL_FAIL( "Invalid content type detected" );
512 //------------------------------------------------------------------------
514 //------------------------------------------------------------------------
516 sal_Bool SAL_CALL
CDOTransferable::cmpFullMediaType(
517 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
) const
519 return xLhs
->getFullMediaType().equalsIgnoreAsciiCase( xRhs
->getFullMediaType( ) );
522 //------------------------------------------------------------------------
524 //------------------------------------------------------------------------
526 sal_Bool SAL_CALL
CDOTransferable::cmpAllContentTypeParameter(
527 const Reference
< XMimeContentType
>& xLhs
, const Reference
< XMimeContentType
>& xRhs
) const
529 Sequence
< OUString
> xLhsFlavors
= xLhs
->getParameters( );
530 Sequence
< OUString
> xRhsFlavors
= xRhs
->getParameters( );
531 sal_Bool bRet
= sal_True
;
535 if ( xLhsFlavors
.getLength( ) == xRhsFlavors
.getLength( ) )
540 for ( sal_Int32 i
= 0; i
< xLhsFlavors
.getLength( ); i
++ )
542 pLhs
= xLhs
->getParameterValue( xLhsFlavors
[i
] );
543 pRhs
= xRhs
->getParameterValue( xLhsFlavors
[i
] );
545 if ( !pLhs
.equalsIgnoreAsciiCase( pRhs
) )
555 catch( NoSuchElementException
& )
559 catch( IllegalArgumentException
& )
567 ::com::sun::star::uno::Any SAL_CALL
CDOTransferable::getData( const Sequence
< sal_Int8
>& aProcessId
)
568 throw (::com::sun::star::uno::RuntimeException
)
572 sal_uInt8
* arProcCaller
= (sal_uInt8
*)(sal_Int8
*) aProcessId
.getConstArray();
574 rtl_getGlobalProcessId(arId
);
575 if( ! memcmp( arId
, arProcCaller
,16))
577 if (m_rDataObject
.is())
579 IDataObject
* pObj
= m_rDataObject
.get();
581 retVal
.setValue( &pObj
, getCppuType((sal_uInt32
*)0));
588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */