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 <osl/diagnose.h>
22 #include "XTDataObject.hxx"
23 #include <com/sun/star/datatransfer/DataFlavor.hpp>
24 #include "../misc/ImplHelper.hxx"
25 #include "DTransHelper.hxx"
26 #include "TxtCnvtHlp.hxx"
27 #include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
28 #include "FmtFilter.hxx"
29 #include <comphelper/processfactory.hxx>
32 #pragma warning(push,1)
33 #pragma warning(disable:4917)
45 #define __uuidof(I) IID_##I
48 // namespace directives
50 using namespace com::sun::star::datatransfer
;
51 using namespace com::sun::star::datatransfer::clipboard
;
52 using namespace com::sun::star::uno
;
53 using namespace com::sun::star::lang
;
55 // a helper class that will be thrown by the function validateFormatEtc
57 class CInvalidFormatEtcException
61 CInvalidFormatEtcException( HRESULT hr
) : m_hr( hr
) {};
66 CXTDataObject::CXTDataObject( const Reference
< XComponentContext
>& rxContext
,
67 const Reference
< XTransferable
>& aXTransferable
)
69 , m_XTransferable( aXTransferable
)
70 , m_bFormatEtcContainerInitialized( sal_False
)
71 , m_DataFormatTranslator( rxContext
)
72 , m_FormatRegistrar( rxContext
, m_DataFormatTranslator
)
76 // IUnknown->QueryInterface
78 STDMETHODIMP
CXTDataObject::QueryInterface( REFIID iid
, LPVOID
* ppvObject
)
80 if ( NULL
== ppvObject
)
83 HRESULT hr
= E_NOINTERFACE
;
86 if ( ( __uuidof( IUnknown
) == iid
) ||
87 ( __uuidof( IDataObject
) == iid
) )
89 *ppvObject
= static_cast< IUnknown
* >( this );
90 ( (LPUNKNOWN
)*ppvObject
)->AddRef( );
99 STDMETHODIMP_(ULONG
) CXTDataObject::AddRef( )
101 return static_cast< ULONG
>( InterlockedIncrement( &m_nRefCnt
) );
106 STDMETHODIMP_(ULONG
) CXTDataObject::Release( )
109 static_cast< ULONG
>( InterlockedDecrement( &m_nRefCnt
) );
117 STDMETHODIMP
CXTDataObject::GetData( LPFORMATETC pFormatetc
, LPSTGMEDIUM pmedium
)
119 if ( !(pFormatetc
&& pmedium
) )
124 // prepare data transfer
125 invalidateStgMedium( *pmedium
);
126 validateFormatEtc( pFormatetc
);
128 // handle locale request, because locale is a artificial format for us
129 if ( CF_LOCALE
== pFormatetc
->cfFormat
)
130 renderLocaleAndSetupStgMedium( *pFormatetc
, *pmedium
);
131 else if ( CF_UNICODETEXT
== pFormatetc
->cfFormat
)
132 renderUnicodeAndSetupStgMedium( *pFormatetc
, *pmedium
);
134 renderAnyDataAndSetupStgMedium( *pFormatetc
, *pmedium
);
136 catch(UnsupportedFlavorException
&)
138 HRESULT hr
= DV_E_FORMATETC
;
140 if ( m_FormatRegistrar
.isSynthesizeableFormat( *pFormatetc
) )
141 hr
= renderSynthesizedFormatAndSetupStgMedium( *pFormatetc
, *pmedium
);
145 catch( CInvalidFormatEtcException
& ex
)
149 catch( CStgTransferHelper::CStgTransferException
& ex
)
151 return translateStgExceptionCode( ex
.m_hr
);
162 void SAL_CALL
CXTDataObject::renderDataAndSetupStgMedium(
163 const sal_Int8
* lpStorage
, const FORMATETC
& fetc
, sal_uInt32 nInitStgSize
,
164 sal_uInt32 nBytesToTransfer
, STGMEDIUM
& stgmedium
)
166 OSL_PRECOND( !nInitStgSize
|| nInitStgSize
&& (nInitStgSize
>= nBytesToTransfer
),
167 "Memory size less than number of bytes to transfer" );
169 CStgTransferHelper
stgTransfHelper( AUTO_INIT
);
171 // setup storage size
172 if ( nInitStgSize
> 0 )
173 stgTransfHelper
.init( nInitStgSize
, GHND
);
175 #if OSL_DEBUG_LEVEL > 0
176 sal_uInt32 nBytesWritten
= 0;
177 stgTransfHelper
.write( lpStorage
, nBytesToTransfer
, &nBytesWritten
);
178 OSL_ASSERT( nBytesWritten
== nBytesToTransfer
);
180 stgTransfHelper
.write( lpStorage
, nBytesToTransfer
);
183 setupStgMedium( fetc
, stgTransfHelper
, stgmedium
);
187 void SAL_CALL
CXTDataObject::renderLocaleAndSetupStgMedium(
188 FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
190 if ( m_FormatRegistrar
.hasSynthesizedLocale( ) )
192 LCID lcid
= m_FormatRegistrar
.getSynthesizedLocale( );
193 renderDataAndSetupStgMedium(
194 reinterpret_cast< sal_Int8
* >( &lcid
),
201 throw CInvalidFormatEtcException( DV_E_FORMATETC
);
205 void SAL_CALL
CXTDataObject::renderUnicodeAndSetupStgMedium(
206 FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
208 DataFlavor aFlavor
= formatEtcToDataFlavor( fetc
);
210 Any aAny
= m_XTransferable
->getTransferData( aFlavor
);
212 // unfortunately not all transferables fulfill the
213 // spec. an do throw an UnsupportedFlavorException
214 // so we must check the any
215 if ( !aAny
.hasValue( ) )
217 OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
218 throw UnsupportedFlavorException( );
224 sal_uInt32 nBytesToTransfer
= aText
.getLength( ) * sizeof( sal_Unicode
);
226 // to be sure there is an ending 0
227 sal_uInt32 nRequiredMemSize
= nBytesToTransfer
+ sizeof( sal_Unicode
);
229 renderDataAndSetupStgMedium(
230 reinterpret_cast< const sal_Int8
* >( aText
.getStr( ) ),
238 void SAL_CALL
CXTDataObject::renderAnyDataAndSetupStgMedium(
239 FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
241 DataFlavor aFlavor
= formatEtcToDataFlavor( fetc
);
243 Any aAny
= m_XTransferable
->getTransferData( aFlavor
);
245 // unfortunately not all transferables fulfill the
246 // spec. an do throw an UnsupportedFlavorException
247 // so we must check the any
248 if ( !aAny
.hasValue( ) )
250 OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
251 throw UnsupportedFlavorException( );
254 // unfortunately not all transferables fulfill the
255 // spec. an do throw an UnsupportedFlavorException
256 // so we must check the any
257 if ( !aAny
.hasValue( ) )
258 throw UnsupportedFlavorException( );
260 Sequence
< sal_Int8
> clipDataStream
;
261 aAny
>>= clipDataStream
;
263 sal_uInt32 nRequiredMemSize
= 0;
264 if ( m_DataFormatTranslator
.isOemOrAnsiTextFormat( fetc
.cfFormat
) )
265 nRequiredMemSize
= sizeof( sal_Int8
) * clipDataStream
.getLength( ) + 1;
267 // prepare data for transmision
268 // #i124085# DIBV5 should not happen for now, but keep as hint here
269 if ( CF_DIBV5
== fetc
.cfFormat
|| CF_DIB
== fetc
.cfFormat
)
272 if(CF_DIBV5
== fetc
.cfFormat
)
274 OSL_ENSURE(clipDataStream
.getLength( ) > (sizeof(BITMAPFILEHEADER
) + sizeof(BITMAPV5HEADER
)), "Wrong size on CF_DIBV5 data (!)");
276 else // CF_DIB == fetc.cfFormat
278 OSL_ENSURE(clipDataStream
.getLength( ) > (sizeof(BITMAPFILEHEADER
) + sizeof(BITMAPINFOHEADER
)), "Wrong size on CF_DIB data (!)");
282 // remove BITMAPFILEHEADER
283 clipDataStream
= OOBmpToWinDIB( clipDataStream
);
286 if ( CF_METAFILEPICT
== fetc
.cfFormat
)
288 stgmedium
.tymed
= TYMED_MFPICT
;
289 stgmedium
.hMetaFilePict
= OOMFPictToWinMFPict( clipDataStream
);
290 stgmedium
.pUnkForRelease
= NULL
;
292 else if( CF_ENHMETAFILE
== fetc
.cfFormat
)
294 stgmedium
.tymed
= TYMED_ENHMF
;
295 stgmedium
.hMetaFilePict
= OOMFPictToWinENHMFPict( clipDataStream
);
296 stgmedium
.pUnkForRelease
= NULL
;
299 renderDataAndSetupStgMedium(
300 clipDataStream
.getArray( ),
303 clipDataStream
.getLength( ),
307 HRESULT SAL_CALL
CXTDataObject::renderSynthesizedFormatAndSetupStgMedium( FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
313 if ( CF_UNICODETEXT
== fetc
.cfFormat
)
314 // the transferable seems to have only text
315 renderSynthesizedUnicodeAndSetupStgMedium( fetc
, stgmedium
);
316 else if ( m_DataFormatTranslator
.isOemOrAnsiTextFormat( fetc
.cfFormat
) )
317 // the transferable seems to have only unicode text
318 renderSynthesizedTextAndSetupStgMedium( fetc
, stgmedium
);
320 // the transferable seems to have only text/html
321 renderSynthesizedHtmlAndSetupStgMedium( fetc
, stgmedium
);
323 catch(UnsupportedFlavorException
&)
327 catch( CInvalidFormatEtcException
& )
329 OSL_FAIL( "Unexpected exception" );
331 catch( CStgTransferHelper::CStgTransferException
& ex
)
333 return translateStgExceptionCode( ex
.m_hr
);
343 // the transferable must have only text, so we will synthesize unicode text
345 void SAL_CALL
CXTDataObject::renderSynthesizedUnicodeAndSetupStgMedium( FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
347 OSL_ASSERT( CF_UNICODETEXT
== fetc
.cfFormat
);
349 Any aAny
= m_XTransferable
->getTransferData( m_FormatRegistrar
.getRegisteredTextFlavor( ) );
351 // unfortunately not all transferables fulfill the
352 // spec. an do throw an UnsupportedFlavorException
353 // so we must check the any
354 if ( !aAny
.hasValue( ) )
356 OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
357 throw UnsupportedFlavorException( );
360 Sequence
< sal_Int8
> aText
;
363 CStgTransferHelper stgTransfHelper
;
365 MultiByteToWideCharEx(
366 m_FormatRegistrar
.getRegisteredTextCodePage( ),
367 reinterpret_cast< char* >( aText
.getArray( ) ),
371 setupStgMedium( fetc
, stgTransfHelper
, stgmedium
);
374 // the transferable must have only unicode text so we will sythesize text
376 void SAL_CALL
CXTDataObject::renderSynthesizedTextAndSetupStgMedium( FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
378 OSL_ASSERT( m_DataFormatTranslator
.isOemOrAnsiTextFormat( fetc
.cfFormat
) );
380 DataFlavor aFlavor
= formatEtcToDataFlavor(
381 m_DataFormatTranslator
.getFormatEtcForClipformat( CF_UNICODETEXT
) );
383 Any aAny
= m_XTransferable
->getTransferData( aFlavor
);
385 // unfortunately not all transferables fulfill the
386 // spec. an do throw an UnsupportedFlavorException
387 // so we must check the any
388 if ( !aAny
.hasValue( ) )
390 OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
391 throw UnsupportedFlavorException( );
394 OUString aUnicodeText
;
395 aAny
>>= aUnicodeText
;
397 CStgTransferHelper stgTransfHelper
;
399 WideCharToMultiByteEx(
401 reinterpret_cast<LPCWSTR
>( aUnicodeText
.getStr( ) ),
402 aUnicodeText
.getLength( ),
405 setupStgMedium( fetc
, stgTransfHelper
, stgmedium
);
408 void SAL_CALL
CXTDataObject::renderSynthesizedHtmlAndSetupStgMedium( FORMATETC
& fetc
, STGMEDIUM
& stgmedium
)
410 OSL_ASSERT( m_DataFormatTranslator
.isHTMLFormat( fetc
.cfFormat
) );
414 // creating a DataFlavor on the fly
415 aFlavor
.MimeType
= "text/html";
416 aFlavor
.DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
418 Any aAny
= m_XTransferable
->getTransferData( aFlavor
);
420 // unfortunately not all transferables fulfill the
421 // spec. an do throw an UnsupportedFlavorException
422 // so we must check the any
423 if ( !aAny
.hasValue( ) )
425 OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
426 throw UnsupportedFlavorException( );
429 Sequence
< sal_Int8
> aTextHtmlSequence
;
430 aAny
>>= aTextHtmlSequence
;
432 Sequence
< sal_Int8
> aHTMLFormatSequence
= TextHtmlToHTMLFormat( aTextHtmlSequence
);
434 sal_uInt32 nBytesToTransfer
= aHTMLFormatSequence
.getLength( );
436 renderDataAndSetupStgMedium(
437 reinterpret_cast< const sal_Int8
* >( aHTMLFormatSequence
.getArray( ) ),
444 // IDataObject->EnumFormatEtc
446 STDMETHODIMP
CXTDataObject::EnumFormatEtc(
447 DWORD dwDirection
, IEnumFORMATETC
** ppenumFormatetc
)
449 if ( NULL
== ppenumFormatetc
)
452 if ( DATADIR_SET
== dwDirection
)
455 *ppenumFormatetc
= NULL
;
457 InitializeFormatEtcContainer( );
460 if ( DATADIR_GET
== dwDirection
)
462 *ppenumFormatetc
= new CEnumFormatEtc( this, m_FormatEtcContainer
);
463 static_cast< LPUNKNOWN
>( *ppenumFormatetc
)->AddRef( );
473 // IDataObject->QueryGetData
475 STDMETHODIMP
CXTDataObject::QueryGetData( LPFORMATETC pFormatetc
)
477 if ( (NULL
== pFormatetc
) || IsBadReadPtr( pFormatetc
, sizeof( FORMATETC
) ) )
480 InitializeFormatEtcContainer( );
482 return m_FormatEtcContainer
.hasFormatEtc( *pFormatetc
) ? S_OK
: S_FALSE
;
485 // IDataObject->GetDataHere
487 STDMETHODIMP
CXTDataObject::GetDataHere( LPFORMATETC
, LPSTGMEDIUM
)
492 // IDataObject->GetCanonicalFormatEtc
494 STDMETHODIMP
CXTDataObject::GetCanonicalFormatEtc( LPFORMATETC
, LPFORMATETC
)
499 // IDataObject->SetData
501 STDMETHODIMP
CXTDataObject::SetData( LPFORMATETC
, LPSTGMEDIUM
, BOOL
)
506 // IDataObject->DAdvise
508 STDMETHODIMP
CXTDataObject::DAdvise( LPFORMATETC
, DWORD
, LPADVISESINK
, DWORD
* )
513 // IDataObject->DUnadvise
515 STDMETHODIMP
CXTDataObject::DUnadvise( DWORD
)
520 // IDataObject->EnumDAdvise
522 STDMETHODIMP
CXTDataObject::EnumDAdvise( LPENUMSTATDATA
* )
527 // for our convenience
529 CXTDataObject::operator IDataObject
*( )
531 return static_cast< IDataObject
* >( this );
535 DataFlavor SAL_CALL
CXTDataObject::formatEtcToDataFlavor( const FORMATETC
& aFormatEtc
) const
539 if ( m_FormatRegistrar
.hasSynthesizedLocale( ) )
541 m_DataFormatTranslator
.getDataFlavorFromFormatEtc( aFormatEtc
, m_FormatRegistrar
.getSynthesizedLocale( ) );
543 aFlavor
= m_DataFormatTranslator
.getDataFlavorFromFormatEtc( aFormatEtc
);
545 if ( !aFlavor
.MimeType
.getLength( ) )
546 throw UnsupportedFlavorException( );
552 void CXTDataObject::validateFormatEtc( LPFORMATETC lpFormatEtc
) const
554 OSL_ASSERT( lpFormatEtc
);
556 if ( lpFormatEtc
->lindex
!= -1 )
557 throw CInvalidFormatEtcException( DV_E_LINDEX
);
559 if ( !(lpFormatEtc
->dwAspect
& DVASPECT_CONTENT
) &&
560 !(lpFormatEtc
->dwAspect
& DVASPECT_SHORTNAME
) )
561 throw CInvalidFormatEtcException( DV_E_DVASPECT
);
563 if ( !(lpFormatEtc
->tymed
& TYMED_HGLOBAL
) &&
564 !(lpFormatEtc
->tymed
& TYMED_ISTREAM
) &&
565 !(lpFormatEtc
->tymed
& TYMED_MFPICT
) &&
566 !(lpFormatEtc
->tymed
& TYMED_ENHMF
) )
567 throw CInvalidFormatEtcException( DV_E_TYMED
);
569 if ( lpFormatEtc
->cfFormat
== CF_METAFILEPICT
&&
570 !(lpFormatEtc
->tymed
& TYMED_MFPICT
) )
571 throw CInvalidFormatEtcException( DV_E_TYMED
);
573 if ( lpFormatEtc
->cfFormat
== CF_ENHMETAFILE
&&
574 !(lpFormatEtc
->tymed
& TYMED_ENHMF
) )
575 throw CInvalidFormatEtcException( DV_E_TYMED
);
579 void SAL_CALL
CXTDataObject::setupStgMedium( const FORMATETC
& fetc
,
580 CStgTransferHelper
& stgTransHlp
,
581 STGMEDIUM
& stgmedium
)
583 stgmedium
.pUnkForRelease
= NULL
;
585 if ( fetc
.cfFormat
== CF_METAFILEPICT
)
587 stgmedium
.tymed
= TYMED_MFPICT
;
588 stgmedium
.hMetaFilePict
= static_cast< HMETAFILEPICT
>( stgTransHlp
.getHGlobal( ) );
590 else if ( fetc
.cfFormat
== CF_ENHMETAFILE
)
592 stgmedium
.tymed
= TYMED_ENHMF
;
593 stgmedium
.hEnhMetaFile
= static_cast< HENHMETAFILE
>( stgTransHlp
.getHGlobal( ) );
595 else if ( fetc
.tymed
& TYMED_HGLOBAL
)
597 stgmedium
.tymed
= TYMED_HGLOBAL
;
598 stgmedium
.hGlobal
= stgTransHlp
.getHGlobal( );
600 else if ( fetc
.tymed
& TYMED_ISTREAM
)
602 stgmedium
.tymed
= TYMED_ISTREAM
;
603 stgTransHlp
.getIStream( &stgmedium
.pstm
);
606 OSL_ASSERT( sal_False
);
610 void SAL_CALL
CXTDataObject::invalidateStgMedium( STGMEDIUM
& stgmedium
) const
612 stgmedium
.tymed
= TYMED_NULL
;
616 HRESULT SAL_CALL
CXTDataObject::translateStgExceptionCode( HRESULT hr
) const
622 case STG_E_MEDIUMFULL
:
627 hrTransl
= E_UNEXPECTED
;
634 inline void SAL_CALL
CXTDataObject::InitializeFormatEtcContainer( )
636 if ( !m_bFormatEtcContainerInitialized
)
638 m_FormatRegistrar
.RegisterFormats( m_XTransferable
, m_FormatEtcContainer
);
639 m_bFormatEtcContainerInitialized
= sal_True
;
647 CEnumFormatEtc::CEnumFormatEtc( LPUNKNOWN lpUnkOuter
, const CFormatEtcContainer
& aFormatEtcContainer
) :
649 m_lpUnkOuter( lpUnkOuter
),
650 m_FormatEtcContainer( aFormatEtcContainer
)
655 // IUnknown->QueryInterface
657 STDMETHODIMP
CEnumFormatEtc::QueryInterface( REFIID iid
, LPVOID
* ppvObject
)
659 if ( NULL
== ppvObject
)
662 HRESULT hr
= E_NOINTERFACE
;
666 if ( ( __uuidof( IUnknown
) == iid
) ||
667 ( __uuidof( IEnumFORMATETC
) == iid
) )
669 *ppvObject
= static_cast< IUnknown
* >( this );
670 static_cast< LPUNKNOWN
>( *ppvObject
)->AddRef( );
679 STDMETHODIMP_(ULONG
) CEnumFormatEtc::AddRef( )
681 // keep the dataobject alive
682 m_lpUnkOuter
->AddRef( );
683 return InterlockedIncrement( &m_nRefCnt
);
688 STDMETHODIMP_(ULONG
) CEnumFormatEtc::Release( )
690 // release the outer dataobject
691 m_lpUnkOuter
->Release( );
693 ULONG nRefCnt
= InterlockedDecrement( &m_nRefCnt
);
700 // IEnumFORMATETC->Next
702 STDMETHODIMP
CEnumFormatEtc::Next( ULONG nRequested
, LPFORMATETC lpDest
, ULONG
* lpFetched
)
704 if ( ( nRequested
< 1 ) ||
705 (( nRequested
> 1 ) && ( NULL
== lpFetched
)) ||
706 IsBadWritePtr( lpDest
, sizeof( FORMATETC
) * nRequested
) )
709 sal_uInt32 nFetched
= m_FormatEtcContainer
.nextFormatEtc( lpDest
, nRequested
);
711 if ( NULL
!= lpFetched
)
712 *lpFetched
= nFetched
;
714 return (nFetched
== nRequested
) ? S_OK
: S_FALSE
;
717 // IEnumFORMATETC->Skip
719 STDMETHODIMP
CEnumFormatEtc::Skip( ULONG celt
)
721 return m_FormatEtcContainer
.skipFormatEtc( celt
) ? S_OK
: S_FALSE
;
724 // IEnumFORMATETC->Reset
726 STDMETHODIMP
CEnumFormatEtc::Reset( )
728 m_FormatEtcContainer
.beginEnumFormatEtc( );
732 // IEnumFORMATETC->Clone
734 STDMETHODIMP
CEnumFormatEtc::Clone( IEnumFORMATETC
** ppenum
)
736 if ( NULL
== ppenum
)
739 *ppenum
= new CEnumFormatEtc( m_lpUnkOuter
, m_FormatEtcContainer
);
740 static_cast< LPUNKNOWN
>( *ppenum
)->AddRef( );
745 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */