blob: 36ed1a5dc827b805da1a9322784f9b89b813a52e [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dtrans.hxx"
//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------
#include <sal/types.h>
#include <rtl/process.h>
#ifndef _DOWRAPPERTRANSFERABLE_HXX_
#include "DOTransferable.hxx"
#endif
#include "..\misc\ImplHelper.hxx"
#include "..\misc\WinClip.hxx"
#include "DTransHelper.hxx"
#include "..\misc\ImplHelper.hxx"
#include "TxtCnvtHlp.hxx"
#include "MimeAttrib.hxx"
#include "FmtFilter.hxx"
#include "Fetc.hxx"
#if(_MSC_VER < 1300) && !defined(__MINGW32__)
#include <olestd.h>
#endif
#define STR2(x) #x
#define STR(x) STR2(x)
#define PRAGMA_MSG( msg ) message( __FILE__ "(" STR(__LINE__) "): " #msg )
//------------------------------------------------------------------------
// namespace directives
//------------------------------------------------------------------------
using namespace rtl;
using namespace std;
using namespace osl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::datatransfer;
using namespace com::sun::star::io;
using namespace com::sun::star::lang;
using namespace com::sun::star::container;
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
namespace
{
const Type CPPUTYPE_SEQINT8 = getCppuType( ( Sequence< sal_Int8 >* )0 );
const Type CPPUTYPE_OUSTRING = getCppuType( (OUString*)0 );
inline
sal_Bool isValidFlavor( const DataFlavor& aFlavor )
{
return ( aFlavor.MimeType.getLength( ) &&
( ( aFlavor.DataType == CPPUTYPE_SEQINT8 ) ||
( aFlavor.DataType == CPPUTYPE_OUSTRING ) ) );
}
} // end namespace
//------------------------------------------------------------------------
// ctor
//------------------------------------------------------------------------
CDOTransferable::CDOTransferable(
const Reference< XMultiServiceFactory >& ServiceManager, IDataObjectPtr rDataObject ) :
m_rDataObject( rDataObject ),
m_SrvMgr( ServiceManager ),
m_DataFormatTranslator( m_SrvMgr ),
m_bUnicodeRegistered( sal_False ),
m_TxtFormatOnClipboard( CF_INVALID )
{
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
Any SAL_CALL CDOTransferable::getTransferData( const DataFlavor& aFlavor )
throw( UnsupportedFlavorException, IOException, RuntimeException )
{
OSL_ASSERT( isValidFlavor( aFlavor ) );
MutexGuard aGuard( m_aMutex );
//------------------------------------------------
// convert dataflavor to formatetc
//------------------------------------------------
CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor );
OSL_ASSERT( CF_INVALID != fetc.getClipformat() );
//------------------------------------------------
// get the data from clipboard in a byte stream
//------------------------------------------------
ByteSequence_t clipDataStream;
try
{
clipDataStream = getClipboardData( fetc );
}
catch( UnsupportedFlavorException& )
{
if ( m_DataFormatTranslator.isUnicodeTextFormat( fetc.getClipformat( ) ) &&
m_bUnicodeRegistered )
{
OUString aUnicodeText = synthesizeUnicodeText( );
Any aAny = makeAny( aUnicodeText );
return aAny;
}
// #124085# CF_DIBV5 should not be possible, but keep for reading from the
// clipboard for being on the safe side
else if(CF_DIBV5 == fetc.getClipformat())
{
// #123407# CF_DIBV5 has priority; if the try to fetch this failed,
// check CF_DIB availability as an alternative
fetc.setClipformat(CF_DIB);
try
{
clipDataStream = getClipboardData( fetc );
}
catch( UnsupportedFlavorException& )
{
throw; // pass through, tried all possibilities
}
}
else
throw; // pass through exception
}
//------------------------------------------------
// return the data as any
//------------------------------------------------
return byteStreamToAny( clipDataStream, aFlavor.DataType );
}
//------------------------------------------------------------------------
// getTransferDataFlavors
//------------------------------------------------------------------------
Sequence< DataFlavor > SAL_CALL CDOTransferable::getTransferDataFlavors( )
throw( RuntimeException )
{
return m_FlavorList;
}
//------------------------------------------------------------------------
// isDataFlavorSupported
// returns true if we find a DataFlavor with the same MimeType and
// DataType
//------------------------------------------------------------------------
sal_Bool SAL_CALL CDOTransferable::isDataFlavorSupported( const DataFlavor& aFlavor )
throw( RuntimeException )
{
OSL_ASSERT( isValidFlavor( aFlavor ) );
for ( sal_Int32 i = 0; i < m_FlavorList.getLength( ); i++ )
if ( compareDataFlavors( aFlavor, m_FlavorList[i] ) )
return sal_True;
return sal_False;
}
//------------------------------------------------------------------------
// helper function
// the list of datafalvors currently on the clipboard will be initialized
// only once; if the client of this Transferable will hold a reference
// to it und the underlying clipboard content changes, the client does
// possible operate on a invalid list
// if there is only text on the clipboard we will also offer unicode text
// an synthesize this format on the fly if requested, to accomplish this
// we save the first offered text format which we will later use for the
// conversion
//------------------------------------------------------------------------
void SAL_CALL CDOTransferable::initFlavorList( )
{
IEnumFORMATETCPtr pEnumFormatEtc;
HRESULT hr = m_rDataObject->EnumFormatEtc( DATADIR_GET, &pEnumFormatEtc );
if ( SUCCEEDED( hr ) )
{
pEnumFormatEtc->Reset( );
FORMATETC fetc;
while ( S_FALSE != pEnumFormatEtc->Next( 1, &fetc, NULL ) )
{
// we use locales only to determine the
// charset if there is text on the cliboard
// we don't offer this format
if ( CF_LOCALE == fetc.cfFormat )
continue;
DataFlavor aFlavor = formatEtcToDataFlavor( fetc );
// if text or oemtext is offered we also pretend to have unicode text
if ( m_DataFormatTranslator.isOemOrAnsiTextFormat( fetc.cfFormat ) &&
!m_bUnicodeRegistered )
{
addSupportedFlavor( aFlavor );
m_TxtFormatOnClipboard = fetc.cfFormat;
m_bUnicodeRegistered = sal_True;
// register unicode text as accompany format
aFlavor = formatEtcToDataFlavor(
m_DataFormatTranslator.getFormatEtcForClipformat( CF_UNICODETEXT ) );
addSupportedFlavor( aFlavor );
}
else if ( (CF_UNICODETEXT == fetc.cfFormat) && !m_bUnicodeRegistered )
{
addSupportedFlavor( aFlavor );
m_bUnicodeRegistered = sal_True;
}
else
addSupportedFlavor( aFlavor );
// see MSDN IEnumFORMATETC
CoTaskMemFree( fetc.ptd );
}
}
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
inline
void SAL_CALL CDOTransferable::addSupportedFlavor( const DataFlavor& aFlavor )
{
// we ignore all formats that couldn't be translated
if ( aFlavor.MimeType.getLength( ) )
{
OSL_ASSERT( isValidFlavor( aFlavor ) );
m_FlavorList.realloc( m_FlavorList.getLength( ) + 1 );
m_FlavorList[m_FlavorList.getLength( ) - 1] = aFlavor;
}
}
//------------------------------------------------------------------------
// helper function
//------------------------------------------------------------------------
//inline
DataFlavor SAL_CALL CDOTransferable::formatEtcToDataFlavor( const FORMATETC& aFormatEtc )
{
DataFlavor aFlavor;
LCID lcid = 0;
// for non-unicode text format we must provid a locale to get
// the character-set of the text, if there is no locale on the
// clipboard we assume the text is in a charset appropriate for
// the current thread locale
if ( (CF_TEXT == aFormatEtc.cfFormat) || (CF_OEMTEXT == aFormatEtc.cfFormat) )
lcid = getLocaleFromClipboard( );
return m_DataFormatTranslator.getDataFlavorFromFormatEtc( aFormatEtc, lcid );
}
//------------------------------------------------------------------------
// returns the current locale on clipboard; if there is no locale on
// clipboard the function returns the current thread locale
//------------------------------------------------------------------------
LCID SAL_CALL CDOTransferable::getLocaleFromClipboard( )
{
LCID lcid = GetThreadLocale( );
try
{
CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcForClipformat( CF_LOCALE );
ByteSequence_t aLCIDSeq = getClipboardData( fetc );
lcid = *(reinterpret_cast<LCID*>( aLCIDSeq.getArray( ) ) );
// because of a Win95/98 Bug; there the high word
// of a locale has the same value as the
// low word e.g. 0x07040704 that's not right
// correct is 0x00000704
lcid &= 0x0000FFFF;
}
catch(...)
{
// we take the default locale
}
return lcid;
}
//------------------------------------------------------------------------
// i think it's not necessary to call ReleaseStgMedium
// in case of failures because nothing should have been
// allocated etc.
//------------------------------------------------------------------------
CDOTransferable::ByteSequence_t SAL_CALL CDOTransferable::getClipboardData( CFormatEtc& aFormatEtc )
{
STGMEDIUM stgmedium;
HRESULT hr = m_rDataObject->GetData( aFormatEtc, &stgmedium );
// in case of failure to get a WMF metafile handle, try to get a memory block
if( FAILED( hr ) &&
( CF_METAFILEPICT == aFormatEtc.getClipformat() ) &&
( TYMED_MFPICT == aFormatEtc.getTymed() ) )
{
CFormatEtc aTempFormat( aFormatEtc );
aTempFormat.setTymed( TYMED_HGLOBAL );
hr = m_rDataObject->GetData( aTempFormat, &stgmedium );
}
if ( FAILED( hr ) )
{
OSL_ASSERT( (hr != E_INVALIDARG) &&
(hr != DV_E_DVASPECT) &&
(hr != DV_E_LINDEX) &&
(hr != DV_E_TYMED) );
if ( DV_E_FORMATETC == hr )
throw UnsupportedFlavorException( );
else if ( STG_E_MEDIUMFULL == hr )
throw IOException( );
else
throw RuntimeException( );
}
ByteSequence_t byteStream;
try
{
if ( CF_ENHMETAFILE == aFormatEtc.getClipformat() )
byteStream = WinENHMFPictToOOMFPict( stgmedium.hEnhMetaFile );
else if (CF_HDROP == aFormatEtc.getClipformat())
byteStream = CF_HDROPToFileList(stgmedium.hGlobal);
else if ( CF_BITMAP == aFormatEtc.getClipformat() )
{
byteStream = WinBITMAPToOOBMP(stgmedium.hBitmap);
if( aFormatEtc.getTymed() == TYMED_GDI &&
! stgmedium.pUnkForRelease )
{
DeleteObject(stgmedium.hBitmap);
}
}
else
{
clipDataToByteStream( aFormatEtc.getClipformat( ), stgmedium, byteStream );
// format conversion if necessary
// #124085# DIBV5 should not happen currently, but keep as a hint here
if(CF_DIBV5 == aFormatEtc.getClipformat() || CF_DIB == aFormatEtc.getClipformat())
{
byteStream = WinDIBToOOBMP(byteStream);
}
else if(CF_METAFILEPICT == aFormatEtc.getClipformat())
{
byteStream = WinMFPictToOOMFPict(byteStream);
}
}
ReleaseStgMedium( &stgmedium );
}
catch( CStgTransferHelper::CStgTransferException& )
{
ReleaseStgMedium( &stgmedium );
throw IOException( );
}
return byteStream;
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
OUString SAL_CALL CDOTransferable::synthesizeUnicodeText( )
{
ByteSequence_t aTextSequence;
CFormatEtc fetc;
LCID lcid = getLocaleFromClipboard( );
sal_uInt32 cpForTxtCnvt = 0;
if ( CF_TEXT == m_TxtFormatOnClipboard )
{
fetc = m_DataFormatTranslator.getFormatEtcForClipformat( CF_TEXT );
aTextSequence = getClipboardData( fetc );
// determine the codepage used for text conversion
cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTANSICODEPAGE ).toInt32( );
}
else if ( CF_OEMTEXT == m_TxtFormatOnClipboard )
{
fetc = m_DataFormatTranslator.getFormatEtcForClipformat( CF_OEMTEXT );
aTextSequence = getClipboardData( fetc );
// determine the codepage used for text conversion
cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTCODEPAGE ).toInt32( );
}
else
OSL_ASSERT( sal_False );
CStgTransferHelper stgTransferHelper;
// convert the text
MultiByteToWideCharEx( cpForTxtCnvt,
reinterpret_cast<char*>( aTextSequence.getArray( ) ),
sal::static_int_cast<sal_uInt32>(-1), // Huh ?
stgTransferHelper,
sal_False);
CRawHGlobalPtr ptrHGlob(stgTransferHelper);
sal_Unicode* pWChar = reinterpret_cast<sal_Unicode*>(ptrHGlob.GetMemPtr());
return OUString(pWChar);
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
void CDOTransferable::clipDataToByteStream( CLIPFORMAT cf, STGMEDIUM stgmedium, ByteSequence_t& aByteSequence )
{
CStgTransferHelper memTransferHelper;
switch( stgmedium.tymed )
{
case TYMED_HGLOBAL:
memTransferHelper.init( stgmedium.hGlobal );
break;
case TYMED_MFPICT:
memTransferHelper.init( stgmedium.hMetaFilePict );
break;
case TYMED_ENHMF:
memTransferHelper.init( stgmedium.hEnhMetaFile );
break;
case TYMED_ISTREAM:
#ifdef _MSC_VER
#pragma PRAGMA_MSG( Has to be implemented )
#endif
break;
default:
throw UnsupportedFlavorException( );
break;
}
int nMemSize = memTransferHelper.memSize( cf );
aByteSequence.realloc( nMemSize );
memTransferHelper.read( aByteSequence.getArray( ), nMemSize );
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
inline
Any CDOTransferable::byteStreamToAny( ByteSequence_t& aByteStream, const Type& aRequestedDataType )
{
Any aAny;
if ( aRequestedDataType == CPPUTYPE_OUSTRING )
{
OUString str = byteStreamToOUString( aByteStream );
aAny = makeAny( str );
}
else
aAny = makeAny( aByteStream );
return aAny;
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
inline
OUString CDOTransferable::byteStreamToOUString( ByteSequence_t& aByteStream )
{
sal_Int32 nWChars;
sal_Int32 nMemSize = aByteStream.getLength( );
// if there is a trailing L"\0" substract 1 from length
if ( 0 == aByteStream[ aByteStream.getLength( ) - 2 ] &&
0 == aByteStream[ aByteStream.getLength( ) - 1 ] )
nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) ) - 1;
else
nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) );
return OUString( reinterpret_cast< sal_Unicode* >( aByteStream.getArray( ) ), nWChars );
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
sal_Bool SAL_CALL CDOTransferable::compareDataFlavors(
const DataFlavor& lhs, const DataFlavor& rhs )
{
if ( !m_rXMimeCntFactory.is( ) )
{
m_rXMimeCntFactory = Reference< XMimeContentTypeFactory >( m_SrvMgr->createInstance(
OUString::createFromAscii( "com.sun.star.datatransfer.MimeContentTypeFactory" ) ), UNO_QUERY );
}
OSL_ASSERT( m_rXMimeCntFactory.is( ) );
sal_Bool bRet = sal_False;
try
{
Reference< XMimeContentType > xLhs( m_rXMimeCntFactory->createMimeContentType( lhs.MimeType ) );
Reference< XMimeContentType > xRhs( m_rXMimeCntFactory->createMimeContentType( rhs.MimeType ) );
if ( cmpFullMediaType( xLhs, xRhs ) )
{
bRet = cmpAllContentTypeParameter( xLhs, xRhs );
}
}
catch( IllegalArgumentException& )
{
OSL_ENSURE( sal_False, "Invalid content type detected" );
bRet = sal_False;
}
return bRet;
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
sal_Bool SAL_CALL CDOTransferable::cmpFullMediaType(
const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs ) const
{
return xLhs->getFullMediaType().equalsIgnoreAsciiCase( xRhs->getFullMediaType( ) );
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
sal_Bool SAL_CALL CDOTransferable::cmpAllContentTypeParameter(
const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs ) const
{
Sequence< OUString > xLhsFlavors = xLhs->getParameters( );
Sequence< OUString > xRhsFlavors = xRhs->getParameters( );
sal_Bool bRet = sal_True;
try
{
if ( xLhsFlavors.getLength( ) == xRhsFlavors.getLength( ) )
{
OUString pLhs;
OUString pRhs;
for ( sal_Int32 i = 0; i < xLhsFlavors.getLength( ); i++ )
{
pLhs = xLhs->getParameterValue( xLhsFlavors[i] );
pRhs = xRhs->getParameterValue( xLhsFlavors[i] );
if ( !pLhs.equalsIgnoreAsciiCase( pRhs ) )
{
bRet = sal_False;
break;
}
}
}
else
bRet = sal_False;
}
catch( NoSuchElementException& )
{
bRet = sal_False;
}
catch( IllegalArgumentException& )
{
bRet = sal_False;
}
return bRet;
}
::com::sun::star::uno::Any SAL_CALL CDOTransferable::getData( const Sequence< sal_Int8>& aProcessId )
throw (::com::sun::star::uno::RuntimeException)
{
Any retVal;
sal_uInt8 * arProcCaller= (sal_uInt8*)(sal_Int8*) aProcessId.getConstArray();
sal_uInt8 arId[16];
rtl_getGlobalProcessId(arId);
if( ! memcmp( arId, arProcCaller,16))
{
if (m_rDataObject.is())
{
IDataObject* pObj= m_rDataObject.get();
pObj->AddRef();
retVal.setValue( &pObj, getCppuType((sal_uInt32*)0));
}
}
return retVal;
}