Coverage Report

Created: 2026-02-14 09:37

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, osl_getThreadTextEncoding() );
103
0
    rOStm.WriteUniOrByteString( rObjDesc.maDisplayName, osl_getThreadTextEncoding() );
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(osl_getThreadTextEncoding());
137
0
    const OUString displayName = rIStm.ReadUniOrByteString(osl_getThreadTextEncoding());
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
            // explicitly use Bitmap::Write with bCompressed = sal_False and bFileHeader = sal_True
745
0
            WriteDIB(rBitmap, aMemStm, false, true);
746
0
        }
747
748
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
749
0
    }
750
751
0
    return maAny.hasValue();
752
0
}
753
754
755
bool TransferableHelper::SetGDIMetaFile( const GDIMetaFile& rMtf )
756
0
{
757
0
    if( rMtf.GetActionSize() )
758
0
    {
759
0
        SvMemoryStream aMemStm( 65535, 65535 );
760
761
0
        SvmWriter aWriter( aMemStm );
762
0
        aWriter.Write( rMtf );
763
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
764
0
    }
765
766
0
    return maAny.hasValue();
767
0
}
768
769
770
bool TransferableHelper::SetGraphic( const Graphic& rGraphic )
771
0
{
772
0
    if( rGraphic.GetType() != GraphicType::NONE )
773
0
    {
774
0
        SvMemoryStream aMemStm( 65535, 65535 );
775
776
0
        aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
777
0
        aMemStm.SetCompressMode( SvStreamCompressFlags::NATIVE );
778
779
0
        TypeSerializer aSerializer(aMemStm);
780
0
        aSerializer.writeGraphic(rGraphic);
781
782
0
        maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
783
0
    }
784
785
0
    return maAny.hasValue();
786
0
}
787
788
789
bool TransferableHelper::SetImageMap( const ImageMap& rIMap )
790
0
{
791
0
    SvMemoryStream aMemStm( 8192, 8192 );
792
793
0
    aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
794
0
    rIMap.Write( aMemStm );
795
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
796
797
0
    return maAny.hasValue();
798
0
}
799
800
801
bool TransferableHelper::SetTransferableObjectDescriptor( const TransferableObjectDescriptor& rDesc )
802
0
{
803
0
    PrepareOLE( rDesc );
804
805
0
    SvMemoryStream aMemStm( 1024, 1024 );
806
807
0
    WriteTransferableObjectDescriptor( aMemStm, rDesc );
808
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
809
810
0
    return maAny.hasValue();
811
0
 }
812
813
814
bool TransferableHelper::SetINetBookmark( const INetBookmark& rBmk,
815
                                          const css::datatransfer::DataFlavor& rFlavor )
816
0
{
817
0
    rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
818
819
0
    switch( SotExchange::GetFormat( rFlavor ) )
820
0
    {
821
0
        case SotClipboardFormatId::SOLK:
822
0
        {
823
0
            OString sURL(OUStringToOString(rBmk.GetURL(), eSysCSet));
824
0
            OString sDesc(OUStringToOString(rBmk.GetDescription(), eSysCSet));
825
0
            OString sOut =
826
0
                OString::number(sURL.getLength())
827
0
                + "@" + sURL
828
0
                + OString::number(sDesc.getLength())
829
0
                + "@" + sDesc;
830
831
0
            Sequence< sal_Int8 > aSeq(sOut.getLength());
832
0
            memcpy(aSeq.getArray(), sOut.getStr(), sOut.getLength());
833
0
            maAny <<= aSeq;
834
0
        }
835
0
        break;
836
837
0
        case SotClipboardFormatId::STRING:
838
0
        case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
839
0
            maAny <<= rBmk.GetURL();
840
0
            break;
841
842
0
        case SotClipboardFormatId::NETSCAPE_BOOKMARK:
843
0
        {
844
0
            Sequence< sal_Int8 > aSeq( 2048 );
845
0
            char* pSeq = reinterpret_cast< char* >( aSeq.getArray() );
846
847
            // strncpy fills the rest with nulls, as we need
848
0
            strncpy( pSeq, OUStringToOString(rBmk.GetURL(), eSysCSet).getStr(), 1024 );
849
0
            strncpy( pSeq + 1024, OUStringToOString(rBmk.GetDescription(), eSysCSet).getStr(), 1024 );
850
851
0
            maAny <<= aSeq;
852
0
        }
853
0
        break;
854
855
#ifdef _WIN32
856
        case SotClipboardFormatId::FILEGRPDESCRIPTOR:
857
        {
858
            Sequence< sal_Int8 >    aSeq( sizeof( FILEGROUPDESCRIPTORW ) );
859
            FILEGROUPDESCRIPTORW*   pFDesc = reinterpret_cast<FILEGROUPDESCRIPTORW*>(aSeq.getArray());
860
            FILEDESCRIPTORW&        rFDesc1 = pFDesc->fgd[ 0 ];
861
862
            pFDesc->cItems = 1;
863
            memset( &rFDesc1, 0, sizeof( rFDesc1 ) );
864
            rFDesc1.dwFlags = FD_LINKUI;
865
866
            OUStringBuffer aStr(rBmk.GetDescription());
867
            for( size_t nChar = 0; (nChar = std::u16string_view(aStr).find_first_of(u"\\/:*?\"<>|"sv, nChar)) != std::u16string_view::npos; )
868
                aStr.remove(nChar, 1);
869
870
            aStr.insert(0, "Shortcut to ");
871
            aStr.append(".URL");
872
            wcscpy( rFDesc1.cFileName, o3tl::toW(aStr.getStr()) );
873
874
            maAny <<= aSeq;
875
        }
876
        break;
877
878
        case SotClipboardFormatId::FILECONTENT:
879
        {
880
            maAny <<= "[InternetShortcut]\x0aURL=" + rBmk.GetURL();
881
        }
882
        break;
883
#endif
884
885
0
        default:
886
0
        break;
887
0
    }
888
889
0
    return maAny.hasValue();
890
0
}
891
892
893
bool TransferableHelper::SetINetImage( const INetImage& rINtImg,
894
                                       const css::datatransfer::DataFlavor& rFlavor )
895
0
{
896
0
    SvMemoryStream aMemStm( 1024, 1024 );
897
898
0
    aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
899
0
    rINtImg.Write( aMemStm, SotExchange::GetFormat( rFlavor ) );
900
901
0
    maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
902
903
0
    return maAny.hasValue();
904
0
}
905
906
907
bool TransferableHelper::SetObject( void* pUserObject, sal_uInt32 nUserObjectId, const DataFlavor& rFlavor )
908
0
{
909
0
    SvMemoryStream aStm;
910
911
0
    aStm.SetVersion( SOFFICE_FILEFORMAT_50 );
912
913
0
    if( pUserObject && WriteObject( aStm, pUserObject, nUserObjectId, rFlavor ) )
914
0
    {
915
0
        const sal_uInt32        nLen = aStm.TellEnd();
916
0
        Sequence< sal_Int8 >    aSeq( nLen );
917
918
0
        aStm.Seek( STREAM_SEEK_TO_BEGIN );
919
0
        aStm.ReadBytes(aSeq.getArray(), nLen);
920
921
0
        if( nLen && ( SotExchange::GetFormat( rFlavor ) == SotClipboardFormatId::STRING ) )
922
0
        {
923
            //JP 24.7.2001: as I know was this only for the writer application and this
924
            //              writes now UTF16 format into the stream
925
            //JP 6.8.2001:  and now it writes UTF8 because then exist no problem with
926
            //              little / big endians! - Bug 88121
927
0
            maAny <<= OUString( reinterpret_cast< const char* >( aSeq.getConstArray() ), nLen - 1, RTL_TEXTENCODING_UTF8 );
928
0
        }
929
0
        else
930
0
            maAny <<= aSeq;
931
0
    }
932
933
0
    return maAny.hasValue();
934
0
}
935
936
937
bool TransferableHelper::WriteObject( SvStream&, void*, sal_uInt32, const DataFlavor& )
938
0
{
939
0
    OSL_FAIL( "TransferableHelper::WriteObject( ... ) not implemented" );
940
0
    return false;
941
0
}
942
943
944
void TransferableHelper::DragFinished( sal_Int8 )
945
0
{
946
0
}
947
948
949
void TransferableHelper::ObjectReleased()
950
0
{
951
0
}
952
953
954
void TransferableHelper::PrepareOLE( const TransferableObjectDescriptor& rObjDesc )
955
0
{
956
0
    mxObjDesc.reset(new TransferableObjectDescriptor(rObjDesc));
957
958
0
    if( HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) )
959
0
        AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
960
0
}
961
962
void TransferableHelper::CopyToClipboard(const Reference<XClipboard>& rClipboard)
963
0
{
964
0
    if( rClipboard.is() )
965
0
        mxClipboard = rClipboard;
966
967
0
    if( !mxClipboard.is() || mxTerminateListener.is() )
968
0
        return;
969
970
0
    try
971
0
    {
972
0
        mxTerminateListener = new TerminateListener(*this);
973
0
        Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
974
0
        xDesktop->addTerminateListener(mxTerminateListener);
975
976
0
        mxClipboard->setContents(this, this);
977
0
    }
978
0
    catch( const css::uno::Exception& )
979
0
    {
980
0
    }
981
0
}
982
983
void TransferableHelper::CopyToClipboard(vcl::Window* pWindow)
984
0
{
985
0
    DBG_ASSERT( pWindow, "Window pointer is NULL" );
986
0
    Reference< XClipboard > xClipboard;
987
988
0
    if( pWindow )
989
0
        xClipboard = pWindow->GetClipboard();
990
991
0
    CopyToClipboard(xClipboard);
992
0
}
993
994
void TransferableHelper::CopyToSelection(const Reference<XClipboard>& rSelection)
995
0
{
996
0
    if( !rSelection.is() || mxTerminateListener.is() )
997
0
        return;
998
999
0
    try
1000
0
    {
1001
0
        mxTerminateListener = new TerminateListener(*this);
1002
0
        Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1003
0
        xDesktop->addTerminateListener(mxTerminateListener);
1004
1005
0
        rSelection->setContents(this, this);
1006
0
    }
1007
0
    catch( const css::uno::Exception& )
1008
0
    {
1009
0
    }
1010
0
}
1011
1012
void TransferableHelper::CopyToPrimarySelection()
1013
0
{
1014
0
    CopyToSelection(GetSystemPrimarySelection());
1015
0
}
1016
1017
void TransferableHelper::StartDrag( vcl::Window* pWindow, sal_Int8 nDnDSourceActions )
1018
0
{
1019
0
    assert(pWindow && "Window pointer is NULL");
1020
0
    Reference< XDragSource > xDragSource( pWindow->GetDragSource() );
1021
1022
0
    if( !xDragSource.is() )
1023
0
        return;
1024
1025
    /*
1026
     *    #96792# release mouse before actually starting DnD.
1027
     *    This is necessary for the X11 DnD implementation to work.
1028
     */
1029
0
    if( pWindow->IsMouseCaptured() )
1030
0
        pWindow->ReleaseMouse();
1031
1032
0
    const Point aPt( pWindow->GetPointerPosPixel() );
1033
1034
    // On macOS we are forced to execute 'startDrag' synchronously
1035
    // contrary to the XDragSource interface specification because
1036
    // we can receive drag events from the system only in the main
1037
    // thread
1038
0
#if !defined(MACOSX)
1039
0
    SolarMutexReleaser aReleaser;
1040
0
#endif
1041
1042
0
    try
1043
0
    {
1044
0
        DragGestureEvent    aEvt;
1045
0
        aEvt.DragAction = DNDConstants::ACTION_COPY;
1046
0
        aEvt.DragOriginX = aPt.X();
1047
0
        aEvt.DragOriginY = aPt.Y();
1048
0
        aEvt.DragSource = xDragSource;
1049
1050
0
        xDragSource->startDrag( aEvt, nDnDSourceActions, DND_POINTER_NONE, DND_IMAGE_NONE, this, this );
1051
0
    }
1052
0
    catch( const css::uno::Exception& )
1053
0
    {
1054
0
    }
1055
0
}
1056
1057
void TransferableHelper::ClearPrimarySelection()
1058
0
{
1059
0
    Reference< XClipboard > xSelection(GetSystemPrimarySelection());
1060
1061
0
    if( xSelection.is() )
1062
0
        xSelection->setContents( nullptr, nullptr );
1063
0
}
1064
1065
class TransferableClipboardNotifier : public ::cppu::WeakImplHelper< XClipboardListener >
1066
{
1067
private:
1068
    Reference< XClipboardNotifier > mxNotifier;
1069
    TransferableDataHelper*         mpListener;
1070
1071
protected:
1072
    // XClipboardListener
1073
    virtual void SAL_CALL changedContents( const clipboard::ClipboardEvent& event ) override;
1074
1075
    // XEventListener
1076
    virtual void SAL_CALL disposing( const EventObject& Source ) override;
1077
1078
public:
1079
    TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener );
1080
1081
    /// determines whether we're currently listening
1082
0
    bool isListening() const { return mpListener != nullptr; }
1083
1084
    /// makes the instance non-functional
1085
    void    dispose();
1086
};
1087
1088
1089
TransferableClipboardNotifier::TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener )
1090
0
    :mxNotifier( _rxClipboard, UNO_QUERY )
1091
0
    ,mpListener( &_rListener )
1092
0
{
1093
0
    osl_atomic_increment( &m_refCount );
1094
0
    {
1095
0
        if ( mxNotifier.is() )
1096
0
            mxNotifier->addClipboardListener( this );
1097
0
        else
1098
            // born dead
1099
0
            mpListener = nullptr;
1100
0
    }
1101
0
    osl_atomic_decrement( &m_refCount );
1102
0
}
1103
1104
1105
void SAL_CALL TransferableClipboardNotifier::changedContents( const clipboard::ClipboardEvent& event )
1106
0
{
1107
0
    SolarMutexGuard aSolarGuard;
1108
0
    if( mpListener )
1109
0
        mpListener->Rebind( event.Contents );
1110
0
}
1111
1112
1113
void SAL_CALL TransferableClipboardNotifier::disposing( const EventObject& )
1114
0
{
1115
    // clipboard is being disposed. Hmm. Okay, become disfunctional myself.
1116
0
    dispose();
1117
0
}
1118
1119
1120
void TransferableClipboardNotifier::dispose()
1121
0
{
1122
0
    SolarMutexGuard g;
1123
1124
0
    Reference< XClipboardListener > xKeepMeAlive( this );
1125
1126
0
    if ( mxNotifier.is() )
1127
0
        mxNotifier->removeClipboardListener( this );
1128
0
    mxNotifier.clear();
1129
1130
0
    mpListener = nullptr;
1131
0
}
1132
1133
TransferableDataHelper::TransferableDataHelper()
1134
0
    : mxObjDesc(new TransferableObjectDescriptor)
1135
0
{
1136
0
}
1137
1138
TransferableDataHelper::TransferableDataHelper(const Reference< css::datatransfer::XTransferable >& rxTransferable)
1139
0
    : mxTransfer(rxTransferable)
1140
0
    , mxObjDesc(new TransferableObjectDescriptor)
1141
0
{
1142
0
    InitFormats();
1143
0
}
1144
1145
TransferableDataHelper::TransferableDataHelper(const TransferableDataHelper& rDataHelper)
1146
0
    : mxTransfer(rDataHelper.mxTransfer)
1147
0
    , mxClipboard(rDataHelper.mxClipboard)
1148
0
    , maFormats(rDataHelper.maFormats)
1149
0
    , mxObjDesc(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc))
1150
0
{
1151
0
}
1152
1153
TransferableDataHelper::TransferableDataHelper(TransferableDataHelper&& rDataHelper) noexcept
1154
0
    : mxTransfer(std::move(rDataHelper.mxTransfer))
1155
0
    , mxClipboard(std::move(rDataHelper.mxClipboard))
1156
0
    , maFormats(std::move(rDataHelper.maFormats))
1157
0
    , mxObjDesc(std::move(rDataHelper.mxObjDesc))
1158
0
{
1159
0
}
1160
1161
TransferableDataHelper& TransferableDataHelper::operator=( const TransferableDataHelper& rDataHelper )
1162
0
{
1163
0
    if ( this != &rDataHelper )
1164
0
    {
1165
0
        SolarMutexGuard g;
1166
1167
0
        const bool bWasClipboardListening = mxClipboardListener.is();
1168
1169
0
        if (bWasClipboardListening)
1170
0
            StopClipboardListening();
1171
1172
0
        mxTransfer = rDataHelper.mxTransfer;
1173
0
        maFormats = rDataHelper.maFormats;
1174
0
        mxObjDesc.reset(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc));
1175
0
        mxClipboard = rDataHelper.mxClipboard;
1176
1177
0
        if (bWasClipboardListening)
1178
0
            StartClipboardListening();
1179
0
    }
1180
1181
0
    return *this;
1182
0
}
1183
1184
TransferableDataHelper& TransferableDataHelper::operator=(TransferableDataHelper&& rDataHelper)
1185
0
{
1186
0
    SolarMutexGuard g;
1187
1188
0
    const bool bWasClipboardListening = mxClipboardListener.is();
1189
1190
0
    if (bWasClipboardListening)
1191
0
        StopClipboardListening();
1192
1193
0
    mxTransfer = std::move(rDataHelper.mxTransfer);
1194
0
    maFormats = std::move(rDataHelper.maFormats);
1195
0
    mxObjDesc = std::move(rDataHelper.mxObjDesc);
1196
0
    mxClipboard = std::move(rDataHelper.mxClipboard);
1197
1198
0
    if (bWasClipboardListening)
1199
0
        StartClipboardListening();
1200
1201
0
    return *this;
1202
0
}
1203
1204
TransferableDataHelper::~TransferableDataHelper()
1205
0
{
1206
0
    StopClipboardListening( );
1207
0
    {
1208
0
        SolarMutexGuard g;
1209
0
        maFormats.clear();
1210
0
        mxObjDesc.reset();
1211
0
    }
1212
0
}
1213
1214
void TransferableDataHelper::FillDataFlavorExVector( const Sequence< DataFlavor >& rDataFlavorSeq,
1215
                                                     DataFlavorExVector& rDataFlavorExVector )
1216
0
{
1217
0
    try
1218
0
    {
1219
0
        const Reference< XComponentContext >&          xContext( ::comphelper::getProcessComponentContext() );
1220
0
        Reference< XMimeContentTypeFactory >    xMimeFact = MimeContentTypeFactory::create( xContext );
1221
0
        DataFlavorEx                            aFlavorEx;
1222
0
        static constexpr OUString        aCharsetStr( u"charset"_ustr );
1223
1224
1225
0
        for (auto const& rFlavor : rDataFlavorSeq)
1226
0
        {
1227
0
            Reference< XMimeContentType >   xMimeType;
1228
1229
0
            try
1230
0
            {
1231
0
                if( !rFlavor.MimeType.isEmpty() )
1232
0
                    xMimeType = xMimeFact->createMimeContentType( rFlavor.MimeType );
1233
0
            }
1234
0
            catch( const css::uno::Exception& )
1235
0
            {
1236
0
            }
1237
1238
0
            aFlavorEx.MimeType = rFlavor.MimeType;
1239
0
            aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
1240
0
            aFlavorEx.DataType = rFlavor.DataType;
1241
0
            aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
1242
1243
0
            rDataFlavorExVector.push_back( aFlavorEx );
1244
1245
            // add additional formats for special mime types
1246
0
            if(SotClipboardFormatId::BMP == aFlavorEx.mnSotId || SotClipboardFormatId::PNG == aFlavorEx.mnSotId || SotClipboardFormatId::JPEG == aFlavorEx.mnSotId)
1247
0
            {
1248
0
                if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavorEx ) )
1249
0
                {
1250
0
                    aFlavorEx.mnSotId = SotClipboardFormatId::BITMAP;
1251
0
                    rDataFlavorExVector.push_back( aFlavorEx );
1252
0
                }
1253
0
            }
1254
0
            else if( SotClipboardFormatId::WMF == aFlavorEx.mnSotId
1255
0
                     || SotClipboardFormatId::EMF == aFlavorEx.mnSotId
1256
0
                     || SotClipboardFormatId::SVG == aFlavorEx.mnSotId )
1257
0
            {
1258
0
                if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavorEx ) )
1259
0
                {
1260
0
                    aFlavorEx.mnSotId = SotClipboardFormatId::GDIMETAFILE;
1261
0
                    rDataFlavorExVector.push_back( aFlavorEx );
1262
0
                }
1263
0
            }
1264
0
            else if ( SotClipboardFormatId::HTML_SIMPLE == aFlavorEx.mnSotId  )
1265
0
            {
1266
                // #104735# HTML_SIMPLE may also be inserted without comments
1267
0
                aFlavorEx.mnSotId = SotClipboardFormatId::HTML_NO_COMMENT;
1268
0
                rDataFlavorExVector.push_back( aFlavorEx );
1269
0
            }
1270
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
1271
0
            {
1272
                // add, if it is a UTF-8 byte buffer
1273
0
                if( xMimeType->hasParameter( aCharsetStr ) )
1274
0
                {
1275
0
                    if( xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "unicode" ) ||
1276
0
                        xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "utf-16" ) )
1277
0
                    {
1278
0
                        rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::STRING;
1279
1280
0
                    }
1281
0
                }
1282
0
            }
1283
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/rtf" ) )
1284
0
            {
1285
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RTF;
1286
0
            }
1287
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/richtext" ) )
1288
0
            {
1289
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RICHTEXT;
1290
0
            }
1291
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/html" ) )
1292
1293
0
            {
1294
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::HTML;
1295
0
            }
1296
0
            else if(xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/markdown "))
1297
0
            {
1298
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::MARKDOWN;
1299
0
            }
1300
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/uri-list" ) )
1301
0
            {
1302
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::FILE_LIST;
1303
0
            }
1304
0
            else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice-objectdescriptor-xml" ) )
1305
0
            {
1306
0
                rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::OBJECTDESCRIPTOR;
1307
0
            }
1308
0
        }
1309
0
    }
1310
0
    catch( const css::uno::Exception& )
1311
0
    {
1312
0
    }
1313
0
}
1314
1315
void TransferableDataHelper::InitFormats()
1316
0
{
1317
0
    SolarMutexGuard aSolarGuard;
1318
1319
0
    maFormats.clear();
1320
0
    mxObjDesc.reset(new TransferableObjectDescriptor);
1321
1322
0
    if( !mxTransfer.is() )
1323
0
        return;
1324
1325
0
    TransferableDataHelper::FillDataFlavorExVector(mxTransfer->getTransferDataFlavors(), maFormats);
1326
1327
0
    for (auto const& format : maFormats)
1328
0
    {
1329
0
        if( SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId )
1330
0
        {
1331
0
            ImplSetParameterString(*mxObjDesc, format);
1332
0
            auto data = GetSequence(format, {});
1333
0
            SvMemoryStream aSrcStm(data.getArray(), data.getLength(), StreamMode::READ);
1334
0
            TryReadTransferableObjectDescriptor(aSrcStm, *mxObjDesc);
1335
0
            break;
1336
0
        }
1337
0
    }
1338
0
}
1339
1340
1341
bool TransferableDataHelper::HasFormat( SotClipboardFormatId nFormat ) const
1342
0
{
1343
0
    SolarMutexGuard g;
1344
0
    return std::any_of(maFormats.begin(), maFormats.end(),
1345
0
              [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
1346
0
}
1347
1348
bool TransferableDataHelper::HasFormat( const DataFlavor& rFlavor ) const
1349
0
{
1350
0
    SolarMutexGuard g;
1351
0
    for (auto const& format : maFormats)
1352
0
    {
1353
0
        if( TransferableDataHelper::IsEqual( rFlavor, format ) )
1354
0
            return true;
1355
0
    }
1356
1357
0
    return false;
1358
0
}
1359
1360
sal_uInt32 TransferableDataHelper::GetFormatCount() const
1361
0
{
1362
0
    SolarMutexGuard g;
1363
0
    return maFormats.size();
1364
0
}
1365
1366
SotClipboardFormatId TransferableDataHelper::GetFormat( sal_uInt32 nFormat ) const
1367
0
{
1368
0
    SolarMutexGuard g;
1369
0
    DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
1370
0
    return( ( nFormat < maFormats.size() ) ? maFormats[ nFormat ].mnSotId : SotClipboardFormatId::NONE );
1371
0
}
1372
1373
DataFlavor TransferableDataHelper::GetFormatDataFlavor( sal_uInt32 nFormat ) const
1374
0
{
1375
0
    SolarMutexGuard g;
1376
0
    DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
1377
1378
0
    DataFlavor aRet;
1379
1380
0
    if (nFormat < maFormats.size())
1381
0
        aRet = maFormats[nFormat];
1382
1383
0
    return aRet;
1384
0
}
1385
1386
1387
Reference< XTransferable > TransferableDataHelper::GetXTransferable() const
1388
0
{
1389
0
    Reference< XTransferable > xRet;
1390
1391
0
    if( mxTransfer.is() )
1392
0
    {
1393
0
        try
1394
0
        {
1395
0
            xRet = mxTransfer;
1396
1397
            // do a dummy call to check, if this interface is valid (nasty)
1398
0
            xRet->getTransferDataFlavors();
1399
1400
0
        }
1401
0
        catch( const css::uno::Exception& )
1402
0
        {
1403
0
            xRet.clear();
1404
0
        }
1405
0
    }
1406
1407
0
    return xRet;
1408
0
}
1409
1410
1411
Any TransferableDataHelper::GetAny( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
1412
0
{
1413
0
    Any aReturn;
1414
1415
0
    DataFlavor aFlavor;
1416
0
    if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
1417
0
        aReturn = GetAny(aFlavor, rDestDoc);
1418
1419
0
    return aReturn;
1420
0
}
1421
1422
Any TransferableDataHelper::GetAny( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
1423
0
{
1424
0
    Any aRet;
1425
1426
0
    try
1427
0
    {
1428
0
        SolarMutexGuard g;
1429
0
        if( mxTransfer.is() )
1430
0
        {
1431
0
            const SotClipboardFormatId         nRequestFormat = SotExchange::GetFormat( rFlavor );
1432
1433
0
            Reference<css::datatransfer::XTransferable2> xTransfer2(mxTransfer, UNO_QUERY);
1434
1435
0
            if( nRequestFormat != SotClipboardFormatId::NONE )
1436
0
            {
1437
                // try to get alien format first
1438
0
                for (auto const& format : maFormats)
1439
0
                {
1440
0
                    if( ( nRequestFormat == format.mnSotId ) && !rFlavor.MimeType.equalsIgnoreAsciiCase( format.MimeType ) )
1441
0
                    {
1442
// tdf#133365: only release solar mutex on Windows
1443
#ifdef _WIN32
1444
                        // Our own thread may handle the nested IDataObject::GetData call,
1445
                        // and try to acquire solar mutex
1446
                        SolarMutexReleaser r;
1447
#endif // _WIN32
1448
1449
0
                        if (xTransfer2.is())
1450
0
                            aRet = xTransfer2->getTransferData2(format, rDestDoc);
1451
0
                        else
1452
0
                            aRet = mxTransfer->getTransferData(format);
1453
0
                    }
1454
1455
0
                    if( aRet.hasValue() )
1456
0
                        break;
1457
0
                }
1458
0
            }
1459
1460
0
            if( !aRet.hasValue() )
1461
0
            {
1462
// tdf#133365: only release solar mutex on Windows
1463
#ifdef _WIN32
1464
                // Our own thread may handle the nested IDataObject::GetData call,
1465
                // and try to acquire solar mutex
1466
                SolarMutexReleaser r;
1467
#endif // _WIN32
1468
1469
0
                if (xTransfer2.is())
1470
0
                    aRet = xTransfer2->getTransferData2(rFlavor, rDestDoc);
1471
0
                else
1472
0
                    aRet = mxTransfer->getTransferData(rFlavor);
1473
0
            }
1474
0
        }
1475
0
    }
1476
0
    catch( const css::uno::Exception& )
1477
0
    {
1478
0
    }
1479
1480
0
    return aRet;
1481
0
}
1482
1483
1484
bool TransferableDataHelper::GetString( SotClipboardFormatId nFormat, OUString& rStr ) const
1485
0
{
1486
0
    DataFlavor aFlavor;
1487
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetString( aFlavor, rStr ) );
1488
0
}
1489
1490
1491
bool TransferableDataHelper::GetString( const DataFlavor& rFlavor, OUString& rStr ) const
1492
0
{
1493
0
    Any aAny = GetAny(rFlavor, OUString());
1494
0
    bool        bRet = false;
1495
1496
0
    if( aAny.hasValue() )
1497
0
    {
1498
0
        OUString         aOUString;
1499
0
        Sequence< sal_Int8 >    aSeq;
1500
1501
0
        if( aAny >>= aOUString )
1502
0
        {
1503
0
            rStr = aOUString;
1504
0
            bRet = true;
1505
0
        }
1506
0
        else if( aAny >>= aSeq )
1507
0
        {
1508
1509
0
            const char* pChars = reinterpret_cast< const char* >( aSeq.getConstArray() );
1510
0
            sal_Int32       nLen = aSeq.getLength();
1511
1512
            //JP 10.10.2001: 92930 - don't copy the last zero character into the string.
1513
            //DVO 2002-05-27: strip _all_ trailing zeros
1514
0
            while( nLen && ( 0 == *( pChars + nLen - 1 ) ) )
1515
0
                --nLen;
1516
1517
0
            rStr = OUString( pChars, nLen, osl_getThreadTextEncoding() );
1518
0
            bRet = true;
1519
0
        }
1520
0
    }
1521
1522
0
    return bRet;
1523
0
}
1524
1525
bool TransferableDataHelper::GetBitmap(SotClipboardFormatId nFormat, Bitmap& rBmp) const
1526
0
{
1527
0
    if(SotClipboardFormatId::BITMAP == nFormat)
1528
0
    {
1529
        // try to get PNG first
1530
0
        DataFlavor aFlavor;
1531
1532
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
1533
0
        {
1534
0
            if (GetBitmap(aFlavor, rBmp))
1535
0
            {
1536
0
                return true;
1537
0
            }
1538
0
        }
1539
1540
        // then JPEG
1541
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor))
1542
0
        {
1543
0
            if (GetBitmap(aFlavor, rBmp))
1544
0
            {
1545
0
                return true;
1546
0
            }
1547
0
        }
1548
0
    }
1549
1550
0
    DataFlavor aFlavor;
1551
0
    return (SotExchange::GetFormatDataFlavor(nFormat, aFlavor) && GetBitmap(aFlavor, rBmp));
1552
0
}
1553
1554
bool TransferableDataHelper::GetBitmap(const DataFlavor& rFlavor, Bitmap& rBmp) const
1555
0
{
1556
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor);
1557
0
    DataFlavor aSubstFlavor;
1558
0
    bool bRet(xStm);
1559
0
    bool bSuppressPNG(false); // #122982# If PNG stream not accessed, but BMP one, suppress trying to load PNG
1560
0
    bool bSuppressJPEG(false);
1561
1562
0
    if(!bRet && HasFormat(SotClipboardFormatId::PNG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aSubstFlavor))
1563
0
    {
1564
        // when no direct success, try if PNG is available
1565
0
        xStm = GetSotStorageStream(aSubstFlavor);
1566
0
        bRet = bool(xStm);
1567
0
        bSuppressJPEG = bRet;
1568
0
    }
1569
1570
0
    if(!bRet && HasFormat(SotClipboardFormatId::JPEG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aSubstFlavor))
1571
0
    {
1572
0
        xStm = GetSotStorageStream(aSubstFlavor);
1573
0
        bRet = bool(xStm);
1574
0
        bSuppressPNG = bRet;
1575
0
    }
1576
1577
0
    if(!bRet && HasFormat(SotClipboardFormatId::BMP) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor))
1578
0
    {
1579
        // when no direct success, try if BMP is available
1580
0
        xStm = GetSotStorageStream(aSubstFlavor);
1581
0
        bRet = bool(xStm);
1582
0
        bSuppressPNG = bRet;
1583
0
        bSuppressJPEG = bRet;
1584
0
    }
1585
1586
0
    if(bRet)
1587
0
    {
1588
0
        if(!bSuppressPNG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/png"))
1589
0
        {
1590
            // it's a PNG, import to Bitmap
1591
0
            vcl::PngImageReader aPNGReader(*xStm);
1592
0
            rBmp = aPNGReader.read();
1593
0
        }
1594
0
        else if(!bSuppressJPEG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/jpeg"))
1595
0
        {
1596
            // it's a JPEG, import to Bitmap
1597
0
            GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
1598
0
            Graphic aGraphic;
1599
0
            if (rFilter.ImportGraphic(aGraphic, u"", *xStm) == ERRCODE_NONE)
1600
0
                rBmp = aGraphic.GetBitmap();
1601
0
        }
1602
1603
0
        if(rBmp.IsEmpty())
1604
0
        {
1605
0
            Bitmap aBitmap;
1606
0
            AlphaMask aMask;
1607
1608
            // explicitly use Bitmap::Read with bFileHeader = sal_True
1609
            // #i124085# keep DIBV5 for read from clipboard, but should not happen
1610
0
            ReadDIBV5(aBitmap, aMask, *xStm);
1611
1612
0
            if(aMask.GetBitmap().IsEmpty())
1613
0
            {
1614
0
                rBmp = std::move(aBitmap);
1615
0
            }
1616
0
            else
1617
0
            {
1618
0
                rBmp = Bitmap(aBitmap, aMask);
1619
0
            }
1620
0
        }
1621
1622
0
        bRet = (ERRCODE_NONE == xStm->GetError() && !rBmp.IsEmpty());
1623
1624
        /* SJ: #110748# At the moment we are having problems with DDB inserted as DIB. The
1625
           problem is, that some graphics are inserted much too big because the nXPelsPerMeter
1626
           and nYPelsPerMeter of the bitmap fileheader isn't including the correct value.
1627
           Due to this reason the following code assumes that bitmaps with a logical size
1628
           greater than 50 cm aren't having the correct mapmode set.
1629
1630
           The following code should be removed if DDBs and DIBs are supported via clipboard
1631
           properly.
1632
        */
1633
0
        if(bRet)
1634
0
        {
1635
0
            const MapMode aMapMode(rBmp.GetPrefMapMode());
1636
1637
0
            if(MapUnit::MapPixel != aMapMode.GetMapUnit())
1638
0
            {
1639
0
                const Size aSize(OutputDevice::LogicToLogic(rBmp.GetPrefSize(), aMapMode, MapMode(MapUnit::Map100thMM)));
1640
1641
                // #i122388# This wrongly corrects in the given case; changing from 5000 100th mm to
1642
                // the described 50 cm (which is 50000 100th mm)
1643
0
                if((aSize.Width() > 50000) || (aSize.Height() > 50000))
1644
0
                {
1645
0
                    rBmp.SetPrefMapMode(MapMode(MapUnit::MapPixel));
1646
1647
                    // #i122388# also adapt size by applying the mew MapMode
1648
0
                    const Size aNewSize(o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::pt));
1649
0
                    rBmp.SetPrefSize(aNewSize);
1650
0
                }
1651
0
            }
1652
0
        }
1653
0
    }
1654
1655
0
    return bRet;
1656
0
}
1657
1658
1659
bool TransferableDataHelper::GetGDIMetaFile(SotClipboardFormatId nFormat, GDIMetaFile& rMtf, size_t nMaxActions) const
1660
0
{
1661
0
    DataFlavor aFlavor;
1662
0
    return SotExchange::GetFormatDataFlavor(nFormat, aFlavor) &&
1663
0
        GetGDIMetaFile(aFlavor, rMtf) &&
1664
0
        (nMaxActions == 0 || rMtf.GetActionSize() < nMaxActions);
1665
0
}
1666
1667
1668
bool TransferableDataHelper::GetGDIMetaFile( const DataFlavor& rFlavor, GDIMetaFile& rMtf ) const
1669
0
{
1670
0
    std::unique_ptr<SvStream> xStm;
1671
0
    DataFlavor          aSubstFlavor;
1672
0
    bool                bRet = false;
1673
1674
0
    if( (xStm = GetSotStorageStream( rFlavor )) )
1675
0
    {
1676
0
        SvmReader aReader( *xStm );
1677
0
        aReader.Read( rMtf );
1678
0
        bRet = ( xStm->GetError() == ERRCODE_NONE );
1679
0
    }
1680
1681
0
    if( !bRet &&
1682
0
        HasFormat( SotClipboardFormatId::EMF ) &&
1683
0
        SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
1684
0
        (xStm = GetSotStorageStream( aSubstFlavor)) )
1685
0
    {
1686
0
        Graphic aGraphic;
1687
1688
0
        if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
1689
0
        {
1690
0
            rMtf = aGraphic.GetGDIMetaFile();
1691
0
            bRet = true;
1692
0
        }
1693
0
    }
1694
1695
0
    if( !bRet &&
1696
0
        HasFormat( SotClipboardFormatId::WMF ) &&
1697
0
        SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
1698
0
        (xStm = GetSotStorageStream( aSubstFlavor ) ) )
1699
0
    {
1700
0
        Graphic aGraphic;
1701
1702
0
        if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
1703
0
        {
1704
0
            rMtf = aGraphic.GetGDIMetaFile();
1705
0
            bRet = true;
1706
0
        }
1707
0
    }
1708
1709
0
    return bRet;
1710
0
}
1711
1712
1713
bool TransferableDataHelper::GetGraphic( SotClipboardFormatId nFormat, Graphic& rGraphic ) const
1714
0
{
1715
0
    if(SotClipboardFormatId::BITMAP == nFormat)
1716
0
    {
1717
        // try to get PNG first
1718
0
        DataFlavor aFlavor;
1719
1720
0
        if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
1721
0
        {
1722
0
            if(GetGraphic(aFlavor, rGraphic))
1723
0
            {
1724
0
                return true;
1725
0
            }
1726
0
        }
1727
0
    }
1728
1729
0
    DataFlavor aFlavor;
1730
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetGraphic( aFlavor, rGraphic ) );
1731
0
}
1732
1733
1734
bool TransferableDataHelper::GetGraphic( const css::datatransfer::DataFlavor& rFlavor, Graphic& rGraphic ) const
1735
0
{
1736
0
    DataFlavor  aFlavor;
1737
0
    bool        bRet = false;
1738
1739
0
    if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor) &&
1740
0
        TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1741
0
    {
1742
        // try to get PNG first
1743
0
        Bitmap aBmp;
1744
1745
0
        bRet = GetBitmap(aFlavor, aBmp);
1746
0
        if( bRet )
1747
0
            rGraphic = aBmp;
1748
0
    }
1749
0
    else if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PDF, aFlavor) &&
1750
0
            TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1751
0
    {
1752
0
        Graphic aGraphic;
1753
0
        if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(rFlavor))
1754
0
        {
1755
0
            if (GraphicConverter::Import(*xStm, aGraphic) == ERRCODE_NONE)
1756
0
            {
1757
0
                rGraphic = std::move(aGraphic);
1758
0
                bRet = true;
1759
0
            }
1760
0
        }
1761
0
    }
1762
0
    else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor) && TransferableDataHelper::IsEqual(aFlavor, rFlavor))
1763
0
    {
1764
0
        Bitmap aBitmap;
1765
1766
0
        bRet = GetBitmap(aFlavor, aBitmap);
1767
0
        if (bRet)
1768
0
            rGraphic = aBitmap;
1769
0
    }
1770
0
    else if(SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavor ) &&
1771
0
        TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
1772
0
    {
1773
0
        Bitmap aBmp;
1774
1775
0
        bRet = GetBitmap(aFlavor, aBmp);
1776
0
        if( bRet )
1777
0
            rGraphic = aBmp;
1778
0
    }
1779
0
    else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavor ) &&
1780
0
             TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
1781
0
    {
1782
0
        GDIMetaFile aMtf;
1783
1784
0
        bRet = GetGDIMetaFile( aFlavor, aMtf );
1785
0
        if( bRet )
1786
0
            rGraphic = aMtf;
1787
0
    }
1788
0
    else
1789
0
    {
1790
0
        if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor) )
1791
0
        {
1792
0
            TypeSerializer aSerializer(*xStm);
1793
0
            aSerializer.readGraphic(rGraphic);
1794
0
            bRet = ( xStm->GetError() == ERRCODE_NONE );
1795
0
        }
1796
0
    }
1797
1798
0
    return bRet;
1799
0
}
1800
1801
1802
bool TransferableDataHelper::GetImageMap( SotClipboardFormatId nFormat, ImageMap& rIMap ) const
1803
0
{
1804
0
    DataFlavor aFlavor;
1805
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetImageMap( aFlavor, rIMap ) );
1806
0
}
1807
1808
1809
bool TransferableDataHelper::GetImageMap( const css::datatransfer::DataFlavor& rFlavor, ImageMap& rIMap ) const
1810
0
{
1811
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor);
1812
0
    if (!xStm)
1813
0
        return false;
1814
1815
0
    rIMap.Read( *xStm );
1816
0
    bool bRet = ( xStm->GetError() == ERRCODE_NONE );
1817
1818
0
    return bRet;
1819
0
}
1820
1821
1822
bool TransferableDataHelper::GetTransferableObjectDescriptor( SotClipboardFormatId nFormat, TransferableObjectDescriptor& rDesc ) const
1823
0
{
1824
0
    DataFlavor aFlavor;
1825
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetTransferableObjectDescriptor( rDesc ) );
1826
0
}
1827
1828
1829
bool TransferableDataHelper::GetTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc ) const
1830
0
{
1831
0
    rDesc = *mxObjDesc;
1832
0
    return true;
1833
0
}
1834
1835
1836
bool TransferableDataHelper::GetINetBookmark( SotClipboardFormatId nFormat, INetBookmark& rBmk ) const
1837
0
{
1838
0
    DataFlavor aFlavor;
1839
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetBookmark( aFlavor, rBmk ) );
1840
0
}
1841
1842
1843
bool TransferableDataHelper::GetINetBookmark( const css::datatransfer::DataFlavor& rFlavor, INetBookmark& rBmk ) const
1844
0
{
1845
0
    if( !HasFormat( rFlavor ))
1846
0
        return false;
1847
1848
0
    bool bRet = false;
1849
0
    const SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
1850
0
    switch( nFormat )
1851
0
    {
1852
0
        case SotClipboardFormatId::SOLK:
1853
0
        case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
1854
0
        {
1855
0
            OUString aString;
1856
0
            if( GetString( rFlavor, aString ) )
1857
0
            {
1858
0
                if( SotClipboardFormatId::UNIFORMRESOURCELOCATOR == nFormat )
1859
0
                {
1860
0
                    rBmk = INetBookmark( aString, aString );
1861
0
                    bRet = true;
1862
0
                }
1863
0
                else
1864
0
                {
1865
0
                    OUString    aURL, aDesc;
1866
0
                    sal_Int32   nStart = aString.indexOf( '@' ), nLen = aString.toInt32();
1867
1868
0
                    if( !nLen && aString[ 0 ] != '0' )
1869
0
                    {
1870
0
                        SAL_INFO( "svtools", "SOLK: 1. len=0" );
1871
0
                    }
1872
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 3 )
1873
0
                    {
1874
0
                        SAL_INFO( "svtools", "SOLK: 1. illegal start or wrong len" );
1875
0
                    }
1876
0
                    aURL = aString.copy( nStart + 1, nLen );
1877
1878
0
                    aString = aString.replaceAt( 0, nStart + 1 + nLen, u"" );
1879
0
                    nStart = aString.indexOf( '@' );
1880
0
                    nLen = aString.toInt32();
1881
1882
0
                    if( !nLen && aString[ 0 ] != '0' )
1883
0
                    {
1884
0
                        SAL_INFO( "svtools", "SOLK: 2. len=0" );
1885
0
                    }
1886
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 1 )
1887
0
                    {
1888
0
                        SAL_INFO( "svtools", "SOLK: 2. illegal start or wrong len" );
1889
0
                    }
1890
0
                    aDesc = aString.copy( nStart+1, nLen );
1891
1892
0
                    rBmk = INetBookmark( aURL, aDesc );
1893
0
                    bRet = true;
1894
0
                }
1895
0
            }
1896
0
        }
1897
0
        break;
1898
1899
0
        case SotClipboardFormatId::NETSCAPE_BOOKMARK:
1900
0
        {
1901
0
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1902
1903
0
            if (2048 == aSeq.getLength())
1904
0
            {
1905
0
                const char* p1 = reinterpret_cast< const char* >( aSeq.getConstArray() );
1906
0
                const char* p2 =  reinterpret_cast< const char* >( aSeq.getConstArray() ) + 1024;
1907
0
                rBmk = INetBookmark( OUString( p1, strlen(p1), osl_getThreadTextEncoding() ),
1908
0
                                     OUString( p2, strlen(p2), osl_getThreadTextEncoding() ) );
1909
0
                bRet = true;
1910
0
            }
1911
0
        }
1912
0
        break;
1913
1914
#ifdef _WIN32
1915
        case SotClipboardFormatId::FILEGRPDESCRIPTOR:
1916
        {
1917
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1918
1919
            if (aSeq.getLength())
1920
            {
1921
                FILEGROUPDESCRIPTORW const * pFDesc = reinterpret_cast<FILEGROUPDESCRIPTORW const *>(aSeq.getConstArray());
1922
1923
                if( pFDesc->cItems )
1924
                {
1925
                    OUString aDesc( o3tl::toU(pFDesc->fgd[ 0 ].cFileName) );
1926
1927
                    if( ( aDesc.getLength() > 4 ) && aDesc.endsWithIgnoreAsciiCase(".URL") )
1928
                    {
1929
                        std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( INetURLObject( aDesc ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1930
                                                                                  StreamMode::STD_READ ));
1931
1932
                        if( !pStream || pStream->GetError() )
1933
                        {
1934
                            DataFlavor aFileContentFlavor;
1935
1936
                            aSeq.realloc( 0 );
1937
                            pStream.reset();
1938
1939
                            if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::FILECONTENT, aFileContentFlavor))
1940
                            {
1941
                                aSeq = GetSequence(aFileContentFlavor, OUString());
1942
                                if (aSeq.getLength())
1943
                                    pStream.reset(new SvMemoryStream( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), StreamMode::STD_READ ));
1944
                            }
1945
                        }
1946
1947
                        if( pStream )
1948
                        {
1949
                            OString aLine;
1950
                            bool bInA = false, bInW = false, bAFound = false;
1951
1952
                            while( pStream->ReadLine( aLine ) )
1953
                            {
1954
                                if (aLine.startsWithIgnoreAsciiCase("[InternetShortcut", &aLine))
1955
                                {
1956
                                    // May be [InternetShortcut], or [InternetShortcut.A], or
1957
                                    // [InternetShortcut.W] (the latter has UTF-7-encoded URL)
1958
                                    bInW = aLine.equalsIgnoreAsciiCase(".W]");
1959
                                    bInA = !bAFound && !bInW
1960
                                           && (aLine == "]" || aLine.equalsIgnoreAsciiCase(".A]"));
1961
                                }
1962
                                else if (aLine.startsWith("["))
1963
                                {
1964
                                    bInA = bInW = false;
1965
                                }
1966
                                else if ((bInA || bInW) && aLine.startsWithIgnoreAsciiCase("URL="))
1967
                                {
1968
                                    auto eTextEncoding = bInW ? RTL_TEXTENCODING_UTF7
1969
                                                              : osl_getThreadTextEncoding();
1970
                                    rBmk = INetBookmark( OStringToOUString(aLine.subView(4), eTextEncoding),
1971
                                                         aDesc.copy(0, aDesc.getLength() - 4) );
1972
                                    bRet = true;
1973
                                    if (bInW)
1974
                                        break;
1975
                                    else
1976
                                        bAFound = true; // Keep looking for "W"
1977
                                }
1978
                            }
1979
                        }
1980
                    }
1981
                }
1982
            }
1983
        }
1984
        break;
1985
#endif
1986
0
        default: break;
1987
0
    }
1988
0
    return bRet;
1989
0
}
1990
1991
1992
bool TransferableDataHelper::GetINetImage( SotClipboardFormatId nFormat,
1993
                                                INetImage& rINtImg ) const
1994
0
{
1995
0
    DataFlavor aFlavor;
1996
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetImage( aFlavor, rINtImg ) );
1997
0
}
1998
1999
2000
bool TransferableDataHelper::GetINetImage(
2001
        const css::datatransfer::DataFlavor& rFlavor,
2002
        INetImage& rINtImg ) const
2003
0
{
2004
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor );
2005
0
    if (!xStm)
2006
0
        return false;
2007
2008
0
    bool bRet = rINtImg.Read( *xStm, SotExchange::GetFormat( rFlavor ) );
2009
0
    return bRet;
2010
0
}
2011
2012
2013
bool TransferableDataHelper::GetFileList( SotClipboardFormatId nFormat,
2014
                                                FileList& rFileList ) const
2015
0
{
2016
0
    DataFlavor aFlavor;
2017
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetFileList( rFileList ) );
2018
0
}
2019
2020
2021
bool TransferableDataHelper::GetFileList( FileList& rFileList ) const
2022
0
{
2023
0
    bool                bRet = false;
2024
2025
0
    for( sal_uInt32 i = 0, nFormatCount = GetFormatCount(); ( i < nFormatCount ) && !bRet; ++i )
2026
0
    {
2027
0
        if( SotClipboardFormatId::FILE_LIST == GetFormat( i ) )
2028
0
        {
2029
0
            const DataFlavor aFlavor( GetFormatDataFlavor( i ) );
2030
2031
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( aFlavor ))
2032
0
            {
2033
0
                if( aFlavor.MimeType.indexOf( "text/uri-list" ) > -1 )
2034
0
                {
2035
0
                    OStringBuffer aDiskString;
2036
2037
0
                    while( xStm->ReadLine( aDiskString ) )
2038
0
                        if( !aDiskString.isEmpty() && aDiskString[0] != '#' )
2039
0
                            rFileList.AppendFile( OStringToOUString(aDiskString, RTL_TEXTENCODING_UTF8) );
2040
2041
0
                    bRet = true;
2042
0
                }
2043
0
                else
2044
0
                    bRet = ( ReadFileList( *xStm, rFileList ).GetError() == ERRCODE_NONE );
2045
0
            }
2046
0
        }
2047
0
    }
2048
2049
0
    return bRet;
2050
0
}
2051
2052
2053
Sequence<sal_Int8> TransferableDataHelper::GetSequence( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2054
0
{
2055
0
    DataFlavor aFlavor;
2056
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2057
0
        return Sequence<sal_Int8>();
2058
2059
0
    return GetSequence(aFlavor, rDestDoc);
2060
0
}
2061
2062
Sequence<sal_Int8> TransferableDataHelper::GetSequence( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2063
0
{
2064
0
    const Any aAny = GetAny(rFlavor, rDestDoc);
2065
0
    Sequence<sal_Int8> aSeq;
2066
0
    if (aAny.hasValue())
2067
0
        aAny >>= aSeq;
2068
2069
0
    return aSeq;
2070
0
}
2071
2072
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( SotClipboardFormatId nFormat ) const
2073
0
{
2074
0
    DataFlavor aFlavor;
2075
0
    if (!SotExchange::GetFormatDataFlavor( nFormat, aFlavor ))
2076
0
        return nullptr;
2077
0
    return GetSotStorageStream( aFlavor );
2078
0
}
2079
2080
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( const DataFlavor& rFlavor ) const
2081
0
{
2082
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
2083
0
    if (!aSeq.hasElements())
2084
0
        return nullptr;
2085
2086
0
    std::unique_ptr<SvStream> xStream = SotTempStream::Create( u""_ustr );
2087
0
    xStream->WriteBytes( aSeq.getConstArray(), aSeq.getLength() );
2088
0
    xStream->Seek(0);
2089
0
    return xStream;
2090
0
}
2091
2092
Reference<XInputStream> TransferableDataHelper::GetInputStream( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2093
0
{
2094
0
    DataFlavor aFlavor;
2095
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2096
0
        return Reference<XInputStream>();
2097
2098
0
    return GetInputStream(aFlavor, rDestDoc);
2099
0
}
2100
2101
Reference<XInputStream> TransferableDataHelper::GetInputStream( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2102
0
{
2103
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, rDestDoc);
2104
2105
0
    if (!aSeq.hasElements())
2106
0
        return Reference<XInputStream>();
2107
2108
0
    Reference<XInputStream> xStream(new comphelper::SequenceInputStream(aSeq));
2109
0
    return xStream;
2110
0
}
2111
2112
void TransferableDataHelper::Rebind( const Reference< XTransferable >& _rxNewContent )
2113
0
{
2114
0
    mxTransfer = _rxNewContent;
2115
0
    InitFormats();
2116
0
}
2117
2118
bool TransferableDataHelper::StartClipboardListening( )
2119
0
{
2120
0
    SolarMutexGuard g;
2121
2122
0
    StopClipboardListening( );
2123
2124
0
    mxClipboardListener = new TransferableClipboardNotifier(mxClipboard, *this);
2125
2126
0
    return mxClipboardListener->isListening();
2127
0
}
2128
2129
void TransferableDataHelper::StopClipboardListening( )
2130
0
{
2131
0
    SolarMutexGuard g;
2132
2133
0
    if (mxClipboardListener.is())
2134
0
    {
2135
0
        mxClipboardListener->dispose();
2136
0
        mxClipboardListener.clear();
2137
0
    }
2138
0
}
2139
2140
TransferableDataHelper TransferableDataHelper::CreateFromClipboard(const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& rClipboard)
2141
0
{
2142
0
    TransferableDataHelper  aRet;
2143
2144
0
    if( rClipboard.is() )
2145
0
    {
2146
0
        try
2147
0
        {
2148
0
            Reference< XTransferable > xTransferable( rClipboard->getContents() );
2149
2150
0
            if( xTransferable.is() )
2151
0
            {
2152
0
                aRet = TransferableDataHelper( xTransferable );
2153
                // also copy the clipboard
2154
0
                aRet.mxClipboard = rClipboard;
2155
0
            }
2156
0
        }
2157
0
        catch( const css::uno::Exception& )
2158
0
        {
2159
0
        }
2160
0
    }
2161
2162
0
    return aRet;
2163
0
}
2164
2165
TransferableDataHelper TransferableDataHelper::CreateFromSystemClipboard( vcl::Window * pWindow )
2166
0
{
2167
0
    DBG_ASSERT( pWindow, "Window pointer is NULL" );
2168
2169
0
    Reference< XClipboard > xClipboard;
2170
2171
0
    if( pWindow )
2172
0
        xClipboard = pWindow->GetClipboard();
2173
2174
0
    return CreateFromClipboard(xClipboard);
2175
0
}
2176
2177
TransferableDataHelper TransferableDataHelper::CreateFromPrimarySelection()
2178
0
{
2179
0
    Reference< XClipboard > xSelection(GetSystemPrimarySelection());
2180
0
    TransferableDataHelper   aRet;
2181
2182
0
    if( xSelection.is() )
2183
0
    {
2184
0
           SolarMutexReleaser aReleaser;
2185
2186
0
           try
2187
0
               {
2188
0
                   Reference< XTransferable > xTransferable( xSelection->getContents() );
2189
2190
0
                   if( xTransferable.is() )
2191
0
                   {
2192
0
                           aRet = TransferableDataHelper( xTransferable );
2193
0
                           aRet.mxClipboard = std::move(xSelection);
2194
0
                   }
2195
0
               }
2196
0
           catch( const css::uno::Exception& )
2197
0
               {
2198
0
               }
2199
0
    }
2200
2201
0
    return aRet;
2202
0
}
2203
2204
bool TransferableDataHelper::IsEqual( const css::datatransfer::DataFlavor& rInternalFlavor,
2205
                                      const css::datatransfer::DataFlavor& rRequestFlavor )
2206
0
{
2207
0
    const Reference< XComponentContext >&          xContext( ::comphelper::getProcessComponentContext() );
2208
0
    bool                                    bRet = false;
2209
2210
0
    try
2211
0
    {
2212
0
        Reference< XMimeContentTypeFactory >    xMimeFact = MimeContentTypeFactory::create( xContext );
2213
2214
0
        Reference< XMimeContentType > xRequestType1( xMimeFact->createMimeContentType( rInternalFlavor.MimeType ) );
2215
0
        Reference< XMimeContentType > xRequestType2( xMimeFact->createMimeContentType( rRequestFlavor.MimeType ) );
2216
2217
0
        if( xRequestType1.is() && xRequestType2.is() )
2218
0
        {
2219
0
            if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( xRequestType2->getFullMediaType() ) )
2220
0
            {
2221
0
                if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
2222
0
                {
2223
                    // special handling for text/plain media types
2224
0
                    static constexpr OUString aCharsetString( u"charset"_ustr );
2225
2226
0
                    if( !xRequestType2->hasParameter( aCharsetString ) ||
2227
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "utf-16" ) ||
2228
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "unicode" ) )
2229
0
                    {
2230
0
                        bRet = true;
2231
0
                    }
2232
0
                }
2233
0
                else if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice" ) )
2234
0
                {
2235
                    // special handling for application/x-openoffice media types
2236
0
                    static constexpr OUString aFormatString( u"windows_formatname"_ustr );
2237
2238
0
                    if( xRequestType1->hasParameter( aFormatString ) &&
2239
0
                        xRequestType2->hasParameter( aFormatString ) &&
2240
0
                        xRequestType1->getParameterValue( aFormatString ).equalsIgnoreAsciiCase( xRequestType2->getParameterValue( aFormatString ) ) )
2241
0
                    {
2242
0
                        bRet = true;
2243
0
                    }
2244
0
                }
2245
0
                else
2246
0
                    bRet = true;
2247
0
            }
2248
0
        }
2249
0
    }
2250
0
    catch( const css::uno::Exception& )
2251
0
    {
2252
0
        bRet = rInternalFlavor.MimeType.equalsIgnoreAsciiCase( rRequestFlavor.MimeType );
2253
0
    }
2254
2255
0
    return bRet;
2256
0
}
2257
2258
static bool WriteDDELink_impl(SvStream& stream, rtl_TextEncoding encoding,
2259
                              std::u16string_view application, std::u16string_view topic,
2260
                              std::u16string_view item, std::u16string_view extra)
2261
0
{
2262
    // Put Link format, potentially with extra: either application\0topic\0item\0\0
2263
    // or application\0topic\0item\0extra\0\0
2264
0
    assert(rtl_isOctetTextEncoding(encoding)); // The Link format is 8-bit
2265
0
    stream.WriteUnicodeOrByteText(application, encoding, true);
2266
0
    stream.WriteUnicodeOrByteText(topic, encoding, true);
2267
0
    stream.WriteUnicodeOrByteText(item, encoding, true);
2268
0
    if (!extra.empty())
2269
0
        stream.WriteUnicodeOrByteText(extra, encoding, true);
2270
0
    stream.WriteChar('\0'); // One more trailing zero
2271
0
    return stream.GetErrorCode() == ERRCODE_NONE;
2272
0
}
2273
2274
// static
2275
bool TransferableDataHelper::WriteDDELink(SvStream& stream, std::u16string_view application,
2276
                                          std::u16string_view topic, std::u16string_view item,
2277
                                          std::u16string_view extra)
2278
0
{
2279
    // 1. Put standard Link format in the system encoding
2280
0
    WriteDDELink_impl(stream, osl_getThreadTextEncoding(), application, topic, item, extra);
2281
2282
    // 2. Put our extension to the format. Start with a magic string "ULnk", followed by
2283
    // UTF-8-encoded Link format
2284
0
    stream.WriteOString("ULnk");
2285
0
    return WriteDDELink_impl(stream, RTL_TEXTENCODING_UTF8, application, topic, item, extra);
2286
0
}
2287
2288
static size_t ReadDDELink_impl(std::string_view str, std::string_view& application,
2289
                               std::string_view& topic, std::string_view& item,
2290
                               std::string_view& rest)
2291
0
{
2292
0
    application = {};
2293
0
    topic = {};
2294
0
    item = {};
2295
0
    rest = {};
2296
2297
0
    size_t start = 0;
2298
0
    size_t end = str.find('\0', start);
2299
0
    application = str.substr(start, end - start);
2300
0
    if (end == std::string_view::npos)
2301
0
        return end;
2302
0
    start = end + 1;
2303
0
    end = str.find('\0', start);
2304
0
    topic = str.substr(start, end - start);
2305
0
    if (end == std::string_view::npos)
2306
0
        return end;
2307
0
    start = end + 1;
2308
0
    end = str.find('\0', start);
2309
0
    item = str.substr(start, end - start);
2310
0
    if (end >= str.size() - 1 || str[end + 1] == '\0')
2311
0
        return end;
2312
0
    start = end + 1;
2313
0
    do
2314
0
    {
2315
        // Read all remaining data until \0\0 into rest, including possible single \0
2316
0
        end = str.find('\0', end + 1);
2317
0
    } while (end < str.size() - 1 && str[end + 1] != '\0');
2318
0
    rest = str.substr(start, end - start);
2319
0
    return end;
2320
0
}
2321
2322
bool TransferableDataHelper::ReadDDELink(OUString& application, OUString& topic, OUString& item,
2323
                                         OUString& rest) const
2324
0
{
2325
0
    css::uno::Sequence<sal_Int8> sequence = GetSequence(SotClipboardFormatId::LINK, {});
2326
0
    if (!sequence.hasElements())
2327
0
    {
2328
0
        SAL_WARN("svtools", "DDE data not found");
2329
0
        return false;
2330
0
    }
2331
2332
0
    std::string_view str(reinterpret_cast<const char*>(sequence.getConstArray()),
2333
0
                         sequence.getLength());
2334
0
    std::string_view application_view, topic_view, item_view, rest_view;
2335
0
    rtl_TextEncoding encoding = osl_getThreadTextEncoding();
2336
0
    size_t end = ReadDDELink_impl(str, application_view, topic_view, item_view, rest_view);
2337
0
    if (end < str.size() - 1 && str[end + 1] == '\0' && str.substr(end + 2, 4) == "ULnk")
2338
0
    {
2339
        // Now try to read our UTF-8 extension; if it's present, use it unconditionally, even if
2340
        // osl_getThreadTextEncoding gives RTL_TEXTENCODING_UTF8, because the extension is immune
2341
        // to possible different source encoding
2342
0
        encoding = RTL_TEXTENCODING_UTF8;
2343
0
        ReadDDELink_impl(str.substr(end + 6), application_view, topic_view, item_view, rest_view);
2344
0
    }
2345
0
    application = OStringToOUString(application_view, encoding);
2346
0
    topic = OStringToOUString(topic_view, encoding);
2347
0
    item = OStringToOUString(item_view, encoding);
2348
0
    rest = OStringToOUString(rest_view, encoding);
2349
0
    return !application.isEmpty() && !topic.isEmpty() && !item.isEmpty();
2350
0
}
2351
2352
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */