Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/treelist/transfer.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#ifdef _WIN32
21
#include <prewin.h>
22
#include <postwin.h>
23
#include <shlobj.h>
24
#endif
25
#include <o3tl/char16_t2wchar_t.hxx>
26
#include <rtl/uri.hxx>
27
#include <rtl/tencinfo.h>
28
#include <sal/log.hxx>
29
#include <tools/debug.hxx>
30
#include <tools/mapunit.hxx>
31
#include <tools/urlobj.hxx>
32
#include <unotools/ucbstreamhelper.hxx>
33
#include <sot/exchange.hxx>
34
#include <sot/storage.hxx>
35
#include <vcl/alpha.hxx>
36
#include <vcl/bitmap.hxx>
37
#include <vcl/filter/SvmReader.hxx>
38
#include <vcl/filter/SvmWriter.hxx>
39
#include <vcl/gdimtf.hxx>
40
#include <vcl/graph.hxx>
41
#include <vcl/cvtgrf.hxx>
42
#include <vcl/svapp.hxx>
43
#include <vcl/window.hxx>
44
#include <comphelper/fileformat.h>
45
#include <comphelper/processfactory.hxx>
46
#include <comphelper/scopeguard.hxx>
47
#include <comphelper/servicehelper.hxx>
48
#include <comphelper/sequence.hxx>
49
#include <sot/filelist.hxx>
50
#include <cppuhelper/implbase.hxx>
51
52
#include <comphelper/seqstream.hxx>
53
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
54
#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
55
#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
56
#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
57
#include <com/sun/star/datatransfer/XMimeContentType.hpp>
58
#include <com/sun/star/datatransfer/XTransferable2.hpp>
59
#include <com/sun/star/frame/Desktop.hpp>
60
61
#include <svl/urlbmk.hxx>
62
#include <vcl/inetimg.hxx>
63
#include <vcl/wmf.hxx>
64
#include <vcl/imap.hxx>
65
#include <vcl/transfer.hxx>
66
#include <rtl/strbuf.hxx>
67
#include <cstdio>
68
#include <vcl/dibtools.hxx>
69
#include <vcl/filter/PngImageReader.hxx>
70
#include <vcl/filter/PngImageWriter.hxx>
71
#include <vcl/graphicfilter.hxx>
72
#include <memory>
73
#include <utility>
74
#include <vcl/TypeSerializer.hxx>
75
76
using namespace ::com::sun::star::uno;
77
using namespace ::com::sun::star::lang;
78
using namespace ::com::sun::star::frame;
79
using namespace ::com::sun::star::io;
80
using namespace ::com::sun::star::datatransfer;
81
using namespace ::com::sun::star::datatransfer::clipboard;
82
using namespace ::com::sun::star::datatransfer::dnd;
83
using namespace std::literals::string_view_literals;
84
85
86
0
#define TOD_SIG1 0x01234567
87
0
#define TOD_SIG2 0x89abcdef
88
89
SvStream& WriteTransferableObjectDescriptor( SvStream& rOStm, const TransferableObjectDescriptor& rObjDesc )
90
0
{
91
0
    const sal_uInt64    nFirstPos = rOStm.Tell();
92
0
    const sal_uInt32    nViewAspect = rObjDesc.mnViewAspect;
93
0
    const sal_uInt32    nSig1 = TOD_SIG1, nSig2 = TOD_SIG2;
94
95
0
    rOStm.SeekRel( 4 );
96
0
    WriteSvGlobalName( rOStm, rObjDesc.maClassName );
97
0
    rOStm.WriteUInt32( nViewAspect );
98
0
    rOStm.WriteInt32( rObjDesc.maSize.Width() );
99
0
    rOStm.WriteInt32( rObjDesc.maSize.Height() );
100
0
    rOStm.WriteInt32( rObjDesc.maDragStartPos.X() );
101
0
    rOStm.WriteInt32( rObjDesc.maDragStartPos.Y() );
102
0
    rOStm.WriteUniOrByteString( rObjDesc.maTypeName );
103
0
    rOStm.WriteUniOrByteString( rObjDesc.maDisplayName );
104
0
    rOStm.WriteUInt32( nSig1 ).WriteUInt32( nSig2 );
105
106
0
    const sal_uInt64 nLastPos = rOStm.Tell();
107
108
0
    rOStm.Seek( nFirstPos );
109
0
    rOStm.WriteUInt32( nLastPos - nFirstPos  );
110
0
    rOStm.Seek( nLastPos );
111
112
0
    return rOStm;
113
0
}
114
115
static void TryReadTransferableObjectDescriptor(SvStream& rIStm,
116
                                                TransferableObjectDescriptor& rObjDesc)
117
0
{
118
0
    auto nStartPos = rIStm.Tell();
119
0
    comphelper::ScopeGuard streamPosRestore([nStartPos, &rIStm] { rIStm.Seek(nStartPos); });
120
121
0
    sal_uInt32 size;
122
0
    rIStm.ReadUInt32(size);
123
124
0
    SvGlobalName className;
125
0
    rIStm >> className;
126
127
0
    sal_uInt32 viewAspect;
128
0
    rIStm.ReadUInt32(viewAspect);
129
130
0
    sal_Int32 width, height;
131
0
    rIStm.ReadInt32(width).ReadInt32(height);
132
133
0
    sal_Int32 dragStartPosX, dragStartPosY;
134
0
    rIStm.ReadInt32(dragStartPosX).ReadInt32(dragStartPosY);
135
136
0
    const OUString typeName = rIStm.ReadUniOrByteString();
137
0
    const OUString displayName = rIStm.ReadUniOrByteString();
138
139
0
    sal_uInt32 nSig1, nSig2;
140
0
    rIStm.ReadUInt32(nSig1).ReadUInt32(nSig2);
141
142
0
    if (!rIStm.good() || rIStm.Tell() - nStartPos != size || nSig1 != TOD_SIG1 || nSig2 != TOD_SIG2)
143
0
        return;
144
145
0
    rObjDesc.maClassName = className;
146
0
    rObjDesc.mnViewAspect = viewAspect;
147
0
    rObjDesc.maSize = Size(width, height);
148
0
    rObjDesc.maDragStartPos = Point(dragStartPosX, dragStartPosY);
149
0
    rObjDesc.maTypeName = typeName;
150
0
    rObjDesc.maDisplayName = displayName;
151
0
}
152
153
// the reading of the parameter is done using the special service css::datatransfer::MimeContentType,
154
// a similar approach should be implemented for creation of the mimetype string;
155
// for now the set of acceptable characters has to be hardcoded, in future it should be part of the service that creates the mimetype
156
157
static OUString ImplGetParameterString( const TransferableObjectDescriptor& rObjDesc )
158
0
{
159
0
    const OUString   aClassName( rObjDesc.maClassName.GetHexName() );
160
0
    OUString         aParams;
161
162
0
    if( !aClassName.isEmpty() )
163
0
    {
164
0
        aParams += ";classname=\"" + aClassName + "\"";
165
0
    }
166
167
0
    if( !rObjDesc.maTypeName.isEmpty() )
168
0
    {
169
0
        aParams += ";typename=\""  + rObjDesc.maTypeName + "\"";
170
0
    }
171
172
0
    if( !rObjDesc.maDisplayName.isEmpty() )
173
0
    {
174
        // the display name might contain unacceptable characters, encode all of them
175
        // this seems to be the only parameter currently that might contain such characters
176
0
        static constexpr auto pToAccept = rtl::createUriCharClass(
177
0
            u8"()<>@,;:/[]?=!#$&'*+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~. ");
178
179
0
        aParams += ";displayname=\""
180
0
            + rtl::Uri::encode(
181
0
                rObjDesc.maDisplayName, pToAccept.data(), rtl_UriEncodeIgnoreEscapes,
182
0
                RTL_TEXTENCODING_UTF8)
183
0
            + "\"";
184
0
    }
185
186
0
    aParams += ";viewaspect=\"" + OUString::number(rObjDesc.mnViewAspect)
187
0
        + "\";width=\"" + OUString::number(rObjDesc.maSize.Width())
188
0
        + "\";height=\"" + OUString::number(rObjDesc.maSize.Height())
189
0
        + "\";posx=\"" + OUString::number(rObjDesc.maDragStartPos.X())
190
0
        + "\";posy=\"" + OUString::number(rObjDesc.maDragStartPos.X()) + "\"";
191
192
0
    return aParams;
193
0
}
194
195
196
static void ImplSetParameterString( TransferableObjectDescriptor& rObjDesc, const DataFlavorEx& rFlavorEx )
197
0
{
198
0
    const Reference< XComponentContext >&       xContext( ::comphelper::getProcessComponentContext() );
199
200
0
    try
201
0
    {
202
0
        Reference< XMimeContentTypeFactory >  xMimeFact = MimeContentTypeFactory::create( xContext );
203
204
0
        Reference< XMimeContentType > xMimeType( xMimeFact->createMimeContentType( rFlavorEx.MimeType ) );
205
206
0
        if( xMimeType.is() )
207
0
        {
208
0
            static constexpr OUString aClassNameString( u"classname"_ustr );
209
0
            static constexpr OUString aTypeNameString( u"typename"_ustr );
210
0
            static constexpr OUString aDisplayNameString( u"displayname"_ustr );
211
0
            static constexpr OUString aViewAspectString( u"viewaspect"_ustr );
212
0
            static constexpr OUString aWidthString( u"width"_ustr );
213
0
            static constexpr OUString aHeightString( u"height"_ustr );
214
0
            static constexpr OUString aPosXString( u"posx"_ustr );
215
0
            static constexpr OUString aPosYString( u"posy"_ustr );
216
217
0
            if( xMimeType->hasParameter( aClassNameString ) )
218
0
            {
219
0
                rObjDesc.maClassName.MakeId( xMimeType->getParameterValue( aClassNameString ) );
220
0
            }
221
222
0
            if( xMimeType->hasParameter( aTypeNameString ) )
223
0
            {
224
0
                rObjDesc.maTypeName = xMimeType->getParameterValue( aTypeNameString );
225
0
            }
226
227
0
            if( xMimeType->hasParameter( aDisplayNameString ) )
228
0
            {
229
                // the display name might contain unacceptable characters, in this case they should be encoded
230
                // this seems to be the only parameter currently that might contain such characters
231
0
                rObjDesc.maDisplayName = ::rtl::Uri::decode( xMimeType->getParameterValue( aDisplayNameString ), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
232
0
            }
233
234
0
            if( xMimeType->hasParameter( aViewAspectString ) )
235
0
            {
236
0
                rObjDesc.mnViewAspect = static_cast< sal_uInt16 >( xMimeType->getParameterValue( aViewAspectString ).toInt32() );
237
0
            }
238
239
0
            if( xMimeType->hasParameter( aWidthString ) )
240
0
            {
241
0
                rObjDesc.maSize.setWidth( xMimeType->getParameterValue( aWidthString ).toInt32() );
242
0
            }
243
244
0
            if( xMimeType->hasParameter( aHeightString ) )
245
0
            {
246
0
                rObjDesc.maSize.setHeight( xMimeType->getParameterValue( aHeightString ).toInt32() );
247
0
            }
248
249
0
            if( xMimeType->hasParameter( aPosXString ) )
250
0
            {
251
0
                rObjDesc.maDragStartPos.setX( xMimeType->getParameterValue( aPosXString ).toInt32() );
252
0
            }
253
254
0
            if( xMimeType->hasParameter( aPosYString ) )
255
0
            {
256
0
                rObjDesc.maDragStartPos.setY( xMimeType->getParameterValue( aPosYString ).toInt32() );
257
0
            }
258
0
        }
259
0
    }
260
0
    catch( const css::uno::Exception& )
261
0
    {
262
0
    }
263
0
}
264
265
266
TransferableHelper::TerminateListener::TerminateListener( TransferableHelper& rTransferableHelper ) :
267
0
    mrParent( rTransferableHelper )
268
0
{
269
0
}
270
271
272
TransferableHelper::TerminateListener::~TerminateListener()
273
0
{
274
0
}
275
276
277
void SAL_CALL TransferableHelper::TerminateListener::disposing( const EventObject& )
278
0
{
279
0
}
280
281
282
void SAL_CALL TransferableHelper::TerminateListener::queryTermination( const EventObject& )
283
0
{
284
0
}
285
286
287
void SAL_CALL TransferableHelper::TerminateListener::notifyTermination( const EventObject& )
288
0
{
289
0
    mrParent.ImplFlush();
290
0
    mrParent.mxTerminateListener.clear();
291
0
}
292
293
OUString SAL_CALL TransferableHelper::TerminateListener::getImplementationName()
294
0
{
295
0
    return u"com.sun.star.comp.svt.TransferableHelperTerminateListener"_ustr;
296
0
}
297
298
sal_Bool SAL_CALL TransferableHelper::TerminateListener::supportsService(const OUString& /*rServiceName*/)
299
0
{
300
0
    return false;
301
0
}
302
303
css::uno::Sequence<OUString> TransferableHelper::TerminateListener::getSupportedServiceNames()
304
0
{
305
0
    return {};
306
0
}
307
308
TransferableHelper::~TransferableHelper()
309
0
{
310
0
    rtl::Reference< TerminateListener > listener;
311
0
    {
312
0
        const SolarMutexGuard aGuard;
313
0
        std::swap(listener, mxTerminateListener);
314
0
    }
315
0
    if (listener.is()) {
316
0
        Desktop::create(comphelper::getProcessComponentContext())->removeTerminateListener(
317
0
            listener);
318
0
    }
319
0
}
320
321
Any SAL_CALL TransferableHelper::getTransferData( const DataFlavor& rFlavor )
322
0
{
323
0
    return getTransferData2(rFlavor, OUString());
324
0
}
325
326
Any SAL_CALL TransferableHelper::getTransferData2( const DataFlavor& rFlavor, const OUString& rDestDoc )
327
0
{
328
0
    if( !maAny.hasValue() || maFormats.empty() || ( maLastFormat != rFlavor.MimeType ) )
329
0
    {
330
0
        const SolarMutexGuard aGuard;
331
332
0
        maLastFormat = rFlavor.MimeType;
333
0
        maAny = Any();
334
335
0
        try
336
0
        {
337
0
            DataFlavor  aSubstFlavor;
338
0
            bool        bDone = false;
339
340
            // add formats if not already done
341
0
            if (maFormats.empty())
342
0
                AddSupportedFormats();
343
344
            // check alien formats first and try to get a substitution format
345
0
            if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aSubstFlavor ) &&
346
0
                TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) )
347
0
            {
348
0
                GetData(aSubstFlavor, rDestDoc);
349
0
                bDone = maAny.hasValue();
350
0
            }
351
0
            else if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor )
352
0
                && TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor )
353
0
                && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BITMAP, aSubstFlavor))
354
0
            {
355
0
                GetData(aSubstFlavor, rDestDoc);
356
0
                bDone = true;
357
0
            }
358
0
            else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
359
0
                     TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
360
0
                     SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
361
0
            {
362
0
                GetData(aSubstFlavor, rDestDoc);
363
364
0
                if( maAny.hasValue() )
365
0
                {
366
0
                    Sequence< sal_Int8 > aSeq;
367
368
0
                    if( maAny >>= aSeq )
369
0
                    {
370
0
                        GDIMetaFile     aMtf;
371
0
                        {
372
0
                            SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
373
0
                            SvmReader aReader( aSrcStm );
374
0
                            aReader.Read( aMtf );
375
0
                        }
376
377
0
                        Graphic         aGraphic( aMtf );
378
0
                        SvMemoryStream  aDstStm( 65535, 65535 );
379
380
0
                        if( GraphicConverter::Export( aDstStm, aGraphic, ConvertDataFormat::EMF ) == ERRCODE_NONE )
381
0
                        {
382
0
                            maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aDstStm.GetData() ),
383
0
                                                            aDstStm.TellEnd() );
384
0
                            bDone = true;
385
0
                        }
386
0
                    }
387
0
                }
388
0
            }
389
0
            else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
390
0
                     TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
391
0
                     SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
392
0
            {
393
0
                GetData(aSubstFlavor, rDestDoc);
394
395
0
                if( maAny.hasValue() )
396
0
                {
397
0
                    Sequence< sal_Int8 > aSeq;
398
399
0
                    if( maAny >>= aSeq )
400
0
                    {
401
0
                        GDIMetaFile     aMtf;
402
0
                        {
403
0
                            SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
404
0
                            SvmReader aReader( aSrcStm );
405
0
                            aReader.Read( aMtf );
406
0
                        }
407
408
0
                        SvMemoryStream  aDstStm( 65535, 65535 );
409
410
                        // taking wmf without file header
411
0
                        if ( ConvertGDIMetaFileToWMF( aMtf, aDstStm, nullptr, false ) )
412
0
                        {
413
0
                            maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aDstStm.GetData() ),
414
0
                                                            aDstStm.TellEnd() );
415
0
                            bDone = true;
416
0
                        }
417
0
                    }
418
0
                }
419
0
            }
420
0
            else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::SVG, aSubstFlavor ) &&
421
0
                     TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
422
0
                     SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
423
0
            {
424
0
                GetData(aSubstFlavor, rDestDoc);
425
426
0
                if( maAny.hasValue() )
427
0
                {
428
0
                    Sequence< sal_Int8 > aSeq;
429
430
0
                    if( maAny >>= aSeq )
431
0
                    {
432
0
                        GDIMetaFile     aMtf;
433
0
                        {
434
0
                            SvMemoryStream aSrcStm( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC );
435
0
                            SvmReader aReader( aSrcStm );
436
0
                            aReader.Read( aMtf );
437
0
                        }
438
439
0
                        SvMemoryStream  aDstStm( 65535, 65535 );
440
0
                        Graphic         aGraphic( aMtf );
441
442
0
                        if( GraphicConverter::Export( aDstStm, aGraphic, ConvertDataFormat::SVG ) == ERRCODE_NONE )
443
0
                        {
444
0
                            maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aDstStm.GetData() ),
445
0
                                                            aDstStm.TellEnd() );
446
0
                            bDone = true;
447
0
                        }
448
0
                    }
449
0
                }
450
0
            }
451
452
            // reset Any if substitute doesn't work
453
0
            if( !bDone && maAny.hasValue() )
454
0
                maAny = Any();
455
456
            // if any is not yet filled, use standard format
457
0
            if( !maAny.hasValue() )
458
0
                GetData(rFlavor, rDestDoc);
459
0
        }
460
0
        catch( const css::uno::Exception& )
461
0
        {
462
0
        }
463
464
0
        if( !maAny.hasValue() )
465
0
            throw UnsupportedFlavorException();
466
0
    }
467
468
0
    return maAny;
469
0
}
470
471
sal_Bool SAL_CALL TransferableHelper::isComplex()
472
0
{
473
    // By default everything is complex, until proven otherwise
474
    // in the respective document type transferable handler.
475
0
    return true;
476
0
}
477
478
Sequence< DataFlavor > SAL_CALL TransferableHelper::getTransferDataFlavors()
479
0
{
480
0
    const SolarMutexGuard aGuard;
481
482
0
    try
483
0
    {
484
0
        if(maFormats.empty())
485
0
            AddSupportedFormats();
486
0
    }
487
0
    catch( const css::uno::Exception& )
488
0
    {
489
0
    }
490
491
0
    return comphelper::containerToSequence<DataFlavor>(maFormats);
492
0
}
493
494
495
sal_Bool SAL_CALL TransferableHelper::isDataFlavorSupported( const DataFlavor& rFlavor )
496
0
{
497
0
    const SolarMutexGuard aGuard;
498
499
0
    try
500
0
    {
501
0
        if (maFormats.empty())
502
0
            AddSupportedFormats();
503
0
    }
504
0
    catch( const css::uno::Exception& )
505
0
    {
506
0
    }
507
508
0
    for (auto const& format : maFormats)
509
0
    {
510
0
        if( TransferableDataHelper::IsEqual( format, rFlavor ) )
511
0
        {
512
0
            return true;
513
0
        }
514
0
    }
515
516
0
    return false;
517
0
}
518
519
520
void SAL_CALL TransferableHelper::lostOwnership( const Reference< XClipboard >&, const Reference< XTransferable >& )
521
0
{
522
0
    const SolarMutexGuard aGuard;
523
524
0
    try
525
0
    {
526
0
        if( mxTerminateListener.is() )
527
0
        {
528
0
            Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
529
0
            xDesktop->removeTerminateListener( mxTerminateListener );
530
531
0
            mxTerminateListener.clear();
532
0
        }
533
534
0
        ObjectReleased();
535
0
    }
536
0
    catch( const css::uno::Exception& )
537
0
    {
538
0
    }
539
0
}
540
541
542
void SAL_CALL TransferableHelper::disposing( const EventObject& )
543
0
{
544
0
}
545
546
547
void SAL_CALL TransferableHelper::dragDropEnd( const DragSourceDropEvent& rDSDE )
548
0
{
549
0
    const SolarMutexGuard aGuard;
550
551
0
    try
552
0
    {
553
0
        DragFinished( rDSDE.DropSuccess ? ( rDSDE.DropAction & ~DNDConstants::ACTION_DEFAULT ) : DNDConstants::ACTION_NONE );
554
0
        ObjectReleased();
555
0
    }
556
0
    catch( const css::uno::Exception& )
557
0
    {
558
0
    }
559
0
}
560
561
562
void SAL_CALL TransferableHelper::dragEnter( const DragSourceDragEvent& )
563
0
{
564
0
}
565
566
567
void SAL_CALL TransferableHelper::dragExit( const DragSourceEvent& )
568
0
{
569
0
}
570
571
572
void SAL_CALL TransferableHelper::dragOver( const DragSourceDragEvent& )
573
0
{
574
0
}
575
576
577
void SAL_CALL TransferableHelper::dropActionChanged( const DragSourceDragEvent& )
578
0
{
579
0
}
580
581
582
void TransferableHelper::ImplFlush()
583
0
{
584
0
    if( !mxClipboard.is() )
585
0
        return;
586
587
0
    Reference< XFlushableClipboard >    xFlushableClipboard( mxClipboard, UNO_QUERY );
588
0
    SolarMutexReleaser aReleaser;
589
590
0
    try
591
0
    {
592
0
        if( xFlushableClipboard.is() )
593
0
             xFlushableClipboard->flushClipboard();
594
0
    }
595
0
    catch( const css::uno::Exception& )
596
0
    {
597
0
        OSL_FAIL( "Could not flush clipboard" );
598
0
    }
599
0
}
600
601
602
void TransferableHelper::AddFormat( SotClipboardFormatId nFormat )
603
0
{
604
0
    DataFlavor aFlavor;
605
606
0
    if( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
607
0
        AddFormat( aFlavor );
608
0
}
609
610
611
void TransferableHelper::AddFormat( const DataFlavor& rFlavor )
612
0
{
613
0
    bool bAdd = true;
614
615
0
    for (auto & format : maFormats)
616
0
    {
617
0
        if( TransferableDataHelper::IsEqual( format, rFlavor ) )
618
0
        {
619
            // update MimeType for SotClipboardFormatId::OBJECTDESCRIPTOR in every case
620
0
            if ((SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId) && mxObjDesc)
621
0
            {
622
0
                DataFlavor aObjDescFlavor;
623
624
0
                SotExchange::GetFormatDataFlavor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDescFlavor );
625
0
                format.MimeType = aObjDescFlavor.MimeType;
626
0
                format.MimeType += ::ImplGetParameterString(*mxObjDesc);
627
0
            }
628
629
0
            bAdd = false;
630
0
            break;
631
0
        }
632
0
    }
633
634
0
    if( !bAdd )
635
0
        return;
636
637
0
    DataFlavorEx   aFlavorEx;
638
639
0
    aFlavorEx.MimeType = rFlavor.MimeType;
640
0
    aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
641
0
    aFlavorEx.DataType = rFlavor.DataType;
642
0
    aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
643
644
0
    if ((SotClipboardFormatId::OBJECTDESCRIPTOR == aFlavorEx.mnSotId) && mxObjDesc)
645
0
        aFlavorEx.MimeType += ::ImplGetParameterString(*mxObjDesc);
646
647
0
    maFormats.push_back(aFlavorEx);
648
649
0
    if( SotClipboardFormatId::BITMAP == aFlavorEx.mnSotId )
650
0
    {
651
0
        AddFormat( SotClipboardFormatId::PNG );
652
0
        AddFormat( SotClipboardFormatId::BMP );
653
0
    }
654
0
    else if( SotClipboardFormatId::GDIMETAFILE == aFlavorEx.mnSotId )
655
0
    {
656
0
        AddFormat( SotClipboardFormatId::EMF );
657
0
        AddFormat( SotClipboardFormatId::WMF );
658
0
        AddFormat( SotClipboardFormatId::SVG );
659
0
    }
660
0
}
661
662
663
void TransferableHelper::RemoveFormat( SotClipboardFormatId nFormat )
664
0
{
665
0
    DataFlavor aFlavor;
666
667
0
    if( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
668
0
        RemoveFormat( aFlavor );
669
0
}
670
671
672
void TransferableHelper::RemoveFormat( const DataFlavor& rFlavor )
673
0
{
674
0
    DataFlavorExVector::iterator aIter(maFormats.begin());
675
676
0
    while (aIter != maFormats.end())
677
0
    {
678
0
        if( TransferableDataHelper::IsEqual( *aIter, rFlavor ) )
679
0
            aIter = maFormats.erase(aIter);
680
0
        else
681
0
            ++aIter;
682
0
    }
683
0
}
684
685
686
bool TransferableHelper::HasFormat( SotClipboardFormatId nFormat )
687
0
{
688
0
    return std::any_of(maFormats.begin(), maFormats.end(),
689
0
              [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
690
0
}
691
692
693
void TransferableHelper::ClearFormats()
694
0
{
695
0
    maFormats.clear();
696
0
    maAny.clear();
697
0
}
698
699
700
bool TransferableHelper::SetAny( const Any& rAny )
701
0
{
702
0
    maAny = rAny;
703
0
    return maAny.hasValue();
704
0
}
705
706
707
bool TransferableHelper::SetString( const OUString& rString )
708
0
{
709
0
    maAny <<= rString;
710
0
    return maAny.hasValue();
711
0
}
712
713
bool TransferableHelper::SetBitmap(const Bitmap& rBitmap, const DataFlavor& rFlavor)
714
0
{
715
0
    if( !rBitmap.IsEmpty() )
716
0
    {
717
0
        SvMemoryStream aMemStm( 65535, 65535 );
718
719
0
        if(rFlavor.MimeType.equalsIgnoreAsciiCase("image/png"))
720
0
        {
721
            // write a PNG
722
0
            css::uno::Sequence<css::beans::PropertyValue> aFilterData;
723
724
#ifdef IOS
725
            // Use faster compression on slow devices
726
            aFilterData.realloc(aFilterData.getLength() + 1);
727
            aFilterData.getArray()[aFilterData.getLength() - 1].Name = "Compression";
728
729
            // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed. For a
730
            // typical 15 megapixel image from a DSLR, we are talking about a difference of 17 s for
731
            // the default compression level vs 4 s for best speed, on an iPad Pro from 2017.
732
            //
733
            // Sure, the best would be to not have to re-encode the image at all, but have access to
734
            // the original JPEG or PNG when there is a such.
735
736
            aFilterData.getArray()[aFilterData.getLength() - 1].Value <<= 1;
737
#endif
738
0
            vcl::PngImageWriter aPNGWriter(aMemStm);
739
0
            aPNGWriter.setParameters(aFilterData);
740
0
            aPNGWriter.write(rBitmap);
741
0
        }
742
0
        else
743
0
        {
744
            // BMP has no alpha channel, blend any transparency onto white
745
0
            Bitmap aBitmap(rBitmap);
746
0
            aBitmap.ReplaceTransparency(COL_WHITE);
747
            // explicitly use Bitmap::Write with bCompressed = sal_False and bFileHeader = sal_True
748
0
            WriteDIB(aBitmap, aMemStm, false, true);
749
0
        }
750
751
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
752
0
    }
753
754
0
    return maAny.hasValue();
755
0
}
756
757
758
bool TransferableHelper::SetGDIMetaFile( const GDIMetaFile& rMtf )
759
0
{
760
0
    if( rMtf.GetActionSize() )
761
0
    {
762
0
        SvMemoryStream aMemStm( 65535, 65535 );
763
764
0
        SvmWriter aWriter( aMemStm );
765
0
        aWriter.Write( rMtf );
766
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
767
0
    }
768
769
0
    return maAny.hasValue();
770
0
}
771
772
773
bool TransferableHelper::SetGraphic( const Graphic& rGraphic )
774
0
{
775
0
    if( rGraphic.GetType() != GraphicType::NONE )
776
0
    {
777
0
        SvMemoryStream aMemStm( 65535, 65535 );
778
779
0
        aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
780
0
        aMemStm.SetCompressMode( SvStreamCompressFlags::NATIVE );
781
782
0
        TypeSerializer aSerializer(aMemStm);
783
0
        aSerializer.writeGraphic(rGraphic);
784
785
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
786
0
    }
787
788
0
    return maAny.hasValue();
789
0
}
790
791
792
bool TransferableHelper::SetImageMap( const ImageMap& rIMap )
793
0
{
794
0
    SvMemoryStream aMemStm( 8192, 8192 );
795
796
0
    aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
797
0
    rIMap.Write( aMemStm );
798
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
799
800
0
    return maAny.hasValue();
801
0
}
802
803
804
bool TransferableHelper::SetTransferableObjectDescriptor( const TransferableObjectDescriptor& rDesc )
805
0
{
806
0
    PrepareOLE( rDesc );
807
808
0
    SvMemoryStream aMemStm( 1024, 1024 );
809
810
0
    WriteTransferableObjectDescriptor( aMemStm, rDesc );
811
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
812
813
0
    return maAny.hasValue();
814
0
 }
815
816
817
bool TransferableHelper::SetINetBookmark( const INetBookmark& rBmk,
818
                                          const css::datatransfer::DataFlavor& rFlavor )
819
0
{
820
0
    rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
821
822
0
    switch( SotExchange::GetFormat( rFlavor ) )
823
0
    {
824
0
        case SotClipboardFormatId::SOLK:
825
0
        {
826
0
            OString sURL(OUStringToOString(rBmk.GetURL(), eSysCSet));
827
0
            OString sDesc(OUStringToOString(rBmk.GetDescription(), eSysCSet));
828
0
            OString sOut =
829
0
                OString::number(sURL.getLength())
830
0
                + "@" + sURL
831
0
                + OString::number(sDesc.getLength())
832
0
                + "@" + sDesc;
833
834
0
            Sequence< sal_Int8 > aSeq(sOut.getLength());
835
0
            memcpy(aSeq.getArray(), sOut.getStr(), sOut.getLength());
836
0
            maAny <<= aSeq;
837
0
        }
838
0
        break;
839
840
0
        case SotClipboardFormatId::STRING:
841
0
        case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
842
0
            maAny <<= rBmk.GetURL();
843
0
            break;
844
845
0
        case SotClipboardFormatId::NETSCAPE_BOOKMARK:
846
0
        {
847
0
            Sequence< sal_Int8 > aSeq( 2048 );
848
0
            char* pSeq = reinterpret_cast< char* >( aSeq.getArray() );
849
850
            // strncpy fills the rest with nulls, as we need
851
0
            strncpy( pSeq, OUStringToOString(rBmk.GetURL(), eSysCSet).getStr(), 1024 );
852
0
            strncpy( pSeq + 1024, OUStringToOString(rBmk.GetDescription(), eSysCSet).getStr(), 1024 );
853
854
0
            maAny <<= aSeq;
855
0
        }
856
0
        break;
857
858
#ifdef _WIN32
859
        case SotClipboardFormatId::FILEGRPDESCRIPTOR:
860
        {
861
            Sequence< sal_Int8 >    aSeq( sizeof( FILEGROUPDESCRIPTORW ) );
862
            FILEGROUPDESCRIPTORW*   pFDesc = reinterpret_cast<FILEGROUPDESCRIPTORW*>(aSeq.getArray());
863
            FILEDESCRIPTORW&        rFDesc1 = pFDesc->fgd[ 0 ];
864
865
            pFDesc->cItems = 1;
866
            memset( &rFDesc1, 0, sizeof( rFDesc1 ) );
867
            rFDesc1.dwFlags = FD_LINKUI;
868
869
            OUStringBuffer aStr(rBmk.GetDescription());
870
            for( size_t nChar = 0; (nChar = std::u16string_view(aStr).find_first_of(u"\\/:*?\"<>|"sv, nChar)) != std::u16string_view::npos; )
871
                aStr.remove(nChar, 1);
872
873
            aStr.insert(0, "Shortcut to ");
874
            aStr.append(".URL");
875
            wcscpy( rFDesc1.cFileName, o3tl::toW(aStr.getStr()) );
876
877
            maAny <<= aSeq;
878
        }
879
        break;
880
881
        case SotClipboardFormatId::FILECONTENT:
882
        {
883
            maAny <<= "[InternetShortcut]\x0aURL=" + rBmk.GetURL();
884
        }
885
        break;
886
#endif
887
888
0
        default:
889
0
        break;
890
0
    }
891
892
0
    return maAny.hasValue();
893
0
}
894
895
896
bool TransferableHelper::SetINetImage( const INetImage& rINtImg,
897
                                       const css::datatransfer::DataFlavor& rFlavor )
898
0
{
899
0
    SvMemoryStream aMemStm( 1024, 1024 );
900
901
0
    aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
902
0
    rINtImg.Write( aMemStm, SotExchange::GetFormat( rFlavor ) );
903
904
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
905
906
0
    return maAny.hasValue();
907
0
}
908
909
910
bool TransferableHelper::SetObject( void* pUserObject, sal_uInt32 nUserObjectId, const DataFlavor& rFlavor )
911
0
{
912
0
    SvMemoryStream aStm;
913
914
0
    aStm.SetVersion( SOFFICE_FILEFORMAT_50 );
915
916
0
    if( pUserObject && WriteObject( aStm, pUserObject, nUserObjectId, rFlavor ) )
917
0
    {
918
0
        const sal_uInt32        nLen = aStm.TellEnd();
919
0
        Sequence< sal_Int8 >    aSeq( nLen );
920
921
0
        aStm.Seek( STREAM_SEEK_TO_BEGIN );
922
0
        aStm.ReadBytes(aSeq.getArray(), nLen);
923
924
0
        if( nLen && ( SotExchange::GetFormat( rFlavor ) == SotClipboardFormatId::STRING ) )
925
0
        {
926
            //JP 24.7.2001: as I know this was only for the writer application and this
927
            //              writes now UTF16 format into the stream
928
            //JP 6.8.2001:  and now it writes UTF8 because then there is no problem with
929
            //              little / big endian! - Bug 88121
930
0
            maAny <<= OUString( reinterpret_cast< const char* >( aSeq.getConstArray() ), nLen - 1, RTL_TEXTENCODING_UTF8 );
931
0
        }
932
0
        else
933
0
            maAny <<= aSeq;
934
0
    }
935
936
0
    return maAny.hasValue();
937
0
}
938
939
940
bool TransferableHelper::WriteObject( SvStream&, void*, sal_uInt32, const DataFlavor& )
941
0
{
942
0
    OSL_FAIL( "TransferableHelper::WriteObject( ... ) not implemented" );
943
0
    return false;
944
0
}
945
946
947
void TransferableHelper::DragFinished( sal_Int8 )
948
0
{
949
0
}
950
951
952
void TransferableHelper::ObjectReleased()
953
0
{
954
0
}
955
956
957
void TransferableHelper::PrepareOLE( const TransferableObjectDescriptor& rObjDesc )
958
0
{
959
0
    mxObjDesc.reset(new TransferableObjectDescriptor(rObjDesc));
960
961
0
    if( HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) )
962
0
        AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
963
0
}
964
965
void TransferableHelper::CopyToClipboard(const Reference<XClipboard>& rClipboard)
966
0
{
967
0
    if( rClipboard.is() )
968
0
        mxClipboard = rClipboard;
969
970
0
    if( !mxClipboard.is() || mxTerminateListener.is() )
971
0
        return;
972
973
0
    try
974
0
    {
975
0
        mxTerminateListener = new TerminateListener(*this);
976
0
        Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
977
0
        xDesktop->addTerminateListener(mxTerminateListener);
978
979
0
        mxClipboard->setContents(this, this);
980
0
    }
981
0
    catch( const css::uno::Exception& )
982
0
    {
983
0
    }
984
0
}
985
986
void TransferableHelper::CopyToClipboard(vcl::Window* pWindow)
987
0
{
988
0
    DBG_ASSERT( pWindow, "Window pointer is NULL" );
989
0
    Reference< XClipboard > xClipboard;
990
991
0
    if( pWindow )
992
0
        xClipboard = pWindow->GetClipboard();
993
994
0
    CopyToClipboard(xClipboard);
995
0
}
996
997
void TransferableHelper::CopyToSelection(const Reference<XClipboard>& rSelection)
998
0
{
999
0
    if( !rSelection.is() || mxTerminateListener.is() )
1000
0
        return;
1001
1002
0
    try
1003
0
    {
1004
0
        mxTerminateListener = new TerminateListener(*this);
1005
0
        Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1006
0
        xDesktop->addTerminateListener(mxTerminateListener);
1007
1008
0
        rSelection->setContents(this, this);
1009
0
    }
1010
0
    catch( const css::uno::Exception& )
1011
0
    {
1012
0
    }
1013
0
}
1014
1015
void TransferableHelper::CopyToPrimarySelection()
1016
0
{
1017
0
    CopyToSelection(GetSystemPrimarySelection());
1018
0
}
1019
1020
void TransferableHelper::StartDrag( vcl::Window* pWindow, sal_Int8 nDnDSourceActions )
1021
0
{
1022
0
    assert(pWindow && "Window pointer is NULL");
1023
0
    Reference< XDragSource > xDragSource( pWindow->GetDragSource() );
1024
1025
0
    if( !xDragSource.is() )
1026
0
        return;
1027
1028
    /*
1029
     *    #96792# release mouse before actually starting DnD.
1030
     *    This is necessary for the X11 DnD implementation to work.
1031
     */
1032
0
    if( pWindow->IsMouseCaptured() )
1033
0
        pWindow->ReleaseMouse();
1034
1035
0
    const Point aPt( pWindow->GetPointerPosPixel() );
1036
1037
    // On macOS we are forced to execute 'startDrag' synchronously
1038
    // contrary to the XDragSource interface specification because
1039
    // we can receive drag events from the system only in the main
1040
    // thread
1041
0
#if !defined(MACOSX)
1042
0
    SolarMutexReleaser aReleaser;
1043
0
#endif
1044
1045
0
    try
1046
0
    {
1047
0
        DragGestureEvent    aEvt;
1048
0
        aEvt.DragAction = DNDConstants::ACTION_COPY;
1049
0
        aEvt.DragOriginX = aPt.X();
1050
0
        aEvt.DragOriginY = aPt.Y();
1051
0
        aEvt.DragSource = xDragSource;
1052
1053
0
        xDragSource->startDrag( aEvt, nDnDSourceActions, DND_POINTER_NONE, DND_IMAGE_NONE, this, this );
1054
0
    }
1055
0
    catch( const css::uno::Exception& )
1056
0
    {
1057
0
    }
1058
0
}
1059
1060
void TransferableHelper::ClearPrimarySelection()
1061
0
{
1062
0
    Reference< XClipboard > xSelection(GetSystemPrimarySelection());
1063
1064
0
    if( xSelection.is() )
1065
0
        xSelection->setContents( nullptr, nullptr );
1066
0
}
1067
1068
class TransferableClipboardNotifier : public ::cppu::WeakImplHelper< XClipboardListener >
1069
{
1070
private:
1071
    Reference< XClipboardNotifier > mxNotifier;
1072
    TransferableDataHelper*         mpListener;
1073
1074
protected:
1075
    // XClipboardListener
1076
    virtual void SAL_CALL changedContents( const clipboard::ClipboardEvent& event ) override;
1077
1078
    // XEventListener
1079
    virtual void SAL_CALL disposing( const EventObject& Source ) override;
1080
1081
public:
1082
    TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener );
1083
1084
    /// determines whether we're currently listening
1085
0
    bool isListening() const { return mpListener != nullptr; }
1086
1087
    /// makes the instance non-functional
1088
    void    dispose();
1089
};
1090
1091
1092
TransferableClipboardNotifier::TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener )
1093
0
    :mxNotifier( _rxClipboard, UNO_QUERY )
1094
0
    ,mpListener( &_rListener )
1095
0
{
1096
0
    osl_atomic_increment( &m_refCount );
1097
0
    {
1098
0
        if ( mxNotifier.is() )
1099
0
            mxNotifier->addClipboardListener( this );
1100
0
        else
1101
            // born dead
1102
0
            mpListener = nullptr;
1103
0
    }
1104
0
    osl_atomic_decrement( &m_refCount );
1105
0
}
1106
1107
1108
void SAL_CALL TransferableClipboardNotifier::changedContents( const clipboard::ClipboardEvent& event )
1109
0
{
1110
0
    SolarMutexGuard aSolarGuard;
1111
0
    if( mpListener )
1112
0
        mpListener->Rebind( event.Contents );
1113
0
}
1114
1115
1116
void SAL_CALL TransferableClipboardNotifier::disposing( const EventObject& )
1117
0
{
1118
    // clipboard is being disposed. Hmm. Okay, become dysfunctional myself.
1119
0
    dispose();
1120
0
}
1121
1122
1123
void TransferableClipboardNotifier::dispose()
1124
0
{
1125
0
    SolarMutexGuard g;
1126
1127
0
    Reference< XClipboardListener > xKeepMeAlive( this );
1128
1129
0
    if ( mxNotifier.is() )
1130
0
        mxNotifier->removeClipboardListener( this );
1131
0
    mxNotifier.clear();
1132
1133
0
    mpListener = nullptr;
1134
0
}
1135
1136
TransferableDataHelper::TransferableDataHelper()
1137
0
    : mxObjDesc(new TransferableObjectDescriptor)
1138
0
{
1139
0
}
1140
1141
TransferableDataHelper::TransferableDataHelper(const Reference< css::datatransfer::XTransferable >& rxTransferable)
1142
0
    : mxTransfer(rxTransferable)
1143
0
    , mxObjDesc(new TransferableObjectDescriptor)
1144
0
{
1145
0
    InitFormats();
1146
0
}
1147
1148
TransferableDataHelper::TransferableDataHelper(const TransferableDataHelper& rDataHelper)
1149
0
    : mxTransfer(rDataHelper.mxTransfer)
1150
0
    , mxClipboard(rDataHelper.mxClipboard)
1151
0
    , maFormats(rDataHelper.maFormats)
1152
0
    , mxObjDesc(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc))
1153
0
{
1154
0
}
1155
1156
TransferableDataHelper::TransferableDataHelper(TransferableDataHelper&& rDataHelper) noexcept
1157
0
    : mxTransfer(std::move(rDataHelper.mxTransfer))
1158
0
    , mxClipboard(std::move(rDataHelper.mxClipboard))
1159
0
    , maFormats(std::move(rDataHelper.maFormats))
1160
0
    , mxObjDesc(std::move(rDataHelper.mxObjDesc))
1161
0
{
1162
0
}
1163
1164
TransferableDataHelper& TransferableDataHelper::operator=( const TransferableDataHelper& rDataHelper )
1165
0
{
1166
0
    if ( this != &rDataHelper )
1167
0
    {
1168
0
        SolarMutexGuard g;
1169
1170
0
        const bool bWasClipboardListening = mxClipboardListener.is();
1171
1172
0
        if (bWasClipboardListening)
1173
0
            StopClipboardListening();
1174
1175
0
        mxTransfer = rDataHelper.mxTransfer;
1176
0
        maFormats = rDataHelper.maFormats;
1177
0
        mxObjDesc.reset(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc));
1178
0
        mxClipboard = rDataHelper.mxClipboard;
1179
1180
0
        if (bWasClipboardListening)
1181
0
            StartClipboardListening();
1182
0
    }
1183
1184
0
    return *this;
1185
0
}
1186
1187
TransferableDataHelper& TransferableDataHelper::operator=(TransferableDataHelper&& rDataHelper)
1188
0
{
1189
0
    SolarMutexGuard g;
1190
1191
0
    const bool bWasClipboardListening = mxClipboardListener.is();
1192
1193
0
    if (bWasClipboardListening)
1194
0
        StopClipboardListening();
1195
1196
0
    mxTransfer = std::move(rDataHelper.mxTransfer);
1197
0
    maFormats = std::move(rDataHelper.maFormats);
1198
0
    mxObjDesc = std::move(rDataHelper.mxObjDesc);
1199
0
    mxClipboard = std::move(rDataHelper.mxClipboard);
1200
1201
0
    if (bWasClipboardListening)
1202
0
        StartClipboardListening();
1203
1204
0
    return *this;
1205
0
}
1206
1207
TransferableDataHelper::~TransferableDataHelper()
1208
0
{
1209
0
    StopClipboardListening( );
1210
0
    {
1211
0
        SolarMutexGuard g;
1212
0
        maFormats.clear();
1213
0
        mxObjDesc.reset();
1214
0
    }
1215
0
}
1216
1217
void TransferableDataHelper::FillDataFlavorExVector( const Sequence< DataFlavor >& rDataFlavorSeq,
1218
                                                     DataFlavorExVector& rDataFlavorExVector )
1219
0
{
1220
0
    try
1221
0
    {
1222
0
        const Reference< XComponentContext >&          xContext( ::comphelper::getProcessComponentContext() );
1223
0
        Reference< XMimeContentTypeFactory >    xMimeFact = MimeContentTypeFactory::create( xContext );
1224
0
        DataFlavorEx                            aFlavorEx;
1225
0
        static constexpr OUString        aCharsetStr( u"charset"_ustr );
1226
1227
1228
0
        for (auto const& rFlavor : rDataFlavorSeq)
1229
0
        {
1230
0
            Reference< XMimeContentType >   xMimeType;
1231
1232
0
            try
1233
0
            {
1234
0
                if( !rFlavor.MimeType.isEmpty() )
1235
0
                    xMimeType = xMimeFact->createMimeContentType( rFlavor.MimeType );
1236
0
            }
1237
0
            catch( const css::uno::Exception& )
1238
0
            {
1239
0
            }
1240
1241
0
            aFlavorEx.MimeType = rFlavor.MimeType;
1242
0
            aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
1243
0
            aFlavorEx.DataType = rFlavor.DataType;
1244
0
            aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
1245
1246
0
            rDataFlavorExVector.push_back( aFlavorEx );
1247
1248
            // add additional formats for special mime types
1249
0
            if(SotClipboardFormatId::BMP == aFlavorEx.mnSotId || SotClipboardFormatId::PNG == aFlavorEx.mnSotId || SotClipboardFormatId::JPEG == aFlavorEx.mnSotId)
1250
0
            {
1251
0
                if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavorEx ) )
1252
0
                {
1253
0
                    aFlavorEx.mnSotId = SotClipboardFormatId::BITMAP;
1254
0
                    rDataFlavorExVector.push_back( aFlavorEx );
1255
0
                }
1256
0
            }
1257
0
            else if( SotClipboardFormatId::WMF == aFlavorEx.mnSotId
1258
0
                     || SotClipboardFormatId::EMF == aFlavorEx.mnSotId
1259
0
                     || SotClipboardFormatId::SVG == aFlavorEx.mnSotId )
1260
0
            {
1261
0
                if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavorEx ) )
1262
0
                {
1263
0
                    aFlavorEx.mnSotId = SotClipboardFormatId::GDIMETAFILE;
1264
0
                    rDataFlavorExVector.push_back( aFlavorEx );
1265
0
                }
1266
0
            }
1267
0
            else if ( SotClipboardFormatId::HTML_SIMPLE == aFlavorEx.mnSotId  )
1268
0
            {
1269
                // #104735# HTML_SIMPLE may also be inserted without comments
1270
0
                aFlavorEx.mnSotId = SotClipboardFormatId::HTML_NO_COMMENT;
1271
0
                rDataFlavorExVector.push_back( aFlavorEx );
1272
0
            }
1273
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
1274
0
            {
1275
                // add, if it is a UTF-8 byte buffer
1276
0
                if( xMimeType->hasParameter( aCharsetStr ) )
1277
0
                {
1278
0
                    if( xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "unicode" ) ||
1279
0
                        xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "utf-16" ) )
1280
0
                    {
1281
0
                        rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::STRING;
1282
1283
0
                    }
1284
0
                }
1285
0
            }
1286
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/rtf" ) )
1287
0
            {
1288
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RTF;
1289
0
            }
1290
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/richtext" ) )
1291
0
            {
1292
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RICHTEXT;
1293
0
            }
1294
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/html" ) )
1295
1296
0
            {
1297
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::HTML;
1298
0
            }
1299
0
            else if(xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/markdown "))
1300
0
            {
1301
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::MARKDOWN;
1302
0
            }
1303
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/uri-list" ) )
1304
0
            {
1305
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::FILE_LIST;
1306
0
            }
1307
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice-objectdescriptor-xml" ) )
1308
0
            {
1309
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::OBJECTDESCRIPTOR;
1310
0
            }
1311
0
        }
1312
0
    }
1313
0
    catch( const css::uno::Exception& )
1314
0
    {
1315
0
    }
1316
0
}
1317
1318
void TransferableDataHelper::InitFormats()
1319
0
{
1320
0
    SolarMutexGuard aSolarGuard;
1321
1322
0
    maFormats.clear();
1323
0
    mxObjDesc.reset(new TransferableObjectDescriptor);
1324
1325
0
    if( !mxTransfer.is() )
1326
0
        return;
1327
1328
0
    TransferableDataHelper::FillDataFlavorExVector(mxTransfer->getTransferDataFlavors(), maFormats);
1329
1330
0
    for (auto const& format : maFormats)
1331
0
    {
1332
0
        if( SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId )
1333
0
        {
1334
0
            ImplSetParameterString(*mxObjDesc, format);
1335
0
            auto data = GetSequence(format, {});
1336
0
            SvMemoryStream aSrcStm(data.getArray(), data.getLength(), StreamMode::READ);
1337
0
            TryReadTransferableObjectDescriptor(aSrcStm, *mxObjDesc);
1338
0
            break;
1339
0
        }
1340
0
    }
1341
0
}
1342
1343
1344
bool TransferableDataHelper::HasFormat( SotClipboardFormatId nFormat ) const
1345
0
{
1346
0
    SolarMutexGuard g;
1347
0
    return std::any_of(maFormats.begin(), maFormats.end(),
1348
0
              [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
1349
0
}
1350
1351
bool TransferableDataHelper::HasFormat( const DataFlavor& rFlavor ) const
1352
0
{
1353
0
    SolarMutexGuard g;
1354
0
    for (auto const& format : maFormats)
1355
0
    {
1356
0
        if( TransferableDataHelper::IsEqual( rFlavor, format ) )
1357
0
            return true;
1358
0
    }
1359
1360
0
    return false;
1361
0
}
1362
1363
sal_uInt32 TransferableDataHelper::GetFormatCount() const
1364
0
{
1365
0
    SolarMutexGuard g;
1366
0
    return maFormats.size();
1367
0
}
1368
1369
SotClipboardFormatId TransferableDataHelper::GetFormat( sal_uInt32 nFormat ) const
1370
0
{
1371
0
    SolarMutexGuard g;
1372
0
    DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
1373
0
    return( ( nFormat < maFormats.size() ) ? maFormats[ nFormat ].mnSotId : SotClipboardFormatId::NONE );
1374
0
}
1375
1376
DataFlavor TransferableDataHelper::GetFormatDataFlavor( sal_uInt32 nFormat ) const
1377
0
{
1378
0
    SolarMutexGuard g;
1379
0
    DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
1380
1381
0
    DataFlavor aRet;
1382
1383
0
    if (nFormat < maFormats.size())
1384
0
        aRet = maFormats[nFormat];
1385
1386
0
    return aRet;
1387
0
}
1388
1389
1390
Reference< XTransferable > TransferableDataHelper::GetXTransferable() const
1391
0
{
1392
0
    Reference< XTransferable > xRet;
1393
1394
0
    if( mxTransfer.is() )
1395
0
    {
1396
0
        try
1397
0
        {
1398
0
            xRet = mxTransfer;
1399
1400
            // do a dummy call to check, if this interface is valid (nasty)
1401
0
            xRet->getTransferDataFlavors();
1402
1403
0
        }
1404
0
        catch( const css::uno::Exception& )
1405
0
        {
1406
0
            xRet.clear();
1407
0
        }
1408
0
    }
1409
1410
0
    return xRet;
1411
0
}
1412
1413
1414
Any TransferableDataHelper::GetAny( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
1415
0
{
1416
0
    Any aReturn;
1417
1418
0
    DataFlavor aFlavor;
1419
0
    if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
1420
0
        aReturn = GetAny(aFlavor, rDestDoc);
1421
1422
0
    return aReturn;
1423
0
}
1424
1425
Any TransferableDataHelper::GetAny( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
1426
0
{
1427
0
    Any aRet;
1428
1429
0
    try
1430
0
    {
1431
0
        SolarMutexGuard g;
1432
0
        if( mxTransfer.is() )
1433
0
        {
1434
0
            const SotClipboardFormatId         nRequestFormat = SotExchange::GetFormat( rFlavor );
1435
1436
0
            Reference<css::datatransfer::XTransferable2> xTransfer2(mxTransfer, UNO_QUERY);
1437
1438
0
            if( nRequestFormat != SotClipboardFormatId::NONE )
1439
0
            {
1440
                // try to get alien format first
1441
0
                for (auto const& format : maFormats)
1442
0
                {
1443
0
                    if( ( nRequestFormat == format.mnSotId ) && !rFlavor.MimeType.equalsIgnoreAsciiCase( format.MimeType ) )
1444
0
                    {
1445
// tdf#133365: only release solar mutex on Windows
1446
#ifdef _WIN32
1447
                        // Our own thread may handle the nested IDataObject::GetData call,
1448
                        // and try to acquire solar mutex
1449
                        SolarMutexReleaser r;
1450
#endif // _WIN32
1451
1452
0
                        if (xTransfer2.is())
1453
0
                            aRet = xTransfer2->getTransferData2(format, rDestDoc);
1454
0
                        else
1455
0
                            aRet = mxTransfer->getTransferData(format);
1456
0
                    }
1457
1458
0
                    if( aRet.hasValue() )
1459
0
                        break;
1460
0
                }
1461
0
            }
1462
1463
0
            if( !aRet.hasValue() )
1464
0
            {
1465
// tdf#133365: only release solar mutex on Windows
1466
#ifdef _WIN32
1467
                // Our own thread may handle the nested IDataObject::GetData call,
1468
                // and try to acquire solar mutex
1469
                SolarMutexReleaser r;
1470
#endif // _WIN32
1471
1472
0
                if (xTransfer2.is())
1473
0
                    aRet = xTransfer2->getTransferData2(rFlavor, rDestDoc);
1474
0
                else
1475
0
                    aRet = mxTransfer->getTransferData(rFlavor);
1476
0
            }
1477
0
        }
1478
0
    }
1479
0
    catch( const css::uno::Exception& )
1480
0
    {
1481
0
    }
1482
1483
0
    return aRet;
1484
0
}
1485
1486
1487
bool TransferableDataHelper::GetString( SotClipboardFormatId nFormat, OUString& rStr ) const
1488
0
{
1489
0
    DataFlavor aFlavor;
1490
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetString( aFlavor, rStr ) );
1491
0
}
1492
1493
1494
bool TransferableDataHelper::GetString( const DataFlavor& rFlavor, OUString& rStr ) const
1495
0
{
1496
0
    Any aAny = GetAny(rFlavor, OUString());
1497
0
    bool        bRet = false;
1498
1499
0
    if( aAny.hasValue() )
1500
0
    {
1501
0
        OUString         aOUString;
1502
0
        Sequence< sal_Int8 >    aSeq;
1503
1504
0
        if( aAny >>= aOUString )
1505
0
        {
1506
0
            rStr = aOUString;
1507
0
            bRet = true;
1508
0
        }
1509
0
        else if( aAny >>= aSeq )
1510
0
        {
1511
1512
0
            const char* pChars = reinterpret_cast< const char* >( aSeq.getConstArray() );
1513
0
            sal_Int32       nLen = aSeq.getLength();
1514
1515
            //JP 10.10.2001: 92930 - don't copy the last zero character into the string.
1516
            //DVO 2002-05-27: strip _all_ trailing zeros
1517
0
            while( nLen && ( 0 == *( pChars + nLen - 1 ) ) )
1518
0
                --nLen;
1519
1520
0
            rStr = OUString( pChars, nLen, osl_getThreadTextEncoding() );
1521
0
            bRet = true;
1522
0
        }
1523
0
    }
1524
1525
0
    return bRet;
1526
0
}
1527
1528
bool TransferableDataHelper::GetBitmap(SotClipboardFormatId nFormat, Bitmap& rBmp) const
1529
0
{
1530
0
    if(SotClipboardFormatId::BITMAP == nFormat)
1531
0
    {
1532
        // try to get PNG first
1533
0
        DataFlavor aFlavor;
1534
1535
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
1536
0
        {
1537
0
            if (GetBitmap(aFlavor, rBmp))
1538
0
            {
1539
0
                return true;
1540
0
            }
1541
0
        }
1542
1543
        // then JPEG
1544
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor))
1545
0
        {
1546
0
            if (GetBitmap(aFlavor, rBmp))
1547
0
            {
1548
0
                return true;
1549
0
            }
1550
0
        }
1551
0
    }
1552
1553
0
    DataFlavor aFlavor;
1554
0
    return (SotExchange::GetFormatDataFlavor(nFormat, aFlavor) && GetBitmap(aFlavor, rBmp));
1555
0
}
1556
1557
bool TransferableDataHelper::GetBitmap(const DataFlavor& rFlavor, Bitmap& rBmp) const
1558
0
{
1559
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor);
1560
0
    DataFlavor aSubstFlavor;
1561
0
    bool bRet(xStm);
1562
0
    bool bSuppressPNG(false); // #122982# If PNG stream not accessed, but BMP one, suppress trying to load PNG
1563
0
    bool bSuppressJPEG(false);
1564
1565
0
    if(!bRet && HasFormat(SotClipboardFormatId::PNG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aSubstFlavor))
1566
0
    {
1567
        // when no direct success, try if PNG is available
1568
0
        xStm = GetSotStorageStream(aSubstFlavor);
1569
0
        bRet = bool(xStm);
1570
0
        bSuppressJPEG = bRet;
1571
0
    }
1572
1573
0
    if(!bRet && HasFormat(SotClipboardFormatId::JPEG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aSubstFlavor))
1574
0
    {
1575
0
        xStm = GetSotStorageStream(aSubstFlavor);
1576
0
        bRet = bool(xStm);
1577
0
        bSuppressPNG = bRet;
1578
0
    }
1579
1580
0
    if(!bRet && HasFormat(SotClipboardFormatId::BMP) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor))
1581
0
    {
1582
        // when no direct success, try if BMP is available
1583
0
        xStm = GetSotStorageStream(aSubstFlavor);
1584
0
        bRet = bool(xStm);
1585
0
        bSuppressPNG = bRet;
1586
0
        bSuppressJPEG = bRet;
1587
0
    }
1588
1589
0
    if(bRet)
1590
0
    {
1591
0
        if(!bSuppressPNG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/png"))
1592
0
        {
1593
            // it's a PNG, import to Bitmap
1594
0
            vcl::PngImageReader aPNGReader(*xStm);
1595
0
            rBmp = aPNGReader.read();
1596
0
        }
1597
0
        else if(!bSuppressJPEG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/jpeg"))
1598
0
        {
1599
            // it's a JPEG, import to Bitmap
1600
0
            GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
1601
0
            Graphic aGraphic;
1602
0
            if (rFilter.ImportGraphic(aGraphic, u"", *xStm) == ERRCODE_NONE)
1603
0
                rBmp = aGraphic.GetBitmap();
1604
0
        }
1605
1606
0
        if(rBmp.IsEmpty())
1607
0
        {
1608
0
            Bitmap aBitmap;
1609
0
            AlphaMask aMask;
1610
1611
            // explicitly use Bitmap::Read with bFileHeader = sal_True
1612
            // #i124085# keep DIBV5 for read from clipboard, but should not happen
1613
0
            ReadDIBV5(aBitmap, aMask, *xStm);
1614
1615
0
            if(aMask.GetBitmap().IsEmpty())
1616
0
            {
1617
0
                rBmp = std::move(aBitmap);
1618
0
            }
1619
0
            else
1620
0
            {
1621
0
                rBmp = Bitmap(aBitmap, aMask);
1622
0
            }
1623
0
        }
1624
1625
0
        bRet = (ERRCODE_NONE == xStm->GetError() && !rBmp.IsEmpty());
1626
1627
        /* SJ: #110748# At the moment we are having problems with DDB inserted as DIB. The
1628
           problem is, that some graphics are inserted much too big because the nXPelsPerMeter
1629
           and nYPelsPerMeter of the bitmap fileheader isn't including the correct value.
1630
           Due to this reason the following code assumes that bitmaps with a logical size
1631
           greater than 50 cm aren't having the correct mapmode set.
1632
1633
           The following code should be removed if DDBs and DIBs are supported via clipboard
1634
           properly.
1635
        */
1636
0
        if(bRet)
1637
0
        {
1638
0
            const MapMode aMapMode(rBmp.GetPrefMapMode());
1639
1640
0
            if(MapUnit::MapPixel != aMapMode.GetMapUnit())
1641
0
            {
1642
0
                const Size aSize(OutputDevice::LogicToLogic(rBmp.GetPrefSize(), aMapMode, MapMode(MapUnit::Map100thMM)));
1643
1644
                // #i122388# This wrongly corrects in the given case; changing from 5000 100th mm to
1645
                // the described 50 cm (which is 50000 100th mm)
1646
0
                if((aSize.Width() > 50000) || (aSize.Height() > 50000))
1647
0
                {
1648
0
                    rBmp.SetPrefMapMode(MapMode(MapUnit::MapPixel));
1649
1650
                    // #i122388# also adapt size by applying the new MapMode
1651
0
                    const Size aNewSize(o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::pt));
1652
0
                    rBmp.SetPrefSize(aNewSize);
1653
0
                }
1654
0
            }
1655
0
        }
1656
0
    }
1657
1658
0
    return bRet;
1659
0
}
1660
1661
1662
bool TransferableDataHelper::GetGDIMetaFile(SotClipboardFormatId nFormat, GDIMetaFile& rMtf, size_t nMaxActions) const
1663
0
{
1664
0
    DataFlavor aFlavor;
1665
0
    return SotExchange::GetFormatDataFlavor(nFormat, aFlavor) &&
1666
0
        GetGDIMetaFile(aFlavor, rMtf) &&
1667
0
        (nMaxActions == 0 || rMtf.GetActionSize() < nMaxActions);
1668
0
}
1669
1670
1671
bool TransferableDataHelper::GetGDIMetaFile( const DataFlavor& rFlavor, GDIMetaFile& rMtf ) const
1672
0
{
1673
0
    std::unique_ptr<SvStream> xStm;
1674
0
    DataFlavor          aSubstFlavor;
1675
0
    bool                bRet = false;
1676
1677
0
    if( (xStm = GetSotStorageStream( rFlavor )) )
1678
0
    {
1679
0
        SvmReader aReader( *xStm );
1680
0
        aReader.Read( rMtf );
1681
0
        bRet = ( xStm->GetError() == ERRCODE_NONE );
1682
0
    }
1683
1684
0
    if( !bRet &&
1685
0
        HasFormat( SotClipboardFormatId::EMF ) &&
1686
0
        SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
1687
0
        (xStm = GetSotStorageStream( aSubstFlavor)) )
1688
0
    {
1689
0
        Graphic aGraphic;
1690
1691
0
        if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
1692
0
        {
1693
0
            rMtf = aGraphic.GetGDIMetaFile();
1694
0
            bRet = true;
1695
0
        }
1696
0
    }
1697
1698
0
    if( !bRet &&
1699
0
        HasFormat( SotClipboardFormatId::WMF ) &&
1700
0
        SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
1701
0
        (xStm = GetSotStorageStream( aSubstFlavor ) ) )
1702
0
    {
1703
0
        Graphic aGraphic;
1704
1705
0
        if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
1706
0
        {
1707
0
            rMtf = aGraphic.GetGDIMetaFile();
1708
0
            bRet = true;
1709
0
        }
1710
0
    }
1711
1712
0
    return bRet;
1713
0
}
1714
1715
1716
bool TransferableDataHelper::GetGraphic( SotClipboardFormatId nFormat, Graphic& rGraphic ) const
1717
0
{
1718
0
    if(SotClipboardFormatId::BITMAP == nFormat)
1719
0
    {
1720
        // try to get PNG first
1721
0
        DataFlavor aFlavor;
1722
1723
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
1724
0
        {
1725
0
            if(GetGraphic(aFlavor, rGraphic))
1726
0
            {
1727
0
                return true;
1728
0
            }
1729
0
        }
1730
0
    }
1731
1732
0
    DataFlavor aFlavor;
1733
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetGraphic( aFlavor, rGraphic ) );
1734
0
}
1735
1736
1737
bool TransferableDataHelper::GetGraphic( const css::datatransfer::DataFlavor& rFlavor, Graphic& rGraphic ) const
1738
0
{
1739
0
    DataFlavor  aFlavor;
1740
0
    bool        bRet = false;
1741
1742
0
    if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor) &&
1743
0
        TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1744
0
    {
1745
        // try to get PNG first
1746
0
        Bitmap aBmp;
1747
1748
0
        bRet = GetBitmap(aFlavor, aBmp);
1749
0
        if( bRet )
1750
0
            rGraphic = aBmp;
1751
0
    }
1752
0
    else if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PDF, aFlavor) &&
1753
0
            TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1754
0
    {
1755
0
        Graphic aGraphic;
1756
0
        if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor))
1757
0
        {
1758
0
            if (GraphicConverter::Import(*xStm, aGraphic) == ERRCODE_NONE)
1759
0
            {
1760
0
                rGraphic = std::move(aGraphic);
1761
0
                bRet = true;
1762
0
            }
1763
0
        }
1764
0
    }
1765
0
    else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor) && TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1766
0
    {
1767
0
        Bitmap aBitmap;
1768
1769
0
        bRet = GetBitmap(aFlavor, aBitmap);
1770
0
        if (bRet)
1771
0
            rGraphic = aBitmap;
1772
0
    }
1773
0
    else if(SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavor ) &&
1774
0
        TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
1775
0
    {
1776
0
        Bitmap aBmp;
1777
1778
0
        bRet = GetBitmap(aFlavor, aBmp);
1779
0
        if( bRet )
1780
0
            rGraphic = aBmp;
1781
0
    }
1782
0
    else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavor ) &&
1783
0
             TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
1784
0
    {
1785
        // Import EMF/WMF directly into Graphic to preserve GfxLink with
1786
        // original bytes. This avoids re-export through EMFWriter at DOCX
1787
        // save time, which changes reference device metrics and causes
1788
        // wrong scaling on reimport (tdf#168886, tdf#168237).
1789
        // Try EMF first, then WMF (same order as GetGDIMetaFile).
1790
0
        DataFlavor aMetaFlavor;
1791
0
        if (HasFormat(SotClipboardFormatId::EMF) &&
1792
0
            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::EMF, aMetaFlavor))
1793
0
        {
1794
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(aMetaFlavor))
1795
0
            {
1796
0
                if (GraphicConverter::Import(*xStm, rGraphic) == ERRCODE_NONE)
1797
0
                    bRet = true;
1798
0
            }
1799
0
        }
1800
0
        if (!bRet && HasFormat(SotClipboardFormatId::WMF) &&
1801
0
            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::WMF, aMetaFlavor))
1802
0
        {
1803
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(aMetaFlavor))
1804
0
            {
1805
0
                if (GraphicConverter::Import(*xStm, rGraphic) == ERRCODE_NONE)
1806
0
                    bRet = true;
1807
0
            }
1808
0
        }
1809
0
        if (!bRet)
1810
0
        {
1811
            // Fallback to old path if direct import fails
1812
0
            GDIMetaFile aMtf;
1813
0
            bRet = GetGDIMetaFile( aFlavor, aMtf );
1814
0
            if( bRet )
1815
0
                rGraphic = aMtf;
1816
0
        }
1817
0
    }
1818
0
    else
1819
0
    {
1820
0
        if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor) )
1821
0
        {
1822
0
            TypeSerializer aSerializer(*xStm);
1823
0
            aSerializer.readGraphic(rGraphic);
1824
0
            bRet = ( xStm->GetError() == ERRCODE_NONE );
1825
0
        }
1826
0
    }
1827
1828
0
    return bRet;
1829
0
}
1830
1831
1832
bool TransferableDataHelper::GetImageMap( SotClipboardFormatId nFormat, ImageMap& rIMap ) const
1833
0
{
1834
0
    DataFlavor aFlavor;
1835
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetImageMap( aFlavor, rIMap ) );
1836
0
}
1837
1838
1839
bool TransferableDataHelper::GetImageMap( const css::datatransfer::DataFlavor& rFlavor, ImageMap& rIMap ) const
1840
0
{
1841
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor);
1842
0
    if (!xStm)
1843
0
        return false;
1844
1845
0
    rIMap.Read( *xStm );
1846
0
    bool bRet = ( xStm->GetError() == ERRCODE_NONE );
1847
1848
0
    return bRet;
1849
0
}
1850
1851
1852
bool TransferableDataHelper::GetTransferableObjectDescriptor( SotClipboardFormatId nFormat, TransferableObjectDescriptor& rDesc ) const
1853
0
{
1854
0
    DataFlavor aFlavor;
1855
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetTransferableObjectDescriptor( rDesc ) );
1856
0
}
1857
1858
1859
bool TransferableDataHelper::GetTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc ) const
1860
0
{
1861
0
    rDesc = *mxObjDesc;
1862
0
    return true;
1863
0
}
1864
1865
1866
bool TransferableDataHelper::GetINetBookmark( SotClipboardFormatId nFormat, INetBookmark& rBmk ) const
1867
0
{
1868
0
    DataFlavor aFlavor;
1869
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetBookmark( aFlavor, rBmk ) );
1870
0
}
1871
1872
1873
bool TransferableDataHelper::GetINetBookmark( const css::datatransfer::DataFlavor& rFlavor, INetBookmark& rBmk ) const
1874
0
{
1875
0
    if( !HasFormat( rFlavor ))
1876
0
        return false;
1877
1878
0
    bool bRet = false;
1879
0
    const SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
1880
0
    switch( nFormat )
1881
0
    {
1882
0
        case SotClipboardFormatId::SOLK:
1883
0
        case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
1884
0
        {
1885
0
            OUString aString;
1886
0
            if( GetString( rFlavor, aString ) )
1887
0
            {
1888
0
                if( SotClipboardFormatId::UNIFORMRESOURCELOCATOR == nFormat )
1889
0
                {
1890
0
                    rBmk = INetBookmark( aString, aString );
1891
0
                    bRet = true;
1892
0
                }
1893
0
                else
1894
0
                {
1895
0
                    OUString    aURL, aDesc;
1896
0
                    sal_Int32   nStart = aString.indexOf( '@' ), nLen = aString.toInt32();
1897
1898
0
                    if( !nLen && aString[ 0 ] != '0' )
1899
0
                    {
1900
0
                        SAL_INFO( "svtools", "SOLK: 1. len=0" );
1901
0
                    }
1902
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 3 )
1903
0
                    {
1904
0
                        SAL_INFO( "svtools", "SOLK: 1. illegal start or wrong len" );
1905
0
                    }
1906
0
                    aURL = aString.copy( nStart + 1, nLen );
1907
1908
0
                    aString = aString.replaceAt( 0, nStart + 1 + nLen, u"" );
1909
0
                    nStart = aString.indexOf( '@' );
1910
0
                    nLen = aString.toInt32();
1911
1912
0
                    if( !nLen && aString[ 0 ] != '0' )
1913
0
                    {
1914
0
                        SAL_INFO( "svtools", "SOLK: 2. len=0" );
1915
0
                    }
1916
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 1 )
1917
0
                    {
1918
0
                        SAL_INFO( "svtools", "SOLK: 2. illegal start or wrong len" );
1919
0
                    }
1920
0
                    aDesc = aString.copy( nStart+1, nLen );
1921
1922
0
                    rBmk = INetBookmark( aURL, aDesc );
1923
0
                    bRet = true;
1924
0
                }
1925
0
            }
1926
0
        }
1927
0
        break;
1928
1929
0
        case SotClipboardFormatId::NETSCAPE_BOOKMARK:
1930
0
        {
1931
0
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1932
1933
0
            if (2048 == aSeq.getLength())
1934
0
            {
1935
0
                const char* p1 = reinterpret_cast< const char* >( aSeq.getConstArray() );
1936
0
                const char* p2 =  reinterpret_cast< const char* >( aSeq.getConstArray() ) + 1024;
1937
0
                rBmk = INetBookmark( OUString( p1, strlen(p1), osl_getThreadTextEncoding() ),
1938
0
                                     OUString( p2, strlen(p2), osl_getThreadTextEncoding() ) );
1939
0
                bRet = true;
1940
0
            }
1941
0
        }
1942
0
        break;
1943
1944
#ifdef _WIN32
1945
        case SotClipboardFormatId::FILEGRPDESCRIPTOR:
1946
        {
1947
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1948
1949
            if (aSeq.getLength())
1950
            {
1951
                FILEGROUPDESCRIPTORW const * pFDesc = reinterpret_cast<FILEGROUPDESCRIPTORW const *>(aSeq.getConstArray());
1952
1953
                if( pFDesc->cItems )
1954
                {
1955
                    OUString aDesc( o3tl::toU(pFDesc->fgd[ 0 ].cFileName) );
1956
1957
                    if( ( aDesc.getLength() > 4 ) && aDesc.endsWithIgnoreAsciiCase(".URL") )
1958
                    {
1959
                        std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( INetURLObject( aDesc ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1960
                                                                                  StreamMode::STD_READ ));
1961
1962
                        if( !pStream || pStream->GetError() )
1963
                        {
1964
                            DataFlavor aFileContentFlavor;
1965
1966
                            aSeq.realloc( 0 );
1967
                            pStream.reset();
1968
1969
                            if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::FILECONTENT, aFileContentFlavor))
1970
                            {
1971
                                aSeq = GetSequence(aFileContentFlavor, OUString());
1972
                                if (aSeq.getLength())
1973
                                    pStream.reset(new SvMemoryStream( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), StreamMode::STD_READ ));
1974
                            }
1975
                        }
1976
1977
                        if( pStream )
1978
                        {
1979
                            OString aLine;
1980
                            bool bInA = false, bInW = false, bAFound = false;
1981
1982
                            while( pStream->ReadLine( aLine ) )
1983
                            {
1984
                                if (aLine.startsWithIgnoreAsciiCase("[InternetShortcut", &aLine))
1985
                                {
1986
                                    // May be [InternetShortcut], or [InternetShortcut.A], or
1987
                                    // [InternetShortcut.W] (the latter has UTF-7-encoded URL)
1988
                                    bInW = aLine.equalsIgnoreAsciiCase(".W]");
1989
                                    bInA = !bAFound && !bInW
1990
                                           && (aLine == "]" || aLine.equalsIgnoreAsciiCase(".A]"));
1991
                                }
1992
                                else if (aLine.startsWith("["))
1993
                                {
1994
                                    bInA = bInW = false;
1995
                                }
1996
                                else if ((bInA || bInW) && aLine.startsWithIgnoreAsciiCase("URL="))
1997
                                {
1998
                                    auto eTextEncoding = bInW ? RTL_TEXTENCODING_UTF7
1999
                                                              : osl_getThreadTextEncoding();
2000
                                    rBmk = INetBookmark( OStringToOUString(aLine.subView(4), eTextEncoding),
2001
                                                         aDesc.copy(0, aDesc.getLength() - 4) );
2002
                                    bRet = true;
2003
                                    if (bInW)
2004
                                        break;
2005
                                    else
2006
                                        bAFound = true; // Keep looking for "W"
2007
                                }
2008
                            }
2009
                        }
2010
                    }
2011
                }
2012
            }
2013
        }
2014
        break;
2015
#endif
2016
0
        default: break;
2017
0
    }
2018
0
    return bRet;
2019
0
}
2020
2021
2022
bool TransferableDataHelper::GetINetImage( SotClipboardFormatId nFormat,
2023
                                                INetImage& rINtImg ) const
2024
0
{
2025
0
    DataFlavor aFlavor;
2026
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetImage( aFlavor, rINtImg ) );
2027
0
}
2028
2029
2030
bool TransferableDataHelper::GetINetImage(
2031
        const css::datatransfer::DataFlavor& rFlavor,
2032
        INetImage& rINtImg ) const
2033
0
{
2034
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor );
2035
0
    if (!xStm)
2036
0
        return false;
2037
2038
0
    bool bRet = rINtImg.Read( *xStm, SotExchange::GetFormat( rFlavor ) );
2039
0
    return bRet;
2040
0
}
2041
2042
2043
bool TransferableDataHelper::GetFileList( SotClipboardFormatId nFormat,
2044
                                                FileList& rFileList ) const
2045
0
{
2046
0
    DataFlavor aFlavor;
2047
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetFileList( rFileList ) );
2048
0
}
2049
2050
2051
bool TransferableDataHelper::GetFileList( FileList& rFileList ) const
2052
0
{
2053
0
    bool                bRet = false;
2054
2055
0
    for( sal_uInt32 i = 0, nFormatCount = GetFormatCount(); ( i < nFormatCount ) && !bRet; ++i )
2056
0
    {
2057
0
        if( SotClipboardFormatId::FILE_LIST == GetFormat( i ) )
2058
0
        {
2059
0
            const DataFlavor aFlavor( GetFormatDataFlavor( i ) );
2060
2061
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( aFlavor ))
2062
0
            {
2063
0
                if( aFlavor.MimeType.indexOf( "text/uri-list" ) > -1 )
2064
0
                {
2065
0
                    OStringBuffer aDiskString;
2066
2067
0
                    while( xStm->ReadLine( aDiskString ) )
2068
0
                        if( !aDiskString.isEmpty() && aDiskString[0] != '#' )
2069
0
                            rFileList.AppendFile( OStringToOUString(aDiskString, RTL_TEXTENCODING_UTF8) );
2070
2071
0
                    bRet = true;
2072
0
                }
2073
0
                else
2074
0
                    bRet = ( ReadFileList( *xStm, rFileList ).GetError() == ERRCODE_NONE );
2075
0
            }
2076
0
        }
2077
0
    }
2078
2079
0
    return bRet;
2080
0
}
2081
2082
2083
Sequence<sal_Int8> TransferableDataHelper::GetSequence( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2084
0
{
2085
0
    DataFlavor aFlavor;
2086
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2087
0
        return Sequence<sal_Int8>();
2088
2089
0
    return GetSequence(aFlavor, rDestDoc);
2090
0
}
2091
2092
Sequence<sal_Int8> TransferableDataHelper::GetSequence( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2093
0
{
2094
0
    const Any aAny = GetAny(rFlavor, rDestDoc);
2095
0
    Sequence<sal_Int8> aSeq;
2096
0
    if (aAny.hasValue())
2097
0
        aAny >>= aSeq;
2098
2099
0
    return aSeq;
2100
0
}
2101
2102
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( SotClipboardFormatId nFormat ) const
2103
0
{
2104
0
    DataFlavor aFlavor;
2105
0
    if (!SotExchange::GetFormatDataFlavor( nFormat, aFlavor ))
2106
0
        return nullptr;
2107
0
    return GetSotStorageStream( aFlavor );
2108
0
}
2109
2110
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( const DataFlavor& rFlavor ) const
2111
0
{
2112
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
2113
0
    if (!aSeq.hasElements())
2114
0
        return nullptr;
2115
2116
0
    std::unique_ptr<SvStream> xStream = SotTempStream::Create( u""_ustr );
2117
0
    xStream->WriteBytes( aSeq.getConstArray(), aSeq.getLength() );
2118
0
    xStream->Seek(0);
2119
0
    return xStream;
2120
0
}
2121
2122
Reference<XInputStream> TransferableDataHelper::GetInputStream( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2123
0
{
2124
0
    DataFlavor aFlavor;
2125
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2126
0
        return Reference<XInputStream>();
2127
2128
0
    return GetInputStream(aFlavor, rDestDoc);
2129
0
}
2130
2131
Reference<XInputStream> TransferableDataHelper::GetInputStream( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2132
0
{
2133
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, rDestDoc);
2134
2135
0
    if (!aSeq.hasElements())
2136
0
        return Reference<XInputStream>();
2137
2138
0
    Reference<XInputStream> xStream(new comphelper::SequenceInputStream(aSeq));
2139
0
    return xStream;
2140
0
}
2141
2142
void TransferableDataHelper::Rebind( const Reference< XTransferable >& _rxNewContent )
2143
0
{
2144
0
    mxTransfer = _rxNewContent;
2145
0
    InitFormats();
2146
0
}
2147
2148
bool TransferableDataHelper::StartClipboardListening( )
2149
0
{
2150
0
    SolarMutexGuard g;
2151
2152
0
    StopClipboardListening( );
2153
2154
0
    mxClipboardListener = new TransferableClipboardNotifier(mxClipboard, *this);
2155
2156
0
    return mxClipboardListener->isListening();
2157
0
}
2158
2159
void TransferableDataHelper::StopClipboardListening( )
2160
0
{
2161
0
    SolarMutexGuard g;
2162
2163
0
    if (mxClipboardListener.is())
2164
0
    {
2165
0
        mxClipboardListener->dispose();
2166
0
        mxClipboardListener.clear();
2167
0
    }
2168
0
}
2169
2170
TransferableDataHelper TransferableDataHelper::CreateFromClipboard(const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& rClipboard)
2171
0
{
2172
0
    TransferableDataHelper  aRet;
2173
2174
0
    if( rClipboard.is() )
2175
0
    {
2176
0
        try
2177
0
        {
2178
0
            Reference< XTransferable > xTransferable( rClipboard->getContents() );
2179
2180
0
            if( xTransferable.is() )
2181
0
            {
2182
0
                aRet = TransferableDataHelper( xTransferable );
2183
                // also copy the clipboard
2184
0
                aRet.mxClipboard = rClipboard;
2185
0
            }
2186
0
        }
2187
0
        catch( const css::uno::Exception& )
2188
0
        {
2189
0
        }
2190
0
    }
2191
2192
0
    return aRet;
2193
0
}
2194
2195
TransferableDataHelper TransferableDataHelper::CreateFromSystemClipboard( vcl::Window * pWindow )
2196
0
{
2197
0
    DBG_ASSERT( pWindow, "Window pointer is NULL" );
2198
2199
0
    Reference< XClipboard > xClipboard;
2200
2201
0
    if( pWindow )
2202
0
        xClipboard = pWindow->GetClipboard();
2203
2204
0
    return CreateFromClipboard(xClipboard);
2205
0
}
2206
2207
TransferableDataHelper TransferableDataHelper::CreateFromPrimarySelection()
2208
0
{
2209
0
    Reference< XClipboard > xSelection(GetSystemPrimarySelection());
2210
0
    TransferableDataHelper   aRet;
2211
2212
0
    if( xSelection.is() )
2213
0
    {
2214
0
           SolarMutexReleaser aReleaser;
2215
2216
0
           try
2217
0
               {
2218
0
                   Reference< XTransferable > xTransferable( xSelection->getContents() );
2219
2220
0
                   if( xTransferable.is() )
2221
0
                   {
2222
0
                           aRet = TransferableDataHelper( xTransferable );
2223
0
                           aRet.mxClipboard = std::move(xSelection);
2224
0
                   }
2225
0
               }
2226
0
           catch( const css::uno::Exception& )
2227
0
               {
2228
0
               }
2229
0
    }
2230
2231
0
    return aRet;
2232
0
}
2233
2234
bool TransferableDataHelper::IsEqual( const css::datatransfer::DataFlavor& rInternalFlavor,
2235
                                      const css::datatransfer::DataFlavor& rRequestFlavor )
2236
0
{
2237
0
    const Reference< XComponentContext >&          xContext( ::comphelper::getProcessComponentContext() );
2238
0
    bool                                    bRet = false;
2239
2240
0
    try
2241
0
    {
2242
0
        Reference< XMimeContentTypeFactory >    xMimeFact = MimeContentTypeFactory::create( xContext );
2243
2244
0
        Reference< XMimeContentType > xRequestType1( xMimeFact->createMimeContentType( rInternalFlavor.MimeType ) );
2245
0
        Reference< XMimeContentType > xRequestType2( xMimeFact->createMimeContentType( rRequestFlavor.MimeType ) );
2246
2247
0
        if( xRequestType1.is() && xRequestType2.is() )
2248
0
        {
2249
0
            if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( xRequestType2->getFullMediaType() ) )
2250
0
            {
2251
0
                if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
2252
0
                {
2253
                    // special handling for text/plain media types
2254
0
                    static constexpr OUString aCharsetString( u"charset"_ustr );
2255
2256
0
                    if( !xRequestType2->hasParameter( aCharsetString ) ||
2257
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "utf-16" ) ||
2258
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "unicode" ) )
2259
0
                    {
2260
0
                        bRet = true;
2261
0
                    }
2262
0
                }
2263
0
                else if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice" ) )
2264
0
                {
2265
                    // special handling for application/x-openoffice media types
2266
0
                    static constexpr OUString aFormatString( u"windows_formatname"_ustr );
2267
2268
0
                    if( xRequestType1->hasParameter( aFormatString ) &&
2269
0
                        xRequestType2->hasParameter( aFormatString ) &&
2270
0
                        xRequestType1->getParameterValue( aFormatString ).equalsIgnoreAsciiCase( xRequestType2->getParameterValue( aFormatString ) ) )
2271
0
                    {
2272
0
                        bRet = true;
2273
0
                    }
2274
0
                }
2275
0
                else
2276
0
                    bRet = true;
2277
0
            }
2278
0
        }
2279
0
    }
2280
0
    catch( const css::uno::Exception& )
2281
0
    {
2282
0
        bRet = rInternalFlavor.MimeType.equalsIgnoreAsciiCase( rRequestFlavor.MimeType );
2283
0
    }
2284
2285
0
    return bRet;
2286
0
}
2287
2288
static bool WriteDDELink_impl(SvStream& stream, rtl_TextEncoding encoding,
2289
                              std::u16string_view application, std::u16string_view topic,
2290
                              std::u16string_view item, std::u16string_view extra)
2291
0
{
2292
    // Put Link format, potentially with extra: either application\0topic\0item\0\0
2293
    // or application\0topic\0item\0extra\0\0
2294
0
    assert(rtl_isOctetTextEncoding(encoding)); // The Link format is 8-bit
2295
0
    stream.WriteUnicodeOrByteText(application, encoding, true);
2296
0
    stream.WriteUnicodeOrByteText(topic, encoding, true);
2297
0
    stream.WriteUnicodeOrByteText(item, encoding, true);
2298
0
    if (!extra.empty())
2299
0
        stream.WriteUnicodeOrByteText(extra, encoding, true);
2300
0
    stream.WriteChar('\0'); // One more trailing zero
2301
0
    return stream.GetErrorCode() == ERRCODE_NONE;
2302
0
}
2303
2304
// static
2305
bool TransferableDataHelper::WriteDDELink(SvStream& stream, std::u16string_view application,
2306
                                          std::u16string_view topic, std::u16string_view item,
2307
                                          std::u16string_view extra)
2308
0
{
2309
    // 1. Put standard Link format in the system encoding
2310
0
    WriteDDELink_impl(stream, osl_getThreadTextEncoding(), application, topic, item, extra);
2311
2312
    // 2. Put our extension to the format. Start with a magic string "ULnk", followed by
2313
    // UTF-8-encoded Link format
2314
0
    stream.WriteOString("ULnk");
2315
0
    return WriteDDELink_impl(stream, RTL_TEXTENCODING_UTF8, application, topic, item, extra);
2316
0
}
2317
2318
static size_t ReadDDELink_impl(std::string_view str, std::string_view& application,
2319
                               std::string_view& topic, std::string_view& item,
2320
                               std::string_view& rest)
2321
0
{
2322
0
    application = {};
2323
0
    topic = {};
2324
0
    item = {};
2325
0
    rest = {};
2326
2327
0
    size_t start = 0;
2328
0
    size_t end = str.find('\0', start);
2329
0
    application = str.substr(start, end - start);
2330
0
    if (end == std::string_view::npos)
2331
0
        return end;
2332
0
    start = end + 1;
2333
0
    end = str.find('\0', start);
2334
0
    topic = str.substr(start, end - start);
2335
0
    if (end == std::string_view::npos)
2336
0
        return end;
2337
0
    start = end + 1;
2338
0
    end = str.find('\0', start);
2339
0
    item = str.substr(start, end - start);
2340
0
    if (end >= str.size() - 1 || str[end + 1] == '\0')
2341
0
        return end;
2342
0
    start = end + 1;
2343
0
    do
2344
0
    {
2345
        // Read all remaining data until \0\0 into rest, including possible single \0
2346
0
        end = str.find('\0', end + 1);
2347
0
    } while (end < str.size() - 1 && str[end + 1] != '\0');
2348
0
    rest = str.substr(start, end - start);
2349
0
    return end;
2350
0
}
2351
2352
bool TransferableDataHelper::ReadDDELink(OUString& application, OUString& topic, OUString& item,
2353
                                         OUString& rest) const
2354
0
{
2355
0
    css::uno::Sequence<sal_Int8> sequence = GetSequence(SotClipboardFormatId::LINK, {});
2356
0
    if (!sequence.hasElements())
2357
0
    {
2358
0
        SAL_WARN("svtools", "DDE data not found");
2359
0
        return false;
2360
0
    }
2361
2362
0
    std::string_view str(reinterpret_cast<const char*>(sequence.getConstArray()),
2363
0
                         sequence.getLength());
2364
0
    std::string_view application_view, topic_view, item_view, rest_view;
2365
0
    rtl_TextEncoding encoding = osl_getThreadTextEncoding();
2366
0
    size_t end = ReadDDELink_impl(str, application_view, topic_view, item_view, rest_view);
2367
0
    if (end < str.size() - 1 && str[end + 1] == '\0' && str.substr(end + 2, 4) == "ULnk")
2368
0
    {
2369
        // Now try to read our UTF-8 extension; if it's present, use it unconditionally, even if
2370
        // osl_getThreadTextEncoding gives RTL_TEXTENCODING_UTF8, because the extension is immune
2371
        // to possible different source encoding
2372
0
        encoding = RTL_TEXTENCODING_UTF8;
2373
0
        ReadDDELink_impl(str.substr(end + 6), application_view, topic_view, item_view, rest_view);
2374
0
    }
2375
0
    application = OStringToOUString(application_view, encoding);
2376
0
    topic = OStringToOUString(topic_view, encoding);
2377
0
    item = OStringToOUString(item_view, encoding);
2378
0
    rest = OStringToOUString(rest_view, encoding);
2379
0
    return !application.isEmpty() && !topic.isEmpty() && !item.isEmpty();
2380
0
}
2381
2382
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */