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