1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
25 #include <unx/saldisp.hxx>
32 #include <X11/Xutil.h>
33 #include <X11/XKBlib.h>
34 #include <X11/Xatom.h>
35 #include <X11/keysym.h>
37 #if defined(NETBSD) || defined (FREEBSD) || defined(OPENBSD)
43 #include <sal/macros.h>
45 #include "X11_selection.hxx"
46 #include "X11_clipboard.hxx"
47 #include "X11_transferable.hxx"
48 #include "X11_dndcontext.hxx"
51 #include <vcl/svapp.hxx>
54 #include "copydata_curs.h"
55 #include "copydata_mask.h"
56 #include "movedata_curs.h"
57 #include "movedata_mask.h"
58 #include "linkdata_curs.h"
59 #include "linkdata_mask.h"
60 #include "nodrop_curs.h"
61 #include "nodrop_mask.h"
62 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
63 #include <com/sun/star/awt/MouseEvent.hpp>
64 #include <com/sun/star/awt/MouseButton.hpp>
65 #include <com/sun/star/frame/Desktop.hpp>
66 #include <rtl/tencinfo.h>
67 #include <rtl/ustrbuf.hxx>
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/solarmutex.hxx>
72 #include <cppuhelper/supportsservice.hxx>
75 constexpr auto DRAG_EVENT_MASK
= ButtonPressMask
|
81 using namespace com::sun::star::datatransfer
;
82 using namespace com::sun::star::datatransfer::dnd
;
83 using namespace com::sun::star::lang
;
84 using namespace com::sun::star::awt
;
85 using namespace com::sun::star::uno
;
86 using namespace com::sun::star::frame
;
91 // stubs to satisfy solaris compiler's rather rigid linking warning
94 static void call_SelectionManager_run( void * pMgr
)
96 SelectionManager::run( pMgr
);
99 static void call_SelectionManager_runDragExecute( void * pMgr
)
101 osl_setThreadName("SelectionManager::runDragExecute()");
102 SelectionManager::runDragExecute( pMgr
);
106 const tools::Long nXdndProtocolRevision
= 5;
110 // mapping between mime types (or what the office thinks of mime types)
111 // and X convention types
112 struct NativeTypeEntry
115 const char* pType
; // Mime encoding on our side
116 const char* pNativeType
; // string corresponding to nAtom for the case of nAtom being uninitialized
117 int nFormat
; // the corresponding format
122 // the convention for Xdnd is mime types as specified by the corresponding
123 // RFC's with the addition that text/plain without charset tag contains iso8859-1
124 // sadly some applications (e.g. gtk) do not honor the mimetype only rule,
125 // so for compatibility add UTF8_STRING
126 static NativeTypeEntry aXdndConversionTab
[] =
128 { 0, "text/plain;charset=iso8859-1", "text/plain", 8 },
129 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }
132 // for clipboard and primary selections there is only a convention for text
133 // that the encoding name of the text is taken as type in all capitalized letters
134 static NativeTypeEntry aNativeConversionTab
[] =
136 { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 },
137 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 },
138 { 0, "text/plain;charset=utf-8", "UTF-8", 8 },
139 { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 },
141 { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 },
142 { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 },
143 { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 },
144 { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 },
145 { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 },
146 { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 },
147 { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 },
148 { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 },
149 { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 },
150 { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 },
151 { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 },
152 { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 },
154 { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 },
155 { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 },
156 { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 },
157 { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 },
158 { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 },
159 { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 },
160 // eastern european encodings
161 { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 },
162 { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 },
163 // String (== iso8859-1)
164 { XA_STRING
, "text/plain;charset=iso8859-1", "STRING", 8 },
165 // special for compound text
166 { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 },
169 { XA_PIXMAP
, "image/bmp", "PIXMAP", 32 }
172 rtl_TextEncoding
x11::getTextPlainEncoding( const OUString
& rMimeType
)
174 rtl_TextEncoding aEncoding
= RTL_TEXTENCODING_DONTKNOW
;
175 OUString
aMimeType( rMimeType
.toAsciiLowerCase() );
176 sal_Int32 nIndex
= 0;
177 if( aMimeType
.getToken( 0, ';', nIndex
) == "text/plain" )
179 if( aMimeType
.getLength() == 10 ) // only "text/plain"
180 aEncoding
= RTL_TEXTENCODING_ISO_8859_1
;
183 while( nIndex
!= -1 )
185 OUString aToken
= aMimeType
.getToken( 0, ';', nIndex
);
187 if( aToken
.getToken( 0, '=', nPos
) == "charset" )
189 OString aEncToken
= OUStringToOString( aToken
.getToken( 0, '=', nPos
), RTL_TEXTENCODING_ISO_8859_1
);
190 aEncoding
= rtl_getTextEncodingFromUnixCharset( aEncToken
.getStr() );
191 if( aEncoding
== RTL_TEXTENCODING_DONTKNOW
)
193 if( aEncToken
.equalsIgnoreAsciiCase( "utf-8" ) )
194 aEncoding
= RTL_TEXTENCODING_UTF8
;
196 if( aEncoding
!= RTL_TEXTENCODING_DONTKNOW
)
202 #if OSL_DEBUG_LEVEL > 1
203 SAL_WARN_IF(aEncoding
== RTL_TEXTENCODING_DONTKNOW
,
204 "vcl.unx.dtrans", "getTextPlainEncoding( "
205 << rMimeType
<< " ) failed.");
210 std::unordered_map
< OUString
, SelectionManager
* >& SelectionManager::getInstances()
212 static std::unordered_map
< OUString
, SelectionManager
* > aInstances
;
216 SelectionManager::SelectionManager() :
217 m_nIncrementalThreshold( 15*1024 ),
218 m_pDisplay( nullptr ),
219 m_aThread( nullptr ),
220 m_aDragExecuteThread( nullptr ),
222 m_nSelectionTimeout( 0 ),
223 m_nSelectionTimestamp( CurrentTime
),
224 m_bDropEnterSent( true ),
225 m_aCurrentDropWindow( None
),
227 m_nLastDropAction( 0 ),
230 m_bDropWaitingForCompletion( false ),
231 m_aDropWindow( None
),
232 m_aDropProxy( None
),
233 m_aDragSourceWindow( None
),
241 m_nUserDragAction( 0 ),
242 m_nTargetAcceptAction( 0 ),
243 m_nSourceActions( 0 ),
244 m_bLastDropAccepted( false ),
245 m_bDropSuccess( false ),
246 m_bDropSent( false ),
248 m_bWaitingForPrimaryConversion( false ),
249 m_aMoveCursor( None
),
250 m_aCopyCursor( None
),
251 m_aLinkCursor( None
),
252 m_aNoneCursor( None
),
253 m_aCurrentCursor( None
),
254 m_nCurrentProtocolVersion( nXdndProtocolRevision
),
255 m_nTARGETSAtom( None
),
256 m_nTIMESTAMPAtom( None
),
259 m_nCOMPOUNDAtom( None
),
260 m_nMULTIPLEAtom( None
),
261 m_nImageBmpAtom( None
),
262 m_nXdndAware( None
),
263 m_nXdndEnter( None
),
264 m_nXdndLeave( None
),
265 m_nXdndPosition( None
),
266 m_nXdndStatus( None
),
268 m_nXdndFinished( None
),
269 m_nXdndSelection( None
),
270 m_nXdndTypeList( None
),
271 m_nXdndProxy( None
),
272 m_nXdndActionCopy( None
),
273 m_nXdndActionMove( None
),
274 m_nXdndActionLink( None
),
275 m_nXdndActionAsk( None
),
278 memset(&m_aDropEnterEvent
, 0, sizeof(m_aDropEnterEvent
));
279 m_EndThreadPipe
[0] = 0;
280 m_EndThreadPipe
[1] = 0;
281 m_aDragRunning
.reset();
284 Cursor
SelectionManager::createCursor( const unsigned char* pPointerData
, const unsigned char* pMaskData
, int width
, int height
, int hotX
, int hotY
)
288 XColor aBlack
, aWhite
;
290 aBlack
.pixel
= BlackPixel( m_pDisplay
, 0 );
291 aBlack
.red
= aBlack
.green
= aBlack
.blue
= 0;
292 aBlack
.flags
= DoRed
| DoGreen
| DoBlue
;
294 aWhite
.pixel
= WhitePixel( m_pDisplay
, 0 );
295 aWhite
.red
= aWhite
.green
= aWhite
.blue
= 0xffff;
296 aWhite
.flags
= DoRed
| DoGreen
| DoBlue
;
299 XCreateBitmapFromData( m_pDisplay
,
301 reinterpret_cast<const char*>(pPointerData
),
305 = XCreateBitmapFromData( m_pDisplay
,
307 reinterpret_cast<const char*>(pMaskData
),
311 XCreatePixmapCursor( m_pDisplay
, aPointer
, aMask
,
315 XFreePixmap( m_pDisplay
, aPointer
);
316 XFreePixmap( m_pDisplay
, aMask
);
321 void SelectionManager::initialize( const Sequence
< Any
>& arguments
)
323 osl::MutexGuard
aGuard(m_aMutex
);
325 if( ! m_xDisplayConnection
.is() )
328 * first argument must be a css::awt::XDisplayConnection
329 * from this we will get the XEvents of the vcl event loop by
330 * registering us as XEventHandler on it.
332 * implementor's note:
334 * finally the clipboard and XDND service is back in the module it belongs
335 * now cleanup and sharing of resources with the normal vcl event loop
336 * needs to be added. The display used would be that of the normal event loop
337 * and synchronization should be done via the SolarMutex.
339 if( arguments
.hasElements() )
340 arguments
.getConstArray()[0] >>= m_xDisplayConnection
;
341 if( ! m_xDisplayConnection
.is() )
345 m_xDisplayConnection
->addEventHandler( Any(), this, ~0 );
352 if( m_xDisplayConnection
.is() )
354 Any aIdentifier
= m_xDisplayConnection
->getIdentifier();
355 aIdentifier
>>= aUDisplay
;
358 OString
aDisplayName( OUStringToOString( aUDisplay
, RTL_TEXTENCODING_ISO_8859_1
) );
360 m_pDisplay
= XOpenDisplay( aDisplayName
.isEmpty() ? nullptr : aDisplayName
.getStr());
366 XSynchronize( m_pDisplay
, True
);
369 m_nTARGETSAtom
= getAtom( "TARGETS" );
370 m_nTIMESTAMPAtom
= getAtom( "TIMESTAMP" );
371 m_nTEXTAtom
= getAtom( "TEXT" );
372 m_nINCRAtom
= getAtom( "INCR" );
373 m_nCOMPOUNDAtom
= getAtom( "COMPOUND_TEXT" );
374 m_nMULTIPLEAtom
= getAtom( "MULTIPLE" );
375 m_nImageBmpAtom
= getAtom( "image/bmp" );
377 // Atoms for Xdnd protocol
378 m_nXdndAware
= getAtom( "XdndAware" );
379 m_nXdndEnter
= getAtom( "XdndEnter" );
380 m_nXdndLeave
= getAtom( "XdndLeave" );
381 m_nXdndPosition
= getAtom( "XdndPosition" );
382 m_nXdndStatus
= getAtom( "XdndStatus" );
383 m_nXdndDrop
= getAtom( "XdndDrop" );
384 m_nXdndFinished
= getAtom( "XdndFinished" );
385 m_nXdndSelection
= getAtom( "XdndSelection" );
386 m_nXdndTypeList
= getAtom( "XdndTypeList" );
387 m_nXdndProxy
= getAtom( "XdndProxy" );
388 m_nXdndActionCopy
= getAtom( "XdndActionCopy" );
389 m_nXdndActionMove
= getAtom( "XdndActionMove" );
390 m_nXdndActionLink
= getAtom( "XdndActionLink" );
391 m_nXdndActionAsk
= getAtom( "XdndActionAsk" );
393 // initialize map with member none
394 m_aAtomToString
[ 0 ]= "None";
395 m_aAtomToString
[ XA_PRIMARY
] = "PRIMARY";
397 // create a (invisible) message window
398 m_aWindow
= XCreateSimpleWindow( m_pDisplay
, DefaultRootWindow( m_pDisplay
),
399 10, 10, 10, 10, 0, 0, 1 );
401 // initialize threshold for incremental transfers
402 // ICCCM says it should be smaller that the max request size
403 // which in turn is guaranteed to be at least 16k bytes
404 m_nIncrementalThreshold
= XMaxRequestSize( m_pDisplay
) - 1024;
409 // initialize default cursors
410 m_aMoveCursor
= createCursor( movedata_curs_bits
,
413 movedata_curs_height
,
415 movedata_curs_y_hot
);
416 m_aCopyCursor
= createCursor( copydata_curs_bits
,
419 copydata_curs_height
,
421 copydata_curs_y_hot
);
422 m_aLinkCursor
= createCursor( linkdata_curs_bits
,
425 linkdata_curs_height
,
427 linkdata_curs_y_hot
);
428 m_aNoneCursor
= createCursor( nodrop_curs_bits
,
435 // just interested in SelectionClear/Notify/Request and PropertyChange
436 XSelectInput( m_pDisplay
, m_aWindow
, PropertyChangeMask
);
437 // create the transferable for Drag operations
438 m_xDropTransferable
= new X11Transferable( *this, m_nXdndSelection
);
439 registerHandler( m_nXdndSelection
, *this );
441 m_aThread
= osl_createSuspendedThread( call_SelectionManager_run
, this );
443 osl_resumeThread( m_aThread
);
444 #if OSL_DEBUG_LEVEL > 1
446 SAL_WARN("vcl.unx.dtrans", "SelectionManager::initialize: "
447 << "creation of dispatch thread failed !.");
450 if (pipe(m_EndThreadPipe
) != 0) {
451 #if OSL_DEBUG_LEVEL > 1
452 SAL_WARN("vcl.unx.dtrans", "Failed to create endThreadPipe.");
454 m_EndThreadPipe
[0] = m_EndThreadPipe
[1] = 0;
458 SelectionManager::~SelectionManager()
460 #if OSL_DEBUG_LEVEL > 1
461 SAL_INFO("vcl.unx.dtrans", "SelectionManager::~SelectionManager ("
462 << (m_pDisplay
? DisplayString(m_pDisplay
) : "no display")
466 osl::MutexGuard
aGuard( *osl::Mutex::getGlobalMutex() );
468 auto it
= std::find_if(getInstances().begin(), getInstances().end(),
469 [&](const std::pair
< OUString
, SelectionManager
* >& rInstance
) { return rInstance
.second
== this; });
470 if( it
!= getInstances().end() )
471 getInstances().erase( it
);
476 osl_terminateThread( m_aThread
);
477 osl_joinWithThread( m_aThread
);
478 osl_destroyThread( m_aThread
);
481 if( m_aDragExecuteThread
)
483 osl_terminateThread( m_aDragExecuteThread
);
484 osl_joinWithThread( m_aDragExecuteThread
);
485 m_aDragExecuteThread
= nullptr;
486 // thread handle is freed in dragDoDispatch()
489 osl::MutexGuard
aGuard(m_aMutex
);
491 #if OSL_DEBUG_LEVEL > 1
492 SAL_INFO("vcl.unx.dtrans", "shutting down SelectionManager.");
498 deregisterHandler( m_nXdndSelection
);
499 // destroy message window
501 XDestroyWindow( m_pDisplay
, m_aWindow
);
503 if (m_aMoveCursor
!= None
)
504 XFreeCursor(m_pDisplay
, m_aMoveCursor
);
505 if (m_aCopyCursor
!= None
)
506 XFreeCursor(m_pDisplay
, m_aCopyCursor
);
507 if (m_aLinkCursor
!= None
)
508 XFreeCursor(m_pDisplay
, m_aLinkCursor
);
509 if (m_aNoneCursor
!= None
)
510 XFreeCursor(m_pDisplay
, m_aNoneCursor
);
512 // paranoia setting, the drag thread should have
514 XUngrabPointer( m_pDisplay
, CurrentTime
);
515 XUngrabKeyboard( m_pDisplay
, CurrentTime
);
517 XCloseDisplay( m_pDisplay
);
520 SelectionAdaptor
* SelectionManager::getAdaptor( Atom selection
)
522 std::unordered_map
< Atom
, Selection
* >::iterator it
=
523 m_aSelections
.find( selection
);
524 return it
!= m_aSelections
.end() ? it
->second
->m_pAdaptor
: nullptr;
527 OUString
SelectionManager::convertFromCompound( const char* pText
, int nLen
)
529 osl::MutexGuard
aGuard( m_aMutex
);
532 nLen
= strlen( pText
);
534 char** pTextList
= nullptr;
538 aProp
.value
= reinterpret_cast<unsigned char *>(const_cast<char *>(pText
));
539 aProp
.encoding
= m_nCOMPOUNDAtom
;
542 XmbTextPropertyToTextList( m_pDisplay
,
546 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
547 for( int i
= 0; i
< nTexts
; i
++ )
548 aRet
.append(OStringToOUString( pTextList
[i
], aEncoding
));
551 XFreeStringList( pTextList
);
553 return aRet
.makeStringAndClear();
556 OString
SelectionManager::convertToCompound( const OUString
& rText
)
558 osl::MutexGuard
aGuard( m_aMutex
);
560 aProp
.value
= nullptr;
561 aProp
.encoding
= XA_STRING
;
565 OString
aRet( rText
.getStr(), rText
.getLength(), osl_getThreadTextEncoding() );
566 char* pT
= const_cast<char*>(aRet
.getStr());
568 XmbTextListToTextProperty( m_pDisplay
,
575 aRet
= reinterpret_cast<char*>(aProp
.value
);
576 XFree( aProp
.value
);
579 * for currently unknown reasons XmbTextListToTextProperty on Solaris returns
580 * no data in ISO8859-n encodings (at least for n = 1, 15)
581 * in these encodings the directly converted text does the
584 if( aRet
.isEmpty() && !rText
.isEmpty() )
585 aRet
= OUStringToOString( rText
, osl_getThreadTextEncoding() );
594 bool SelectionManager::convertData(
595 const css::uno::Reference
< XTransferable
>& xTransferable
,
599 Sequence
< sal_Int8
>& rData
)
601 bool bSuccess
= false;
603 if( ! xTransferable
.is() )
610 aFlavor
.MimeType
= convertTypeFromNative( nType
, nSelection
, rFormat
);
612 sal_Int32 nIndex
= 0;
613 if( aFlavor
.MimeType
.getToken( 0, ';', nIndex
) == "text/plain" )
615 if( aFlavor
.MimeType
.getToken( 0, ';', nIndex
) == "charset=utf-16" )
616 aFlavor
.DataType
= cppu::UnoType
<OUString
>::get();
618 aFlavor
.DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
621 aFlavor
.DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
623 if( xTransferable
->isDataFlavorSupported( aFlavor
) )
625 Any
aValue( xTransferable
->getTransferData( aFlavor
) );
626 if( aValue
.getValueTypeClass() == TypeClass_STRING
)
630 rData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aString
.getStr()), aString
.getLength() * sizeof( sal_Unicode
) );
633 else if( aValue
.getValueType() == cppu::UnoType
<Sequence
< sal_Int8
>>::get() )
639 else if( aFlavor
.MimeType
.startsWith("text/plain") )
641 rtl_TextEncoding aEncoding
= RTL_TEXTENCODING_DONTKNOW
;
642 bool bCompoundText
= false;
643 if( nType
== m_nCOMPOUNDAtom
)
644 bCompoundText
= true;
646 aEncoding
= getTextPlainEncoding( aFlavor
.MimeType
);
647 if( aEncoding
!= RTL_TEXTENCODING_DONTKNOW
|| bCompoundText
)
649 aFlavor
.MimeType
= "text/plain;charset=utf-16";
650 aFlavor
.DataType
= cppu::UnoType
<OUString
>::get();
651 if( xTransferable
->isDataFlavorSupported( aFlavor
) )
653 Any
aValue( xTransferable
->getTransferData( aFlavor
) );
656 OString
aByteString( bCompoundText
? convertToCompound( aString
) : OUStringToOString( aString
, aEncoding
) );
657 rData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aByteString
.getStr()), aByteString
.getLength() * sizeof( char ) );
663 // various exceptions possible ... which all lead to a failed conversion
664 // so simplify here to a catch all
672 SelectionManager
& SelectionManager::get( const OUString
& rDisplayName
)
674 osl::MutexGuard
aGuard( *osl::Mutex::getGlobalMutex() );
676 OUString
aDisplayName( rDisplayName
);
677 if( aDisplayName
.isEmpty() )
678 aDisplayName
= OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1
);
679 SelectionManager
* pInstance
= nullptr;
681 std::unordered_map
< OUString
, SelectionManager
* >::iterator it
= getInstances().find( aDisplayName
);
682 if( it
!= getInstances().end() )
683 pInstance
= it
->second
;
684 else pInstance
= getInstances()[ aDisplayName
] = new SelectionManager();
689 OUString
SelectionManager::getString( Atom aAtom
)
691 osl::MutexGuard
aGuard(m_aMutex
);
693 if( m_aAtomToString
.find( aAtom
) == m_aAtomToString
.end() )
695 char* pAtom
= m_pDisplay
? XGetAtomName( m_pDisplay
, aAtom
) : nullptr;
698 OUString
aString( OStringToOUString( pAtom
, RTL_TEXTENCODING_ISO_8859_1
) );
700 m_aStringToAtom
[ aString
] = aAtom
;
701 m_aAtomToString
[ aAtom
] = aString
;
703 return m_aAtomToString
[ aAtom
];
706 Atom
SelectionManager::getAtom( const OUString
& rString
)
708 osl::MutexGuard
aGuard(m_aMutex
);
710 if( m_aStringToAtom
.find( rString
) == m_aStringToAtom
.end() )
712 static Atom nNoDisplayAtoms
= 1;
713 Atom aAtom
= m_pDisplay
? XInternAtom( m_pDisplay
, OUStringToOString( rString
, RTL_TEXTENCODING_ISO_8859_1
).getStr(), False
) : nNoDisplayAtoms
++;
714 m_aStringToAtom
[ rString
] = aAtom
;
715 m_aAtomToString
[ aAtom
] = rString
;
717 return m_aStringToAtom
[ rString
];
720 bool SelectionManager::requestOwnership( Atom selection
)
722 bool bSuccess
= false;
723 if( m_pDisplay
&& m_aWindow
)
725 osl::MutexGuard
aGuard(m_aMutex
);
727 SelectionAdaptor
* pAdaptor
= getAdaptor( selection
);
730 XSetSelectionOwner( m_pDisplay
, selection
, m_aWindow
, CurrentTime
);
731 if( XGetSelectionOwner( m_pDisplay
, selection
) == m_aWindow
)
734 #if OSL_DEBUG_LEVEL > 1
735 SAL_INFO("vcl.unx.dtrans",
736 (bSuccess
? "acquired" : "failed to acquire")
737 << " ownership for selection "
738 << getString( selection
));
741 Selection
* pSel
= m_aSelections
[ selection
];
742 pSel
->m_bOwner
= bSuccess
;
743 delete pSel
->m_pPixmap
;
744 pSel
->m_pPixmap
= nullptr;
745 pSel
->m_nOrigTimestamp
= m_nSelectionTimestamp
;
747 #if OSL_DEBUG_LEVEL > 1
749 SAL_WARN("vcl.unx.dtrans", "no adaptor for selection "
750 << getString( selection
));
752 if( pAdaptor
->getTransferable().is() )
754 Sequence
< DataFlavor
> aTypes
= pAdaptor
->getTransferable()->getTransferDataFlavors();
755 for( int i
= 0; i
< aTypes
.getLength(); i
++ )
757 SAL_INFO("vcl.unx.dtrans", " " << aTypes
.getConstArray()[i
].MimeType
);
765 void SelectionManager::convertTypeToNative( const OUString
& rType
, Atom selection
, int& rFormat
, ::std::list
< Atom
>& rConversions
, bool bPushFront
)
767 NativeTypeEntry
* pTab
= selection
== m_nXdndSelection
? aXdndConversionTab
: aNativeConversionTab
;
768 int nTabEntries
= selection
== m_nXdndSelection
? SAL_N_ELEMENTS(aXdndConversionTab
) : SAL_N_ELEMENTS(aNativeConversionTab
);
770 OString
aType( OUStringToOString( rType
, RTL_TEXTENCODING_ISO_8859_1
) );
771 SAL_INFO( "vcl.unx.dtrans", "convertTypeToNative " << aType
);
773 for( int i
= 0; i
< nTabEntries
; i
++ )
775 if( aType
.equalsIgnoreAsciiCase( pTab
[i
].pType
) )
777 if( ! pTab
[i
].nAtom
)
778 pTab
[i
].nAtom
= getAtom( OStringToOUString( pTab
[i
].pNativeType
, RTL_TEXTENCODING_ISO_8859_1
) );
779 rFormat
= pTab
[i
].nFormat
;
781 rConversions
.push_front( pTab
[i
].nAtom
);
783 rConversions
.push_back( pTab
[i
].nAtom
);
784 if( pTab
[i
].nFormat
== XA_PIXMAP
)
788 rConversions
.push_front( XA_VISUALID
);
789 rConversions
.push_front( XA_COLORMAP
);
793 rConversions
.push_back( XA_VISUALID
);
794 rConversions
.push_back( XA_COLORMAP
);
800 rFormat
= 8; // byte buffer
802 rConversions
.push_front( getAtom( rType
) );
804 rConversions
.push_back( getAtom( rType
) );
807 void SelectionManager::getNativeTypeList( const Sequence
< DataFlavor
>& rTypes
, std::list
< Atom
>& rOutTypeList
, Atom targetselection
)
809 rOutTypeList
.clear();
812 bool bHaveText
= false;
813 for( const auto& rFlavor
: rTypes
)
815 if( rFlavor
.MimeType
.startsWith("text/plain"))
818 convertTypeToNative( rFlavor
.MimeType
, targetselection
, nFormat
, rOutTypeList
);
822 if( targetselection
!= m_nXdndSelection
)
824 // only mimetypes should go into Xdnd type list
825 rOutTypeList
.push_front( XA_STRING
);
826 rOutTypeList
.push_front( m_nCOMPOUNDAtom
);
828 convertTypeToNative( "text/plain;charset=utf-8", targetselection
, nFormat
, rOutTypeList
, true );
830 if( targetselection
!= m_nXdndSelection
)
831 rOutTypeList
.push_back( m_nMULTIPLEAtom
);
834 OUString
SelectionManager::convertTypeFromNative( Atom nType
, Atom selection
, int& rFormat
)
836 NativeTypeEntry
* pTab
= (selection
== m_nXdndSelection
) ? aXdndConversionTab
: aNativeConversionTab
;
837 int nTabEntries
= (selection
== m_nXdndSelection
) ? SAL_N_ELEMENTS(aXdndConversionTab
) : SAL_N_ELEMENTS(aNativeConversionTab
);
839 for( int i
= 0; i
< nTabEntries
; i
++ )
841 if( ! pTab
[i
].nAtom
)
842 pTab
[i
].nAtom
= getAtom( OStringToOUString( pTab
[i
].pNativeType
, RTL_TEXTENCODING_ISO_8859_1
) );
843 if( nType
== pTab
[i
].nAtom
)
845 rFormat
= pTab
[i
].nFormat
;
846 return OStringToOUString( pTab
[i
].pType
, RTL_TEXTENCODING_ISO_8859_1
);
850 return getString( nType
);
853 bool SelectionManager::getPasteData( Atom selection
, Atom type
, Sequence
< sal_Int8
>& rData
)
855 osl::ResettableMutexGuard
aGuard(m_aMutex
);
856 std::unordered_map
< Atom
, Selection
* >::iterator it
;
857 bool bSuccess
= false;
859 #if OSL_DEBUG_LEVEL > 1
860 SAL_INFO("vcl.unx.dtrans", "getPasteData( " << getString( selection
)
861 << ", native: " << getString( type
) << " ).");
867 it
= m_aSelections
.find( selection
);
868 if( it
== m_aSelections
.end() )
871 ::Window aSelectionOwner
= XGetSelectionOwner( m_pDisplay
, selection
);
872 if( aSelectionOwner
== None
)
874 if( aSelectionOwner
== m_aWindow
)
876 // probably bad timing led us here
877 #if OSL_DEBUG_LEVEL > 1
878 SAL_WARN("vcl.unx.dtrans", "Innere Nabelschau.");
883 // ICCCM recommends to destroy property before convert request unless
884 // parameters are transported; we do only in case of MULTIPLE,
885 // so destroy property unless target is MULTIPLE
886 if( type
!= m_nMULTIPLEAtom
)
887 XDeleteProperty( m_pDisplay
, m_aWindow
, selection
);
889 XConvertSelection( m_pDisplay
, selection
, type
, selection
, m_aWindow
, selection
== m_nXdndSelection
? m_nDropTime
: CurrentTime
);
890 it
->second
->m_eState
= Selection::WaitingForResponse
;
891 it
->second
->m_aRequestedType
= type
;
892 it
->second
->m_aData
= Sequence
< sal_Int8
>();
893 it
->second
->m_aDataArrived
.reset();
894 // really start the request; if we don't flush the
895 // queue the request won't leave it because there are no more
896 // X calls after this until the data arrived or timeout
897 XFlush( m_pDisplay
);
900 struct timeval tv_last
, tv_current
;
901 gettimeofday( &tv_last
, nullptr );
902 tv_current
= tv_last
;
907 bool bAdjustTime
= false;
909 bool bHandle
= false;
911 if( XCheckTypedEvent( m_pDisplay
,
917 if( aEvent
.xproperty
.window
== m_aWindow
918 && aEvent
.xproperty
.atom
== selection
)
921 else if( XCheckTypedEvent( m_pDisplay
,
928 else if( XCheckTypedEvent( m_pDisplay
,
935 else if( XCheckTypedEvent( m_pDisplay
,
941 if( aEvent
.xselection
.selection
== selection
942 && ( aEvent
.xselection
.requestor
== m_aWindow
||
943 aEvent
.xselection
.requestor
== m_aCurrentDropWindow
)
950 osl::Thread::wait(std::chrono::milliseconds(100));
956 handleXEvent( aEvent
);
960 gettimeofday( &tv_current
, nullptr );
962 tv_last
= tv_current
;
963 } while( ! it
->second
->m_aDataArrived
.check() && (tv_current
.tv_sec
- tv_last
.tv_sec
) < getSelectionTimeout() );
965 #if OSL_DEBUG_LEVEL > 1
966 SAL_WARN_IF((tv_current
.tv_sec
- tv_last
.tv_sec
) > getSelectionTimeout(),
967 "vcl.unx.dtrans", "timed out.");
970 if( it
->second
->m_aDataArrived
.check() &&
971 it
->second
->m_aData
.getLength() )
973 rData
= it
->second
->m_aData
;
976 #if OSL_DEBUG_LEVEL > 1
978 SAL_WARN("vcl.unx.dtrans", "conversion unsuccessful.");
983 bool SelectionManager::getPasteData( Atom selection
, const OUString
& rType
, Sequence
< sal_Int8
>& rData
)
985 bool bSuccess
= false;
987 std::unordered_map
< Atom
, Selection
* >::iterator it
;
989 osl::MutexGuard
aGuard(m_aMutex
);
991 it
= m_aSelections
.find( selection
);
992 if( it
== m_aSelections
.end() )
996 if( it
->second
->m_aTypes
.getLength() == 0 )
998 Sequence
< DataFlavor
> aFlavors
;
999 getPasteDataTypes( selection
, aFlavors
);
1000 if( it
->second
->m_aTypes
.getLength() == 0 )
1004 const Sequence
< DataFlavor
>& rTypes( it
->second
->m_aTypes
);
1005 const std::vector
< Atom
>& rNativeTypes( it
->second
->m_aNativeTypes
);
1007 #if OSL_DEBUG_LEVEL > 1
1008 SAL_INFO("vcl.unx.dtrans", "getPasteData( \""
1009 << getString( selection
)
1011 << rType
<< "\" ).");
1014 if( rType
== "text/plain;charset=utf-16" )
1016 // lets see if we have UTF16 else try to find something convertible
1017 if( it
->second
->m_aTypes
.getLength() && ! it
->second
->m_bHaveUTF16
)
1019 Sequence
< sal_Int8
> aData
;
1020 if( it
->second
->m_aUTF8Type
!= None
&&
1021 getPasteData( selection
,
1022 it
->second
->m_aUTF8Type
,
1026 OUString
aRet( reinterpret_cast<const char*>(aData
.getConstArray()), aData
.getLength(), RTL_TEXTENCODING_UTF8
);
1027 rData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aRet
.getStr()), (aRet
.getLength()+1)*sizeof( sal_Unicode
) );
1030 else if( it
->second
->m_bHaveCompound
&&
1031 getPasteData( selection
,
1036 OUString
aRet( convertFromCompound( reinterpret_cast<const char*>(aData
.getConstArray()), aData
.getLength() ) );
1037 rData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aRet
.getStr()), (aRet
.getLength()+1)*sizeof( sal_Unicode
) );
1042 for( int i
= 0; i
< rTypes
.getLength(); i
++ )
1044 rtl_TextEncoding aEncoding
= getTextPlainEncoding( rTypes
.getConstArray()[i
].MimeType
);
1045 if( aEncoding
!= RTL_TEXTENCODING_DONTKNOW
&&
1046 aEncoding
!= RTL_TEXTENCODING_UNICODE
&&
1047 getPasteData( selection
,
1052 #if OSL_DEBUG_LEVEL > 1
1053 SAL_INFO("vcl.unx.dtrans", "using \""
1054 << rTypes
.getConstArray()[i
].MimeType
1055 << "\" instead of \""
1060 OString
aConvert( reinterpret_cast<char const *>(aData
.getConstArray()), aData
.getLength() );
1061 OUString
aUTF( OStringToOUString( aConvert
, aEncoding
) );
1062 rData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aUTF
.getStr()), (aUTF
.getLength()+1)*sizeof( sal_Unicode
) );
1070 else if( rType
== "image/bmp" )
1072 // #i83376# try if someone has the data in image/bmp already before
1073 // doing the PIXMAP stuff (e.g. the Gimp has this)
1074 bSuccess
= getPasteData( selection
, m_nImageBmpAtom
, rData
);
1075 #if OSL_DEBUG_LEVEL > 1
1076 SAL_INFO_IF(bSuccess
, "vcl.unx.dtrans",
1077 "got " << (int) rData
.getLength() << " bytes of image/bmp.");
1081 Pixmap aPixmap
= None
;
1082 Colormap aColormap
= None
;
1084 // prepare property for MULTIPLE request
1085 Sequence
< sal_Int8
> aData
;
1086 Atom
const pTypes
[4] = { XA_PIXMAP
, XA_PIXMAP
, XA_COLORMAP
, XA_COLORMAP
};
1088 osl::MutexGuard
aGuard(m_aMutex
);
1090 XChangeProperty( m_pDisplay
,
1096 reinterpret_cast<unsigned char const *>(pTypes
),
1100 // try MULTIPLE request
1101 if( getPasteData( selection
, m_nMULTIPLEAtom
, aData
) )
1103 Atom
* pReturnedTypes
= reinterpret_cast<Atom
*>(aData
.getArray());
1104 if( pReturnedTypes
[0] == XA_PIXMAP
&& pReturnedTypes
[1] == XA_PIXMAP
)
1106 osl::MutexGuard
aGuard(m_aMutex
);
1110 unsigned long nItems
= 0;
1111 unsigned long nBytes
= 0;
1112 unsigned char* pReturn
= nullptr;
1113 XGetWindowProperty( m_pDisplay
, m_aWindow
, XA_PIXMAP
, 0, 1, True
, XA_PIXMAP
, &type
, &format
, &nItems
, &nBytes
, &pReturn
);
1116 if( type
== XA_PIXMAP
)
1117 aPixmap
= *reinterpret_cast<Pixmap
*>(pReturn
);
1120 if( pReturnedTypes
[2] == XA_COLORMAP
&& pReturnedTypes
[3] == XA_COLORMAP
)
1122 XGetWindowProperty( m_pDisplay
, m_aWindow
, XA_COLORMAP
, 0, 1, True
, XA_COLORMAP
, &type
, &format
, &nItems
, &nBytes
, &pReturn
);
1125 if( type
== XA_COLORMAP
)
1126 aColormap
= *reinterpret_cast<Colormap
*>(pReturn
);
1131 #if OSL_DEBUG_LEVEL > 1
1134 SAL_WARN("vcl.unx.dtrans", "could not get PIXMAP property: type="
1135 << getString( type
)
1136 << ", format=" << format
1137 << ", items=" << nItems
1138 << ", bytes=" << nBytes
1139 << ", ret=0x" << pReturn
);
1145 if( aPixmap
== None
)
1147 // perhaps two normal requests will work
1148 if( getPasteData( selection
, XA_PIXMAP
, aData
) )
1150 aPixmap
= *reinterpret_cast<Pixmap
*>(aData
.getArray());
1151 if( aColormap
== None
&& getPasteData( selection
, XA_COLORMAP
, aData
) )
1152 aColormap
= *reinterpret_cast<Colormap
*>(aData
.getArray());
1156 // convert data if possible
1157 if( aPixmap
!= None
)
1159 osl::MutexGuard
aGuard(m_aMutex
);
1161 sal_Int32 nOutSize
= 0;
1162 sal_uInt8
* pBytes
= X11_getBmpFromPixmap( m_pDisplay
, aPixmap
, aColormap
, nOutSize
);
1167 rData
= Sequence
< sal_Int8
>( nOutSize
);
1168 memcpy( rData
.getArray(), pBytes
, nOutSize
);
1171 std::free( pBytes
);
1180 ::std::list
< Atom
> aTypes
;
1181 convertTypeToNative( rType
, selection
, nFormat
, aTypes
);
1182 Atom nSelectedType
= None
;
1183 for (auto const& type
: aTypes
)
1185 for( auto const & nativeType
: rNativeTypes
)
1186 if(nativeType
== type
)
1188 nSelectedType
= type
;
1189 if (nSelectedType
!= None
)
1193 if( nSelectedType
!= None
)
1194 bSuccess
= getPasteData( selection
, nSelectedType
, rData
);
1196 #if OSL_DEBUG_LEVEL > 1
1197 SAL_INFO("vcl.unx.dtrans", "getPasteData for selection "
1198 << getString( selection
)
1199 << " and data type "
1202 << ( bSuccess
? "true" : "false")
1203 << ", returned sequence has length "
1204 << rData
.getLength() << ".");
1209 bool SelectionManager::getPasteDataTypes( Atom selection
, Sequence
< DataFlavor
>& rTypes
)
1211 std::unordered_map
< Atom
, Selection
* >::iterator it
;
1213 osl::MutexGuard
aGuard(m_aMutex
);
1215 it
= m_aSelections
.find( selection
);
1216 if( it
!= m_aSelections
.end() &&
1217 it
->second
->m_aTypes
.getLength() &&
1218 std::abs( it
->second
->m_nLastTimestamp
- time( nullptr ) ) < 2
1221 rTypes
= it
->second
->m_aTypes
;
1226 bool bSuccess
= false;
1227 bool bHaveUTF16
= false;
1228 Atom aUTF8Type
= None
;
1229 bool bHaveCompound
= false;
1230 Sequence
< sal_Int8
> aAtoms
;
1232 if( selection
== m_nXdndSelection
)
1234 // xdnd sends first three types with XdndEnter
1235 // if more than three types are supported then the XDndTypeList
1236 // property on the source window is used
1237 if( m_aDropEnterEvent
.data
.l
[0] && m_aCurrentDropWindow
)
1239 if( m_aDropEnterEvent
.data
.l
[1] & 1 )
1241 const unsigned int atomcount
= 256;
1242 // more than three types; look in property
1243 osl::MutexGuard
aGuard(m_aMutex
);
1247 unsigned long nItems
, nBytes
;
1248 unsigned char* pBytes
= nullptr;
1250 XGetWindowProperty( m_pDisplay
, m_aDropEnterEvent
.data
.l
[0],
1251 m_nXdndTypeList
, 0, atomcount
, False
,
1253 &nType
, &nFormat
, &nItems
, &nBytes
, &pBytes
);
1254 #if OSL_DEBUG_LEVEL > 1
1255 SAL_INFO("vcl.unx.dtrans", "have "
1257 << " data types in XdndTypeList.");
1259 if( nItems
== atomcount
&& nBytes
> 0 )
1261 // wow ... more than 256 types !
1262 aAtoms
.realloc( sizeof( Atom
)*atomcount
+nBytes
);
1263 memcpy( aAtoms
.getArray(), pBytes
, sizeof( Atom
)*atomcount
);
1266 XGetWindowProperty( m_pDisplay
, m_aDropEnterEvent
.data
.l
[0],
1267 m_nXdndTypeList
, atomcount
, nBytes
/sizeof(Atom
),
1269 &nType
, &nFormat
, &nItems
, &nBytes
, &pBytes
);
1271 memcpy( aAtoms
.getArray()+atomcount
*sizeof(Atom
), pBytes
, nItems
*sizeof(Atom
) );
1277 aAtoms
.realloc( sizeof(Atom
)*nItems
);
1278 memcpy( aAtoms
.getArray(), pBytes
, nItems
*sizeof(Atom
) );
1284 // one to three types
1286 for( i
= 0; i
< 3; i
++ )
1287 if( m_aDropEnterEvent
.data
.l
[2+i
] )
1289 #if OSL_DEBUG_LEVEL > 1
1290 SAL_INFO("vcl.unx.dtrans", "have "
1292 << " data types in XdndEnter.");
1294 aAtoms
.realloc( sizeof(Atom
)*n
);
1295 for( i
= 0, n
= 0; i
< 3; i
++ )
1296 if( m_aDropEnterEvent
.data
.l
[2+i
] )
1297 reinterpret_cast<Atom
*>(aAtoms
.getArray())[n
++] = m_aDropEnterEvent
.data
.l
[2+i
];
1301 // get data of type TARGETS
1302 else if( ! getPasteData( selection
, m_nTARGETSAtom
, aAtoms
) )
1303 aAtoms
= Sequence
< sal_Int8
>();
1305 std::vector
< Atom
> aNativeTypes
;
1306 if( aAtoms
.hasElements() )
1308 sal_Int32 nAtoms
= aAtoms
.getLength() / sizeof(Atom
);
1309 Atom
* pAtoms
= reinterpret_cast<Atom
*>(aAtoms
.getArray());
1310 rTypes
.realloc( nAtoms
);
1311 aNativeTypes
.resize( nAtoms
);
1312 DataFlavor
* pFlavors
= rTypes
.getArray();
1313 sal_Int32 nNativeTypesIndex
= 0;
1314 bool bHaveText
= false;
1317 SAL_INFO_IF(*pAtoms
&& *pAtoms
< 0x01000000, "vcl.unx.dtrans",
1318 "getPasteDataTypes: available: \"" << getString(*pAtoms
) << "\"");
1319 if( *pAtoms
== m_nCOMPOUNDAtom
)
1320 bHaveText
= bHaveCompound
= true;
1321 else if( *pAtoms
&& *pAtoms
< 0x01000000 )
1324 pFlavors
->MimeType
= convertTypeFromNative( *pAtoms
, selection
, nFormat
);
1325 pFlavors
->DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
1326 sal_Int32 nIndex
= 0;
1327 if( pFlavors
->MimeType
.getToken( 0, ';', nIndex
) == "text/plain" )
1329 OUString
aToken(pFlavors
->MimeType
.getToken( 0, ';', nIndex
));
1330 // omit text/plain;charset=unicode since it is not well defined
1331 if( aToken
== "charset=unicode" )
1337 if( aToken
== "charset=utf-16" )
1340 pFlavors
->DataType
= cppu::UnoType
<OUString
>::get();
1342 else if( aToken
== "charset=utf-8" )
1344 aUTF8Type
= *pAtoms
;
1348 aNativeTypes
[ nNativeTypesIndex
] = *pAtoms
;
1349 nNativeTypesIndex
++;
1353 if( (pFlavors
- rTypes
.getArray()) < rTypes
.getLength() )
1354 rTypes
.realloc(pFlavors
- rTypes
.getArray());
1355 bSuccess
= rTypes
.hasElements();
1356 if( bHaveText
&& ! bHaveUTF16
)
1360 int nNewFlavors
= rTypes
.getLength()+1;
1361 Sequence
< DataFlavor
> aTemp( nNewFlavors
);
1362 for( i
= 0; i
< nNewFlavors
-1; i
++ )
1363 aTemp
.getArray()[i
+1] = rTypes
.getConstArray()[i
];
1364 aTemp
.getArray()[0].MimeType
= "text/plain;charset=utf-16";
1365 aTemp
.getArray()[0].DataType
= cppu::UnoType
<OUString
>::get();
1368 std::vector
< Atom
> aNativeTemp( nNewFlavors
);
1369 for( i
= 0; i
< nNewFlavors
-1; i
++ )
1370 aNativeTemp
[ i
+ 1 ] = aNativeTypes
[ i
];
1371 aNativeTemp
[0] = None
;
1372 aNativeTypes
= aNativeTemp
;
1377 osl::MutexGuard
aGuard(m_aMutex
);
1379 it
= m_aSelections
.find( selection
);
1380 if( it
!= m_aSelections
.end() )
1384 it
->second
->m_aTypes
= rTypes
;
1385 it
->second
->m_aNativeTypes
= aNativeTypes
;
1386 it
->second
->m_nLastTimestamp
= time( nullptr );
1387 it
->second
->m_bHaveUTF16
= bHaveUTF16
;
1388 it
->second
->m_aUTF8Type
= aUTF8Type
;
1389 it
->second
->m_bHaveCompound
= bHaveCompound
;
1393 it
->second
->m_aTypes
= Sequence
< DataFlavor
>();
1394 it
->second
->m_aNativeTypes
= std::vector
< Atom
>();
1395 it
->second
->m_nLastTimestamp
= 0;
1396 it
->second
->m_bHaveUTF16
= false;
1397 it
->second
->m_aUTF8Type
= None
;
1398 it
->second
->m_bHaveCompound
= false;
1403 #if OSL_DEBUG_LEVEL > 1
1405 SAL_INFO("vcl.unx.dtrans", "SelectionManager::getPasteDataTypes( "
1406 << getString( selection
)
1408 << (bSuccess
? "true" : "false"));
1409 for( int i
= 0; i
< rTypes
.getLength(); i
++ )
1410 SAL_INFO("vcl.unx.dtrans", "type: " << rTypes
.getConstArray()[i
].MimeType
);
1417 PixmapHolder
* SelectionManager::getPixmapHolder( Atom selection
)
1419 std::unordered_map
< Atom
, Selection
* >::const_iterator it
= m_aSelections
.find( selection
);
1420 if( it
== m_aSelections
.end() )
1422 if( ! it
->second
->m_pPixmap
)
1423 it
->second
->m_pPixmap
= new PixmapHolder( m_pDisplay
);
1424 return it
->second
->m_pPixmap
;
1427 static std::size_t GetTrueFormatSize(int nFormat
)
1429 // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
1430 return nFormat
== 32 ? sizeof(long) : nFormat
/8;
1433 bool SelectionManager::sendData( SelectionAdaptor
* pAdaptor
,
1439 osl::ResettableMutexGuard
aGuard( m_aMutex
);
1441 // handle targets related to image/bmp
1442 if( target
== XA_COLORMAP
|| target
== XA_PIXMAP
|| target
== XA_BITMAP
|| target
== XA_VISUALID
)
1444 PixmapHolder
* pPixmap
= getPixmapHolder( selection
);
1445 if( ! pPixmap
) return false;
1448 // handle colormap request
1449 if( target
== XA_COLORMAP
)
1450 nValue
= static_cast<XID
>(pPixmap
->getColormap());
1451 else if( target
== XA_VISUALID
)
1452 nValue
= static_cast<XID
>(pPixmap
->getVisualID());
1453 else if( target
== XA_PIXMAP
|| target
== XA_BITMAP
)
1455 nValue
= static_cast<XID
>(pPixmap
->getPixmap());
1456 if( nValue
== None
)
1459 Sequence
< sal_Int8
> aData
;
1462 bool bConverted
= convertData( pAdaptor
->getTransferable(), target
, selection
, nFormat
, aData
);
1466 // get pixmap again since clearing the guard could have invalidated
1467 // the pixmap in another thread
1468 pPixmap
= getPixmapHolder( selection
);
1469 // conversion succeeded, so aData contains image/bmp now
1470 if( pPixmap
->needsConversion( reinterpret_cast<const sal_uInt8
*>(aData
.getConstArray()) ) )
1472 SAL_INFO( "vcl.unx.dtrans", "trying bitmap conversion" );
1473 int depth
= pPixmap
->getDepth();
1475 aData
= convertBitmapDepth(aData
, depth
);
1478 // get pixmap again since clearing the guard could have invalidated
1479 // the pixmap in another thread
1480 pPixmap
= getPixmapHolder( selection
);
1481 nValue
= static_cast<XID
>(pPixmap
->setBitmapData( reinterpret_cast<const sal_uInt8
*>(aData
.getConstArray()) ));
1483 if( nValue
== None
)
1486 if( target
== XA_BITMAP
)
1487 nValue
= static_cast<XID
>(pPixmap
->getBitmap());
1490 XChangeProperty( m_pDisplay
,
1496 reinterpret_cast<const unsigned char*>(&nValue
),
1502 * special target TEXT allows us to transfer
1503 * the data in an encoding of our choice
1504 * COMPOUND_TEXT will work with most applications
1506 if( target
== m_nTEXTAtom
)
1507 target
= m_nCOMPOUNDAtom
;
1509 Sequence
< sal_Int8
> aData
;
1512 bool bConverted
= convertData( pAdaptor
->getTransferable(), target
, selection
, nFormat
, aData
);
1516 // conversion succeeded
1517 if( aData
.getLength() > m_nIncrementalThreshold
)
1519 #if OSL_DEBUG_LEVEL > 1
1520 SAL_INFO("vcl.unx.dtrans", "using INCR protocol.");
1521 std::unordered_map
< ::Window
, std::unordered_map
< Atom
, IncrementalTransfer
> >::const_iterator win_it
= m_aIncrementals
.find( requestor
);
1522 if( win_it
!= m_aIncrementals
.end() )
1524 std::unordered_map
< Atom
, IncrementalTransfer
>::const_iterator inc_it
= win_it
->second
.find( property
);
1525 if( inc_it
!= win_it
->second
.end() )
1527 const IncrementalTransfer
& rInc
= inc_it
->second
;
1528 SAL_INFO("vcl.unx.dtrans", "premature end and new start for INCR transfer for window "
1529 << std::showbase
<< std::hex
1530 << rInc
.m_aRequestor
1532 << getString( rInc
.m_aProperty
)
1534 << getString( rInc
.m_aTarget
));
1539 // insert IncrementalTransfer
1540 IncrementalTransfer
& rInc
= m_aIncrementals
[ requestor
][ property
];
1541 rInc
.m_aData
= aData
;
1542 rInc
.m_nBufferPos
= 0;
1543 rInc
.m_aRequestor
= requestor
;
1544 rInc
.m_aProperty
= property
;
1545 rInc
.m_aTarget
= target
;
1546 rInc
.m_nFormat
= nFormat
;
1547 rInc
.m_nTransferStartTime
= time( nullptr );
1549 // use incr protocol, signal start to requestor
1550 tools::Long nMinSize
= m_nIncrementalThreshold
;
1551 XSelectInput( m_pDisplay
, requestor
, PropertyChangeMask
);
1552 XChangeProperty( m_pDisplay
, requestor
, property
,
1553 m_nINCRAtom
, 32, PropModeReplace
, reinterpret_cast<unsigned char*>(&nMinSize
), 1 );
1554 XFlush( m_pDisplay
);
1558 std::size_t nUnitSize
= GetTrueFormatSize(nFormat
);
1559 XChangeProperty( m_pDisplay
,
1565 reinterpret_cast<const unsigned char*>(aData
.getConstArray()),
1566 aData
.getLength()/nUnitSize
);
1569 #if OSL_DEBUG_LEVEL > 1
1571 SAL_WARN("vcl.unx.dtrans", "convertData failed for type: "
1572 << convertTypeFromNative( target
, selection
, nFormat
));
1577 bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent
& rRequest
)
1579 osl::ResettableMutexGuard
aGuard( m_aMutex
);
1581 #if OSL_DEBUG_LEVEL > 1
1582 SAL_INFO("vcl.unx.dtrans", "handleSelectionRequest for selection "
1583 << getString( rRequest
.selection
)
1585 << getString( rRequest
.target
));
1590 aNotify
.type
= SelectionNotify
;
1591 aNotify
.xselection
.display
= rRequest
.display
;
1592 aNotify
.xselection
.send_event
= True
;
1593 aNotify
.xselection
.requestor
= rRequest
.requestor
;
1594 aNotify
.xselection
.selection
= rRequest
.selection
;
1595 aNotify
.xselection
.time
= rRequest
.time
;
1596 aNotify
.xselection
.target
= rRequest
.target
;
1597 aNotify
.xselection
.property
= None
;
1599 SelectionAdaptor
* pAdaptor
= getAdaptor( rRequest
.selection
);
1600 // ensure that we still own that selection
1602 XGetSelectionOwner( m_pDisplay
, rRequest
.selection
) == m_aWindow
)
1604 css::uno::Reference
< XTransferable
> xTrans( pAdaptor
->getTransferable() );
1605 if( rRequest
.target
== m_nTARGETSAtom
)
1607 // someone requests our types
1611 Sequence
< DataFlavor
> aFlavors
= xTrans
->getTransferDataFlavors();
1614 ::std::list
< Atom
> aConversions
;
1615 getNativeTypeList( aFlavors
, aConversions
, rRequest
.selection
);
1617 int i
, nTypes
= aConversions
.size();
1618 Atom
* pTypes
= static_cast<Atom
*>(alloca( nTypes
* sizeof( Atom
) ));
1619 std::list
< Atom
>::const_iterator it
;
1620 for( i
= 0, it
= aConversions
.begin(); i
< nTypes
; i
++, ++it
)
1622 XChangeProperty( m_pDisplay
, rRequest
.requestor
, rRequest
.property
,
1623 XA_ATOM
, 32, PropModeReplace
, reinterpret_cast<unsigned char*>(pTypes
), nTypes
);
1624 aNotify
.xselection
.property
= rRequest
.property
;
1625 #if OSL_DEBUG_LEVEL > 1
1626 SAL_INFO("vcl.unx.dtrans", "sending type list:");
1627 for( int k
= 0; k
< nTypes
; k
++ )
1628 SAL_INFO("vcl.unx.dtrans", " "
1629 << (pTypes
[k
] ? XGetAtomName( m_pDisplay
, pTypes
[k
] ) :
1634 else if( rRequest
.target
== m_nTIMESTAMPAtom
)
1636 tools::Long nTimeStamp
= static_cast<tools::Long
>(m_aSelections
[rRequest
.selection
]->m_nOrigTimestamp
);
1637 XChangeProperty( m_pDisplay
, rRequest
.requestor
, rRequest
.property
,
1638 XA_INTEGER
, 32, PropModeReplace
, reinterpret_cast<unsigned char*>(&nTimeStamp
), 1 );
1639 aNotify
.xselection
.property
= rRequest
.property
;
1640 #if OSL_DEBUG_LEVEL > 1
1641 SAL_INFO("vcl.unx.dtrans", "sending timestamp: " << (int)nTimeStamp
);
1646 bool bEventSuccess
= false;
1647 if( rRequest
.target
== m_nMULTIPLEAtom
)
1652 unsigned long nItems
= 0, nBytes
= 0;
1653 unsigned char* pData
= nullptr;
1655 // get number of atoms
1656 XGetWindowProperty( m_pDisplay
,
1665 if( nFormat
== 32 && nBytes
/4 )
1667 if( pData
) // ?? should not happen
1672 XGetWindowProperty( m_pDisplay
,
1681 if( pData
&& nItems
)
1683 #if OSL_DEBUG_LEVEL > 1
1684 SAL_INFO("vcl.unx.dtrans", "found "
1686 << " atoms in MULTIPLE request.");
1688 bEventSuccess
= true;
1689 bool bResetAtoms
= false;
1690 Atom
* pAtoms
= reinterpret_cast<Atom
*>(pData
);
1692 for( unsigned long i
= 0; i
< nItems
; i
+= 2 )
1694 #if OSL_DEBUG_LEVEL > 1
1695 std::ostringstream oss
;
1697 << getString( pAtoms
[i
] )
1699 << getString( pAtoms
[i
+1] )
1703 bool bSuccess
= sendData( pAdaptor
, rRequest
.requestor
, pAtoms
[i
], pAtoms
[i
+1], rRequest
.selection
);
1704 #if OSL_DEBUG_LEVEL > 1
1705 oss
<< (bSuccess
? "succeeded" : "failed");
1706 SAL_INFO("vcl.unx.dtrans", oss
.str());
1716 XChangeProperty( m_pDisplay
,
1728 #if OSL_DEBUG_LEVEL > 1
1731 std::ostringstream oss
;
1732 oss
<< "could not get type list from \""
1733 << getString( rRequest
.property
)
1735 << getString( nType
)
1736 << "\" on requestor "
1737 << std::showbase
<< std::hex
1738 << rRequest
.requestor
1739 << ", requestor has properties:";
1742 Atom
* pProps
= XListProperties( m_pDisplay
, rRequest
.requestor
, &nProps
);
1745 for( int i
= 0; i
< nProps
; i
++ )
1746 oss
<< " \"" << getString( pProps
[i
]) << "\"";
1749 SAL_INFO("vcl.unx.dtrans", oss
.str());
1756 bEventSuccess
= sendData( pAdaptor
, rRequest
.requestor
, rRequest
.target
, rRequest
.property
, rRequest
.selection
);
1761 aNotify
.xselection
.target
= rRequest
.target
;
1762 aNotify
.xselection
.property
= rRequest
.property
;
1769 XSendEvent( m_pDisplay
, rRequest
.requestor
, False
, 0, &aNotify
);
1771 if( rRequest
.selection
== XA_PRIMARY
&&
1772 m_bWaitingForPrimaryConversion
&&
1773 m_xDragSourceListener
.is() )
1775 DragSourceDropEvent dsde
;
1776 dsde
.Source
= static_cast< OWeakObject
* >(this);
1777 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
1778 dsde
.DragSource
= static_cast< XDragSource
* >(this);
1779 if( aNotify
.xselection
.property
!= None
)
1781 dsde
.DropAction
= DNDConstants::ACTION_COPY
;
1782 dsde
.DropSuccess
= true;
1786 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
1787 dsde
.DropSuccess
= false;
1789 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
1790 m_xDragSourceListener
.clear();
1792 if( xListener
.is() )
1793 xListener
->dragDropEnd( dsde
);
1796 // we handled the event in any case by answering
1800 bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent
const & rNotify
)
1802 osl::MutexGuard
aGuard( m_aMutex
);
1803 // data we requested arrived
1804 #if OSL_DEBUG_LEVEL > 1
1805 SAL_INFO("vcl.unx.dtrans", "handleReceivePropertyNotify for property "
1806 << getString( rNotify
.atom
));
1808 bool bHandled
= false;
1810 std::unordered_map
< Atom
, Selection
* >::iterator it
=
1811 m_aSelections
.find( rNotify
.atom
);
1812 if( it
!= m_aSelections
.end() &&
1813 rNotify
.state
== PropertyNewValue
&&
1814 ( it
->second
->m_eState
== Selection::WaitingForResponse
||
1815 it
->second
->m_eState
== Selection::WaitingForData
||
1816 it
->second
->m_eState
== Selection::IncrementalTransfer
1820 // MULTIPLE requests are only complete after selection notify
1821 if( it
->second
->m_aRequestedType
== m_nMULTIPLEAtom
&&
1822 ( it
->second
->m_eState
== Selection::WaitingForResponse
||
1823 it
->second
->m_eState
== Selection::WaitingForData
) )
1830 unsigned long nItems
= 0, nBytes
= 0;
1831 unsigned char* pData
= nullptr;
1833 // get type and length
1834 XGetWindowProperty( m_pDisplay
,
1843 #if OSL_DEBUG_LEVEL > 1
1844 SAL_INFO("vcl.unx.dtrans", "found "
1846 << " bytes data of type "
1847 << getString( nType
)
1859 if( nType
== m_nINCRAtom
)
1861 // start data transfer
1862 XDeleteProperty( m_pDisplay
, rNotify
.window
, rNotify
.atom
);
1863 it
->second
->m_eState
= Selection::IncrementalTransfer
;
1865 else if( nType
!= None
)
1867 XGetWindowProperty( m_pDisplay
,
1877 #if OSL_DEBUG_LEVEL > 1
1878 SAL_INFO("vcl.unx.dtrans", "read "
1880 << " items data of type "
1881 << getString( nType
)
1886 << " bytes left in property.");
1889 std::size_t nUnitSize
= GetTrueFormatSize(nFormat
);
1891 if( it
->second
->m_eState
== Selection::WaitingForData
||
1892 it
->second
->m_eState
== Selection::WaitingForResponse
)
1895 it
->second
->m_aData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
*>(pData
), nItems
*nUnitSize
);
1896 it
->second
->m_eState
= Selection::Inactive
;
1897 it
->second
->m_aDataArrived
.set();
1899 else if( it
->second
->m_eState
== Selection::IncrementalTransfer
)
1904 Sequence
< sal_Int8
> aData( it
->second
->m_aData
.getLength() + nItems
*nUnitSize
);
1905 memcpy( aData
.getArray(), it
->second
->m_aData
.getArray(), it
->second
->m_aData
.getLength() );
1906 memcpy( aData
.getArray() + it
->second
->m_aData
.getLength(), pData
, nItems
*nUnitSize
);
1907 it
->second
->m_aData
= aData
;
1911 it
->second
->m_eState
= Selection::Inactive
;
1912 it
->second
->m_aDataArrived
.set();
1918 else if( it
->second
->m_eState
== Selection::IncrementalTransfer
)
1920 it
->second
->m_eState
= Selection::Inactive
;
1921 it
->second
->m_aDataArrived
.set();
1927 bool SelectionManager::handleSendPropertyNotify( XPropertyEvent
const & rNotify
)
1929 osl::MutexGuard
aGuard( m_aMutex
);
1931 // ready for next part of an IncrementalTransfer
1932 #if OSL_DEBUG_LEVEL > 1
1933 SAL_INFO("vcl.unx.dtrans", "handleSendPropertyNotify for property "
1934 << getString( rNotify
.atom
)
1936 << (rNotify
.state
== PropertyNewValue
?
1938 (rNotify
.state
== PropertyDelete
?
1944 bool bHandled
= false;
1945 // feed incrementals
1946 if( rNotify
.state
== PropertyDelete
)
1948 auto it
= m_aIncrementals
.find( rNotify
.window
);
1949 if( it
!= m_aIncrementals
.end() )
1952 int nCurrentTime
= time( nullptr );
1953 // throw out aborted transfers
1954 std::vector
< Atom
> aTimeouts
;
1955 for (auto const& incrementalTransfer
: it
->second
)
1957 if( (nCurrentTime
- incrementalTransfer
.second
.m_nTransferStartTime
) > (getSelectionTimeout()+2) )
1959 aTimeouts
.push_back( incrementalTransfer
.first
);
1960 #if OSL_DEBUG_LEVEL > 1
1961 const IncrementalTransfer
& rInc
= incrementalTransfer
.second
;
1962 SAL_INFO("vcl.unx.dtrans",
1963 "timeout on INCR transfer for window "
1964 << std::showbase
<< std::hex
1965 << rInc
.m_aRequestor
1967 << getString( rInc
.m_aProperty
)
1969 << getString( rInc
.m_aTarget
));
1974 for (auto const& timeout
: aTimeouts
)
1976 // transfer broken, might even be a new client with the
1978 it
->second
.erase( timeout
);
1982 auto inc_it
= it
->second
.find( rNotify
.atom
);
1983 if( inc_it
!= it
->second
.end() )
1985 IncrementalTransfer
& rInc
= inc_it
->second
;
1987 int nBytes
= rInc
.m_aData
.getLength() - rInc
.m_nBufferPos
;
1988 nBytes
= std::min(nBytes
, m_nIncrementalThreshold
);
1989 if( nBytes
< 0 ) // sanity check
1991 #if OSL_DEBUG_LEVEL > 1
1992 SAL_INFO("vcl.unx.dtrans", "pushing "
1995 << std::setw(std::min(nBytes
, 32))
1996 << ((const unsigned char*)
1997 rInc
.m_aData
.getConstArray()+rInc
.m_nBufferPos
)
2000 std::size_t nUnitSize
= GetTrueFormatSize(rInc
.m_nFormat
);
2002 XChangeProperty( m_pDisplay
,
2008 reinterpret_cast<const unsigned char*>(rInc
.m_aData
.getConstArray())+rInc
.m_nBufferPos
,
2010 rInc
.m_nBufferPos
+= nBytes
;
2011 rInc
.m_nTransferStartTime
= nCurrentTime
;
2013 if( nBytes
== 0 ) // transfer finished
2015 #if OSL_DEBUG_LEVEL > 1
2016 SAL_INFO("vcl.unx.dtrans", "finished INCR transfer for "
2018 << std::showbase
<< std::hex
2019 << rInc
.m_aRequestor
2021 << getString( rInc
.m_aProperty
)
2023 << getString( rInc
.m_aTarget
));
2025 it
->second
.erase( inc_it
);
2029 // eventually clean up the hash map
2030 if( it
->second
.empty() )
2031 m_aIncrementals
.erase( it
);
2037 bool SelectionManager::handleSelectionNotify( XSelectionEvent
const & rNotify
)
2039 osl::MutexGuard
aGuard( m_aMutex
);
2041 bool bHandled
= false;
2043 // notification about success/failure of one of our conversion requests
2044 #if OSL_DEBUG_LEVEL > 1
2045 SAL_INFO("vcl.unx.dtrans", "handleSelectionNotify for selection "
2046 << getString( rNotify
.selection
)
2048 << (rNotify
.property
? getString( rNotify
.property
) : "None")
2050 << std::showbase
<< std::hex
2053 SAL_WARN_IF(rNotify
.requestor
!= m_aWindow
&&
2054 rNotify
.requestor
!= m_aCurrentDropWindow
,
2055 "vcl.unx.dtrans", "selection notify for unknown window "
2056 << std::showbase
<< std::hex
2057 << rNotify
.requestor
);
2059 std::unordered_map
< Atom
, Selection
* >::iterator it
=
2060 m_aSelections
.find( rNotify
.selection
);
2062 (rNotify
.requestor
== m_aWindow
|| rNotify
.requestor
== m_aCurrentDropWindow
) &&
2063 it
!= m_aSelections
.end() &&
2065 (it
->second
->m_eState
== Selection::WaitingForResponse
) ||
2066 (it
->second
->m_eState
== Selection::WaitingForData
)
2071 if( it
->second
->m_aRequestedType
== m_nMULTIPLEAtom
)
2075 unsigned long nItems
= 0, nBytes
= 0;
2076 unsigned char* pData
= nullptr;
2078 // get type and length
2079 XGetWindowProperty( m_pDisplay
,
2088 if( nBytes
) // HUGE request !!!
2092 XGetWindowProperty( m_pDisplay
,
2095 0, 256+(nBytes
+3)/4,
2102 it
->second
->m_eState
= Selection::Inactive
;
2103 std::size_t nUnitSize
= GetTrueFormatSize(nFormat
);
2104 it
->second
->m_aData
= Sequence
< sal_Int8
>(reinterpret_cast<sal_Int8
*>(pData
), nItems
* nUnitSize
);
2105 it
->second
->m_aDataArrived
.set();
2109 // WaitingForData can actually happen; some
2110 // applications (e.g. cmdtool on Solaris) first send
2111 // a success and then cancel it. Weird !
2112 else if( rNotify
.property
== None
)
2114 // conversion failed, stop transfer
2115 it
->second
->m_eState
= Selection::Inactive
;
2116 it
->second
->m_aData
= Sequence
< sal_Int8
>();
2117 it
->second
->m_aDataArrived
.set();
2119 // get the bytes, by INCR if necessary
2121 it
->second
->m_eState
= Selection::WaitingForData
;
2123 #if OSL_DEBUG_LEVEL > 1
2124 else if( it
!= m_aSelections
.end() )
2125 SAL_WARN("vcl.unx.dtrans", "selection in state " << it
->second
->m_eState
);
2130 bool SelectionManager::handleDropEvent( XClientMessageEvent
const & rMessage
)
2132 osl::ResettableMutexGuard
aGuard(m_aMutex
);
2134 // handle drop related events
2135 ::Window aSource
= rMessage
.data
.l
[0];
2136 ::Window aTarget
= rMessage
.window
;
2138 bool bHandled
= false;
2140 std::unordered_map
< ::Window
, DropTargetEntry
>::iterator it
=
2141 m_aDropTargets
.find( aTarget
);
2143 #if OSL_DEBUG_LEVEL > 1
2144 if( rMessage
.message_type
== m_nXdndEnter
||
2145 rMessage
.message_type
== m_nXdndLeave
||
2146 rMessage
.message_type
== m_nXdndDrop
||
2147 rMessage
.message_type
== m_nXdndPosition
)
2149 std::ostringstream oss
;
2150 oss
<< "got drop event "
2151 << getString( rMessage
.message_type
)
2154 if( it
== m_aDropTargets
.end() )
2155 oss
<< "but no target found.";
2156 else if( ! it
->second
.m_pTarget
->m_bActive
)
2157 oss
<< "but target is inactive.";
2158 else if( m_aDropEnterEvent
.data
.l
[0] != None
&& (::Window
)m_aDropEnterEvent
.data
.l
[0] != aSource
)
2159 oss
<< "but source "
2160 << std::showbase
<< std::hex
2162 << " is unknown (expected "
2163 << m_aDropEnterEvent
.data
.l
[0]
2166 oss
<< "processing.";
2167 SAL_INFO("vcl.unx.dtrans", oss
.str());
2171 if( it
!= m_aDropTargets
.end() && it
->second
.m_pTarget
->m_bActive
&&
2172 m_bDropWaitingForCompletion
&& m_aDropEnterEvent
.data
.l
[0] )
2175 OSL_FAIL( "someone forgot to call dropComplete ?" );
2176 // some listener forgot to call dropComplete in the last operation
2177 // let us end it now and accept the new enter event
2179 dropComplete( false, m_aCurrentDropWindow
);
2183 if( it
!= m_aDropTargets
.end() &&
2184 it
->second
.m_pTarget
->m_bActive
&&
2185 ( m_aDropEnterEvent
.data
.l
[0] == None
|| ::Window(m_aDropEnterEvent
.data
.l
[0]) == aSource
)
2188 if( rMessage
.message_type
== m_nXdndEnter
)
2191 m_aDropEnterEvent
= rMessage
;
2192 m_bDropEnterSent
= false;
2193 m_aCurrentDropWindow
= aTarget
;
2194 m_nCurrentProtocolVersion
= m_aDropEnterEvent
.data
.l
[1] >> 24;
2195 #if OSL_DEBUG_LEVEL > 1
2196 SAL_INFO("vcl.unx.dtrans", "received XdndEnter on "
2197 << std::showbase
<< std::hex
2202 rMessage
.message_type
== m_nXdndPosition
&&
2203 aSource
== ::Window(m_aDropEnterEvent
.data
.l
[0])
2207 m_nDropTime
= m_nCurrentProtocolVersion
> 0 ? rMessage
.data
.l
[3] : CurrentTime
;
2210 XTranslateCoordinates( m_pDisplay
,
2211 it
->second
.m_aRootWindow
,
2213 rMessage
.data
.l
[2] >> 16,
2214 rMessage
.data
.l
[2] & 0xffff,
2215 &m_nLastX
, &m_nLastY
,
2217 #if OSL_DEBUG_LEVEL > 1
2218 SAL_INFO("vcl.unx.dtrans", "received XdndPosition on "
2219 << std::showbase
<< std::hex
2228 DropTargetDragEnterEvent aEvent
;
2229 aEvent
.Source
= static_cast< XDropTarget
* >(it
->second
.m_pTarget
);
2230 aEvent
.Context
= new DropTargetDragContext( m_aCurrentDropWindow
, *this );
2231 aEvent
.LocationX
= m_nLastX
;
2232 aEvent
.LocationY
= m_nLastY
;
2233 aEvent
.SourceActions
= m_nSourceActions
;
2234 if( m_nCurrentProtocolVersion
< 2 )
2235 aEvent
.DropAction
= DNDConstants::ACTION_COPY
;
2236 else if( Atom(rMessage
.data
.l
[4]) == m_nXdndActionCopy
)
2237 aEvent
.DropAction
= DNDConstants::ACTION_COPY
;
2238 else if( Atom(rMessage
.data
.l
[4]) == m_nXdndActionMove
)
2239 aEvent
.DropAction
= DNDConstants::ACTION_MOVE
;
2240 else if( Atom(rMessage
.data
.l
[4]) == m_nXdndActionLink
)
2241 aEvent
.DropAction
= DNDConstants::ACTION_LINK
;
2242 else if( Atom(rMessage
.data
.l
[4]) == m_nXdndActionAsk
)
2243 // currently no interface to implement ask
2244 aEvent
.DropAction
= ~0;
2246 aEvent
.DropAction
= DNDConstants::ACTION_NONE
;
2248 m_nLastDropAction
= aEvent
.DropAction
;
2249 if( ! m_bDropEnterSent
)
2251 m_bDropEnterSent
= true;
2252 aEvent
.SupportedDataFlavors
= m_xDropTransferable
->getTransferDataFlavors();
2254 it
->second
->dragEnter( aEvent
);
2259 it
->second
->dragOver( aEvent
);
2263 rMessage
.message_type
== m_nXdndLeave
&&
2264 aSource
== ::Window(m_aDropEnterEvent
.data
.l
[0])
2268 #if OSL_DEBUG_LEVEL > 1
2269 SAL_INFO("vcl.unx.dtrans", "received XdndLeave on "
2270 << std::showbase
<< std::hex
2273 DropTargetEvent aEvent
;
2274 aEvent
.Source
= static_cast< XDropTarget
* >(it
->second
.m_pTarget
);
2275 m_aDropEnterEvent
.data
.l
[0] = None
;
2276 if( m_aCurrentDropWindow
== aTarget
)
2277 m_aCurrentDropWindow
= None
;
2278 m_nCurrentProtocolVersion
= nXdndProtocolRevision
;
2280 it
->second
->dragExit( aEvent
);
2283 rMessage
.message_type
== m_nXdndDrop
&&
2284 aSource
== ::Window(m_aDropEnterEvent
.data
.l
[0])
2288 m_nDropTime
= m_nCurrentProtocolVersion
> 0 ? rMessage
.data
.l
[2] : CurrentTime
;
2289 #if OSL_DEBUG_LEVEL > 1
2290 SAL_INFO("vcl.unx.dtrans", "received XdndDrop on "
2291 << std::showbase
<< std::hex
2299 if( m_bLastDropAccepted
)
2301 DropTargetDropEvent aEvent
;
2302 aEvent
.Source
= static_cast< XDropTarget
* >(it
->second
.m_pTarget
);
2303 aEvent
.Context
= new DropTargetDropContext( m_aCurrentDropWindow
, *this );
2304 aEvent
.LocationX
= m_nLastX
;
2305 aEvent
.LocationY
= m_nLastY
;
2306 aEvent
.DropAction
= m_nLastDropAction
;
2307 // there is nothing corresponding to source supported actions
2308 // every source can do link, copy and move
2309 aEvent
.SourceActions
= m_nLastDropAction
;
2310 aEvent
.Transferable
= m_xDropTransferable
;
2312 m_bDropWaitingForCompletion
= true;
2314 it
->second
->drop( aEvent
);
2318 #if OSL_DEBUG_LEVEL > 1
2319 SAL_INFO("vcl.unx.dtrans", "XdndDrop canceled due to "
2320 << "m_bLastDropAccepted = false." );
2322 DropTargetEvent aEvent
;
2323 aEvent
.Source
= static_cast< XDropTarget
* >(it
->second
.m_pTarget
);
2325 it
->second
->dragExit( aEvent
);
2326 // reset the drop status, notify source
2327 dropComplete( false, m_aCurrentDropWindow
);
2335 * methods for XDropTargetDropContext
2338 void SelectionManager::dropComplete( bool bSuccess
, ::Window aDropWindow
)
2340 osl::ClearableMutexGuard
aGuard(m_aMutex
);
2342 if( aDropWindow
== m_aCurrentDropWindow
)
2344 if( m_xDragSourceListener
.is() )
2346 DragSourceDropEvent dsde
;
2347 dsde
.Source
= static_cast< OWeakObject
* >(this);
2348 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2349 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2350 dsde
.DropAction
= getUserDragAction();
2351 dsde
.DropSuccess
= bSuccess
;
2352 css::uno::Reference
< XDragSourceListener
> xListener
= m_xDragSourceListener
;
2353 m_xDragSourceListener
.clear();
2356 xListener
->dragDropEnd( dsde
);
2358 else if( m_aDropEnterEvent
.data
.l
[0] && m_aCurrentDropWindow
)
2361 aEvent
.xclient
.type
= ClientMessage
;
2362 aEvent
.xclient
.display
= m_pDisplay
;
2363 aEvent
.xclient
.window
= m_aDropEnterEvent
.data
.l
[0];
2364 aEvent
.xclient
.message_type
= m_nXdndFinished
;
2365 aEvent
.xclient
.format
= 32;
2366 aEvent
.xclient
.data
.l
[0] = m_aCurrentDropWindow
;
2367 aEvent
.xclient
.data
.l
[1] = bSuccess
? 1 : 0;
2368 aEvent
.xclient
.data
.l
[2] = 0;
2369 aEvent
.xclient
.data
.l
[3] = 0;
2370 aEvent
.xclient
.data
.l
[4] = 0;
2373 if( m_nLastDropAction
& DNDConstants::ACTION_MOVE
)
2374 aEvent
.xclient
.data
.l
[2] = m_nXdndActionMove
;
2375 else if( m_nLastDropAction
& DNDConstants::ACTION_COPY
)
2376 aEvent
.xclient
.data
.l
[2] = m_nXdndActionCopy
;
2377 else if( m_nLastDropAction
& DNDConstants::ACTION_LINK
)
2378 aEvent
.xclient
.data
.l
[2] = m_nXdndActionLink
;
2380 #if OSL_DEBUG_LEVEL > 1
2381 SAL_INFO("vcl.unx.dtrans", "Sending XdndFinished to "
2382 << std::showbase
<< std::hex
2383 << m_aDropEnterEvent
.data
.l
[0]);
2385 XSendEvent( m_pDisplay
, m_aDropEnterEvent
.data
.l
[0],
2386 False
, NoEventMask
, & aEvent
);
2388 m_aDropEnterEvent
.data
.l
[0] = None
;
2389 m_aCurrentDropWindow
= None
;
2390 m_nCurrentProtocolVersion
= nXdndProtocolRevision
;
2392 m_bDropWaitingForCompletion
= false;
2395 OSL_FAIL( "dropComplete from invalid DropTargetDropContext" );
2399 * methods for XDropTargetDragContext
2402 void SelectionManager::sendDragStatus( Atom nDropAction
)
2404 osl::ClearableMutexGuard
aGuard(m_aMutex
);
2406 if( m_xDragSourceListener
.is() )
2408 sal_Int8 nNewDragAction
;
2409 if( nDropAction
== m_nXdndActionMove
)
2410 nNewDragAction
= DNDConstants::ACTION_MOVE
;
2411 else if( nDropAction
== m_nXdndActionCopy
)
2412 nNewDragAction
= DNDConstants::ACTION_COPY
;
2413 else if( nDropAction
== m_nXdndActionLink
)
2414 nNewDragAction
= DNDConstants::ACTION_LINK
;
2416 nNewDragAction
= DNDConstants::ACTION_NONE
;
2417 nNewDragAction
&= m_nSourceActions
;
2419 if( nNewDragAction
!= m_nTargetAcceptAction
)
2421 setCursor( getDefaultCursor( nNewDragAction
), m_aDropWindow
);
2422 m_nTargetAcceptAction
= nNewDragAction
;
2425 DragSourceDragEvent dsde
;
2426 dsde
.Source
= static_cast< OWeakObject
* >(this);
2427 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2428 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2429 dsde
.DropAction
= m_nSourceActions
;
2430 dsde
.UserAction
= getUserDragAction();
2432 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
2433 // caution: do not change anything after this
2435 if( xListener
.is() )
2436 xListener
->dragOver( dsde
);
2438 else if( m_aDropEnterEvent
.data
.l
[0] && m_aCurrentDropWindow
)
2441 aEvent
.xclient
.type
= ClientMessage
;
2442 aEvent
.xclient
.display
= m_pDisplay
;
2443 aEvent
.xclient
.window
= m_aDropEnterEvent
.data
.l
[0];
2444 aEvent
.xclient
.message_type
= m_nXdndStatus
;
2445 aEvent
.xclient
.format
= 32;
2446 aEvent
.xclient
.data
.l
[0] = m_aCurrentDropWindow
;
2447 aEvent
.xclient
.data
.l
[1] = 2;
2448 if( nDropAction
== m_nXdndActionMove
||
2449 nDropAction
== m_nXdndActionLink
||
2450 nDropAction
== m_nXdndActionCopy
)
2451 aEvent
.xclient
.data
.l
[1] |= 1;
2452 aEvent
.xclient
.data
.l
[2] = 0;
2453 aEvent
.xclient
.data
.l
[3] = 0;
2454 aEvent
.xclient
.data
.l
[4] = m_nCurrentProtocolVersion
> 1 ? nDropAction
: 0;
2456 #if OSL_DEBUG_LEVEL > 1
2457 SAL_INFO("vcl.unx.dtrans", "Sending XdndStatus to "
2458 << std::showbase
<< std::hex
2459 << m_aDropEnterEvent
.data
.l
[0]
2461 << getString( nDropAction
));
2464 XSendEvent( m_pDisplay
, m_aDropEnterEvent
.data
.l
[0],
2465 False
, NoEventMask
, & aEvent
);
2466 XFlush( m_pDisplay
);
2470 sal_Int8
SelectionManager::getUserDragAction() const
2472 return (m_nTargetAcceptAction
!= DNDConstants::ACTION_DEFAULT
) ? m_nTargetAcceptAction
: m_nUserDragAction
;
2475 bool SelectionManager::updateDragAction( int modifierState
)
2479 sal_Int8 nNewDropAction
= DNDConstants::ACTION_MOVE
;
2480 if( ( modifierState
& ShiftMask
) && ! ( modifierState
& ControlMask
) )
2481 nNewDropAction
= DNDConstants::ACTION_MOVE
;
2482 else if( ( modifierState
& ControlMask
) && ! ( modifierState
& ShiftMask
) )
2483 nNewDropAction
= DNDConstants::ACTION_COPY
;
2484 else if( ( modifierState
& ShiftMask
) && ( modifierState
& ControlMask
) )
2485 nNewDropAction
= DNDConstants::ACTION_LINK
;
2486 if( m_nCurrentProtocolVersion
< 0 && m_aDropWindow
!= None
)
2487 nNewDropAction
= DNDConstants::ACTION_COPY
;
2488 nNewDropAction
&= m_nSourceActions
;
2490 if( ! ( modifierState
& ( ControlMask
| ShiftMask
) ) )
2492 if( ! nNewDropAction
)
2494 // default to an action so the user does not have to press
2496 if( m_nSourceActions
& DNDConstants::ACTION_MOVE
)
2497 nNewDropAction
= DNDConstants::ACTION_MOVE
;
2498 else if( m_nSourceActions
& DNDConstants::ACTION_COPY
)
2499 nNewDropAction
= DNDConstants::ACTION_COPY
;
2500 else if( m_nSourceActions
& DNDConstants::ACTION_LINK
)
2501 nNewDropAction
= DNDConstants::ACTION_LINK
;
2503 nNewDropAction
|= DNDConstants::ACTION_DEFAULT
;
2506 if( nNewDropAction
!= m_nUserDragAction
|| m_nTargetAcceptAction
!= DNDConstants::ACTION_DEFAULT
)
2508 #if OSL_DEBUG_LEVEL > 1
2509 SAL_INFO("vcl.unx.dtrans", "updateDragAction: "
2511 << (int)m_nUserDragAction
2513 << (int)nNewDropAction
);
2516 m_nUserDragAction
= nNewDropAction
;
2518 DragSourceDragEvent dsde
;
2519 dsde
.Source
= static_cast< OWeakObject
* >(this);
2520 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2521 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2522 dsde
.DropAction
= m_nUserDragAction
;
2523 dsde
.UserAction
= m_nUserDragAction
;
2524 m_nTargetAcceptAction
= DNDConstants::ACTION_DEFAULT
; // invalidate last accept
2525 m_xDragSourceListener
->dropActionChanged( dsde
);
2530 void SelectionManager::sendDropPosition( bool bForce
, Time eventTime
)
2532 osl::ClearableMutexGuard
aGuard(m_aMutex
);
2537 std::unordered_map
< ::Window
, DropTargetEntry
>::const_iterator it
=
2538 m_aDropTargets
.find( m_aDropWindow
);
2539 if( it
!= m_aDropTargets
.end() )
2541 if( it
->second
.m_pTarget
->m_bActive
)
2545 XTranslateCoordinates( m_pDisplay
, it
->second
.m_aRootWindow
, m_aDropWindow
, m_nLastDragX
, m_nLastDragY
, &x
, &y
, &aChild
);
2546 DropTargetDragEvent dtde
;
2547 dtde
.Source
= static_cast< OWeakObject
* >(it
->second
.m_pTarget
);
2548 dtde
.Context
= new DropTargetDragContext( m_aCurrentDropWindow
, *this );
2551 dtde
.DropAction
= getUserDragAction();
2552 dtde
.SourceActions
= m_nSourceActions
;
2554 it
->second
->dragOver( dtde
);
2559 m_nLastDragX
< m_nNoPosX
|| m_nLastDragX
>= m_nNoPosX
+m_nNoPosWidth
||
2560 m_nLastDragY
< m_nNoPosY
|| m_nLastDragY
>= m_nNoPosY
+m_nNoPosHeight
2563 // send XdndPosition
2565 aEvent
.type
= ClientMessage
;
2566 aEvent
.xclient
.display
= m_pDisplay
;
2567 aEvent
.xclient
.format
= 32;
2568 aEvent
.xclient
.message_type
= m_nXdndPosition
;
2569 aEvent
.xclient
.window
= m_aDropWindow
;
2570 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
2571 aEvent
.xclient
.data
.l
[1] = 0;
2572 aEvent
.xclient
.data
.l
[2] = m_nLastDragX
<< 16 | (m_nLastDragY
&0xffff);
2573 aEvent
.xclient
.data
.l
[3] = eventTime
;
2575 if( m_nUserDragAction
& DNDConstants::ACTION_COPY
)
2576 aEvent
.xclient
.data
.l
[4]=m_nXdndActionCopy
;
2577 else if( m_nUserDragAction
& DNDConstants::ACTION_MOVE
)
2578 aEvent
.xclient
.data
.l
[4]=m_nXdndActionMove
;
2579 else if( m_nUserDragAction
& DNDConstants::ACTION_LINK
)
2580 aEvent
.xclient
.data
.l
[4]=m_nXdndActionLink
;
2582 aEvent
.xclient
.data
.l
[4]=m_nXdndActionCopy
;
2583 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
2584 m_nNoPosX
= m_nNoPosY
= m_nNoPosWidth
= m_nNoPosHeight
= 0;
2588 bool SelectionManager::handleDragEvent( XEvent
const & rMessage
)
2590 if( ! m_xDragSourceListener
.is() )
2593 osl::ResettableMutexGuard
aGuard(m_aMutex
);
2595 bool bHandled
= false;
2598 std::unordered_map
< ::Window
, DropTargetEntry
>::const_iterator it
=
2599 m_aDropTargets
.find( m_aDropWindow
);
2601 #if OSL_DEBUG_LEVEL > 1
2602 switch( rMessage
.type
)
2605 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: "
2606 << getString( rMessage
.xclient
.message_type
));
2611 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: EnterNotify.");
2614 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: LeaveNotify.");
2617 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: ButtonPress "
2618 << rMessage
.xbutton
.button
2619 << " (m_nDragButton = "
2624 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: ButtonRelease "
2625 << rMessage
.xbutton
.button
2626 << " (m_nDragButton = "
2631 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: KeyPress.");
2634 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: KeyRelease.");
2637 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: <unknown type "
2644 // handle drag related events
2645 if( rMessage
.type
== ClientMessage
)
2647 if( rMessage
.xclient
.message_type
== m_nXdndStatus
&& Atom(rMessage
.xclient
.data
.l
[0]) == m_aDropWindow
)
2650 DragSourceDragEvent dsde
;
2651 dsde
.Source
= static_cast< OWeakObject
* >(this);
2652 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2653 dsde
.DragSource
= static_cast< XDragSource
* >( this );
2654 dsde
.UserAction
= getUserDragAction();
2655 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
2656 m_bDropSuccess
= (rMessage
.xclient
.data
.l
[1] & 1) != 0;
2657 #if OSL_DEBUG_LEVEL > 1
2658 SAL_INFO("vcl.unx.dtrans", "status drop action: accept = "
2659 << (m_bDropSuccess
? "true" : "false")
2661 << getString( rMessage
.xclient
.data
.l
[4] ));
2663 if( rMessage
.xclient
.data
.l
[1] & 1 )
2665 if( m_nCurrentProtocolVersion
> 1 )
2667 if( Atom(rMessage
.xclient
.data
.l
[4]) == m_nXdndActionCopy
)
2668 dsde
.DropAction
= DNDConstants::ACTION_COPY
;
2669 else if( Atom(rMessage
.xclient
.data
.l
[4]) == m_nXdndActionMove
)
2670 dsde
.DropAction
= DNDConstants::ACTION_MOVE
;
2671 else if( Atom(rMessage
.xclient
.data
.l
[4]) == m_nXdndActionLink
)
2672 dsde
.DropAction
= DNDConstants::ACTION_LINK
;
2675 dsde
.DropAction
= DNDConstants::ACTION_COPY
;
2677 m_nTargetAcceptAction
= dsde
.DropAction
;
2679 if( ! ( rMessage
.xclient
.data
.l
[1] & 2 ) )
2681 m_nNoPosX
= rMessage
.xclient
.data
.l
[2] >> 16;
2682 m_nNoPosY
= rMessage
.xclient
.data
.l
[2] & 0xffff;
2683 m_nNoPosWidth
= rMessage
.xclient
.data
.l
[3] >> 16;
2684 m_nNoPosHeight
= rMessage
.xclient
.data
.l
[3] & 0xffff;
2687 m_nNoPosX
= m_nNoPosY
= m_nNoPosWidth
= m_nNoPosHeight
= 0;
2689 setCursor( getDefaultCursor( dsde
.DropAction
), m_aDropWindow
);
2691 m_xDragSourceListener
->dragOver( dsde
);
2693 else if( rMessage
.xclient
.message_type
== m_nXdndFinished
&& m_aDropWindow
== Atom(rMessage
.xclient
.data
.l
[0]) )
2696 // notify the listener
2697 DragSourceDropEvent dsde
;
2698 dsde
.Source
= static_cast< OWeakObject
* >(this);
2699 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2700 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2701 dsde
.DropAction
= m_nTargetAcceptAction
;
2702 dsde
.DropSuccess
= m_bDropSuccess
;
2703 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
2704 m_xDragSourceListener
.clear();
2706 xListener
->dragDropEnd( dsde
);
2709 else if( rMessage
.type
== MotionNotify
||
2710 rMessage
.type
== EnterNotify
|| rMessage
.type
== LeaveNotify
2714 bool bForce
= false;
2715 int root_x
= rMessage
.type
== MotionNotify
? rMessage
.xmotion
.x_root
: rMessage
.xcrossing
.x_root
;
2716 int root_y
= rMessage
.type
== MotionNotify
? rMessage
.xmotion
.y_root
: rMessage
.xcrossing
.y_root
;
2717 ::Window root
= rMessage
.type
== MotionNotify
? rMessage
.xmotion
.root
: rMessage
.xcrossing
.root
;
2720 if( rMessage
.type
== MotionNotify
)
2722 bForce
= updateDragAction( rMessage
.xmotion
.state
);
2724 updateDragWindow( root_x
, root_y
, root
);
2727 if( m_nCurrentProtocolVersion
>= 0 && m_aDropProxy
!= None
)
2730 sendDropPosition( bForce
, rMessage
.type
== MotionNotify
? rMessage
.xmotion
.time
: rMessage
.xcrossing
.time
);
2733 else if( rMessage
.type
== KeyPress
|| rMessage
.type
== KeyRelease
)
2736 KeySym aKey
= XkbKeycodeToKeysym( m_pDisplay
, rMessage
.xkey
.keycode
, 0, 0 );
2737 if( aKey
== XK_Escape
)
2740 if( it
!= m_aDropTargets
.end() )
2742 DropTargetEvent dte
;
2743 dte
.Source
= static_cast< OWeakObject
* >( it
->second
.m_pTarget
);
2745 it
->second
.m_pTarget
->dragExit( dte
);
2748 else if( m_aDropProxy
!= None
&& m_nCurrentProtocolVersion
>= 0 )
2752 aEvent
.type
= ClientMessage
;
2753 aEvent
.xclient
.display
= m_pDisplay
;
2754 aEvent
.xclient
.format
= 32;
2755 aEvent
.xclient
.message_type
= m_nXdndLeave
;
2756 aEvent
.xclient
.window
= m_aDropWindow
;
2757 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
2758 memset( aEvent
.xclient
.data
.l
+1, 0, sizeof(long)*4);
2759 m_aDropWindow
= m_aDropProxy
= None
;
2760 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
2762 // notify the listener
2763 DragSourceDropEvent dsde
;
2764 dsde
.Source
= static_cast< OWeakObject
* >(this);
2765 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2766 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2767 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
2768 dsde
.DropSuccess
= false;
2769 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
2770 m_xDragSourceListener
.clear();
2772 xListener
->dragDropEnd( dsde
);
2777 * man page says: state is state immediate PRIOR to the
2778 * event. It would seem that this is a somewhat arguable
2781 int nState
= rMessage
.xkey
.state
;
2786 case XK_Shift_L
: nNewState
= ShiftMask
;break;
2788 case XK_Control_L
: nNewState
= ControlMask
;break;
2789 // just interested in shift and ctrl for dnd
2791 if( rMessage
.type
== KeyPress
)
2792 nState
|= nNewState
;
2794 nState
&= ~nNewState
;
2796 if( updateDragAction( nState
) )
2797 sendDropPosition( true, rMessage
.xkey
.time
);
2801 ( rMessage
.type
== ButtonPress
|| rMessage
.type
== ButtonRelease
) &&
2802 rMessage
.xbutton
.button
== m_nDragButton
)
2804 bool bCancel
= true;
2805 if( m_aDropWindow
!= None
)
2807 if( it
!= m_aDropTargets
.end() )
2809 if( it
->second
.m_pTarget
->m_bActive
&& m_nUserDragAction
!= DNDConstants::ACTION_NONE
&& m_bLastDropAccepted
)
2814 XTranslateCoordinates( m_pDisplay
, rMessage
.xbutton
.root
, m_aDropWindow
, rMessage
.xbutton
.x_root
, rMessage
.xbutton
.y_root
, &x
, &y
, &aChild
);
2815 DropTargetDropEvent dtde
;
2816 dtde
.Source
= static_cast< OWeakObject
* >(it
->second
.m_pTarget
);
2817 dtde
.Context
= new DropTargetDropContext( m_aCurrentDropWindow
, *this );
2820 dtde
.DropAction
= m_nUserDragAction
;
2821 dtde
.SourceActions
= m_nSourceActions
;
2822 dtde
.Transferable
= m_xDragSourceTransferable
;
2824 m_nDropTimeout
= time( nullptr );
2825 m_bDropWaitingForCompletion
= true;
2827 it
->second
->drop( dtde
);
2830 else bCancel
= true;
2832 else if( m_nCurrentProtocolVersion
>= 0 )
2837 aEvent
.type
= ClientMessage
;
2838 aEvent
.xclient
.display
= m_pDisplay
;
2839 aEvent
.xclient
.format
= 32;
2840 aEvent
.xclient
.message_type
= m_nXdndDrop
;
2841 aEvent
.xclient
.window
= m_aDropWindow
;
2842 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
2843 aEvent
.xclient
.data
.l
[1] = 0;
2844 aEvent
.xclient
.data
.l
[2] = rMessage
.xbutton
.time
;
2845 aEvent
.xclient
.data
.l
[3] = 0;
2846 aEvent
.xclient
.data
.l
[4] = 0;
2849 m_nDropTimeout
= time( nullptr );
2850 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
2855 // dropping on non XdndWindows: acquire ownership of
2856 // PRIMARY and send a middle mouse button click down/up to
2858 SelectionAdaptor
* pAdaptor
= getAdaptor( XA_PRIMARY
);
2865 aEvent
.type
= ButtonPress
;
2866 aEvent
.xbutton
.display
= m_pDisplay
;
2867 aEvent
.xbutton
.window
= m_aDropWindow
;
2868 aEvent
.xbutton
.root
= rMessage
.xbutton
.root
;
2869 aEvent
.xbutton
.subwindow
= m_aDropWindow
;
2870 aEvent
.xbutton
.time
= rMessage
.xbutton
.time
+1;
2871 aEvent
.xbutton
.x_root
= rMessage
.xbutton
.x_root
;
2872 aEvent
.xbutton
.y_root
= rMessage
.xbutton
.y_root
;
2873 aEvent
.xbutton
.state
= rMessage
.xbutton
.state
;
2874 aEvent
.xbutton
.button
= Button2
;
2875 aEvent
.xbutton
.same_screen
= True
;
2876 XTranslateCoordinates( m_pDisplay
,
2877 rMessage
.xbutton
.root
, m_aDropWindow
,
2878 rMessage
.xbutton
.x_root
, rMessage
.xbutton
.y_root
,
2879 &aEvent
.xbutton
.x
, &aEvent
.xbutton
.y
,
2881 XSendEvent( m_pDisplay
, m_aDropWindow
, False
, ButtonPressMask
, &aEvent
);
2882 aEvent
.xbutton
.type
= ButtonRelease
;
2883 aEvent
.xbutton
.time
++;
2884 aEvent
.xbutton
.state
|= Button2Mask
;
2885 XSendEvent( m_pDisplay
, m_aDropWindow
, False
, ButtonReleaseMask
, &aEvent
);
2888 m_nDropTimeout
= time( nullptr );
2889 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
2890 m_bWaitingForPrimaryConversion
= true;
2892 m_nDropTimeout
= time( nullptr );
2895 static_cast< X11Clipboard
* >( pAdaptor
)->setContents( m_xDragSourceTransferable
, css::uno::Reference
< css::datatransfer::clipboard::XClipboardOwner
>() );
2904 DragSourceDropEvent dsde
;
2905 dsde
.Source
= static_cast< OWeakObject
* >(this);
2906 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2907 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2908 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
2909 dsde
.DropSuccess
= false;
2910 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
2911 m_xDragSourceListener
.clear();
2913 xListener
->dragDropEnd( dsde
);
2920 void SelectionManager::accept( sal_Int8 dragOperation
, ::Window aDropWindow
)
2922 if( aDropWindow
!= m_aCurrentDropWindow
)
2925 #if OSL_DEBUG_LEVEL > 1
2926 SAL_INFO("vcl.unx.dtrans", "accept: " << std::hex
<< dragOperation
);
2928 Atom nAction
= None
;
2929 dragOperation
&= (DNDConstants::ACTION_MOVE
| DNDConstants::ACTION_COPY
| DNDConstants::ACTION_LINK
);
2930 if( dragOperation
& DNDConstants::ACTION_MOVE
)
2931 nAction
= m_nXdndActionMove
;
2932 else if( dragOperation
& DNDConstants::ACTION_COPY
)
2933 nAction
= m_nXdndActionCopy
;
2934 else if( dragOperation
& DNDConstants::ACTION_LINK
)
2935 nAction
= m_nXdndActionLink
;
2936 m_bLastDropAccepted
= true;
2937 sendDragStatus( nAction
);
2940 void SelectionManager::reject( ::Window aDropWindow
)
2942 if( aDropWindow
!= m_aCurrentDropWindow
)
2945 #if OSL_DEBUG_LEVEL > 1
2946 SAL_INFO("vcl.unx.dtrans", "reject.");
2948 m_bLastDropAccepted
= false;
2949 sendDragStatus( None
);
2950 if( m_bDropSent
&& m_xDragSourceListener
.is() )
2952 DragSourceDropEvent dsde
;
2953 dsde
.Source
= static_cast< OWeakObject
* >(this);
2954 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
2955 dsde
.DragSource
= static_cast< XDragSource
* >(this);
2956 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
2957 dsde
.DropSuccess
= false;
2958 m_xDragSourceListener
->dragDropEnd( dsde
);
2959 m_xDragSourceListener
.clear();
2967 sal_Bool
SelectionManager::isDragImageSupported()
2972 sal_Int32
SelectionManager::getDefaultCursor( sal_Int8 dragAction
)
2974 Cursor aCursor
= m_aNoneCursor
;
2975 if( dragAction
& DNDConstants::ACTION_MOVE
)
2976 aCursor
= m_aMoveCursor
;
2977 else if( dragAction
& DNDConstants::ACTION_COPY
)
2978 aCursor
= m_aCopyCursor
;
2979 else if( dragAction
& DNDConstants::ACTION_LINK
)
2980 aCursor
= m_aLinkCursor
;
2984 int SelectionManager::getXdndVersion( ::Window aWindow
, ::Window
& rProxy
)
2986 Atom
* pProperties
= nullptr;
2987 int nProperties
= 0;
2990 unsigned long nItems
, nBytes
;
2991 unsigned char* pBytes
= nullptr;
2997 * XListProperties is used here to avoid unnecessary XGetWindowProperty calls
2998 * and therefore reducing latency penalty
3000 pProperties
= XListProperties( m_pDisplay
, aWindow
, &nProperties
);
3001 // first look for proxy
3003 for( i
= 0; i
< nProperties
; i
++ )
3005 if( pProperties
[i
] == m_nXdndProxy
)
3007 XGetWindowProperty( m_pDisplay
, aWindow
, m_nXdndProxy
, 0, 1, False
, XA_WINDOW
,
3008 &nType
, &nFormat
, &nItems
, &nBytes
, &pBytes
);
3011 if( nType
== XA_WINDOW
)
3012 rProxy
= *reinterpret_cast< ::Window
* >(pBytes
);
3015 if( rProxy
!= None
)
3017 // now check proxy whether it points to itself
3018 XGetWindowProperty( m_pDisplay
, rProxy
, m_nXdndProxy
, 0, 1, False
, XA_WINDOW
,
3019 &nType
, &nFormat
, &nItems
, &nBytes
, &pBytes
);
3022 if( nType
== XA_WINDOW
&& *reinterpret_cast< ::Window
* >(pBytes
) != rProxy
)
3035 XFree (pProperties
);
3037 ::Window aAwareWindow
= rProxy
!= None
? rProxy
: aWindow
;
3039 XGetWindowProperty( m_pDisplay
, aAwareWindow
, m_nXdndAware
, 0, 1, False
, XA_ATOM
,
3040 &nType
, &nFormat
, &nItems
, &nBytes
, &pBytes
);
3043 if( nType
== XA_ATOM
)
3044 nVersion
= *reinterpret_cast<Atom
*>(pBytes
);
3048 nVersion
= std::min
<int>(nVersion
, nXdndProtocolRevision
);
3053 void SelectionManager::updateDragWindow( int nX
, int nY
, ::Window aRoot
)
3055 osl::ResettableMutexGuard
aGuard( m_aMutex
);
3057 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
3062 ::Window aParent
= aRoot
;
3064 ::Window aNewProxy
= None
, aNewCurrentWindow
= None
;
3065 int nNewProtocolVersion
= -1;
3068 // find the first XdndAware window or check if root window is
3069 // XdndAware or has XdndProxy
3072 XTranslateCoordinates( m_pDisplay
, aRoot
, aParent
, nX
, nY
, &nWinX
, &nWinY
, &aChild
);
3073 if( aChild
!= None
)
3075 if( aChild
== m_aCurrentDropWindow
&& aChild
!= aRoot
&& m_nCurrentProtocolVersion
>= 0 )
3080 nNewProtocolVersion
= getXdndVersion( aChild
, aNewProxy
);
3083 } while( aChild
!= None
&& nNewProtocolVersion
< 0 );
3085 aNewCurrentWindow
= aParent
;
3086 if( aNewCurrentWindow
== aRoot
)
3088 // no children, try root drop
3089 nNewProtocolVersion
= getXdndVersion( aNewCurrentWindow
, aNewProxy
);
3090 if( nNewProtocolVersion
< 3 )
3092 aNewCurrentWindow
= aNewProxy
= None
;
3093 nNewProtocolVersion
= nXdndProtocolRevision
;
3097 DragSourceDragEvent dsde
;
3098 dsde
.Source
= static_cast< OWeakObject
* >(this);
3099 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
3100 dsde
.DragSource
= static_cast< XDragSource
* >(this);
3101 dsde
.DropAction
= nNewProtocolVersion
>= 0 ? m_nUserDragAction
: DNDConstants::ACTION_COPY
;
3102 dsde
.UserAction
= nNewProtocolVersion
>= 0 ? m_nUserDragAction
: DNDConstants::ACTION_COPY
;
3104 std::unordered_map
< ::Window
, DropTargetEntry
>::const_iterator it
;
3105 if( aNewCurrentWindow
!= m_aDropWindow
)
3107 #if OSL_DEBUG_LEVEL > 1
3108 SAL_INFO("vcl.unx.dtrans", "drag left window "
3109 << std::showbase
<< std::hex
3113 << m_nCurrentProtocolVersion
3114 << "), entered window "
3115 << std::showbase
<< std::hex
3116 << aNewCurrentWindow
3119 << nNewProtocolVersion
3122 if( m_aDropWindow
!= None
)
3124 it
= m_aDropTargets
.find( m_aDropWindow
);
3125 if( it
!= m_aDropTargets
.end() )
3126 // shortcut for own drop targets
3128 DropTargetEvent dte
;
3129 dte
.Source
= static_cast< OWeakObject
* >( it
->second
.m_pTarget
);
3131 it
->second
.m_pTarget
->dragExit( dte
);
3136 // send old drop target a XdndLeave
3138 aEvent
.type
= ClientMessage
;
3139 aEvent
.xclient
.display
= m_pDisplay
;
3140 aEvent
.xclient
.format
= 32;
3141 aEvent
.xclient
.message_type
= m_nXdndLeave
;
3142 aEvent
.xclient
.window
= m_aDropWindow
;
3143 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
3144 aEvent
.xclient
.data
.l
[1] = 0;
3145 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
3147 if( xListener
.is() )
3150 xListener
->dragExit( dsde
);
3155 m_nCurrentProtocolVersion
= nNewProtocolVersion
;
3156 m_aDropWindow
= aNewCurrentWindow
;
3157 m_aDropProxy
= aNewProxy
!= None
? aNewProxy
: m_aDropWindow
;
3159 it
= m_aDropTargets
.find( m_aDropWindow
);
3160 if( it
!= m_aDropTargets
.end() && ! it
->second
.m_pTarget
->m_bActive
)
3161 m_aDropProxy
= None
;
3163 if( m_aDropProxy
!= None
&& xListener
.is() )
3166 xListener
->dragEnter( dsde
);
3170 if( m_aDropProxy
!= None
&& m_nCurrentProtocolVersion
>= 0 )
3172 it
= m_aDropTargets
.find( m_aDropWindow
);
3173 if( it
!= m_aDropTargets
.end() )
3175 XTranslateCoordinates( m_pDisplay
, aRoot
, m_aDropWindow
, nX
, nY
, &nWinX
, &nWinY
, &aChild
);
3176 DropTargetDragEnterEvent dtde
;
3177 dtde
.Source
= static_cast< OWeakObject
* >( it
->second
.m_pTarget
);
3178 dtde
.Context
= new DropTargetDragContext( m_aCurrentDropWindow
, *this );
3179 dtde
.LocationX
= nWinX
;
3180 dtde
.LocationY
= nWinY
;
3181 dtde
.DropAction
= m_nUserDragAction
;
3182 dtde
.SourceActions
= m_nSourceActions
;
3183 dtde
.SupportedDataFlavors
= m_xDragSourceTransferable
->getTransferDataFlavors();
3185 it
->second
.m_pTarget
->dragEnter( dtde
);
3191 aEvent
.type
= ClientMessage
;
3192 aEvent
.xclient
.display
= m_pDisplay
;
3193 aEvent
.xclient
.format
= 32;
3194 aEvent
.xclient
.message_type
= m_nXdndEnter
;
3195 aEvent
.xclient
.window
= m_aDropWindow
;
3196 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
3197 aEvent
.xclient
.data
.l
[1] = m_nCurrentProtocolVersion
<< 24;
3198 memset( aEvent
.xclient
.data
.l
+ 2, 0, sizeof( long )*3 );
3199 // fill in data types
3200 ::std::list
< Atom
> aConversions
;
3201 getNativeTypeList( m_aDragFlavors
, aConversions
, m_nXdndSelection
);
3202 if( aConversions
.size() > 3 )
3203 aEvent
.xclient
.data
.l
[1] |= 1;
3204 ::std::list
< Atom
>::const_iterator type_it
= aConversions
.begin();
3205 for( int i
= 0; type_it
!= aConversions
.end() && i
< 3; i
++, ++type_it
)
3206 aEvent
.xclient
.data
.l
[i
+2] = *type_it
;
3207 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
3210 m_nNoPosX
= m_nNoPosY
= m_nNoPosWidth
= m_nNoPosHeight
= 0;
3212 else if( m_aDropProxy
!= None
&& xListener
.is() )
3215 // drag over for XdndAware windows comes when receiving XdndStatus
3216 xListener
->dragOver( dsde
);
3220 void SelectionManager::startDrag(
3221 const DragGestureEvent
& trigger
,
3222 sal_Int8 sourceActions
,
3225 const css::uno::Reference
< XTransferable
>& transferable
,
3226 const css::uno::Reference
< XDragSourceListener
>& listener
3229 #if OSL_DEBUG_LEVEL > 1
3230 SAL_INFO("vcl.unx.dtrans", "startDrag( sourceActions = "
3232 << (int)sourceActions
3235 DragSourceDropEvent aDragFailedEvent
;
3236 aDragFailedEvent
.Source
= static_cast< OWeakObject
* >(this);
3237 aDragFailedEvent
.DragSource
= static_cast< XDragSource
* >(this);
3238 aDragFailedEvent
.DragSourceContext
= new DragSourceContext( None
, *this );
3239 aDragFailedEvent
.DropAction
= DNDConstants::ACTION_NONE
;
3240 aDragFailedEvent
.DropSuccess
= false;
3242 if( m_aDragRunning
.check() )
3245 listener
->dragDropEnd( aDragFailedEvent
);
3247 #if OSL_DEBUG_LEVEL > 1
3248 SAL_WARN("vcl.unx.dtrans",
3249 "*** ERROR *** second drag and drop started.");
3250 if( m_xDragSourceListener
.is() )
3251 SAL_WARN("vcl.unx.dtrans",
3252 "*** ERROR *** drag source listener already set.");
3254 SAL_WARN("vcl.unx.dtrans",
3255 "*** ERROR *** drag thread already running.");
3260 SalFrame
* pCaptureFrame
= nullptr;
3263 osl::ClearableMutexGuard
aGuard(m_aMutex
);
3265 // first get the current pointer position and the window that
3266 // the pointer is located in. since said window should be one
3267 // of our DropTargets at the time of executeDrag we can use
3269 ::Window aRoot
, aParent
, aChild
;
3270 int root_x(0), root_y(0), win_x(0), win_y(0);
3271 unsigned int mask(0);
3273 bool bPointerFound
= false;
3274 for (auto const& dropTarget
: m_aDropTargets
)
3276 if( XQueryPointer( m_pDisplay
, dropTarget
.second
.m_aRootWindow
,
3282 aParent
= dropTarget
.second
.m_aRootWindow
;
3284 bPointerFound
= true;
3289 // don't start DnD if there is none of our windows on the same screen as
3290 // the pointer or if no mouse button is pressed
3291 if( !bPointerFound
|| (mask
& (Button1Mask
|Button2Mask
|Button3Mask
)) == 0 )
3295 listener
->dragDropEnd( aDragFailedEvent
);
3299 // try to find which of our drop targets is the drag source
3300 // if that drop target is deregistered we should stop executing
3301 // the drag (actually this is a poor substitute for an "endDrag"
3303 m_aDragSourceWindow
= None
;
3306 XTranslateCoordinates( m_pDisplay
, aRoot
, aParent
, root_x
, root_y
, &win_x
, &win_y
, &aChild
);
3307 if( aChild
!= None
&& m_aDropTargets
.find( aChild
) != m_aDropTargets
.end() )
3309 m_aDragSourceWindow
= aChild
;
3310 #if OSL_DEBUG_LEVEL > 1
3311 SAL_INFO("vcl.unx.dtrans", "found drag source window "
3312 << std::showbase
<< std::hex
3313 << m_aDragSourceWindow
);
3318 } while( aChild
!= None
);
3320 #if OSL_DEBUG_LEVEL > 1
3321 SAL_INFO("vcl.unx.dtrans", "try to grab pointer ...");
3323 int nPointerGrabSuccess
=
3324 XGrabPointer( m_pDisplay
, aRoot
, True
,
3326 GrabModeAsync
, GrabModeAsync
,
3330 /* if we could not grab the pointer here, there is a chance
3331 that the pointer is grabbed by the other vcl display (the main loop)
3332 so let's break that grab and reset it later
3334 remark: this whole code should really be molten into normal vcl so only
3335 one display is used...
3337 if( nPointerGrabSuccess
!= GrabSuccess
)
3339 comphelper::SolarMutex
& rSolarMutex( Application::GetSolarMutex() );
3340 if( rSolarMutex
.tryToAcquire() )
3342 pCaptureFrame
= vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetCaptureFrame();
3345 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( nullptr );
3346 nPointerGrabSuccess
=
3347 XGrabPointer( m_pDisplay
, aRoot
, True
,
3349 GrabModeAsync
, GrabModeAsync
,
3356 #if OSL_DEBUG_LEVEL > 1
3357 SAL_INFO("vcl.unx.dtrans", "... grabbed pointer: "
3358 << nPointerGrabSuccess
);
3359 SAL_INFO("vcl.unx.dtrans", "try to grab keyboard ...");
3361 int nKeyboardGrabSuccess
=
3362 XGrabKeyboard( m_pDisplay
, aRoot
, True
,
3363 GrabModeAsync
, GrabModeAsync
, CurrentTime
);
3364 #if OSL_DEBUG_LEVEL > 1
3365 SAL_INFO("vcl.unx.dtrans", "... grabbed keyboard: "
3366 << nKeyboardGrabSuccess
);
3368 if( nPointerGrabSuccess
!= GrabSuccess
|| nKeyboardGrabSuccess
!= GrabSuccess
)
3370 if( nPointerGrabSuccess
== GrabSuccess
)
3371 XUngrabPointer( m_pDisplay
, CurrentTime
);
3372 if( nKeyboardGrabSuccess
== GrabSuccess
)
3373 XUngrabKeyboard( m_pDisplay
, CurrentTime
);
3374 XFlush( m_pDisplay
);
3377 listener
->dragDropEnd( aDragFailedEvent
);
3380 comphelper::SolarMutex
& rSolarMutex( Application::GetSolarMutex() );
3381 if( rSolarMutex
.tryToAcquire() )
3382 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( pCaptureFrame
);
3383 #if OSL_DEBUG_LEVEL > 0
3385 OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" );
3391 m_xDragSourceTransferable
= transferable
;
3392 m_xDragSourceListener
= listener
;
3393 m_aDragFlavors
= transferable
->getTransferDataFlavors();
3394 m_aCurrentCursor
= None
;
3396 requestOwnership( m_nXdndSelection
);
3398 ::std::list
< Atom
> aConversions
;
3399 getNativeTypeList( m_aDragFlavors
, aConversions
, m_nXdndSelection
);
3401 Atom
* pTypes
= static_cast<Atom
*>(alloca( sizeof(Atom
)*aConversions
.size() ));
3403 for (auto const& conversion
: aConversions
)
3404 pTypes
[nTypes
++] = conversion
;
3406 XChangeProperty( m_pDisplay
, m_aWindow
, m_nXdndTypeList
, XA_ATOM
, 32, PropModeReplace
, reinterpret_cast<unsigned char*>(pTypes
), nTypes
);
3408 m_nSourceActions
= sourceActions
| DNDConstants::ACTION_DEFAULT
;
3409 m_nUserDragAction
= DNDConstants::ACTION_MOVE
& m_nSourceActions
;
3410 if( ! m_nUserDragAction
)
3411 m_nUserDragAction
= DNDConstants::ACTION_COPY
& m_nSourceActions
;
3412 if( ! m_nUserDragAction
)
3413 m_nUserDragAction
= DNDConstants::ACTION_LINK
& m_nSourceActions
;
3414 m_nTargetAcceptAction
= DNDConstants::ACTION_DEFAULT
;
3415 m_bDropSent
= false;
3416 m_bDropSuccess
= false;
3417 m_bWaitingForPrimaryConversion
= false;
3418 m_nDragButton
= Button1
; // default to left button
3419 css::awt::MouseEvent aEvent
;
3420 if( trigger
.Event
>>= aEvent
)
3422 if( aEvent
.Buttons
& MouseButton::LEFT
)
3423 m_nDragButton
= Button1
;
3424 else if( aEvent
.Buttons
& MouseButton::RIGHT
)
3425 m_nDragButton
= Button3
;
3426 else if( aEvent
.Buttons
& MouseButton::MIDDLE
)
3427 m_nDragButton
= Button2
;
3429 #if OSL_DEBUG_LEVEL > 1
3430 SAL_INFO("vcl.unx.dtrans", "m_nUserDragAction = "
3432 << (int)m_nUserDragAction
);
3434 updateDragWindow( root_x
, root_y
, aRoot
);
3435 m_nUserDragAction
= ~0;
3436 updateDragAction( mask
);
3439 m_aDragRunning
.set();
3440 m_aDragExecuteThread
= osl_createSuspendedThread( call_SelectionManager_runDragExecute
, this );
3441 if( m_aDragExecuteThread
)
3442 osl_resumeThread( m_aDragExecuteThread
);
3445 #if OSL_DEBUG_LEVEL > 1
3446 SAL_INFO("vcl.unx.dtrans", "osl_createSuspendedThread failed "
3447 << "for drag execute.");
3449 m_xDragSourceListener
.clear();
3450 m_xDragSourceTransferable
.clear();
3452 m_bDropSent
= false;
3453 m_bDropSuccess
= false;
3454 m_bWaitingForPrimaryConversion
= false;
3455 m_aDropWindow
= None
;
3456 m_aDropProxy
= None
;
3457 m_nCurrentProtocolVersion
= nXdndProtocolRevision
;
3462 m_aCurrentCursor
= None
;
3464 XUngrabPointer( m_pDisplay
, CurrentTime
);
3465 XUngrabKeyboard( m_pDisplay
, CurrentTime
);
3466 XFlush( m_pDisplay
);
3470 comphelper::SolarMutex
& rSolarMutex( Application::GetSolarMutex() );
3471 if( rSolarMutex
.tryToAcquire() )
3472 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( pCaptureFrame
);
3473 #if OSL_DEBUG_LEVEL > 0
3475 OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" );
3479 m_aDragRunning
.reset();
3482 listener
->dragDropEnd( aDragFailedEvent
);
3486 void SelectionManager::runDragExecute( void* pThis
)
3488 SelectionManager
* This
= static_cast<SelectionManager
*>(pThis
);
3489 This
->dragDoDispatch();
3492 void SelectionManager::dragDoDispatch()
3496 // m_xDragSourceListener will be cleared on finished drop
3497 #if OSL_DEBUG_LEVEL > 1
3498 SAL_INFO("vcl.unx.dtrans", "begin executeDrag dispatching.");
3500 oslThread aThread
= m_aDragExecuteThread
;
3501 while( m_xDragSourceListener
.is() && ( ! m_bDropSent
|| time(nullptr)-m_nDropTimeout
< 5 ) && osl_scheduleThread( aThread
) )
3503 // let the thread in the run method do the dispatching
3504 // just look occasionally here whether drop timed out or is completed
3505 osl::Thread::wait(std::chrono::milliseconds(200));
3507 #if OSL_DEBUG_LEVEL > 1
3508 SAL_INFO("vcl.unx.dtrans", "end executeDrag dispatching.");
3511 osl::ClearableMutexGuard
aGuard(m_aMutex
);
3513 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
3514 css::uno::Reference
< XTransferable
> xTransferable( m_xDragSourceTransferable
);
3515 m_xDragSourceListener
.clear();
3516 m_xDragSourceTransferable
.clear();
3518 DragSourceDropEvent dsde
;
3519 dsde
.Source
= static_cast< OWeakObject
* >(this);
3520 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
3521 dsde
.DragSource
= static_cast< XDragSource
* >(this);
3522 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
3523 dsde
.DropSuccess
= false;
3525 // cleanup after drag
3526 if( m_bWaitingForPrimaryConversion
)
3528 SelectionAdaptor
* pAdaptor
= getAdaptor( XA_PRIMARY
);
3530 pAdaptor
->clearTransferable();
3533 m_bDropSent
= false;
3534 m_bDropSuccess
= false;
3535 m_bWaitingForPrimaryConversion
= false;
3536 m_aDropWindow
= None
;
3537 m_aDropProxy
= None
;
3538 m_nCurrentProtocolVersion
= nXdndProtocolRevision
;
3543 m_aCurrentCursor
= None
;
3545 XUngrabPointer( m_pDisplay
, CurrentTime
);
3546 XUngrabKeyboard( m_pDisplay
, CurrentTime
);
3547 XFlush( m_pDisplay
);
3549 m_aDragExecuteThread
= nullptr;
3550 m_aDragRunning
.reset();
3553 if( xListener
.is() )
3555 xTransferable
.clear();
3556 xListener
->dragDropEnd( dsde
);
3559 osl_destroyThread( aThread
);
3563 * XDragSourceContext
3567 void SelectionManager::setCursor( sal_Int32 cursor
, ::Window aDropWindow
)
3569 osl::MutexGuard
aGuard( m_aMutex
);
3570 if( aDropWindow
== m_aDropWindow
&& Cursor(cursor
) != m_aCurrentCursor
)
3572 if( m_xDragSourceListener
.is() && ! m_bDropSent
)
3574 m_aCurrentCursor
= cursor
;
3575 XChangeActivePointerGrab( m_pDisplay
, DRAG_EVENT_MASK
, cursor
, CurrentTime
);
3576 XFlush( m_pDisplay
);
3581 void SelectionManager::transferablesFlavorsChanged()
3583 osl::MutexGuard
aGuard(m_aMutex
);
3585 m_aDragFlavors
= m_xDragSourceTransferable
->getTransferDataFlavors();
3587 std::list
< Atom
> aConversions
;
3589 getNativeTypeList( m_aDragFlavors
, aConversions
, m_nXdndSelection
);
3591 Atom
* pTypes
= static_cast<Atom
*>(alloca( sizeof(Atom
)*aConversions
.size() ));
3593 for (auto const& conversion
: aConversions
)
3594 pTypes
[nTypes
++] = conversion
;
3595 XChangeProperty( m_pDisplay
, m_aWindow
, m_nXdndTypeList
, XA_ATOM
, 32, PropModeReplace
, reinterpret_cast<unsigned char*>(pTypes
), nTypes
);
3597 if( m_aCurrentDropWindow
== None
|| m_nCurrentProtocolVersion
< 0 )
3600 // send synthetic leave and enter events
3604 aEvent
.type
= ClientMessage
;
3605 aEvent
.xclient
.display
= m_pDisplay
;
3606 aEvent
.xclient
.format
= 32;
3607 aEvent
.xclient
.window
= m_aDropWindow
;
3608 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
3610 aEvent
.xclient
.message_type
= m_nXdndLeave
;
3611 aEvent
.xclient
.data
.l
[1] = 0;
3612 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
3614 aEvent
.xclient
.message_type
= m_nXdndEnter
;
3615 aEvent
.xclient
.data
.l
[1] = m_nCurrentProtocolVersion
<< 24;
3616 memset( aEvent
.xclient
.data
.l
+ 2, 0, sizeof( long )*3 );
3617 // fill in data types
3619 aEvent
.xclient
.data
.l
[1] |= 1;
3620 for( int j
= 0; j
< nTypes
&& j
< 3; j
++ )
3621 aEvent
.xclient
.data
.l
[j
+2] = pTypes
[j
];
3623 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
3631 bool SelectionManager::handleXEvent( XEvent
& rEvent
)
3634 * since we are XConnectionListener to a second X display
3635 * to get client messages it is essential not to dispatch
3636 * events twice that we get on both connections
3638 * between dispatching ButtonPress and startDrag
3639 * the user can already have released the mouse. The ButtonRelease
3640 * will then be dispatched in VCLs queue and never turn up here.
3641 * Which is not so good, since startDrag will XGrabPointer and
3642 * XGrabKeyboard -> solid lock.
3644 if( rEvent
.xany
.display
!= m_pDisplay
3645 && rEvent
.type
!= ClientMessage
3646 && rEvent
.type
!= ButtonPress
3647 && rEvent
.type
!= ButtonRelease
3651 bool bHandled
= false;
3652 switch (rEvent
.type
)
3654 case SelectionClear
:
3656 osl::ClearableMutexGuard
aGuard(m_aMutex
);
3657 #if OSL_DEBUG_LEVEL > 1
3658 SAL_INFO("vcl.unx.dtrans", "SelectionClear for selection "
3659 << getString( rEvent
.xselectionclear
.selection
));
3661 SelectionAdaptor
* pAdaptor
= getAdaptor( rEvent
.xselectionclear
.selection
);
3662 std::unordered_map
< Atom
, Selection
* >::iterator
it( m_aSelections
.find( rEvent
.xselectionclear
.selection
) );
3663 if( it
!= m_aSelections
.end() )
3664 it
->second
->m_bOwner
= false;
3667 pAdaptor
->clearTransferable();
3671 case SelectionRequest
:
3672 bHandled
= handleSelectionRequest( rEvent
.xselectionrequest
);
3674 case PropertyNotify
:
3675 if( rEvent
.xproperty
.window
== m_aWindow
||
3676 rEvent
.xproperty
.window
== m_aCurrentDropWindow
3678 bHandled
= handleReceivePropertyNotify( rEvent
.xproperty
);
3680 bHandled
= handleSendPropertyNotify( rEvent
.xproperty
);
3682 case SelectionNotify
:
3683 bHandled
= handleSelectionNotify( rEvent
.xselection
);
3686 // messages from drag target
3687 if( rEvent
.xclient
.message_type
== m_nXdndStatus
||
3688 rEvent
.xclient
.message_type
== m_nXdndFinished
)
3689 bHandled
= handleDragEvent( rEvent
);
3690 // messages from drag source
3692 rEvent
.xclient
.message_type
== m_nXdndEnter
||
3693 rEvent
.xclient
.message_type
== m_nXdndLeave
||
3694 rEvent
.xclient
.message_type
== m_nXdndPosition
||
3695 rEvent
.xclient
.message_type
== m_nXdndDrop
3697 bHandled
= handleDropEvent( rEvent
.xclient
);
3706 bHandled
= handleDragEvent( rEvent
);
3714 void SelectionManager::dispatchEvent( int millisec
)
3716 // acquire the mutex to prevent other threads
3717 // from using the same X connection
3718 osl::ResettableMutexGuard
aGuard(m_aMutex
);
3720 if( !XPending( m_pDisplay
))
3723 // wait for any events if none are already queued
3725 aPollFD
[0].fd
= XConnectionNumber( m_pDisplay
);
3726 aPollFD
[0].events
= POLLIN
;
3727 aPollFD
[0].revents
= 0;
3729 // on infinite timeout we need endthreadpipe monitoring too
3732 aPollFD
[1].fd
= m_EndThreadPipe
[0];
3733 aPollFD
[1].events
= POLLIN
| POLLERR
;
3734 aPollFD
[1].revents
= 0;
3738 // release mutex for the time of waiting for possible data
3740 if( poll( aPollFD
, nfds
, millisec
) <= 0 )
3744 while( XPending( m_pDisplay
))
3747 XNextEvent( m_pDisplay
, &event
);
3749 handleXEvent( event
);
3754 void SelectionManager::run( void* pThis
)
3756 #if OSL_DEBUG_LEVEL > 1
3757 SAL_INFO("vcl.unx.dtrans", "SelectionManager::run.");
3759 osl::Thread::setName("SelectionManager");
3760 // dispatch until the cows come home
3762 SelectionManager
* This
= static_cast<SelectionManager
*>(pThis
);
3765 gettimeofday( &aLast
, nullptr );
3767 css::uno::Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
3768 This
->m_xDesktop
.set( Desktop::create(xContext
) );
3769 This
->m_xDesktop
->addTerminateListener(This
);
3771 // if end thread pipe properly initialized, allow infinite wait in poll
3772 // otherwise, fallback on 1 sec timeout
3773 const int timeout
= (This
->m_EndThreadPipe
[0] != This
->m_EndThreadPipe
[1]) ? -1 : 1000;
3775 while( osl_scheduleThread(This
->m_aThread
) )
3777 This
->dispatchEvent( timeout
);
3780 gettimeofday( &aNow
, nullptr );
3782 if( (aNow
.tv_sec
- aLast
.tv_sec
) > 0 )
3784 osl::ClearableMutexGuard
aGuard(This
->m_aMutex
);
3785 std::vector
< std::pair
< SelectionAdaptor
*, css::uno::Reference
< XInterface
> > > aChangeVector
;
3787 for (auto const& selection
: This
->m_aSelections
)
3789 if( selection
.first
!= This
->m_nXdndSelection
&& ! selection
.second
->m_bOwner
)
3791 ::Window aOwner
= XGetSelectionOwner( This
->m_pDisplay
, selection
.first
);
3792 if( aOwner
!= selection
.second
->m_aLastOwner
)
3794 selection
.second
->m_aLastOwner
= aOwner
;
3795 std::pair
< SelectionAdaptor
*, css::uno::Reference
< XInterface
> >
3796 aKeep( selection
.second
->m_pAdaptor
, selection
.second
->m_pAdaptor
->getReference() );
3797 aChangeVector
.push_back( aKeep
);
3802 for (auto const& change
: aChangeVector
)
3804 change
.first
->fireContentsChanged();
3809 // close write end on exit so write() fails and other thread does not block
3811 close(This
->m_EndThreadPipe
[1]);
3812 close(This
->m_EndThreadPipe
[0]);
3813 #if OSL_DEBUG_LEVEL > 1
3814 SAL_INFO("vcl.unx.dtrans", "SelectionManager::run end.");
3818 void SelectionManager::shutdown() throw()
3820 #if OSL_DEBUG_LEVEL > 1
3821 SAL_INFO("vcl.unx.dtrans", "SelectionManager got app termination event.");
3824 osl::ResettableMutexGuard
aGuard(m_aMutex
);
3830 if ( m_xDesktop
.is() )
3831 m_xDesktop
->removeTerminateListener(this);
3833 if( m_xDisplayConnection
.is() )
3834 m_xDisplayConnection
->removeEventHandler(Any(), this);
3839 osl_terminateThread( m_aThread
);
3841 * Allow thread to finish before app exits to avoid pulling the carpet
3842 * out from under it if pasting is occurring during shutdown
3844 * a) allow it to have the Mutex and
3845 * b) reschedule to allow it to complete callbacks to any
3846 * Application::GetSolarMutex protected regions, etc. e.g.
3847 * TransferableHelper::getTransferDataFlavors (via
3848 * SelectionManager::handleSelectionRequest) which it might
3849 * currently be trying to enter.
3851 * Otherwise the thread may be left still waiting on a GlobalMutex
3852 * when that gets destroyed, letting the thread blow up and die
3853 * when enters the section in a now dead OOo instance.
3856 while (osl_isThreadRunning(m_aThread
))
3858 { // drop mutex before write - otherwise may deadlock
3859 SolarMutexGuard guard2
;
3860 Application::Reschedule();
3862 // trigger poll()'s wait end by writing a dummy value
3864 dummy
= write(m_EndThreadPipe
[1], &dummy
, 1);
3866 osl_joinWithThread( m_aThread
);
3867 osl_destroyThread( m_aThread
);
3868 m_aThread
= nullptr;
3872 m_xDisplayConnection
.clear();
3873 m_xDropTransferable
.clear();
3876 sal_Bool
SelectionManager::handleEvent(const Any
& event
)
3878 Sequence
< sal_Int8
> aSeq
;
3879 if( event
>>= aSeq
)
3881 XEvent
* pEvent
= reinterpret_cast<XEvent
*>(aSeq
.getArray());
3882 Time nTimestamp
= CurrentTime
;
3883 if( pEvent
->type
== ButtonPress
|| pEvent
->type
== ButtonRelease
)
3884 nTimestamp
= pEvent
->xbutton
.time
;
3885 else if( pEvent
->type
== KeyPress
|| pEvent
->type
== KeyRelease
)
3886 nTimestamp
= pEvent
->xkey
.time
;
3887 else if( pEvent
->type
== MotionNotify
)
3888 nTimestamp
= pEvent
->xmotion
.time
;
3889 else if( pEvent
->type
== PropertyNotify
)
3890 nTimestamp
= pEvent
->xproperty
.time
;
3892 if( nTimestamp
!= CurrentTime
)
3894 osl::MutexGuard
aGuard(m_aMutex
);
3896 m_nSelectionTimestamp
= nTimestamp
;
3899 return handleXEvent( *pEvent
);
3903 #if OSL_DEBUG_LEVEL > 1
3904 SAL_INFO("vcl.unx.dtrans", "SelectionManager got downing event.");
3911 void SAL_CALL
SelectionManager::disposing( const css::lang::EventObject
& rEvt
)
3913 if (rEvt
.Source
== m_xDesktop
|| rEvt
.Source
== m_xDisplayConnection
)
3917 void SAL_CALL
SelectionManager::queryTermination( const css::lang::EventObject
& )
3922 * To be safe, shutdown needs to be called before the ~SfxApplication is called, waiting until
3923 * the downing event can be too late if paste are requested during shutdown and ~SfxApplication
3924 * has been called before vcl is shutdown
3926 void SAL_CALL
SelectionManager::notifyTermination( const css::lang::EventObject
& rEvent
)
3931 void SelectionManager::registerHandler( Atom selection
, SelectionAdaptor
& rAdaptor
)
3933 osl::MutexGuard
aGuard(m_aMutex
);
3935 Selection
* pNewSelection
= new Selection();
3936 pNewSelection
->m_pAdaptor
= &rAdaptor
;
3937 m_aSelections
[ selection
] = pNewSelection
;
3940 void SelectionManager::deregisterHandler( Atom selection
)
3942 osl::MutexGuard
aGuard(m_aMutex
);
3944 std::unordered_map
< Atom
, Selection
* >::iterator it
=
3945 m_aSelections
.find( selection
);
3946 if( it
!= m_aSelections
.end() )
3948 delete it
->second
->m_pPixmap
;
3950 m_aSelections
.erase( it
);
3954 static bool bWasError
= false;
3958 static int local_xerror_handler(Display
* , XErrorEvent
*)
3963 typedef int(*xerror_hdl_t
)(Display
*,XErrorEvent
*);
3966 void SelectionManager::registerDropTarget( ::Window aWindow
, DropTarget
* pTarget
)
3968 osl::MutexGuard
aGuard(m_aMutex
);
3971 std::unordered_map
< ::Window
, DropTargetEntry
>::const_iterator it
=
3972 m_aDropTargets
.find( aWindow
);
3973 if( it
!= m_aDropTargets
.end() )
3974 OSL_FAIL( "attempt to register window as drop target twice" );
3975 else if( aWindow
&& m_pDisplay
)
3977 DropTargetEntry
aEntry( pTarget
);
3979 /* #i100000# ugly workaround: gtk sets its own XErrorHandler which is not suitable for us
3980 unfortunately XErrorHandler is not per display, so this is just and ugly hack
3981 Need to remove separate display and integrate clipboard/dnd into vcl's unx code ASAP
3983 xerror_hdl_t pOldHandler
= XSetErrorHandler( local_xerror_handler
);
3984 XSelectInput( m_pDisplay
, aWindow
, PropertyChangeMask
);
3988 XChangeProperty( m_pDisplay
, aWindow
, m_nXdndAware
, XA_ATOM
, 32, PropModeReplace
, reinterpret_cast<unsigned char const *>(&nXdndProtocolRevision
), 1 );
3991 // get root window of window (in 99.999% of all cases this will be
3992 // DefaultRootWindow( m_pDisplay )
3994 unsigned int w
, h
, bw
, d
;
3995 XGetGeometry( m_pDisplay
, aWindow
, &aEntry
.m_aRootWindow
,
3996 &x
, &y
, &w
, &h
, &bw
, &d
);
3999 XSetErrorHandler( pOldHandler
);
4002 m_aDropTargets
[ aWindow
] = aEntry
;
4005 OSL_FAIL( "attempt to register None as drop target" );
4008 void SelectionManager::deregisterDropTarget( ::Window aWindow
)
4010 osl::ClearableMutexGuard
aGuard(m_aMutex
);
4012 m_aDropTargets
.erase( aWindow
);
4013 if( aWindow
!= m_aDragSourceWindow
|| !m_aDragRunning
.check() )
4017 std::unordered_map
< ::Window
, DropTargetEntry
>::const_iterator it
=
4018 m_aDropTargets
.find( m_aDropWindow
);
4019 if( it
!= m_aDropTargets
.end() )
4021 DropTargetEvent dte
;
4022 dte
.Source
= static_cast< OWeakObject
* >( it
->second
.m_pTarget
);
4024 it
->second
.m_pTarget
->dragExit( dte
);
4026 else if( m_aDropProxy
!= None
&& m_nCurrentProtocolVersion
>= 0 )
4030 aEvent
.type
= ClientMessage
;
4031 aEvent
.xclient
.display
= m_pDisplay
;
4032 aEvent
.xclient
.format
= 32;
4033 aEvent
.xclient
.message_type
= m_nXdndLeave
;
4034 aEvent
.xclient
.window
= m_aDropWindow
;
4035 aEvent
.xclient
.data
.l
[0] = m_aWindow
;
4036 memset( aEvent
.xclient
.data
.l
+1, 0, sizeof(long)*4);
4037 m_aDropWindow
= m_aDropProxy
= None
;
4038 XSendEvent( m_pDisplay
, m_aDropProxy
, False
, NoEventMask
, &aEvent
);
4040 // notify the listener
4041 DragSourceDropEvent dsde
;
4042 dsde
.Source
= static_cast< OWeakObject
* >(this);
4043 dsde
.DragSourceContext
= new DragSourceContext( m_aDropWindow
, *this );
4044 dsde
.DragSource
= static_cast< XDragSource
* >(this);
4045 dsde
.DropAction
= DNDConstants::ACTION_NONE
;
4046 dsde
.DropSuccess
= false;
4047 css::uno::Reference
< XDragSourceListener
> xListener( m_xDragSourceListener
);
4048 m_xDragSourceListener
.clear();
4050 xListener
->dragDropEnd( dsde
);
4058 css::uno::Reference
< XTransferable
> SelectionManager::getTransferable() throw()
4060 return m_xDragSourceTransferable
;
4063 void SelectionManager::clearTransferable() throw()
4065 m_xDragSourceTransferable
.clear();
4068 void SelectionManager::fireContentsChanged() throw()
4072 css::uno::Reference
< XInterface
> SelectionManager::getReference() throw()
4074 return css::uno::Reference
< XInterface
>( static_cast<OWeakObject
*>(this) );
4078 * SelectionManagerHolder
4081 SelectionManagerHolder::SelectionManagerHolder() :
4082 ::cppu::WeakComponentImplHelper
<
4085 XServiceInfo
> (m_aMutex
)
4089 SelectionManagerHolder::~SelectionManagerHolder()
4093 void SelectionManagerHolder::initialize( const Sequence
< Any
>& arguments
)
4095 OUString aDisplayName
;
4097 if( arguments
.hasElements() )
4099 css::uno::Reference
< XDisplayConnection
> xConn
;
4100 arguments
.getConstArray()[0] >>= xConn
;
4104 aIdentifier
>>= aDisplayName
;
4108 SelectionManager
& rManager
= SelectionManager::get( aDisplayName
);
4109 rManager
.initialize( arguments
);
4110 m_xRealDragSource
= static_cast< XDragSource
* >(&rManager
);
4117 sal_Bool
SelectionManagerHolder::isDragImageSupported()
4119 return m_xRealDragSource
.is() && m_xRealDragSource
->isDragImageSupported();
4122 sal_Int32
SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction
)
4124 return m_xRealDragSource
.is() ? m_xRealDragSource
->getDefaultCursor( dragAction
) : 0;
4127 void SelectionManagerHolder::startDrag(
4128 const css::datatransfer::dnd::DragGestureEvent
& trigger
,
4129 sal_Int8 sourceActions
, sal_Int32 cursor
, sal_Int32 image
,
4130 const css::uno::Reference
< css::datatransfer::XTransferable
>& transferable
,
4131 const css::uno::Reference
< css::datatransfer::dnd::XDragSourceListener
>& listener
4134 if( m_xRealDragSource
.is() )
4135 m_xRealDragSource
->startDrag( trigger
, sourceActions
, cursor
, image
, transferable
, listener
);
4142 OUString
SelectionManagerHolder::getImplementationName()
4144 return "com.sun.star.datatransfer.dnd.XdndSupport";
4147 sal_Bool
SelectionManagerHolder::supportsService( const OUString
& ServiceName
)
4149 return cppu::supportsService(this, ServiceName
);
4152 Sequence
< OUString
> SelectionManagerHolder::getSupportedServiceNames()
4154 return Xdnd_getSupportedServiceNames();
4157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */