Quelle transfer.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* 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 .
*/
#ifdef _WIN32
#include <prewin.h>
#include <postwin.h>
#include <shlobj.h>
#endif
#include <o3tl/char16_t2wchar_t.hxx>
#include <rtl/uri.hxx>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <tools/urlobj.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <sot/exchange.hxx>
#include <sot/storage.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/filter/SvmReader.hxx>
#include <vcl/filter/SvmWriter.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/graph.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/svapp.hxx>
#include <vcl/window.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/processfactory.hxx>
#include <comphelper/scopeguard.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/sequence.hxx>
#include <sot/filelist.hxx>
#include <cppuhelper/implbase.hxx>
#include <comphelper/seqstream.hxx>
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
#include <com/sun/star/datatransfer/XMimeContentType.hpp>
#include <com/sun/star/datatransfer/XTransferable2.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <svl/urlbmk.hxx>
#include <vcl/inetimg.hxx>
#include <vcl/wmf.hxx>
#include <vcl/imap.hxx>
#include <vcl/transfer.hxx>
#include <rtl/strbuf.hxx>
#include <cstdio>
#include <vcl/dibtools.hxx>
#include <vcl/filter/PngImageReader.hxx>
#include <vcl/filter/PngImageWriter.hxx>
#include <vcl/graphicfilter.hxx>
#include <memory>
#include <utility>
#include <vcl/TypeSerializer.hxx>
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::datatransfer;
using namespace ::com::sun::star::datatransfer::clipboard;
using namespace ::com::sun::star::datatransfer::dnd;
using namespace std::literals::string_view_literals;
#define TOD_SIG1 0 x01234567
#define TOD_SIG2 0 x89abcdef
SvStream& WriteTransferableObjectDescriptor( SvStream& rOStm, const TransferableObjectDescriptor& rObjDesc )
{
const sal_uInt64 nFirstPos = rOStm.Tell();
const sal_uInt32 nViewAspect = rObjDesc.mnViewAspect;
const sal_uInt32 nSig1 = TOD_SIG1, nSig2 = TOD_SIG2;
rOStm.SeekRel( 4 );
WriteSvGlobalName( rOStm, rObjDesc.maClassName );
rOStm.WriteUInt32( nViewAspect );
rOStm.WriteInt32( rObjDesc.maSize.Width() );
rOStm.WriteInt32( rObjDesc.maSize.Height() );
rOStm.WriteInt32( rObjDesc.maDragStartPos.X() );
rOStm.WriteInt32( rObjDesc.maDragStartPos.Y() );
rOStm.WriteUniOrByteString( rObjDesc.maTypeName, osl_getThreadTextEncoding() );
rOStm.WriteUniOrByteString( rObjDesc.maDisplayName, osl_getThreadTextEncoding() );
rOStm.WriteUInt32( nSig1 ).WriteUInt32( nSig2 );
const sal_uInt64 nLastPos = rOStm.Tell();
rOStm.Seek( nFirstPos );
rOStm.WriteUInt32( nLastPos - nFirstPos );
rOStm.Seek( nLastPos );
return rOStm;
}
static void TryReadTransferableObjectDescriptor(SvStream& rIStm,
TransferableObjectDescriptor& rObjDesc)
{
auto nStartPos = rIStm.Tell();
comphelper::ScopeGuard streamPosRestore([nStartPos, &rIStm] { rIStm.Seek(nStartPos); });
sal_uInt32 size;
rIStm.ReadUInt32(size);
SvGlobalName className;
rIStm >> className;
sal_uInt32 viewAspect;
rIStm.ReadUInt32(viewAspect);
sal_Int32 width, height;
rIStm.ReadInt32(width).ReadInt32(height);
sal_Int32 dragStartPosX, dragStartPosY;
rIStm.ReadInt32(dragStartPosX).ReadInt32(dragStartPosY);
const OUString typeName = rIStm.ReadUniOrByteString(osl_getThreadTextEncoding());
const OUString displayName = rIStm.ReadUniOrByteString(osl_getThreadTextEncoding());
sal_uInt32 nSig1, nSig2;
rIStm.ReadUInt32(nSig1).ReadUInt32(nSig2);
if (!rIStm.good() || rIStm.Tell() - nStartPos != size || nSig1 != TOD_SIG1 || nSig2 != TOD_SIG2)
return ;
rObjDesc.maClassName = className;
rObjDesc.mnViewAspect = viewAspect;
rObjDesc.maSize = Size(width, height);
rObjDesc.maDragStartPos = Point(dragStartPosX, dragStartPosY);
rObjDesc.maTypeName = typeName ;
rObjDesc.maDisplayName = displayName;
}
// the reading of the parameter is done using the special service css::datatransfer::MimeContentType,
// a similar approach should be implemented for creation of the mimetype string;
// for now the set of acceptable characters has to be hardcoded, in future it should be part of the service that creates the mimetype
static OUString ImplGetParameterString( const TransferableObjectDescriptor& rObjDesc )
{
const OUString aClassName( rObjDesc.maClassName.GetHexName() );
OUString aParams;
if ( !aClassName.isEmpty() )
{
aParams += ";classname=\" " + aClassName + " \"" ;
}
if ( !rObjDesc.maTypeName.isEmpty() )
{
aParams += ";typename=\" " + rObjDesc.maTypeName + " \"" ;
}
if ( !rObjDesc.maDisplayName.isEmpty() )
{
// the display name might contain unacceptable characters, encode all of them
// this seems to be the only parameter currently that might contain such characters
static constexpr auto pToAccept = rtl::createUriCharClass(
u8"()<>@,;:/[]?=!#$&'*+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~. " );
aParams += ";displayname=\" "
+ rtl::Uri::encode(
rObjDesc.maDisplayName, pToAccept.data(), rtl_UriEncodeIgnoreEscapes,
RTL_TEXTENCODING_UTF8)
+ "\" ";
}
aParams += ";viewaspect=\" " + OUString::number(rObjDesc.mnViewAspect)
+ "\" ;width=\"" + OUString::number(rObjDesc.maSize.Width())
+ "\" ;height=\"" + OUString::number(rObjDesc.maSize.Height())
+ "\" ;posx=\"" + OUString::number(rObjDesc.maDragStartPos.X())
+ "\" ;posy=\"" + OUString::number(rObjDesc.maDragStartPos.X()) + "\" ";
return aParams;
}
static void ImplSetParameterString( TransferableObjectDescriptor& rObjDesc, const DataFlavorEx& rFlavorEx )
{
const Reference< XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
try
{
Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
Reference< XMimeContentType > xMimeType( xMimeFact->createMimeContentType( rFlavorEx.MimeType ) );
if ( xMimeType.is() )
{
static constexpr OUString aClassNameString( u"classname" _ustr );
static constexpr OUString aTypeNameString( u"typename" _ustr );
static constexpr OUString aDisplayNameString( u"displayname" _ustr );
static constexpr OUString aViewAspectString( u"viewaspect" _ustr );
static constexpr OUString aWidthString( u"width" _ustr );
static constexpr OUString aHeightString( u"height" _ustr );
static constexpr OUString aPosXString( u"posx" _ustr );
static constexpr OUString aPosYString( u"posy" _ustr );
if ( xMimeType->hasParameter( aClassNameString ) )
{
rObjDesc.maClassName.MakeId( xMimeType->getParameterValue( aClassNameString ) );
}
if ( xMimeType->hasParameter( aTypeNameString ) )
{
rObjDesc.maTypeName = xMimeType->getParameterValue( aTypeNameString );
}
if ( xMimeType->hasParameter( aDisplayNameString ) )
{
// the display name might contain unacceptable characters, in this case they should be encoded
// this seems to be the only parameter currently that might contain such characters
rObjDesc.maDisplayName = ::rtl::Uri::decode( xMimeType->getParameterValue( aDisplayNameString ), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
}
if ( xMimeType->hasParameter( aViewAspectString ) )
{
rObjDesc.mnViewAspect = static_cast < sal_uInt16 >( xMimeType->getParameterValue( aViewAspectString ).toInt32() );
}
if ( xMimeType->hasParameter( aWidthString ) )
{
rObjDesc.maSize.setWidth( xMimeType->getParameterValue( aWidthString ).toInt32() );
}
if ( xMimeType->hasParameter( aHeightString ) )
{
rObjDesc.maSize.setHeight( xMimeType->getParameterValue( aHeightString ).toInt32() );
}
if ( xMimeType->hasParameter( aPosXString ) )
{
rObjDesc.maDragStartPos.setX( xMimeType->getParameterValue( aPosXString ).toInt32() );
}
if ( xMimeType->hasParameter( aPosYString ) )
{
rObjDesc.maDragStartPos.setY( xMimeType->getParameterValue( aPosYString ).toInt32() );
}
}
}
catch ( const css::uno::Exception& )
{
}
}
TransferableHelper::TerminateListener::TerminateListener( TransferableHelper& rTransferableHelper ) :
mrParent( rTransferableHelper )
{
}
TransferableHelper::TerminateListener::~TerminateListener()
{
}
void SAL_CALL TransferableHelper::TerminateListener::disposing( const EventObject& )
{
}
void SAL_CALL TransferableHelper::TerminateListener::queryTermination( const EventObject& )
{
}
void SAL_CALL TransferableHelper::TerminateListener::notifyTermination( const EventObject& )
{
mrParent.ImplFlush();
mrParent.mxTerminateListener.clear();
}
OUString SAL_CALL TransferableHelper::TerminateListener::getImplementationName()
{
return u"com.sun.star.comp.svt.TransferableHelperTerminateListener" _ustr;
}
sal_Bool SAL_CALL TransferableHelper::TerminateListener::supportsService(const OUString& /*rServiceName*/)
{
return false ;
}
css::uno::Sequence<OUString> TransferableHelper::TerminateListener::getSupportedServiceNames()
{
return {};
}
TransferableHelper::~TransferableHelper()
{
rtl::Reference< TerminateListener > listener;
{
const SolarMutexGuard aGuard;
std::swap(listener, mxTerminateListener);
}
if (listener.is()) {
Desktop::create(comphelper::getProcessComponentContext())->removeTerminateListener(
listener);
}
}
Any SAL_CALL TransferableHelper::getTransferData( const DataFlavor& rFlavor )
{
return getTransferData2(rFlavor, OUString());
}
Any SAL_CALL TransferableHelper::getTransferData2( const DataFlavor& rFlavor, const OUString& rDestDoc )
{
if ( !maAny.hasValue() || maFormats.empty() || ( maLastFormat != rFlavor.MimeType ) )
{
const SolarMutexGuard aGuard;
maLastFormat = rFlavor.MimeType;
maAny = Any();
try
{
DataFlavor aSubstFlavor;
bool bDone = false ;
// add formats if not already done
if (maFormats.empty())
AddSupportedFormats();
// check alien formats first and try to get a substitution format
if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aSubstFlavor ) &&
TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) )
{
GetData(aSubstFlavor, rDestDoc);
bDone = maAny.hasValue();
}
else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor )
&& TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor )
&& SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BITMAP, aSubstFlavor))
{
GetData(aSubstFlavor, rDestDoc);
bDone = true ;
}
else if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
{
GetData(aSubstFlavor, rDestDoc);
if ( maAny.hasValue() )
{
Sequence< sal_Int8 > aSeq;
if ( maAny >>= aSeq )
{
GDIMetaFile aMtf;
{
SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
SvmReader aReader( aSrcStm );
aReader.Read( aMtf );
}
Graphic aGraphic( aMtf );
SvMemoryStream aDstStm( 65535 , 65535 );
if ( GraphicConverter::Export( aDstStm, aGraphic, ConvertDataFormat::EMF ) == ERRCODE_NONE )
{
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aDstStm.GetData() ),
aDstStm.TellEnd() );
bDone = true ;
}
}
}
}
else if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
{
GetData(aSubstFlavor, rDestDoc);
if ( maAny.hasValue() )
{
Sequence< sal_Int8 > aSeq;
if ( maAny >>= aSeq )
{
GDIMetaFile aMtf;
{
SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
SvmReader aReader( aSrcStm );
aReader.Read( aMtf );
}
SvMemoryStream aDstStm( 65535 , 65535 );
// taking wmf without file header
if ( ConvertGDIMetaFileToWMF( aMtf, aDstStm, nullptr, false ) )
{
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aDstStm.GetData() ),
aDstStm.TellEnd() );
bDone = true ;
}
}
}
}
else if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::SVG, aSubstFlavor ) &&
TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
{
GetData(aSubstFlavor, rDestDoc);
if ( maAny.hasValue() )
{
Sequence< sal_Int8 > aSeq;
if ( maAny >>= aSeq )
{
GDIMetaFile aMtf;
{
SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
SvmReader aReader( aSrcStm );
aReader.Read( aMtf );
}
SvMemoryStream aDstStm( 65535 , 65535 );
Graphic aGraphic( aMtf );
if ( GraphicConverter::Export( aDstStm, aGraphic, ConvertDataFormat::SVG ) == ERRCODE_NONE )
{
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aDstStm.GetData() ),
aDstStm.TellEnd() );
bDone = true ;
}
}
}
}
// reset Any if substitute doesn't work
if ( !bDone && maAny.hasValue() )
maAny = Any();
// if any is not yet filled, use standard format
if ( !maAny.hasValue() )
GetData(rFlavor, rDestDoc);
}
catch ( const css::uno::Exception& )
{
}
if ( !maAny.hasValue() )
throw UnsupportedFlavorException();
}
return maAny;
}
sal_Bool SAL_CALL TransferableHelper::isComplex()
{
// By default everything is complex, until proven otherwise
// in the respective document type transferable handler.
return true ;
}
Sequence< DataFlavor > SAL_CALL TransferableHelper::getTransferDataFlavors()
{
const SolarMutexGuard aGuard;
try
{
if (maFormats.empty())
AddSupportedFormats();
}
catch ( const css::uno::Exception& )
{
}
return comphelper::containerToSequence<DataFlavor>(maFormats);
}
sal_Bool SAL_CALL TransferableHelper::isDataFlavorSupported( const DataFlavor& rFlavor )
{
const SolarMutexGuard aGuard;
try
{
if (maFormats.empty())
AddSupportedFormats();
}
catch ( const css::uno::Exception& )
{
}
for (auto const & format : maFormats)
{
if ( TransferableDataHelper::IsEqual( format, rFlavor ) )
{
return true ;
}
}
return false ;
}
void SAL_CALL TransferableHelper::lostOwnership( const Reference< XClipboard >&, const Reference< XTransferable >& )
{
const SolarMutexGuard aGuard;
try
{
if ( mxTerminateListener.is() )
{
Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
xDesktop->removeTerminateListener( mxTerminateListener );
mxTerminateListener.clear();
}
ObjectReleased();
}
catch ( const css::uno::Exception& )
{
}
}
void SAL_CALL TransferableHelper::disposing( const EventObject& )
{
}
void SAL_CALL TransferableHelper::dragDropEnd( const DragSourceDropEvent& rDSDE )
{
const SolarMutexGuard aGuard;
try
{
DragFinished( rDSDE.DropSuccess ? ( rDSDE.DropAction & ~DNDConstants::ACTION_DEFAULT ) : DNDConstants::ACTION_NONE );
ObjectReleased();
}
catch ( const css::uno::Exception& )
{
}
}
void SAL_CALL TransferableHelper::dragEnter( const DragSourceDragEvent& )
{
}
void SAL_CALL TransferableHelper::dragExit( const DragSourceEvent& )
{
}
void SAL_CALL TransferableHelper::dragOver( const DragSourceDragEvent& )
{
}
void SAL_CALL TransferableHelper::dropActionChanged( const DragSourceDragEvent& )
{
}
void TransferableHelper::ImplFlush()
{
if ( !mxClipboard.is() )
return ;
Reference< XFlushableClipboard > xFlushableClipboard( mxClipboard, UNO_QUERY );
SolarMutexReleaser aReleaser;
try
{
if ( xFlushableClipboard.is() )
xFlushableClipboard->flushClipboard();
}
catch ( const css::uno::Exception& )
{
OSL_FAIL( "Could not flush clipboard" );
}
}
void TransferableHelper::AddFormat( SotClipboardFormatId nFormat )
{
DataFlavor aFlavor;
if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
AddFormat( aFlavor );
}
void TransferableHelper::AddFormat( const DataFlavor& rFlavor )
{
bool bAdd = true ;
for (auto & format : maFormats)
{
if ( TransferableDataHelper::IsEqual( format, rFlavor ) )
{
// update MimeType for SotClipboardFormatId::OBJECTDESCRIPTOR in every case
if ((SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId) && mxObjDesc)
{
DataFlavor aObjDescFlavor;
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDescFlavor );
format.MimeType = aObjDescFlavor.MimeType;
format.MimeType += ::ImplGetParameterString(*mxObjDesc);
}
bAdd = false ;
break ;
}
}
if ( !bAdd )
return ;
DataFlavorEx aFlavorEx;
aFlavorEx.MimeType = rFlavor.MimeType;
aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
aFlavorEx.DataType = rFlavor.DataType;
aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
if ((SotClipboardFormatId::OBJECTDESCRIPTOR == aFlavorEx.mnSotId) && mxObjDesc)
aFlavorEx.MimeType += ::ImplGetParameterString(*mxObjDesc);
maFormats.push_back(aFlavorEx);
if ( SotClipboardFormatId::BITMAP == aFlavorEx.mnSotId )
{
AddFormat( SotClipboardFormatId::PNG );
AddFormat( SotClipboardFormatId::BMP );
}
else if ( SotClipboardFormatId::GDIMETAFILE == aFlavorEx.mnSotId )
{
AddFormat( SotClipboardFormatId::EMF );
AddFormat( SotClipboardFormatId::WMF );
AddFormat( SotClipboardFormatId::SVG );
}
}
void TransferableHelper::RemoveFormat( SotClipboardFormatId nFormat )
{
DataFlavor aFlavor;
if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
RemoveFormat( aFlavor );
}
void TransferableHelper::RemoveFormat( const DataFlavor& rFlavor )
{
DataFlavorExVector::iterator aIter(maFormats.begin());
while (aIter != maFormats.end())
{
if ( TransferableDataHelper::IsEqual( *aIter, rFlavor ) )
aIter = maFormats.erase(aIter);
else
++aIter;
}
}
bool TransferableHelper::HasFormat( SotClipboardFormatId nFormat )
{
return std::any_of(maFormats.begin(), maFormats.end(),
[&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
}
void TransferableHelper::ClearFormats()
{
maFormats.clear();
maAny.clear();
}
bool TransferableHelper::SetAny( const Any& rAny )
{
maAny = rAny;
return maAny.hasValue();
}
bool TransferableHelper::SetString( const OUString& rString )
{
maAny <<= rString;
return maAny.hasValue();
}
bool TransferableHelper::SetBitmapEx( const BitmapEx& rBitmapEx, const DataFlavor& rFlavor )
{
if ( !rBitmapEx.IsEmpty() )
{
SvMemoryStream aMemStm( 65535 , 65535 );
if (rFlavor.MimeType.equalsIgnoreAsciiCase("image/png" ))
{
// write a PNG
css::uno::Sequence<css::beans::PropertyValue> aFilterData;
#ifdef IOS
// Use faster compression on slow devices
aFilterData.realloc(aFilterData.getLength() + 1 );
aFilterData.getArray()[aFilterData.getLength() - 1 ].Name = "Compression" ;
// We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed. For a
// typical 15 megapixel image from a DSLR, we are talking about a difference of 17 s for
// the default compression level vs 4 s for best speed, on an iPad Pro from 2017.
//
// Sure, the best would be to not have to re-encode the image at all, but have access to
// the original JPEG or PNG when there is a such.
aFilterData.getArray()[aFilterData.getLength() - 1 ].Value <<= 1 ;
#endif
vcl::PngImageWriter aPNGWriter(aMemStm);
aPNGWriter.setParameters(aFilterData);
aPNGWriter.write(rBitmapEx);
}
else
{
// explicitly use Bitmap::Write with bCompressed = sal_False and bFileHeader = sal_True
WriteDIB(rBitmapEx.GetBitmap(), aMemStm, false , true );
}
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
}
return maAny.hasValue();
}
bool TransferableHelper::SetGDIMetaFile( const GDIMetaFile& rMtf )
{
if ( rMtf.GetActionSize() )
{
SvMemoryStream aMemStm( 65535 , 65535 );
SvmWriter aWriter( aMemStm );
aWriter.Write( rMtf );
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
}
return maAny.hasValue();
}
bool TransferableHelper::SetGraphic( const Graphic& rGraphic )
{
if ( rGraphic.GetType() != GraphicType::NONE )
{
SvMemoryStream aMemStm( 65535 , 65535 );
aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
aMemStm.SetCompressMode( SvStreamCompressFlags::NATIVE );
TypeSerializer aSerializer(aMemStm);
aSerializer.writeGraphic(rGraphic);
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
}
return maAny.hasValue();
}
bool TransferableHelper::SetImageMap( const ImageMap& rIMap )
{
SvMemoryStream aMemStm( 8192 , 8192 );
aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
rIMap.Write( aMemStm );
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
return maAny.hasValue();
}
bool TransferableHelper::SetTransferableObjectDescriptor( const TransferableObjectDescriptor& rDesc )
{
PrepareOLE( rDesc );
SvMemoryStream aMemStm( 1024 , 1024 );
WriteTransferableObjectDescriptor( aMemStm, rDesc );
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
return maAny.hasValue();
}
bool TransferableHelper::SetINetBookmark( const INetBookmark& rBmk,
const css::datatransfer::DataFlavor& rFlavor )
{
rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
switch ( SotExchange::GetFormat( rFlavor ) )
{
case SotClipboardFormatId::SOLK:
{
OString sURL(OUStringToOString(rBmk.GetURL(), eSysCSet));
OString sDesc(OUStringToOString(rBmk.GetDescription(), eSysCSet));
OString sOut =
OString::number(sURL.getLength())
+ "@" + sURL
+ OString::number(sDesc.getLength())
+ "@" + sDesc;
Sequence< sal_Int8 > aSeq(sOut.getLength());
memcpy(aSeq.getArray(), sOut.getStr(), sOut.getLength());
maAny <<= aSeq;
}
break ;
case SotClipboardFormatId::STRING:
case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
maAny <<= rBmk.GetURL();
break ;
case SotClipboardFormatId::NETSCAPE_BOOKMARK:
{
Sequence< sal_Int8 > aSeq( 2048 );
char * pSeq = reinterpret_cast < char * >( aSeq.getArray() );
// strncpy fills the rest with nulls, as we need
strncpy( pSeq, OUStringToOString(rBmk.GetURL(), eSysCSet).getStr(), 1024 );
strncpy( pSeq + 1024 , OUStringToOString(rBmk.GetDescription(), eSysCSet).getStr(), 1024 );
maAny <<= aSeq;
}
break ;
#ifdef _WIN32
case SotClipboardFormatId::FILEGRPDESCRIPTOR:
{
Sequence< sal_Int8 > aSeq( sizeof ( FILEGROUPDESCRIPTORW ) );
FILEGROUPDESCRIPTORW* pFDesc = reinterpret_cast <FILEGROUPDESCRIPTORW*>(aSeq.getArray());
FILEDESCRIPTORW& rFDesc1 = pFDesc->fgd[ 0 ];
pFDesc->cItems = 1 ;
memset( &rFDesc1, 0 , sizeof ( rFDesc1 ) );
rFDesc1.dwFlags = FD_LINKUI;
OUStringBuffer aStr(rBmk.GetDescription());
for ( size_t nChar = 0 ; (nChar = std::u16string_view(aStr).find_first_of(u"\\/:*?\" <>|"sv, nChar)) != std::u16string_view::npos; )
aStr.remove(nChar, 1 );
aStr.insert(0 , "Shortcut to " );
aStr.append(".URL" );
wcscpy( rFDesc1.cFileName, o3tl::toW(aStr.getStr()) );
maAny <<= aSeq;
}
break ;
case SotClipboardFormatId::FILECONTENT:
{
maAny <<= "[InternetShortcut]\x0aURL=" + rBmk.GetURL();
}
break ;
#endif
default :
break ;
}
return maAny.hasValue();
}
bool TransferableHelper::SetINetImage( const INetImage& rINtImg,
const css::datatransfer::DataFlavor& rFlavor )
{
SvMemoryStream aMemStm( 1024 , 1024 );
aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
rINtImg.Write( aMemStm, SotExchange::GetFormat( rFlavor ) );
maAny <<= Sequence< sal_Int8 >( static_cast < const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
return maAny.hasValue();
}
bool TransferableHelper::SetObject( void * pUserObject, sal_uInt32 nUserObjectId, const DataFlavor& rFlavor )
{
SvMemoryStream aStm;
aStm.SetVersion( SOFFICE_FILEFORMAT_50 );
if ( pUserObject && WriteObject( aStm, pUserObject, nUserObjectId, rFlavor ) )
{
const sal_uInt32 nLen = aStm.TellEnd();
Sequence< sal_Int8 > aSeq( nLen );
aStm.Seek( STREAM_SEEK_TO_BEGIN );
aStm.ReadBytes(aSeq.getArray(), nLen);
if ( nLen && ( SotExchange::GetFormat( rFlavor ) == SotClipboardFormatId::STRING ) )
{
//JP 24.7.2001: as I know was this only for the writer application and this
// writes now UTF16 format into the stream
//JP 6.8.2001: and now it writes UTF8 because then exist no problem with
// little / big endians! - Bug 88121
maAny <<= OUString( reinterpret_cast < const char * >( aSeq.getConstArray() ), nLen - 1 , RTL_TEXTENCODING_UTF8 );
}
else
maAny <<= aSeq;
}
return maAny.hasValue();
}
bool TransferableHelper::WriteObject( SvStream&, void *, sal_uInt32, const DataFlavor& )
{
OSL_FAIL( "TransferableHelper::WriteObject( ... ) not implemented" );
return false ;
}
void TransferableHelper::DragFinished( sal_Int8 )
{
}
void TransferableHelper::ObjectReleased()
{
}
void TransferableHelper::PrepareOLE( const TransferableObjectDescriptor& rObjDesc )
{
mxObjDesc.reset(new TransferableObjectDescriptor(rObjDesc));
if ( HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) )
AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
}
void TransferableHelper::CopyToClipboard(const Reference<XClipboard>& rClipboard)
{
if ( rClipboard.is() )
mxClipboard = rClipboard;
if ( !mxClipboard.is() || mxTerminateListener.is() )
return ;
try
{
mxTerminateListener = new TerminateListener(*this );
Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
xDesktop->addTerminateListener(mxTerminateListener);
mxClipboard->setContents(this , this );
}
catch ( const css::uno::Exception& )
{
}
}
void TransferableHelper::CopyToClipboard(vcl::Window* pWindow)
{
DBG_ASSERT( pWindow, "Window pointer is NULL" );
Reference< XClipboard > xClipboard;
if ( pWindow )
xClipboard = pWindow->GetClipboard();
CopyToClipboard(xClipboard);
}
void TransferableHelper::CopyToSelection(const Reference<XClipboard>& rSelection)
{
if ( !rSelection.is() || mxTerminateListener.is() )
return ;
try
{
mxTerminateListener = new TerminateListener(*this );
Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
xDesktop->addTerminateListener(mxTerminateListener);
rSelection->setContents(this , this );
}
catch ( const css::uno::Exception& )
{
}
}
void TransferableHelper::CopyToPrimarySelection()
{
CopyToSelection(GetSystemPrimarySelection());
}
void TransferableHelper::StartDrag( vcl::Window* pWindow, sal_Int8 nDnDSourceActions )
{
assert(pWindow && "Window pointer is NULL" );
Reference< XDragSource > xDragSource( pWindow->GetDragSource() );
if ( !xDragSource.is() )
return ;
/*
* # 96792 # release mouse before actually starting DnD .
* This is necessary for the X11 DnD implementation to work .
*/
if ( pWindow->IsMouseCaptured() )
pWindow->ReleaseMouse();
const Point aPt( pWindow->GetPointerPosPixel() );
// On macOS we are forced to execute 'startDrag' synchronously
// contrary to the XDragSource interface specification because
// we can receive drag events from the system only in the main
// thread
#if !defined (MACOSX)
SolarMutexReleaser aReleaser;
#endif
try
{
DragGestureEvent aEvt;
aEvt.DragAction = DNDConstants::ACTION_COPY;
aEvt.DragOriginX = aPt.X();
aEvt.DragOriginY = aPt.Y();
aEvt.DragSource = xDragSource;
xDragSource->startDrag( aEvt, nDnDSourceActions, DND_POINTER_NONE, DND_IMAGE_NONE, this , this );
}
catch ( const css::uno::Exception& )
{
}
}
void TransferableHelper::ClearPrimarySelection()
{
Reference< XClipboard > xSelection(GetSystemPrimarySelection());
if ( xSelection.is() )
xSelection->setContents( nullptr, nullptr );
}
class TransferableClipboardNotifier : public ::cppu::WeakImplHelper< XClipboardListener >
{
private :
Reference< XClipboardNotifier > mxNotifier;
TransferableDataHelper* mpListener;
protected :
// XClipboardListener
virtual void SAL_CALL changedContents( const clipboard::ClipboardEvent& event ) override;
// XEventListener
virtual void SAL_CALL disposing( const EventObject& Source ) override;
public :
TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener );
/// determines whether we're currently listening
bool isListening() const { return mpListener != nullptr; }
/// makes the instance non-functional
void dispose();
};
TransferableClipboardNotifier::TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener )
:mxNotifier( _rxClipboard, UNO_QUERY )
,mpListener( &_rListener )
{
osl_atomic_increment( &m_refCount );
{
if ( mxNotifier.is() )
mxNotifier->addClipboardListener( this );
else
// born dead
mpListener = nullptr;
}
osl_atomic_decrement( &m_refCount );
}
void SAL_CALL TransferableClipboardNotifier::changedContents( const clipboard::ClipboardEvent& event )
{
SolarMutexGuard aSolarGuard;
if ( mpListener )
mpListener->Rebind( event.Contents );
}
void SAL_CALL TransferableClipboardNotifier::disposing( const EventObject& )
{
// clipboard is being disposed. Hmm. Okay, become disfunctional myself.
dispose();
}
void TransferableClipboardNotifier::dispose()
{
SolarMutexGuard g;
Reference< XClipboardListener > xKeepMeAlive( this );
if ( mxNotifier.is() )
mxNotifier->removeClipboardListener( this );
mxNotifier.clear();
mpListener = nullptr;
}
TransferableDataHelper::TransferableDataHelper()
: mxObjDesc(new TransferableObjectDescriptor)
{
}
TransferableDataHelper::TransferableDataHelper(const Reference< css::datatransfer::XTransferable >& rxTransferable)
: mxTransfer(rxTransferable)
, mxObjDesc(new TransferableObjectDescriptor)
{
InitFormats();
}
TransferableDataHelper::TransferableDataHelper(const TransferableDataHelper& rDataHelper)
: mxTransfer(rDataHelper.mxTransfer)
, mxClipboard(rDataHelper.mxClipboard)
, maFormats(rDataHelper.maFormats)
, mxObjDesc(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc))
{
}
TransferableDataHelper::TransferableDataHelper(TransferableDataHelper&& rDataHelper) noexcept
: mxTransfer(std::move(rDataHelper.mxTransfer))
, mxClipboard(std::move(rDataHelper.mxClipboard))
, maFormats(std::move(rDataHelper.maFormats))
, mxObjDesc(std::move(rDataHelper.mxObjDesc))
{
}
TransferableDataHelper& TransferableDataHelper::operator =( const TransferableDataHelper& rDataHelper )
{
if ( this != &rDataHelper )
{
SolarMutexGuard g;
const bool bWasClipboardListening = mxClipboardListener.is();
if (bWasClipboardListening)
StopClipboardListening();
mxTransfer = rDataHelper.mxTransfer;
maFormats = rDataHelper.maFormats;
mxObjDesc.reset(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc));
mxClipboard = rDataHelper.mxClipboard;
if (bWasClipboardListening)
StartClipboardListening();
}
return *this ;
}
TransferableDataHelper& TransferableDataHelper::operator =(TransferableDataHelper&& rDataHelper)
{
SolarMutexGuard g;
const bool bWasClipboardListening = mxClipboardListener.is();
if (bWasClipboardListening)
StopClipboardListening();
mxTransfer = std::move(rDataHelper.mxTransfer);
maFormats = std::move(rDataHelper.maFormats);
mxObjDesc = std::move(rDataHelper.mxObjDesc);
mxClipboard = std::move(rDataHelper.mxClipboard);
if (bWasClipboardListening)
StartClipboardListening();
return *this ;
}
TransferableDataHelper::~TransferableDataHelper()
{
StopClipboardListening( );
{
SolarMutexGuard g;
maFormats.clear();
mxObjDesc.reset();
}
}
void TransferableDataHelper::FillDataFlavorExVector( const Sequence< DataFlavor >& rDataFlavorSeq,
DataFlavorExVector& rDataFlavorExVector )
{
try
{
const Reference< XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
DataFlavorEx aFlavorEx;
static constexpr OUString aCharsetStr( u"charset" _ustr );
for (auto const & rFlavor : rDataFlavorSeq)
{
Reference< XMimeContentType > xMimeType;
try
{
if ( !rFlavor.MimeType.isEmpty() )
xMimeType = xMimeFact->createMimeContentType( rFlavor.MimeType );
}
catch ( const css::uno::Exception& )
{
}
aFlavorEx.MimeType = rFlavor.MimeType;
aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
aFlavorEx.DataType = rFlavor.DataType;
aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
rDataFlavorExVector.push_back( aFlavorEx );
// add additional formats for special mime types
if (SotClipboardFormatId::BMP == aFlavorEx.mnSotId || SotClipboardFormatId::PNG == aFlavorEx.mnSotId || SotClipboardFormatId::JPEG == aFlavorEx.mnSotId)
{
if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavorEx ) )
{
aFlavorEx.mnSotId = SotClipboardFormatId::BITMAP;
rDataFlavorExVector.push_back( aFlavorEx );
}
}
else if ( SotClipboardFormatId::WMF == aFlavorEx.mnSotId
|| SotClipboardFormatId::EMF == aFlavorEx.mnSotId
|| SotClipboardFormatId::SVG == aFlavorEx.mnSotId )
{
if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavorEx ) )
{
aFlavorEx.mnSotId = SotClipboardFormatId::GDIMETAFILE;
rDataFlavorExVector.push_back( aFlavorEx );
}
}
else if ( SotClipboardFormatId::HTML_SIMPLE == aFlavorEx.mnSotId )
{
// #104735# HTML_SIMPLE may also be inserted without comments
aFlavorEx.mnSotId = SotClipboardFormatId::HTML_NO_COMMENT;
rDataFlavorExVector.push_back( aFlavorEx );
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
{
// add, if it is a UTF-8 byte buffer
if ( xMimeType->hasParameter( aCharsetStr ) )
{
if ( xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "unicode" ) ||
xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "utf-16" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::STRING;
}
}
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/rtf" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RTF;
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/richtext" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RICHTEXT;
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/html" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::HTML;
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/uri-list" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::FILE_LIST;
}
else if ( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice-objectdescriptor-xml" ) )
{
rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::OBJECTDESCRIPTOR;
}
}
}
catch ( const css::uno::Exception& )
{
}
}
void TransferableDataHelper::InitFormats()
{
SolarMutexGuard aSolarGuard;
maFormats.clear();
mxObjDesc.reset(new TransferableObjectDescriptor);
if ( !mxTransfer.is() )
return ;
TransferableDataHelper::FillDataFlavorExVector(mxTransfer->getTransferDataFlavors(), maFormats);
for (auto const & format : maFormats)
{
if ( SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId )
{
ImplSetParameterString(*mxObjDesc, format);
auto data = GetSequence(format, {});
SvMemoryStream aSrcStm(data.getArray(), data.getLength(), StreamMode::READ);
TryReadTransferableObjectDescriptor(aSrcStm, *mxObjDesc);
break ;
}
}
}
bool TransferableDataHelper::HasFormat( SotClipboardFormatId nFormat ) const
{
SolarMutexGuard g;
return std::any_of(maFormats.begin(), maFormats.end(),
[&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
}
bool TransferableDataHelper::HasFormat( const DataFlavor& rFlavor ) const
{
SolarMutexGuard g;
for (auto const & format : maFormats)
{
if ( TransferableDataHelper::IsEqual( rFlavor, format ) )
return true ;
}
return false ;
}
sal_uInt32 TransferableDataHelper::GetFormatCount() const
{
SolarMutexGuard g;
return maFormats.size();
}
SotClipboardFormatId TransferableDataHelper::GetFormat( sal_uInt32 nFormat ) const
{
SolarMutexGuard g;
DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index" );
return ( ( nFormat < maFormats.size() ) ? maFormats[ nFormat ].mnSotId : SotClipboardFormatId::NONE );
}
DataFlavor TransferableDataHelper::GetFormatDataFlavor( sal_uInt32 nFormat ) const
{
SolarMutexGuard g;
DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index" );
DataFlavor aRet;
if (nFormat < maFormats.size())
aRet = maFormats[nFormat];
return aRet;
}
Reference< XTransferable > TransferableDataHelper::GetXTransferable() const
{
Reference< XTransferable > xRet;
if ( mxTransfer.is() )
{
try
{
xRet = mxTransfer;
// do a dummy call to check, if this interface is valid (nasty)
xRet->getTransferDataFlavors();
}
catch ( const css::uno::Exception& )
{
xRet.clear();
}
}
return xRet;
}
Any TransferableDataHelper::GetAny( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
{
Any aReturn;
DataFlavor aFlavor;
if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
aReturn = GetAny(aFlavor, rDestDoc);
return aReturn;
}
Any TransferableDataHelper::GetAny( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
{
Any aRet;
try
{
SolarMutexGuard g;
if ( mxTransfer.is() )
{
const SotClipboardFormatId nRequestFormat = SotExchange::GetFormat( rFlavor );
Reference<css::datatransfer::XTransferable2> xTransfer2(mxTransfer, UNO_QUERY);
if ( nRequestFormat != SotClipboardFormatId::NONE )
{
// try to get alien format first
for (auto const & format : maFormats)
{
if ( ( nRequestFormat == format.mnSotId ) && !rFlavor.MimeType.equalsIgnoreAsciiCase( format.MimeType ) )
{
// tdf#133365: only release solar mutex on Windows
#ifdef _WIN32
// Our own thread may handle the nested IDataObject::GetData call,
// and try to acquire solar mutex
SolarMutexReleaser r;
#endif // _WIN32
if (xTransfer2.is())
aRet = xTransfer2->getTransferData2(format, rDestDoc);
else
aRet = mxTransfer->getTransferData(format);
}
if ( aRet.hasValue() )
break ;
}
}
if ( !aRet.hasValue() )
{
// tdf#133365: only release solar mutex on Windows
#ifdef _WIN32
// Our own thread may handle the nested IDataObject::GetData call,
// and try to acquire solar mutex
SolarMutexReleaser r;
#endif // _WIN32
if (xTransfer2.is())
aRet = xTransfer2->getTransferData2(rFlavor, rDestDoc);
else
aRet = mxTransfer->getTransferData(rFlavor);
}
}
}
catch ( const css::uno::Exception& )
{
}
return aRet;
}
bool TransferableDataHelper::GetString( SotClipboardFormatId nFormat, OUString& rStr ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetString( aFlavor, rStr ) );
}
bool TransferableDataHelper::GetString( const DataFlavor& rFlavor, OUString& rStr ) const
{
Any aAny = GetAny(rFlavor, OUString());
bool bRet = false ;
if ( aAny.hasValue() )
{
OUString aOUString;
Sequence< sal_Int8 > aSeq;
if ( aAny >>= aOUString )
{
rStr = aOUString;
bRet = true ;
}
else if ( aAny >>= aSeq )
{
const char * pChars = reinterpret_cast < const char * >( aSeq.getConstArray() );
sal_Int32 nLen = aSeq.getLength();
//JP 10.10.2001: 92930 - don't copy the last zero character into the string.
//DVO 2002-05-27: strip _all_ trailing zeros
while ( nLen && ( 0 == *( pChars + nLen - 1 ) ) )
--nLen;
rStr = OUString( pChars, nLen, osl_getThreadTextEncoding() );
bRet = true ;
}
}
return bRet;
}
bool TransferableDataHelper::GetBitmapEx( SotClipboardFormatId nFormat, BitmapEx& rBmpEx ) const
{
if (SotClipboardFormatId::BITMAP == nFormat)
{
// try to get PNG first
DataFlavor aFlavor;
if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
{
if (GetBitmapEx(aFlavor, rBmpEx))
{
return true ;
}
}
// then JPEG
if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor))
{
if (GetBitmapEx(aFlavor, rBmpEx))
{
return true ;
}
}
}
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetBitmapEx( aFlavor, rBmpEx ) );
}
bool TransferableDataHelper::GetBitmapEx( const DataFlavor& rFlavor, BitmapEx& rBmpEx ) const
{
std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor);
DataFlavor aSubstFlavor;
bool bRet(xStm);
bool bSuppressPNG(false ); // #122982# If PNG stream not accessed, but BMP one, suppress trying to load PNG
bool bSuppressJPEG(false );
if (!bRet && HasFormat(SotClipboardFormatId::PNG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aSubstFlavor))
{
// when no direct success, try if PNG is available
xStm = GetSotStorageStream(aSubstFlavor);
bRet = bool (xStm);
bSuppressJPEG = bRet;
}
if (!bRet && HasFormat(SotClipboardFormatId::JPEG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aSubstFlavor))
{
xStm = GetSotStorageStream(aSubstFlavor);
bRet = bool (xStm);
bSuppressPNG = bRet;
}
if (!bRet && HasFormat(SotClipboardFormatId::BMP) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor))
{
// when no direct success, try if BMP is available
xStm = GetSotStorageStream(aSubstFlavor);
bRet = bool (xStm);
bSuppressPNG = bRet;
bSuppressJPEG = bRet;
}
if (bRet)
{
if (!bSuppressPNG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/png" ))
{
// it's a PNG, import to BitmapEx
vcl::PngImageReader aPNGReader(*xStm);
rBmpEx = aPNGReader.read();
}
else if (!bSuppressJPEG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/jpeg" ))
{
// it's a JPEG, import to BitmapEx
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
Graphic aGraphic;
if (rFilter.ImportGraphic(aGraphic, u"" , *xStm) == ERRCODE_NONE)
rBmpEx = aGraphic.GetBitmapEx();
}
if (rBmpEx.IsEmpty())
{
Bitmap aBitmap;
AlphaMask aMask;
// explicitly use Bitmap::Read with bFileHeader = sal_True
// #i124085# keep DIBV5 for read from clipboard, but should not happen
ReadDIBV5(aBitmap, aMask, *xStm);
if (aMask.GetBitmap().IsEmpty())
{
rBmpEx = aBitmap;
}
else
{
rBmpEx = BitmapEx(aBitmap, aMask);
}
}
bRet = (ERRCODE_NONE == xStm->GetError() && !rBmpEx.IsEmpty());
/* SJ: #110748# At the moment we are having problems with DDB inserted as DIB. The
problem is , that some graphics are inserted much too big because the nXPelsPerMeter
and nYPelsPerMeter of the bitmap fileheader isn ' t including the correct value .
Due to this reason the following code assumes that bitmaps with a logical size
greater than 50 cm aren ' t having the correct mapmode set .
The following code should be removed if DDBs and DIBs are supported via clipboard
properly .
*/
if (bRet)
{
const MapMode aMapMode(rBmpEx.GetPrefMapMode());
if (MapUnit::MapPixel != aMapMode.GetMapUnit())
{
const Size aSize(OutputDevice::LogicToLogic(rBmpEx.GetPrefSize(), aMapMode, MapMode(MapUnit::Map100thMM)));
// #i122388# This wrongly corrects in the given case; changing from 5000 100th mm to
// the described 50 cm (which is 50000 100th mm)
if ((aSize.Width() > 50000 ) || (aSize.Height() > 50000 ))
{
rBmpEx.SetPrefMapMode(MapMode(MapUnit::MapPixel));
// #i122388# also adapt size by applying the mew MapMode
const Size aNewSize(o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::pt));
rBmpEx.SetPrefSize(aNewSize);
}
}
}
}
return bRet;
}
bool TransferableDataHelper::GetGDIMetaFile(SotClipboardFormatId nFormat, GDIMetaFile& rMtf, size_t nMaxActions) const
{
DataFlavor aFlavor;
return SotExchange::GetFormatDataFlavor(nFormat, aFlavor) &&
GetGDIMetaFile(aFlavor, rMtf) &&
(nMaxActions == 0 || rMtf.GetActionSize() < nMaxActions);
}
bool TransferableDataHelper::GetGDIMetaFile( const DataFlavor& rFlavor, GDIMetaFile& rMtf ) const
{
std::unique_ptr<SvStream> xStm;
DataFlavor aSubstFlavor;
bool bRet = false ;
if ( (xStm = GetSotStorageStream( rFlavor )) )
{
SvmReader aReader( *xStm );
aReader.Read( rMtf );
bRet = ( xStm->GetError() == ERRCODE_NONE );
}
if ( !bRet &&
HasFormat( SotClipboardFormatId::EMF ) &&
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
(xStm = GetSotStorageStream( aSubstFlavor)) )
{
Graphic aGraphic;
if ( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
{
rMtf = aGraphic.GetGDIMetaFile();
bRet = true ;
}
}
if ( !bRet &&
HasFormat( SotClipboardFormatId::WMF ) &&
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
(xStm = GetSotStorageStream( aSubstFlavor ) ) )
{
Graphic aGraphic;
if ( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
{
rMtf = aGraphic.GetGDIMetaFile();
bRet = true ;
}
}
return bRet;
}
bool TransferableDataHelper::GetGraphic( SotClipboardFormatId nFormat, Graphic& rGraphic ) const
{
if (SotClipboardFormatId::BITMAP == nFormat)
{
// try to get PNG first
DataFlavor aFlavor;
if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
{
if (GetGraphic(aFlavor, rGraphic))
{
return true ;
}
}
}
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetGraphic( aFlavor, rGraphic ) );
}
bool TransferableDataHelper::GetGraphic( const css::datatransfer::DataFlavor& rFlavor, Graphic& rGraphic ) const
{
DataFlavor aFlavor;
bool bRet = false ;
if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor) &&
TransferableDataHelper::IsEqual(aFlavor, rFlavor))
{
// try to get PNG first
BitmapEx aBmpEx;
bRet = GetBitmapEx( aFlavor, aBmpEx );
if ( bRet )
rGraphic = aBmpEx;
}
else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PDF, aFlavor) &&
TransferableDataHelper::IsEqual(aFlavor, rFlavor))
{
Graphic aGraphic;
if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor))
{
if (GraphicConverter::Import(*xStm, aGraphic) == ERRCODE_NONE)
{
rGraphic = std::move(aGraphic);
bRet = true ;
}
}
}
else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor) && TransferableDataHelper::IsEqual(aFlavor, rFlavor))
{
BitmapEx aBitmapEx;
bRet = GetBitmapEx(aFlavor, aBitmapEx);
if (bRet)
rGraphic = aBitmapEx;
}
else if (SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavor ) &&
TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
{
BitmapEx aBmpEx;
bRet = GetBitmapEx( aFlavor, aBmpEx );
if ( bRet )
rGraphic = aBmpEx;
}
else if ( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavor ) &&
TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
{
GDIMetaFile aMtf;
bRet = GetGDIMetaFile( aFlavor, aMtf );
if ( bRet )
rGraphic = aMtf;
}
else
{
if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor) )
{
TypeSerializer aSerializer(*xStm);
aSerializer.readGraphic(rGraphic);
bRet = ( xStm->GetError() == ERRCODE_NONE );
}
}
return bRet;
}
bool TransferableDataHelper::GetImageMap( SotClipboardFormatId nFormat, ImageMap& rIMap ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetImageMap( aFlavor, rIMap ) );
}
bool TransferableDataHelper::GetImageMap( const css::datatransfer::DataFlavor& rFlavor, ImageMap& rIMap ) const
{
std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor);
if (!xStm)
return false ;
rIMap.Read( *xStm );
bool bRet = ( xStm->GetError() == ERRCODE_NONE );
return bRet;
}
bool TransferableDataHelper::GetTransferableObjectDescriptor( SotClipboardFormatId nFormat, TransferableObjectDescriptor& rDesc ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetTransferableObjectDescriptor( rDesc ) );
}
bool TransferableDataHelper::GetTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc ) const
{
rDesc = *mxObjDesc;
return true ;
}
bool TransferableDataHelper::GetINetBookmark( SotClipboardFormatId nFormat, INetBookmark& rBmk ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetBookmark( aFlavor, rBmk ) );
}
bool TransferableDataHelper::GetINetBookmark( const css::datatransfer::DataFlavor& rFlavor, INetBookmark& rBmk ) const
{
if ( !HasFormat( rFlavor ))
return false ;
bool bRet = false ;
const SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
switch ( nFormat )
{
case SotClipboardFormatId::SOLK:
case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
{
OUString aString;
if ( GetString( rFlavor, aString ) )
{
if ( SotClipboardFormatId::UNIFORMRESOURCELOCATOR == nFormat )
{
rBmk = INetBookmark( aString, aString );
bRet = true ;
}
else
{
OUString aURL, aDesc;
sal_Int32 nStart = aString.indexOf( '@' ), nLen = aString.toInt32();
if ( !nLen && aString[ 0 ] != '0' )
{
SAL_INFO( "svtools" , "SOLK: 1. len=0" );
}
if ( nStart == -1 || nLen > aString.getLength() - nStart - 3 )
{
SAL_INFO( "svtools" , "SOLK: 1. illegal start or wrong len" );
}
aURL = aString.copy( nStart + 1 , nLen );
aString = aString.replaceAt( 0 , nStart + 1 + nLen, u"" );
nStart = aString.indexOf( '@' );
nLen = aString.toInt32();
if ( !nLen && aString[ 0 ] != '0' )
{
SAL_INFO( "svtools" , "SOLK: 2. len=0" );
}
if ( nStart == -1 || nLen > aString.getLength() - nStart - 1 )
{
SAL_INFO( "svtools" , "SOLK: 2. illegal start or wrong len" );
}
aDesc = aString.copy( nStart+1 , nLen );
rBmk = INetBookmark( aURL, aDesc );
bRet = true ;
}
}
}
break ;
case SotClipboardFormatId::NETSCAPE_BOOKMARK:
{
Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
if (2048 == aSeq.getLength())
{
const char * p1 = reinterpret_cast < const char * >( aSeq.getConstArray() );
const char * p2 = reinterpret_cast < const char * >( aSeq.getConstArray() ) + 1024 ;
rBmk = INetBookmark( OUString( p1, strlen(p1), osl_getThreadTextEncoding() ),
OUString( p2, strlen(p2), osl_getThreadTextEncoding() ) );
bRet = true ;
}
}
break ;
#ifdef _WIN32
case SotClipboardFormatId::FILEGRPDESCRIPTOR:
{
Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
if (aSeq.getLength())
{
FILEGROUPDESCRIPTORW const * pFDesc = reinterpret_cast <FILEGROUPDESCRIPTORW const *>(aSeq.getConstArray());
if ( pFDesc->cItems )
{
OUString aDesc( o3tl::toU(pFDesc->fgd[ 0 ].cFileName) );
if ( ( aDesc.getLength() > 4 ) && aDesc.endsWithIgnoreAsciiCase(".URL" ) )
{
std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( INetURLObject( aDesc ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
StreamMode::STD_READ ));
if ( !pStream || pStream->GetError() )
{
DataFlavor aFileContentFlavor;
aSeq.realloc( 0 );
pStream.reset();
if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::FILECONTENT, aFileContentFlavor))
{
aSeq = GetSequence(aFileContentFlavor, OUString());
if (aSeq.getLength())
pStream.reset(new SvMemoryStream( const_cast <sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), StreamMode::STD_READ ));
}
}
if ( pStream )
{
OString aLine;
bool bInA = false , bInW = false , bAFound = false ;
while ( pStream->ReadLine( aLine ) )
{
if (aLine.startsWithIgnoreAsciiCase("[InternetShortcut" , &aLine))
{
// May be [InternetShortcut], or [InternetShortcut.A], or
// [InternetShortcut.W] (the latter has UTF-7-encoded URL)
bInW = aLine.equalsIgnoreAsciiCase(".W]" );
bInA = !bAFound && !bInW
&& (aLine == "]" || aLine.equalsIgnoreAsciiCase(".A]" ));
}
else if (aLine.startsWith("[" ))
{
bInA = bInW = false ;
}
else if ((bInA || bInW) && aLine.startsWithIgnoreAsciiCase("URL=" ))
{
auto eTextEncoding = bInW ? RTL_TEXTENCODING_UTF7
: osl_getThreadTextEncoding();
rBmk = INetBookmark( OStringToOUString(aLine.subView(4 ), eTextEncoding),
aDesc.copy(0 , aDesc.getLength() - 4 ) );
bRet = true ;
if (bInW)
break ;
else
bAFound = true ; // Keep looking for "W"
}
}
}
}
}
}
}
break ;
#endif
default : break ;
}
return bRet;
}
bool TransferableDataHelper::GetINetImage( SotClipboardFormatId nFormat,
INetImage& rINtImg ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetImage( aFlavor, rINtImg ) );
}
bool TransferableDataHelper::GetINetImage(
const css::datatransfer::DataFlavor& rFlavor,
INetImage& rINtImg ) const
{
std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor );
if (!xStm)
return false ;
bool bRet = rINtImg.Read( *xStm, SotExchange::GetFormat( rFlavor ) );
return bRet;
}
bool TransferableDataHelper::GetFileList( SotClipboardFormatId nFormat,
FileList& rFileList ) const
{
DataFlavor aFlavor;
return ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetFileList( rFileList ) );
}
bool TransferableDataHelper::GetFileList( FileList& rFileList ) const
{
bool bRet = false ;
for ( sal_uInt32 i = 0 , nFormatCount = GetFormatCount(); ( i < nFormatCount ) && !bRet; ++i )
{
if ( SotClipboardFormatId::FILE_LIST == GetFormat( i ) )
{
const DataFlavor aFlavor( GetFormatDataFlavor( i ) );
if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( aFlavor ))
{
if ( aFlavor.MimeType.indexOf( "text/uri-list" ) > -1 )
{
OStringBuffer aDiskString;
while ( xStm->ReadLine( aDiskString ) )
if ( !aDiskString.isEmpty() && aDiskString[0 ] != '#' )
rFileList.AppendFile( OStringToOUString(aDiskString, RTL_TEXTENCODING_UTF8) );
bRet = true ;
}
else
bRet = ( ReadFileList( *xStm, rFileList ).GetError() == ERRCODE_NONE );
}
}
}
return bRet;
}
Sequence<sal_Int8> TransferableDataHelper::GetSequence( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
{
DataFlavor aFlavor;
if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
return Sequence<sal_Int8>();
return GetSequence(aFlavor, rDestDoc);
}
Sequence<sal_Int8> TransferableDataHelper::GetSequence( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
{
const Any aAny = GetAny(rFlavor, rDestDoc);
Sequence<sal_Int8> aSeq;
if (aAny.hasValue())
aAny >>= aSeq;
return aSeq;
}
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( SotClipboardFormatId nFormat ) const
{
DataFlavor aFlavor;
if (!SotExchange::GetFormatDataFlavor( nFormat, aFlavor ))
return nullptr;
return GetSotStorageStream( aFlavor );
}
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( const DataFlavor& rFlavor ) const
{
Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
if (!aSeq.hasElements())
return nullptr;
std::unique_ptr<SvStream> xStream = SotTempStream::Create( u"" _ustr );
xStream->WriteBytes( aSeq.getConstArray(), aSeq.getLength() );
xStream->Seek(0 );
return xStream;
}
Reference<XInputStream> TransferableDataHelper::GetInputStream( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
{
DataFlavor aFlavor;
if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
return Reference<XInputStream>();
return GetInputStream(aFlavor, rDestDoc);
}
Reference<XInputStream> TransferableDataHelper::GetInputStream( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
{
Sequence<sal_Int8> aSeq = GetSequence(rFlavor, rDestDoc);
if (!aSeq.hasElements())
return Reference<XInputStream>();
Reference<XInputStream> xStream(new comphelper::SequenceInputStream(aSeq));
return xStream;
}
void TransferableDataHelper::Rebind( const Reference< XTransferable >& _rxNewContent )
{
mxTransfer = _rxNewContent;
InitFormats();
}
bool TransferableDataHelper::StartClipboardListening( )
{
SolarMutexGuard g;
StopClipboardListening( );
mxClipboardListener = new TransferableClipboardNotifier(mxClipboard, *this );
return mxClipboardListener->isListening();
}
void TransferableDataHelper::StopClipboardListening( )
{
SolarMutexGuard g;
if (mxClipboardListener.is())
{
mxClipboardListener->dispose();
mxClipboardListener.clear();
}
}
TransferableDataHelper TransferableDataHelper::CreateFromClipboard(const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& rClipboard)
{
TransferableDataHelper aRet;
if ( rClipboard.is() )
{
try
{
Reference< XTransferable > xTransferable( rClipboard->getContents() );
if ( xTransferable.is() )
{
aRet = TransferableDataHelper( xTransferable );
// also copy the clipboard
aRet.mxClipboard = rClipboard;
}
}
catch ( const css::uno::Exception& )
{
}
}
return aRet;
}
TransferableDataHelper TransferableDataHelper::CreateFromSystemClipboard( vcl::Window * pWindow )
{
DBG_ASSERT( pWindow, "Window pointer is NULL" );
Reference< XClipboard > xClipboard;
if ( pWindow )
xClipboard = pWindow->GetClipboard();
return CreateFromClipboard(xClipboard);
}
TransferableDataHelper TransferableDataHelper::CreateFromPrimarySelection()
{
Reference< XClipboard > xSelection(GetSystemPrimarySelection());
TransferableDataHelper aRet;
if ( xSelection.is() )
{
SolarMutexReleaser aReleaser;
try
{
Reference< XTransferable > xTransferable( xSelection->getContents() );
if ( xTransferable.is() )
{
aRet = TransferableDataHelper( xTransferable );
aRet.mxClipboard = std::move(xSelection);
}
}
catch ( const css::uno::Exception& )
{
}
}
return aRet;
}
bool TransferableDataHelper::IsEqual( const css::datatransfer::DataFlavor& rInternalFlavor,
const css::datatransfer::DataFlavor& rRequestFlavor )
{
const Reference< XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
bool bRet = false ;
try
{
Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
Reference< XMimeContentType > xRequestType1( xMimeFact->createMimeContentType( rInternalFlavor.MimeType ) );
Reference< XMimeContentType > xRequestType2( xMimeFact->createMimeContentType( rRequestFlavor.MimeType ) );
if ( xRequestType1.is() && xRequestType2.is() )
{
if ( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( xRequestType2->getFullMediaType() ) )
{
if ( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
{
// special handling for text/plain media types
static constexpr OUString aCharsetString( u"charset" _ustr );
if ( !xRequestType2->hasParameter( aCharsetString ) ||
xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "utf-16" ) ||
xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "unicode" ) )
{
bRet = true ;
}
}
else if ( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice" ) )
{
// special handling for application/x-openoffice media types
static constexpr OUString aFormatString( u"windows_formatname" _ustr );
if ( xRequestType1->hasParameter( aFormatString ) &&
xRequestType2->hasParameter( aFormatString ) &&
xRequestType1->getParameterValue( aFormatString ).equalsIgnoreAsciiCase( xRequestType2->getParameterValue( aFormatString ) ) )
{
bRet = true ;
}
}
else
bRet = true ;
}
}
}
catch ( const css::uno::Exception& )
{
bRet = rInternalFlavor.MimeType.equalsIgnoreAsciiCase( rRequestFlavor.MimeType );
}
return bRet;
}
static bool WriteDDELink_impl(SvStream& stream, rtl_TextEncoding encoding,
std::u16string_view application, std::u16string_view topic,
std::u16string_view item, std::u16string_view extra)
{
// Put Link format, potentially with extra: either application\0topic\0item\0\0
// or application\0topic\0item\0extra\0\0
assert(rtl_isOctetTextEncoding(encoding)); // The Link format is 8-bit
stream.WriteUnicodeOrByteText(application, encoding, true );
stream.WriteUnicodeOrByteText(topic, encoding, true );
stream.WriteUnicodeOrByteText(item, encoding, true );
if (!extra.empty())
stream.WriteUnicodeOrByteText(extra, encoding, true );
stream.WriteChar('\0' ); // One more trailing zero
return stream.GetErrorCode() == ERRCODE_NONE;
}
// static
bool TransferableDataHelper::WriteDDELink(SvStream& stream, std::u16string_view application,
std::u16string_view topic, std::u16string_view item,
std::u16string_view extra)
{
// 1. Put standard Link format in the system encoding
WriteDDELink_impl(stream, osl_getThreadTextEncoding(), application, topic, item, extra);
// 2. Put our extension to the format. Start with a magic string "ULnk", followed by
// UTF-8-encoded Link format
stream.WriteOString("ULnk" );
return WriteDDELink_impl(stream, RTL_TEXTENCODING_UTF8, application, topic, item, extra);
}
static size_t ReadDDELink_impl(std::string_view str, std::string_view& application,
std::string_view& topic, std::string_view& item,
std::string_view& rest)
{
application = {};
topic = {};
item = {};
rest = {};
size_t start = 0 ;
size_t end = str.find('\0' , start);
application = str.substr(start, end - start);
if (end == std::string_view::npos)
return end;
start = end + 1 ;
end = str.find('\0' , start);
topic = str.substr(start, end - start);
if (end == std::string_view::npos)
return end;
start = end + 1 ;
end = str.find('\0' , start);
item = str.substr(start, end - start);
if (end >= str.size() - 1 || str[end + 1 ] == '\0' )
return end;
start = end + 1 ;
do
{
// Read all remaining data until \0\0 into rest, including possible single \0
end = str.find('\0' , end + 1 );
} while (end < str.size() - 1 && str[end + 1 ] != '\0' );
rest = str.substr(start, end - start);
return end;
}
bool TransferableDataHelper::ReadDDELink(OUString& application, OUString& topic, OUString& item,
OUString& rest) const
{
css::uno::Sequence<sal_Int8> sequence = GetSequence(SotClipboardFormatId::LINK, {});
if (!sequence.hasElements())
{
SAL_WARN("svtools" , "DDE data not found" );
return false ;
}
std::string_view str(reinterpret_cast <const char *>(sequence.getConstArray()),
sequence.getLength());
std::string_view application_view, topic_view, item_view, rest_view;
rtl_TextEncoding encoding = osl_getThreadTextEncoding();
size_t end = ReadDDELink_impl(str, application_view, topic_view, item_view, rest_view);
if (end < str.size() - 1 && str[end + 1 ] == '\0' && str.substr(end + 2 , 4 ) == "ULnk" )
{
// Now try to read our UTF-8 extension; if it's present, use it unconditionally, even if
// osl_getThreadTextEncoding gives RTL_TEXTENCODING_UTF8, because the extension is immune
// to possible different source encoding
encoding = RTL_TEXTENCODING_UTF8;
ReadDDELink_impl(str.substr(end + 6 ), application_view, topic_view, item_view, rest_view);
}
application = OStringToOUString(application_view, encoding);
topic = OStringToOUString(topic_view, encoding);
item = OStringToOUString(item_view, encoding);
rest = OStringToOUString(rest_view, encoding);
return !application.isEmpty() && !topic.isEmpty() && !item.isEmpty();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=97 H=97 G=96
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298