Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / dtrans / source / win32 / dtobj / DOTransferable.cxx
blob230a7fbfed8eb2b9ebac3e9e89497b61963e2bc3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
31 #include "Fetc.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>
38 using namespace std;
39 using namespace osl;
40 using namespace cppu;
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;
47 namespace
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 )
65 case TYMED_HGLOBAL:
66 memTransferHelper.init( stgmedium.hGlobal );
67 break;
69 case TYMED_MFPICT:
70 memTransferHelper.init( stgmedium.hMetaFilePict );
71 break;
73 case TYMED_ENHMF:
74 memTransferHelper.init( stgmedium.hEnhMetaFile );
75 break;
77 case TYMED_ISTREAM:
78 //TODO: Has to be implemented
79 break;
81 default:
82 throw UnsupportedFlavorException( );
83 break;
86 int nMemSize = memTransferHelper.memSize( cf );
87 aByteSequence.realloc( nMemSize );
88 memTransferHelper.read( aByteSequence.getArray( ), nMemSize );
91 OUString byteStreamToOUString( CDOTransferable::ByteSequence_t& aByteStream )
93 sal_Int32 nWChars;
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;
100 else
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 )
108 Any aAny;
110 if ( aRequestedDataType == CPPUTYPE_OUSTRING )
112 OUString str = byteStreamToOUString( aByteStream );
113 aAny <<= str;
115 else
116 aAny <<= aByteStream;
118 return aAny;
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( );
132 bool bRet = true;
136 if ( xLhsFlavors.getLength( ) == xRhsFlavors.getLength( ) )
138 OUString pLhs;
139 OUString pRhs;
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 ) )
148 bRet = false;
149 break;
153 else
154 bRet = false;
156 catch( NoSuchElementException& )
158 bRet = false;
160 catch( IllegalArgumentException& )
162 bRet = false;
165 return bRet;
168 } // end namespace
170 Reference< XTransferable > CDOTransferable::create( const Reference< XComponentContext >& rxContext,
171 IDataObjectPtr pIDataObject )
173 CDOTransferable* pTransf = new CDOTransferable(rxContext, pIDataObject);
174 Reference<XTransferable> refDOTransf(pTransf);
176 pTransf->acquire();
177 pTransf->initFlavorList();
178 pTransf->release();
180 return refDOTransf;
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 );
219 return aAny;
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
232 else
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( )
245 return m_FlavorList;
248 // isDataFlavorSupported
249 // returns true if we find a DataFlavor with the same MimeType and
250 // DataType
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] ) )
258 return true;
260 return false;
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
270 // conversion
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( );
280 FORMATETC fetc;
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 )
287 continue;
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;
310 else
311 addSupportedFlavor( aFlavor );
313 // see MSDN IEnumFORMATETC
314 CoTaskMemFree( fetc.ptd );
319 inline
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 )
334 LCID lcid = 0;
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
363 lcid &= 0x0000FFFF;
365 catch(...)
367 // we take the default locale
370 return lcid;
373 // I think it's not necessary to call ReleaseStgMedium
374 // in case of failures because nothing should have been
375 // allocated etc.
377 CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& aFormatEtc )
379 STGMEDIUM stgmedium;
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
383 if( FAILED( hr ) &&
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 );
392 if ( FAILED( hr ) )
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( );
403 else
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);
424 else
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( );
448 return byteStream;
451 OUString CDOTransferable::synthesizeUnicodeText( )
453 ByteSequence_t aTextSequence;
454 CFormatEtc fetc;
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( );
474 else
475 OSL_ASSERT( false );
477 CStgTransferHelper stgTransferHelper;
479 // convert the text
480 MultiByteToWideCharEx( cpForTxtCnvt,
481 reinterpret_cast<char*>( aTextSequence.getArray( ) ),
482 sal::static_int_cast<sal_uInt32>(-1), // Huh ?
483 stgTransferHelper,
484 false);
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 );
500 bool bRet = false;
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" );
515 bRet = false;
518 return bRet;
521 css::uno::Any SAL_CALL CDOTransferable::getData( const Sequence< sal_Int8>& aProcessId )
523 Any retVal;
525 sal_Int8 const * arProcCaller= aProcessId.getConstArray();
526 sal_uInt8 arId[16];
527 rtl_getGlobalProcessId(arId);
528 if( ! memcmp( arId, arProcCaller,16))
530 if (m_rDataObject.is())
532 IDataObject* pObj= m_rDataObject.get();
533 pObj->AddRef();
534 retVal.setValue( &pObj, cppu::UnoType<sal_uInt32>::get());
537 return retVal;
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */