Coverage Report

Created: 2026-04-09 11:41

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
            // 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
        // Import EMF/WMF directly into Graphic to preserve GfxLink with
1783
        // original bytes. This avoids re-export through EMFWriter at DOCX
1784
        // save time, which changes reference device metrics and causes
1785
        // wrong scaling on reimport (tdf#168886, tdf#168237).
1786
        // Try EMF first, then WMF (same order as GetGDIMetaFile).
1787
0
        DataFlavor aMetaFlavor;
1788
0
        if (HasFormat(SotClipboardFormatId::EMF) &&
1789
0
            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::EMF, aMetaFlavor))
1790
0
        {
1791
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(aMetaFlavor))
1792
0
            {
1793
0
                if (GraphicConverter::Import(*xStm, rGraphic) == ERRCODE_NONE)
1794
0
                    bRet = true;
1795
0
            }
1796
0
        }
1797
0
        if (!bRet && HasFormat(SotClipboardFormatId::WMF) &&
1798
0
            SotExchange::GetFormatDataFlavor(SotClipboardFormatId::WMF, aMetaFlavor))
1799
0
        {
1800
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream(aMetaFlavor))
1801
0
            {
1802
0
                if (GraphicConverter::Import(*xStm, rGraphic) == ERRCODE_NONE)
1803
0
                    bRet = true;
1804
0
            }
1805
0
        }
1806
0
        if (!bRet)
1807
0
        {
1808
            // Fallback to old path if direct import fails
1809
0
            GDIMetaFile aMtf;
1810
0
            bRet = GetGDIMetaFile( aFlavor, aMtf );
1811
0
            if( bRet )
1812
0
                rGraphic = aMtf;
1813
0
        }
1814
0
    }
1815
0
    else
1816
0
    {
1817
0
        if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor) )
1818
0
        {
1819
0
            TypeSerializer aSerializer(*xStm);
1820
0
            aSerializer.readGraphic(rGraphic);
1821
0
            bRet = ( xStm->GetError() == ERRCODE_NONE );
1822
0
        }
1823
0
    }
1824
1825
0
    return bRet;
1826
0
}
1827
1828
1829
bool TransferableDataHelper::GetImageMap( SotClipboardFormatId nFormat, ImageMap& rIMap ) const
1830
0
{
1831
0
    DataFlavor aFlavor;
1832
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetImageMap( aFlavor, rIMap ) );
1833
0
}
1834
1835
1836
bool TransferableDataHelper::GetImageMap( const css::datatransfer::DataFlavor& rFlavor, ImageMap& rIMap ) const
1837
0
{
1838
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor);
1839
0
    if (!xStm)
1840
0
        return false;
1841
1842
0
    rIMap.Read( *xStm );
1843
0
    bool bRet = ( xStm->GetError() == ERRCODE_NONE );
1844
1845
0
    return bRet;
1846
0
}
1847
1848
1849
bool TransferableDataHelper::GetTransferableObjectDescriptor( SotClipboardFormatId nFormat, TransferableObjectDescriptor& rDesc ) const
1850
0
{
1851
0
    DataFlavor aFlavor;
1852
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetTransferableObjectDescriptor( rDesc ) );
1853
0
}
1854
1855
1856
bool TransferableDataHelper::GetTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc ) const
1857
0
{
1858
0
    rDesc = *mxObjDesc;
1859
0
    return true;
1860
0
}
1861
1862
1863
bool TransferableDataHelper::GetINetBookmark( SotClipboardFormatId nFormat, INetBookmark& rBmk ) const
1864
0
{
1865
0
    DataFlavor aFlavor;
1866
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetBookmark( aFlavor, rBmk ) );
1867
0
}
1868
1869
1870
bool TransferableDataHelper::GetINetBookmark( const css::datatransfer::DataFlavor& rFlavor, INetBookmark& rBmk ) const
1871
0
{
1872
0
    if( !HasFormat( rFlavor ))
1873
0
        return false;
1874
1875
0
    bool bRet = false;
1876
0
    const SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
1877
0
    switch( nFormat )
1878
0
    {
1879
0
        case SotClipboardFormatId::SOLK:
1880
0
        case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
1881
0
        {
1882
0
            OUString aString;
1883
0
            if( GetString( rFlavor, aString ) )
1884
0
            {
1885
0
                if( SotClipboardFormatId::UNIFORMRESOURCELOCATOR == nFormat )
1886
0
                {
1887
0
                    rBmk = INetBookmark( aString, aString );
1888
0
                    bRet = true;
1889
0
                }
1890
0
                else
1891
0
                {
1892
0
                    OUString    aURL, aDesc;
1893
0
                    sal_Int32   nStart = aString.indexOf( '@' ), nLen = aString.toInt32();
1894
1895
0
                    if( !nLen && aString[ 0 ] != '0' )
1896
0
                    {
1897
0
                        SAL_INFO( "svtools", "SOLK: 1. len=0" );
1898
0
                    }
1899
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 3 )
1900
0
                    {
1901
0
                        SAL_INFO( "svtools", "SOLK: 1. illegal start or wrong len" );
1902
0
                    }
1903
0
                    aURL = aString.copy( nStart + 1, nLen );
1904
1905
0
                    aString = aString.replaceAt( 0, nStart + 1 + nLen, u"" );
1906
0
                    nStart = aString.indexOf( '@' );
1907
0
                    nLen = aString.toInt32();
1908
1909
0
                    if( !nLen && aString[ 0 ] != '0' )
1910
0
                    {
1911
0
                        SAL_INFO( "svtools", "SOLK: 2. len=0" );
1912
0
                    }
1913
0
                    if( nStart == -1 || nLen > aString.getLength() - nStart - 1 )
1914
0
                    {
1915
0
                        SAL_INFO( "svtools", "SOLK: 2. illegal start or wrong len" );
1916
0
                    }
1917
0
                    aDesc = aString.copy( nStart+1, nLen );
1918
1919
0
                    rBmk = INetBookmark( aURL, aDesc );
1920
0
                    bRet = true;
1921
0
                }
1922
0
            }
1923
0
        }
1924
0
        break;
1925
1926
0
        case SotClipboardFormatId::NETSCAPE_BOOKMARK:
1927
0
        {
1928
0
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1929
1930
0
            if (2048 == aSeq.getLength())
1931
0
            {
1932
0
                const char* p1 = reinterpret_cast< const char* >( aSeq.getConstArray() );
1933
0
                const char* p2 =  reinterpret_cast< const char* >( aSeq.getConstArray() ) + 1024;
1934
0
                rBmk = INetBookmark( OUString( p1, strlen(p1), osl_getThreadTextEncoding() ),
1935
0
                                     OUString( p2, strlen(p2), osl_getThreadTextEncoding() ) );
1936
0
                bRet = true;
1937
0
            }
1938
0
        }
1939
0
        break;
1940
1941
#ifdef _WIN32
1942
        case SotClipboardFormatId::FILEGRPDESCRIPTOR:
1943
        {
1944
            Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
1945
1946
            if (aSeq.getLength())
1947
            {
1948
                FILEGROUPDESCRIPTORW const * pFDesc = reinterpret_cast<FILEGROUPDESCRIPTORW const *>(aSeq.getConstArray());
1949
1950
                if( pFDesc->cItems )
1951
                {
1952
                    OUString aDesc( o3tl::toU(pFDesc->fgd[ 0 ].cFileName) );
1953
1954
                    if( ( aDesc.getLength() > 4 ) && aDesc.endsWithIgnoreAsciiCase(".URL") )
1955
                    {
1956
                        std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( INetURLObject( aDesc ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1957
                                                                                  StreamMode::STD_READ ));
1958
1959
                        if( !pStream || pStream->GetError() )
1960
                        {
1961
                            DataFlavor aFileContentFlavor;
1962
1963
                            aSeq.realloc( 0 );
1964
                            pStream.reset();
1965
1966
                            if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::FILECONTENT, aFileContentFlavor))
1967
                            {
1968
                                aSeq = GetSequence(aFileContentFlavor, OUString());
1969
                                if (aSeq.getLength())
1970
                                    pStream.reset(new SvMemoryStream( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), StreamMode::STD_READ ));
1971
                            }
1972
                        }
1973
1974
                        if( pStream )
1975
                        {
1976
                            OString aLine;
1977
                            bool bInA = false, bInW = false, bAFound = false;
1978
1979
                            while( pStream->ReadLine( aLine ) )
1980
                            {
1981
                                if (aLine.startsWithIgnoreAsciiCase("[InternetShortcut", &aLine))
1982
                                {
1983
                                    // May be [InternetShortcut], or [InternetShortcut.A], or
1984
                                    // [InternetShortcut.W] (the latter has UTF-7-encoded URL)
1985
                                    bInW = aLine.equalsIgnoreAsciiCase(".W]");
1986
                                    bInA = !bAFound && !bInW
1987
                                           && (aLine == "]" || aLine.equalsIgnoreAsciiCase(".A]"));
1988
                                }
1989
                                else if (aLine.startsWith("["))
1990
                                {
1991
                                    bInA = bInW = false;
1992
                                }
1993
                                else if ((bInA || bInW) && aLine.startsWithIgnoreAsciiCase("URL="))
1994
                                {
1995
                                    auto eTextEncoding = bInW ? RTL_TEXTENCODING_UTF7
1996
                                                              : osl_getThreadTextEncoding();
1997
                                    rBmk = INetBookmark( OStringToOUString(aLine.subView(4), eTextEncoding),
1998
                                                         aDesc.copy(0, aDesc.getLength() - 4) );
1999
                                    bRet = true;
2000
                                    if (bInW)
2001
                                        break;
2002
                                    else
2003
                                        bAFound = true; // Keep looking for "W"
2004
                                }
2005
                            }
2006
                        }
2007
                    }
2008
                }
2009
            }
2010
        }
2011
        break;
2012
#endif
2013
0
        default: break;
2014
0
    }
2015
0
    return bRet;
2016
0
}
2017
2018
2019
bool TransferableDataHelper::GetINetImage( SotClipboardFormatId nFormat,
2020
                                                INetImage& rINtImg ) const
2021
0
{
2022
0
    DataFlavor aFlavor;
2023
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetImage( aFlavor, rINtImg ) );
2024
0
}
2025
2026
2027
bool TransferableDataHelper::GetINetImage(
2028
        const css::datatransfer::DataFlavor& rFlavor,
2029
        INetImage& rINtImg ) const
2030
0
{
2031
0
    std::unique_ptr<SvStream> xStm = GetSotStorageStream( rFlavor );
2032
0
    if (!xStm)
2033
0
        return false;
2034
2035
0
    bool bRet = rINtImg.Read( *xStm, SotExchange::GetFormat( rFlavor ) );
2036
0
    return bRet;
2037
0
}
2038
2039
2040
bool TransferableDataHelper::GetFileList( SotClipboardFormatId nFormat,
2041
                                                FileList& rFileList ) const
2042
0
{
2043
0
    DataFlavor aFlavor;
2044
0
    return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetFileList( rFileList ) );
2045
0
}
2046
2047
2048
bool TransferableDataHelper::GetFileList( FileList& rFileList ) const
2049
0
{
2050
0
    bool                bRet = false;
2051
2052
0
    for( sal_uInt32 i = 0, nFormatCount = GetFormatCount(); ( i < nFormatCount ) && !bRet; ++i )
2053
0
    {
2054
0
        if( SotClipboardFormatId::FILE_LIST == GetFormat( i ) )
2055
0
        {
2056
0
            const DataFlavor aFlavor( GetFormatDataFlavor( i ) );
2057
2058
0
            if (std::unique_ptr<SvStream> xStm = GetSotStorageStream( aFlavor ))
2059
0
            {
2060
0
                if( aFlavor.MimeType.indexOf( "text/uri-list" ) > -1 )
2061
0
                {
2062
0
                    OStringBuffer aDiskString;
2063
2064
0
                    while( xStm->ReadLine( aDiskString ) )
2065
0
                        if( !aDiskString.isEmpty() && aDiskString[0] != '#' )
2066
0
                            rFileList.AppendFile( OStringToOUString(aDiskString, RTL_TEXTENCODING_UTF8) );
2067
2068
0
                    bRet = true;
2069
0
                }
2070
0
                else
2071
0
                    bRet = ( ReadFileList( *xStm, rFileList ).GetError() == ERRCODE_NONE );
2072
0
            }
2073
0
        }
2074
0
    }
2075
2076
0
    return bRet;
2077
0
}
2078
2079
2080
Sequence<sal_Int8> TransferableDataHelper::GetSequence( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2081
0
{
2082
0
    DataFlavor aFlavor;
2083
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2084
0
        return Sequence<sal_Int8>();
2085
2086
0
    return GetSequence(aFlavor, rDestDoc);
2087
0
}
2088
2089
Sequence<sal_Int8> TransferableDataHelper::GetSequence( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2090
0
{
2091
0
    const Any aAny = GetAny(rFlavor, rDestDoc);
2092
0
    Sequence<sal_Int8> aSeq;
2093
0
    if (aAny.hasValue())
2094
0
        aAny >>= aSeq;
2095
2096
0
    return aSeq;
2097
0
}
2098
2099
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( SotClipboardFormatId nFormat ) const
2100
0
{
2101
0
    DataFlavor aFlavor;
2102
0
    if (!SotExchange::GetFormatDataFlavor( nFormat, aFlavor ))
2103
0
        return nullptr;
2104
0
    return GetSotStorageStream( aFlavor );
2105
0
}
2106
2107
std::unique_ptr<SvStream> TransferableDataHelper::GetSotStorageStream( const DataFlavor& rFlavor ) const
2108
0
{
2109
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
2110
0
    if (!aSeq.hasElements())
2111
0
        return nullptr;
2112
2113
0
    std::unique_ptr<SvStream> xStream = SotTempStream::Create( u""_ustr );
2114
0
    xStream->WriteBytes( aSeq.getConstArray(), aSeq.getLength() );
2115
0
    xStream->Seek(0);
2116
0
    return xStream;
2117
0
}
2118
2119
Reference<XInputStream> TransferableDataHelper::GetInputStream( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
2120
0
{
2121
0
    DataFlavor aFlavor;
2122
0
    if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
2123
0
        return Reference<XInputStream>();
2124
2125
0
    return GetInputStream(aFlavor, rDestDoc);
2126
0
}
2127
2128
Reference<XInputStream> TransferableDataHelper::GetInputStream( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
2129
0
{
2130
0
    Sequence<sal_Int8> aSeq = GetSequence(rFlavor, rDestDoc);
2131
2132
0
    if (!aSeq.hasElements())
2133
0
        return Reference<XInputStream>();
2134
2135
0
    Reference<XInputStream> xStream(new comphelper::SequenceInputStream(aSeq));
2136
0
    return xStream;
2137
0
}
2138
2139
void TransferableDataHelper::Rebind( const Reference< XTransferable >& _rxNewContent )
2140
0
{
2141
0
    mxTransfer = _rxNewContent;
2142
0
    InitFormats();
2143
0
}
2144
2145
bool TransferableDataHelper::StartClipboardListening( )
2146
0
{
2147
0
    SolarMutexGuard g;
2148
2149
0
    StopClipboardListening( );
2150
2151
0
    mxClipboardListener = new TransferableClipboardNotifier(mxClipboard, *this);
2152
2153
0
    return mxClipboardListener->isListening();
2154
0
}
2155
2156
void TransferableDataHelper::StopClipboardListening( )
2157
0
{
2158
0
    SolarMutexGuard g;
2159
2160
0
    if (mxClipboardListener.is())
2161
0
    {
2162
0
        mxClipboardListener->dispose();
2163
0
        mxClipboardListener.clear();
2164
0
    }
2165
0
}
2166
2167
TransferableDataHelper TransferableDataHelper::CreateFromClipboard(const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& rClipboard)
2168
0
{
2169
0
    TransferableDataHelper  aRet;
2170
2171
0
    if( rClipboard.is() )
2172
0
    {
2173
0
        try
2174
0
        {
2175
0
            Reference< XTransferable > xTransferable( rClipboard->getContents() );
2176
2177
0
            if( xTransferable.is() )
2178
0
            {
2179
0
                aRet = TransferableDataHelper( xTransferable );
2180
                // also copy the clipboard
2181
0
                aRet.mxClipboard = rClipboard;
2182
0
            }
2183
0
        }
2184
0
        catch( const css::uno::Exception& )
2185
0
        {
2186
0
        }
2187
0
    }
2188
2189
0
    return aRet;
2190
0
}
2191
2192
TransferableDataHelper TransferableDataHelper::CreateFromSystemClipboard( vcl::Window * pWindow )
2193
0
{
2194
0
    DBG_ASSERT( pWindow, "Window pointer is NULL" );
2195
2196
0
    Reference< XClipboard > xClipboard;
2197
2198
0
    if( pWindow )
2199
0
        xClipboard = pWindow->GetClipboard();
2200
2201
0
    return CreateFromClipboard(xClipboard);
2202
0
}
2203
2204
TransferableDataHelper TransferableDataHelper::CreateFromPrimarySelection()
2205
0
{
2206
0
    Reference< XClipboard > xSelection(GetSystemPrimarySelection());
2207
0
    TransferableDataHelper   aRet;
2208
2209
0
    if( xSelection.is() )
2210
0
    {
2211
0
           SolarMutexReleaser aReleaser;
2212
2213
0
           try
2214
0
               {
2215
0
                   Reference< XTransferable > xTransferable( xSelection->getContents() );
2216
2217
0
                   if( xTransferable.is() )
2218
0
                   {
2219
0
                           aRet = TransferableDataHelper( xTransferable );
2220
0
                           aRet.mxClipboard = std::move(xSelection);
2221
0
                   }
2222
0
               }
2223
0
           catch( const css::uno::Exception& )
2224
0
               {
2225
0
               }
2226
0
    }
2227
2228
0
    return aRet;
2229
0
}
2230
2231
bool TransferableDataHelper::IsEqual( const css::datatransfer::DataFlavor& rInternalFlavor,
2232
                                      const css::datatransfer::DataFlavor& rRequestFlavor )
2233
0
{
2234
0
    const Reference< XComponentContext >&          xContext( ::comphelper::getProcessComponentContext() );
2235
0
    bool                                    bRet = false;
2236
2237
0
    try
2238
0
    {
2239
0
        Reference< XMimeContentTypeFactory >    xMimeFact = MimeContentTypeFactory::create( xContext );
2240
2241
0
        Reference< XMimeContentType > xRequestType1( xMimeFact->createMimeContentType( rInternalFlavor.MimeType ) );
2242
0
        Reference< XMimeContentType > xRequestType2( xMimeFact->createMimeContentType( rRequestFlavor.MimeType ) );
2243
2244
0
        if( xRequestType1.is() && xRequestType2.is() )
2245
0
        {
2246
0
            if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( xRequestType2->getFullMediaType() ) )
2247
0
            {
2248
0
                if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
2249
0
                {
2250
                    // special handling for text/plain media types
2251
0
                    static constexpr OUString aCharsetString( u"charset"_ustr );
2252
2253
0
                    if( !xRequestType2->hasParameter( aCharsetString ) ||
2254
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "utf-16" ) ||
2255
0
                        xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "unicode" ) )
2256
0
                    {
2257
0
                        bRet = true;
2258
0
                    }
2259
0
                }
2260
0
                else if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice" ) )
2261
0
                {
2262
                    // special handling for application/x-openoffice media types
2263
0
                    static constexpr OUString aFormatString( u"windows_formatname"_ustr );
2264
2265
0
                    if( xRequestType1->hasParameter( aFormatString ) &&
2266
0
                        xRequestType2->hasParameter( aFormatString ) &&
2267
0
                        xRequestType1->getParameterValue( aFormatString ).equalsIgnoreAsciiCase( xRequestType2->getParameterValue( aFormatString ) ) )
2268
0
                    {
2269
0
                        bRet = true;
2270
0
                    }
2271
0
                }
2272
0
                else
2273
0
                    bRet = true;
2274
0
            }
2275
0
        }
2276
0
    }
2277
0
    catch( const css::uno::Exception& )
2278
0
    {
2279
0
        bRet = rInternalFlavor.MimeType.equalsIgnoreAsciiCase( rRequestFlavor.MimeType );
2280
0
    }
2281
2282
0
    return bRet;
2283
0
}
2284
2285
static bool WriteDDELink_impl(SvStream& stream, rtl_TextEncoding encoding,
2286
                              std::u16string_view application, std::u16string_view topic,
2287
                              std::u16string_view item, std::u16string_view extra)
2288
0
{
2289
    // Put Link format, potentially with extra: either application\0topic\0item\0\0
2290
    // or application\0topic\0item\0extra\0\0
2291
0
    assert(rtl_isOctetTextEncoding(encoding)); // The Link format is 8-bit
2292
0
    stream.WriteUnicodeOrByteText(application, encoding, true);
2293
0
    stream.WriteUnicodeOrByteText(topic, encoding, true);
2294
0
    stream.WriteUnicodeOrByteText(item, encoding, true);
2295
0
    if (!extra.empty())
2296
0
        stream.WriteUnicodeOrByteText(extra, encoding, true);
2297
0
    stream.WriteChar('\0'); // One more trailing zero
2298
0
    return stream.GetErrorCode() == ERRCODE_NONE;
2299
0
}
2300
2301
// static
2302
bool TransferableDataHelper::WriteDDELink(SvStream& stream, std::u16string_view application,
2303
                                          std::u16string_view topic, std::u16string_view item,
2304
                                          std::u16string_view extra)
2305
0
{
2306
    // 1. Put standard Link format in the system encoding
2307
0
    WriteDDELink_impl(stream, osl_getThreadTextEncoding(), application, topic, item, extra);
2308
2309
    // 2. Put our extension to the format. Start with a magic string "ULnk", followed by
2310
    // UTF-8-encoded Link format
2311
0
    stream.WriteOString("ULnk");
2312
0
    return WriteDDELink_impl(stream, RTL_TEXTENCODING_UTF8, application, topic, item, extra);
2313
0
}
2314
2315
static size_t ReadDDELink_impl(std::string_view str, std::string_view& application,
2316
                               std::string_view& topic, std::string_view& item,
2317
                               std::string_view& rest)
2318
0
{
2319
0
    application = {};
2320
0
    topic = {};
2321
0
    item = {};
2322
0
    rest = {};
2323
2324
0
    size_t start = 0;
2325
0
    size_t end = str.find('\0', start);
2326
0
    application = str.substr(start, end - start);
2327
0
    if (end == std::string_view::npos)
2328
0
        return end;
2329
0
    start = end + 1;
2330
0
    end = str.find('\0', start);
2331
0
    topic = str.substr(start, end - start);
2332
0
    if (end == std::string_view::npos)
2333
0
        return end;
2334
0
    start = end + 1;
2335
0
    end = str.find('\0', start);
2336
0
    item = str.substr(start, end - start);
2337
0
    if (end >= str.size() - 1 || str[end + 1] == '\0')
2338
0
        return end;
2339
0
    start = end + 1;
2340
0
    do
2341
0
    {
2342
        // Read all remaining data until \0\0 into rest, including possible single \0
2343
0
        end = str.find('\0', end + 1);
2344
0
    } while (end < str.size() - 1 && str[end + 1] != '\0');
2345
0
    rest = str.substr(start, end - start);
2346
0
    return end;
2347
0
}
2348
2349
bool TransferableDataHelper::ReadDDELink(OUString& application, OUString& topic, OUString& item,
2350
                                         OUString& rest) const
2351
0
{
2352
0
    css::uno::Sequence<sal_Int8> sequence = GetSequence(SotClipboardFormatId::LINK, {});
2353
0
    if (!sequence.hasElements())
2354
0
    {
2355
0
        SAL_WARN("svtools", "DDE data not found");
2356
0
        return false;
2357
0
    }
2358
2359
0
    std::string_view str(reinterpret_cast<const char*>(sequence.getConstArray()),
2360
0
                         sequence.getLength());
2361
0
    std::string_view application_view, topic_view, item_view, rest_view;
2362
0
    rtl_TextEncoding encoding = osl_getThreadTextEncoding();
2363
0
    size_t end = ReadDDELink_impl(str, application_view, topic_view, item_view, rest_view);
2364
0
    if (end < str.size() - 1 && str[end + 1] == '\0' && str.substr(end + 2, 4) == "ULnk")
2365
0
    {
2366
        // Now try to read our UTF-8 extension; if it's present, use it unconditionally, even if
2367
        // osl_getThreadTextEncoding gives RTL_TEXTENCODING_UTF8, because the extension is immune
2368
        // to possible different source encoding
2369
0
        encoding = RTL_TEXTENCODING_UTF8;
2370
0
        ReadDDELink_impl(str.substr(end + 6), application_view, topic_view, item_view, rest_view);
2371
0
    }
2372
0
    application = OStringToOUString(application_view, encoding);
2373
0
    topic = OStringToOUString(topic_view, encoding);
2374
0
    item = OStringToOUString(item_view, encoding);
2375
0
    rest = OStringToOUString(rest_view, encoding);
2376
0
    return !application.isEmpty() && !topic.isEmpty() && !item.isEmpty();
2377
0
}
2378
2379
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */