nss: upgrade to release 3.73
[LibreOffice.git] / vcl / win / dtrans / DOTransferable.cxx
blob91133970e8c6c3ac189ccc311704621197063ac0
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>
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"
33 #include "Fetc.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>
40 using namespace std;
41 using namespace osl;
42 using namespace cppu;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::datatransfer;
45 using namespace com::sun::star::io;
46 using namespace com::sun::star::lang;
47 using namespace com::sun::star::container;
49 namespace
51 const Type CPPUTYPE_SEQINT8 = cppu::UnoType<Sequence< sal_Int8 >>::get();
52 const Type CPPUTYPE_OUSTRING = cppu::UnoType<OUString>::get();
54 bool isValidFlavor( const DataFlavor& aFlavor )
56 return ( aFlavor.MimeType.getLength( ) &&
57 ( ( aFlavor.DataType == CPPUTYPE_SEQINT8 ) ||
58 ( aFlavor.DataType == CPPUTYPE_OUSTRING ) ) );
61 void clipDataToByteStream( CLIPFORMAT cf, STGMEDIUM stgmedium, CDOTransferable::ByteSequence_t& aByteSequence )
63 CStgTransferHelper memTransferHelper;
64 LPSTREAM pStream = nullptr;
66 switch( stgmedium.tymed )
68 case TYMED_HGLOBAL:
69 memTransferHelper.init( stgmedium.hGlobal );
70 break;
72 case TYMED_MFPICT:
73 memTransferHelper.init( stgmedium.hMetaFilePict );
74 break;
76 case TYMED_ENHMF:
77 memTransferHelper.init( stgmedium.hEnhMetaFile );
78 break;
80 case TYMED_ISTREAM:
81 pStream = stgmedium.pstm;
82 break;
84 default:
85 throw UnsupportedFlavorException( );
86 break;
89 if (pStream)
91 // We have a stream, read from it.
92 STATSTG aStat;
93 HRESULT hr = pStream->Stat(&aStat, STATFLAG_NONAME);
94 if (FAILED(hr))
96 SAL_WARN("dtrans", "clipDataToByteStream: Stat() failed");
97 return;
100 size_t nMemSize = aStat.cbSize.QuadPart;
101 aByteSequence.realloc(nMemSize);
102 LARGE_INTEGER li;
103 li.QuadPart = 0;
104 hr = pStream->Seek(li, STREAM_SEEK_SET, nullptr);
105 if (FAILED(hr))
107 SAL_WARN("dtrans", "clipDataToByteStream: Seek() failed");
110 ULONG nRead = 0;
111 hr = pStream->Read(aByteSequence.getArray(), nMemSize, &nRead);
112 if (FAILED(hr))
114 SAL_WARN("dtrans", "clipDataToByteStream: Read() failed");
116 if (nRead < nMemSize)
118 SAL_WARN("dtrans", "clipDataToByteStream: Read() was partial");
121 return;
124 int nMemSize = memTransferHelper.memSize( cf );
125 aByteSequence.realloc( nMemSize );
126 memTransferHelper.read( aByteSequence.getArray( ), nMemSize );
129 OUString byteStreamToOUString( CDOTransferable::ByteSequence_t& aByteStream )
131 sal_Int32 nWChars;
132 sal_Int32 nMemSize = aByteStream.getLength( );
134 // if there is a trailing L"\0" subtract 1 from length
135 // for unknown reason, the sequence may sometimes arrive empty
136 if ( aByteStream.getLength( ) > 1 &&
137 0 == aByteStream[ aByteStream.getLength( ) - 2 ] &&
138 0 == aByteStream[ aByteStream.getLength( ) - 1 ] )
139 nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) ) - 1;
140 else
141 nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) );
143 return OUString( reinterpret_cast< sal_Unicode* >( aByteStream.getArray( ) ), nWChars );
146 Any byteStreamToAny( CDOTransferable::ByteSequence_t& aByteStream, const Type& aRequestedDataType )
148 Any aAny;
150 if ( aRequestedDataType == CPPUTYPE_OUSTRING )
152 OUString str = byteStreamToOUString( aByteStream );
153 if (str.isEmpty())
154 throw RuntimeException();
155 aAny <<= str;
157 else
158 aAny <<= aByteStream;
160 return aAny;
163 bool cmpFullMediaType(
164 const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs )
166 return xLhs->getFullMediaType().equalsIgnoreAsciiCase( xRhs->getFullMediaType( ) );
169 bool cmpAllContentTypeParameter(
170 const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs )
172 Sequence< OUString > xLhsFlavors = xLhs->getParameters( );
173 Sequence< OUString > xRhsFlavors = xRhs->getParameters( );
174 bool bRet = true;
178 if ( xLhsFlavors.getLength( ) == xRhsFlavors.getLength( ) )
180 OUString pLhs;
181 OUString pRhs;
183 for ( sal_Int32 i = 0; i < xLhsFlavors.getLength( ); i++ )
185 pLhs = xLhs->getParameterValue( xLhsFlavors[i] );
186 pRhs = xRhs->getParameterValue( xLhsFlavors[i] );
188 if ( !pLhs.equalsIgnoreAsciiCase( pRhs ) )
190 bRet = false;
191 break;
195 else
196 bRet = false;
198 catch( NoSuchElementException& )
200 bRet = false;
202 catch( IllegalArgumentException& )
204 bRet = false;
207 return bRet;
210 } // end namespace
212 CDOTransferable::CDOTransferable(
213 const Reference< XComponentContext >& rxContext, IDataObjectPtr rDataObject ) :
214 m_rDataObject( rDataObject ),
215 m_xContext( rxContext ),
216 m_DataFormatTranslator( rxContext ),
217 m_bUnicodeRegistered( false ),
218 m_TxtFormatOnClipboard( CF_INVALID )
220 initFlavorList();
223 CDOTransferable::CDOTransferable(
224 const Reference<XComponentContext>& rxContext,
225 const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard,
226 const std::vector<sal_uInt32>& rFormats)
227 : m_xClipboard(xClipboard)
228 , m_xContext(rxContext)
229 , m_DataFormatTranslator(rxContext)
230 , m_bUnicodeRegistered(false)
231 , m_TxtFormatOnClipboard(CF_INVALID)
233 initFlavorListFromFormatList(rFormats);
236 Any SAL_CALL CDOTransferable::getTransferData( const DataFlavor& aFlavor )
238 OSL_ASSERT( isValidFlavor( aFlavor ) );
240 MutexGuard aGuard( m_aMutex );
242 // convert dataflavor to formatetc
244 CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor );
245 OSL_ASSERT( CF_INVALID != fetc.getClipformat() );
247 // get the data from clipboard in a byte stream
249 ByteSequence_t clipDataStream;
253 clipDataStream = getClipboardData( fetc );
255 catch( UnsupportedFlavorException& )
257 if ( CDataFormatTranslator::isUnicodeTextFormat( fetc.getClipformat( ) ) &&
258 m_bUnicodeRegistered )
260 OUString aUnicodeText = synthesizeUnicodeText( );
261 Any aAny = makeAny( aUnicodeText );
262 return aAny;
264 // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
265 // clipboard for being on the safe side
266 else if(CF_DIBV5 == fetc.getClipformat())
268 // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
269 // check CF_DIB availability as an alternative
270 fetc.setClipformat(CF_DIB);
272 clipDataStream = getClipboardData( fetc );
273 // pass UnsupportedFlavorException out, tried all possibilities
275 else
276 throw; // pass through exception
279 // return the data as any
281 return byteStreamToAny( clipDataStream, aFlavor.DataType );
284 // getTransferDataFlavors
286 Sequence< DataFlavor > SAL_CALL CDOTransferable::getTransferDataFlavors( )
288 return m_FlavorList;
291 // isDataFlavorSupported
292 // returns true if we find a DataFlavor with the same MimeType and
293 // DataType
295 sal_Bool SAL_CALL CDOTransferable::isDataFlavorSupported( const DataFlavor& aFlavor )
297 OSL_ASSERT( isValidFlavor( aFlavor ) );
299 for ( DataFlavor const & df : std::as_const(m_FlavorList) )
300 if ( compareDataFlavors( aFlavor, df ) )
301 return true;
303 return false;
306 // the list of dataflavors currently on the clipboard will be initialized
307 // only once; if the client of this Transferable will hold a reference
308 // to it and the underlying clipboard content changes, the client does
309 // possible operate on an invalid list
310 // if there is only text on the clipboard we will also offer unicode text
311 // an synthesize this format on the fly if requested, to accomplish this
312 // we save the first offered text format which we will later use for the
313 // conversion
315 void CDOTransferable::initFlavorList( )
317 std::vector<sal_uInt32> aFormats;
318 sal::systools::COMReference<IEnumFORMATETC> pEnumFormatEtc;
319 HRESULT hr = m_rDataObject->EnumFormatEtc( DATADIR_GET, &pEnumFormatEtc );
320 if ( SUCCEEDED( hr ) )
322 pEnumFormatEtc->Reset( );
324 FORMATETC fetc;
325 while ( S_OK == pEnumFormatEtc->Next( 1, &fetc, nullptr ) )
327 aFormats.push_back(fetc.cfFormat);
328 // see MSDN IEnumFORMATETC
329 CoTaskMemFree( fetc.ptd );
331 initFlavorListFromFormatList(aFormats);
335 void CDOTransferable::initFlavorListFromFormatList(const std::vector<sal_uInt32>& rFormats)
337 for (sal_uInt32 cfFormat : rFormats)
339 // we use locales only to determine the
340 // charset if there is text on the cliboard
341 // we don't offer this format
342 if (CF_LOCALE == cfFormat)
343 continue;
345 // if text or oemtext is offered we pretend to have unicode text
346 if (CDataFormatTranslator::isTextFormat(cfFormat))
348 if (!m_bUnicodeRegistered)
350 m_TxtFormatOnClipboard = cfFormat;
351 m_bUnicodeRegistered = true;
353 // register unicode text as format
354 addSupportedFlavor(formatEtcToDataFlavor(CF_UNICODETEXT));
357 else
358 addSupportedFlavor(formatEtcToDataFlavor(cfFormat));
362 inline
363 void CDOTransferable::addSupportedFlavor( const DataFlavor& aFlavor )
365 // we ignore all formats that couldn't be translated
366 if ( aFlavor.MimeType.getLength( ) )
368 OSL_ASSERT( isValidFlavor( aFlavor ) );
370 m_FlavorList.realloc( m_FlavorList.getLength( ) + 1 );
371 m_FlavorList[m_FlavorList.getLength( ) - 1] = aFlavor;
375 DataFlavor CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat)
377 return m_DataFormatTranslator.getDataFlavorFromFormatEtc(cfFormat);
380 // returns the current locale on clipboard; if there is no locale on
381 // clipboard the function returns the current thread locale
383 LCID CDOTransferable::getLocaleFromClipboard( )
385 LCID lcid = GetThreadLocale( );
389 CFormatEtc fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE );
390 ByteSequence_t aLCIDSeq = getClipboardData( fetc );
391 lcid = *reinterpret_cast<LCID*>( aLCIDSeq.getArray( ) );
393 // because of a Win95/98 Bug; there the high word
394 // of a locale has the same value as the
395 // low word e.g. 0x07040704 that's not right
396 // correct is 0x00000704
397 lcid &= 0x0000FFFF;
399 catch(...)
401 // we take the default locale
404 return lcid;
407 void CDOTransferable::tryToGetIDataObjectIfAbsent()
409 if (!m_rDataObject.is())
411 auto xClipboard = m_xClipboard.get(); // holding the reference while we get the object
412 if (CWinClipboard* pWinClipboard = dynamic_cast<CWinClipboard*>(xClipboard.get()))
414 m_rDataObject = pWinClipboard->getIDataObject();
419 // I think it's not necessary to call ReleaseStgMedium
420 // in case of failures because nothing should have been
421 // allocated etc.
423 CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& aFormatEtc )
425 STGMEDIUM stgmedium;
426 tryToGetIDataObjectIfAbsent();
427 if (!m_rDataObject.is()) // Maybe we are shutting down, and clipboard is already destroyed?
428 throw RuntimeException();
429 HRESULT hr = m_rDataObject->GetData( aFormatEtc, &stgmedium );
431 // in case of failure to get a WMF metafile handle, try to get a memory block
432 if( FAILED( hr ) &&
433 ( CF_METAFILEPICT == aFormatEtc.getClipformat() ) &&
434 ( TYMED_MFPICT == aFormatEtc.getTymed() ) )
436 CFormatEtc aTempFormat( aFormatEtc );
437 aTempFormat.setTymed( TYMED_HGLOBAL );
438 hr = m_rDataObject->GetData( aTempFormat, &stgmedium );
441 if (FAILED(hr) && aFormatEtc.getTymed() == TYMED_HGLOBAL)
443 // Handle type is not memory, try stream.
444 CFormatEtc aTempFormat(aFormatEtc);
445 aTempFormat.setTymed(TYMED_ISTREAM);
446 hr = m_rDataObject->GetData(aTempFormat, &stgmedium);
449 if ( FAILED( hr ) )
451 OSL_ASSERT( (hr != E_INVALIDARG) &&
452 (hr != DV_E_DVASPECT) &&
453 (hr != DV_E_LINDEX) &&
454 (hr != DV_E_TYMED) );
456 if ( DV_E_FORMATETC == hr )
457 throw UnsupportedFlavorException( );
458 else if ( STG_E_MEDIUMFULL == hr )
459 throw IOException( );
460 else
461 throw RuntimeException( );
464 ByteSequence_t byteStream;
468 if ( CF_ENHMETAFILE == aFormatEtc.getClipformat() )
469 byteStream = WinENHMFPictToOOMFPict( stgmedium.hEnhMetaFile );
470 else if (CF_HDROP == aFormatEtc.getClipformat())
471 byteStream = CF_HDROPToFileList(stgmedium.hGlobal);
472 else if ( CF_BITMAP == aFormatEtc.getClipformat() )
474 byteStream = WinBITMAPToOOBMP(stgmedium.hBitmap);
475 if( aFormatEtc.getTymed() == TYMED_GDI &&
476 ! stgmedium.pUnkForRelease )
478 DeleteObject(stgmedium.hBitmap);
481 else
483 clipDataToByteStream( aFormatEtc.getClipformat( ), stgmedium, byteStream );
485 // format conversion if necessary
486 // #i124085# DIBV5 should not happen currently, but keep as a hint here
487 if(CF_DIBV5 == aFormatEtc.getClipformat() || CF_DIB == aFormatEtc.getClipformat())
489 byteStream = WinDIBToOOBMP(byteStream);
491 else if(CF_METAFILEPICT == aFormatEtc.getClipformat())
493 byteStream = WinMFPictToOOMFPict(byteStream);
497 ReleaseStgMedium( &stgmedium );
499 catch( CStgTransferHelper::CStgTransferException& )
501 ReleaseStgMedium( &stgmedium );
502 throw IOException( );
505 return byteStream;
508 OUString CDOTransferable::synthesizeUnicodeText( )
510 ByteSequence_t aTextSequence;
511 CFormatEtc fetc;
512 LCID lcid = getLocaleFromClipboard( );
513 sal_uInt32 cpForTxtCnvt = 0;
515 if ( CF_TEXT == m_TxtFormatOnClipboard )
517 fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT );
518 aTextSequence = getClipboardData( fetc );
520 // determine the codepage used for text conversion
521 cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTANSICODEPAGE ).toInt32( );
523 else if ( CF_OEMTEXT == m_TxtFormatOnClipboard )
525 fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT );
526 aTextSequence = getClipboardData( fetc );
528 // determine the codepage used for text conversion
529 cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTCODEPAGE ).toInt32( );
531 else
532 OSL_ASSERT( false );
534 CStgTransferHelper stgTransferHelper;
536 // convert the text
537 MultiByteToWideCharEx( cpForTxtCnvt,
538 reinterpret_cast<char*>( aTextSequence.getArray( ) ),
539 sal::static_int_cast<sal_uInt32>(-1), // Huh ?
540 stgTransferHelper,
541 false);
543 CRawHGlobalPtr ptrHGlob(stgTransferHelper);
544 sal_Unicode* pWChar = static_cast<sal_Unicode*>(ptrHGlob.GetMemPtr());
546 return OUString(pWChar);
549 bool CDOTransferable::compareDataFlavors(
550 const DataFlavor& lhs, const DataFlavor& rhs )
552 if ( !m_rXMimeCntFactory.is( ) )
554 m_rXMimeCntFactory = MimeContentTypeFactory::create( m_xContext );
557 bool bRet = false;
561 Reference< XMimeContentType > xLhs( m_rXMimeCntFactory->createMimeContentType( lhs.MimeType ) );
562 Reference< XMimeContentType > xRhs( m_rXMimeCntFactory->createMimeContentType( rhs.MimeType ) );
564 if ( cmpFullMediaType( xLhs, xRhs ) )
566 bRet = cmpAllContentTypeParameter( xLhs, xRhs );
569 catch( IllegalArgumentException& )
571 OSL_FAIL( "Invalid content type detected" );
572 bRet = false;
575 return bRet;
578 css::uno::Any SAL_CALL CDOTransferable::getData( const Sequence< sal_Int8>& aProcessId )
580 Any retVal;
582 sal_Int8 const * arProcCaller= aProcessId.getConstArray();
583 sal_uInt8 arId[16];
584 rtl_getGlobalProcessId(arId);
585 if( ! memcmp( arId, arProcCaller,16))
587 if (m_rDataObject.is())
589 IDataObject* pObj= m_rDataObject.get();
590 pObj->AddRef();
591 retVal.setValue( &pObj, cppu::UnoType<sal_uInt32>::get());
594 return retVal;
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */