update dev300-m58
[ooovba.git] / dtrans / source / X11 / X11_selection.cxx
blob708a87e669664a20d8ec7b8e2faa1dc6b98f1dfd
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: X11_selection.cxx,v $
10 * $Revision: 1.85 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_dtrans.hxx"
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <sys/time.h>
38 #include <X11/Xatom.h>
39 #include <X11/keysym.h>
40 #include <X11/Xlib.h>
41 #include <X11/X.h>
42 #include <X11/Xutil.h>
43 #if defined(LINUX) || defined(NETBSD) || defined (FREEBSD)
44 #include <sys/poll.h>
45 #else
46 #include <poll.h>
47 #endif
48 #include <sal/alloca.h>
50 #include <X11_selection.hxx>
51 #include <X11_clipboard.hxx>
52 #include <X11_transferable.hxx>
53 #include <X11_dndcontext.hxx>
54 #include <bmp.hxx>
56 // pointer bitmaps
57 #include <copydata_curs.h>
58 #include <copydata_mask.h>
59 #include <movedata_curs.h>
60 #include <movedata_mask.h>
61 #include <linkdata_curs.h>
62 #include <linkdata_mask.h>
63 #include <nodrop_curs.h>
64 #include <nodrop_mask.h>
65 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
66 #include <com/sun/star/awt/MouseEvent.hpp>
67 #include <com/sun/star/awt/MouseButton.hpp>
68 #include <rtl/tencinfo.h>
69 #ifndef OSL_PROCESS_H
70 #include <osl/process.h>
71 #endif
73 #define DRAG_EVENT_MASK ButtonPressMask |\
74 ButtonReleaseMask |\
75 PointerMotionMask |\
76 EnterWindowMask |\
77 LeaveWindowMask
79 using namespace com::sun::star::datatransfer;
80 using namespace com::sun::star::datatransfer::dnd;
81 using namespace com::sun::star::lang;
82 using namespace com::sun::star::awt;
83 using namespace com::sun::star::uno;
84 using namespace cppu;
85 using namespace osl;
86 using namespace rtl;
88 using namespace x11;
90 // stubs to satisfy solaris compiler's rather rigid linking warning
91 extern "C"
93 static void call_SelectionManager_run( void * pMgr )
95 SelectionManager::run( pMgr );
98 static void call_SelectionManager_runDragExecute( void * pMgr )
100 SelectionManager::runDragExecute( pMgr );
105 static const long nXdndProtocolRevision = 5;
107 // mapping between mime types (or what the office thinks of mime types)
108 // and X convention types
109 struct NativeTypeEntry
111 Atom nAtom;
112 const char* pType; // Mime encoding on our side
113 const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized
114 int nFormat; // the corresponding format
117 // the convention for Xdnd is mime types as specified by the corresponding
118 // RFC's with the addition that text/plain without charset tag contains iso8859-1
119 // sadly some applications (e.g. gtk) do not honor the mimetype only rule,
120 // so for compatibility add UTF8_STRING
121 static NativeTypeEntry aXdndConversionTab[] =
123 { 0, "text/plain;charset=iso8859-1", "text/plain", 8 },
124 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }
127 // for clipboard and primary selections there is only a convention for text
128 // that the encoding name of the text is taken as type in all capitalized letters
129 static NativeTypeEntry aNativeConversionTab[] =
131 { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 },
132 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 },
133 { 0, "text/plain;charset=utf-8", "UTF-8", 8 },
134 { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 },
135 // ISO encodings
136 { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 },
137 { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 },
138 { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 },
139 { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 },
140 { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 },
141 { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 },
142 { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 },
143 { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 },
144 { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 },
145 { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 },
146 { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 },
147 { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 },
148 // asian encodings
149 { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 },
150 { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 },
151 { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 },
152 { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 },
153 { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 },
154 { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 },
155 // eastern european encodings
156 { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 },
157 { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 },
158 // String (== iso8859-1)
159 { XA_STRING, "text/plain;charset=iso8859-1", "STRING", 8 },
160 // special for compound text
161 { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 },
163 // PIXMAP
164 { XA_PIXMAP, "image/bmp", "PIXMAP", 32 }
167 rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType )
169 rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
170 OUString aMimeType( rMimeType.toAsciiLowerCase() );
171 sal_Int32 nIndex = 0;
172 if( aMimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain" , 10 ) )
174 if( aMimeType.getLength() == 10 ) // only "text/plain"
175 aEncoding = RTL_TEXTENCODING_ISO_8859_1;
176 else
178 while( nIndex != -1 )
180 OUString aToken = aMimeType.getToken( 0, ';', nIndex );
181 sal_Int32 nPos = 0;
182 if( aToken.getToken( 0, '=', nPos ).equalsAsciiL( "charset", 7 ) )
184 OString aEncToken = OUStringToOString( aToken.getToken( 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 );
185 aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() );
186 if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
188 if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) )
189 aEncoding = RTL_TEXTENCODING_UTF8;
191 if( aEncoding != RTL_TEXTENCODING_DONTKNOW )
192 break;
197 #if OSL_DEBUG_LEVEL > 1
198 if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
199 fprintf( stderr, "getTextPlainEncoding( %s ) failed\n", OUStringToOString( rMimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
200 #endif
201 return aEncoding;
204 // ------------------------------------------------------------------------
206 ::std::hash_map< OUString, SelectionManager*, OUStringHash >& SelectionManager::getInstances()
208 static ::std::hash_map< OUString, SelectionManager*, OUStringHash > aInstances;
209 return aInstances;
212 // ------------------------------------------------------------------------
214 SelectionManager::SelectionManager() :
215 m_nIncrementalThreshold( 15*1024 ),
216 m_pDisplay( NULL ),
217 m_aThread( NULL ),
218 m_aDragExecuteThread( NULL ),
219 m_aWindow( None ),
220 m_nSelectionTimeout( 0 ),
221 m_nSelectionTimestamp( CurrentTime ),
222 m_aCurrentDropWindow( None ),
223 m_bDropWaitingForCompletion( false ),
224 m_aDropWindow( None ),
225 m_aDropProxy( None ),
226 m_aDragSourceWindow( None ),
227 m_nNoPosX( 0 ),
228 m_nNoPosY( 0 ),
229 m_nNoPosWidth( 0 ),
230 m_nNoPosHeight( 0 ),
231 m_bLastDropAccepted( false ),
232 m_bDropSuccess( false ),
233 m_bDropSent( false ),
234 m_bWaitingForPrimaryConversion( false ),
235 m_aMoveCursor( None ),
236 m_aCopyCursor( None ),
237 m_aLinkCursor( None ),
238 m_aNoneCursor( None ),
239 m_aCurrentCursor( None ),
240 m_nCurrentProtocolVersion( nXdndProtocolRevision )
242 m_aDropEnterEvent.data.l[0] = None;
243 m_bDropEnterSent = true;
244 m_aDragRunning.reset();
247 Cursor SelectionManager::createCursor( const char* pPointerData, const char* pMaskData, int width, int height, int hotX, int hotY )
249 Pixmap aPointer;
250 Pixmap aMask;
251 XColor aBlack, aWhite;
253 aBlack.pixel = BlackPixel( m_pDisplay, 0 );
254 aBlack.red = aBlack.green = aBlack.blue = 0;
255 aBlack.flags = DoRed | DoGreen | DoBlue;
257 aWhite.pixel = WhitePixel( m_pDisplay, 0 );
258 aWhite.red = aWhite.green = aWhite.blue = 0xffff;
259 aWhite.flags = DoRed | DoGreen | DoBlue;
261 aPointer =
262 XCreateBitmapFromData( m_pDisplay,
263 m_aWindow,
264 pPointerData,
265 width,
266 height );
267 aMask
268 = XCreateBitmapFromData( m_pDisplay,
269 m_aWindow,
270 pMaskData,
271 width,
272 height );
273 Cursor aCursor =
274 XCreatePixmapCursor( m_pDisplay, aPointer, aMask,
275 &aBlack, &aWhite,
276 hotX,
277 hotY );
278 XFreePixmap( m_pDisplay, aPointer );
279 XFreePixmap( m_pDisplay, aMask );
281 return aCursor;
284 void SelectionManager::initialize( const Sequence< Any >& arguments ) throw (::com::sun::star::uno::Exception)
286 MutexGuard aGuard(m_aMutex);
288 if( ! m_xDisplayConnection.is() )
291 * first argument must be a ::com::sun::star::awt::XDisplayConnection
292 * from this we will get the XEvents of the vcl event loop by
293 * registering us as XEventHandler on it.
295 * implementor's note:
296 * <RANT>
297 * Because XDND implementations send their ClientMessage events with
298 * XSendEvent and event mask 0 (which is perfectly sensible)
299 * these events get only sent to the connection that created
300 * the window in question: the vcl display.
302 * So this whole service should be implemented in vcl where
303 * it belongs. But as usual the dogmatics win and the dogma says:
304 * "vcl bad, service good". So instead of making vcl a service
305 * providing library we make a new one that is "independent" of vcl,
306 * because it is not linked against it and dispatch every event of
307 * the vcl event loop to here via uno mechanisms (that is copying it,
308 * locking and unlocking mutexes). Well, the user has fast computers
309 * these days, so why do we need performance where more memory and
310 * a faster CPU will do the job ?
311 * And that is why this runs under the title:
312 * How NOT to implement a service
313 * or the victory of dogma over common sense.
314 * </RANT>
316 if( arguments.getLength() > 0 )
317 arguments.getConstArray()[0] >>= m_xDisplayConnection;
318 if( ! m_xDisplayConnection.is() )
320 #if 0
321 // for the time being try to live without XDisplayConnection
322 // for the sake of clipboard service
323 // clipboard service should be initialized with a XDisplayConnection
324 // in the future
325 Exception aExc;
326 aExc.Message = OUString::createFromAscii( "initialize me with a valid XDisplayConnection" );
327 aExc.Context = static_cast< OWeakObject* >(this);
328 throw aExc;
329 #endif
331 else
332 m_xDisplayConnection->addEventHandler( Any(), this, ~0 );
335 if( !m_xBitmapConverter.is() )
337 if( arguments.getLength() > 2 )
338 arguments.getConstArray()[2] >>= m_xBitmapConverter;
341 int nParams = osl_getCommandArgCount();
342 OUString aParam;
343 bool bIsHeadless = false;
344 for( int i = 0; i < nParams; i++ )
346 osl_getCommandArg( i, &aParam.pData );
347 if( aParam.equalsAscii( "-headless" ) )
349 bIsHeadless = true;
350 break;
354 if( ! m_pDisplay && ! bIsHeadless )
356 OUString aUDisplay;
357 if( m_xDisplayConnection.is() )
359 Any aIdentifier;
360 aIdentifier = m_xDisplayConnection->getIdentifier();
361 aIdentifier >>= aUDisplay;
364 OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) );
366 m_pDisplay = XOpenDisplay( aDisplayName.getLength() ? aDisplayName.getStr() : NULL );
368 if( m_pDisplay )
370 #ifdef SYNCHRONIZE
371 XSynchronize( m_pDisplay, True );
372 #endif
373 // clipboard selection
374 m_nCLIPBOARDAtom = getAtom( OUString::createFromAscii( "CLIPBOARD" ) );
376 // special targets
377 m_nTARGETSAtom = getAtom( OUString::createFromAscii( "TARGETS" ) );
378 m_nTIMESTAMPAtom = getAtom( OUString::createFromAscii( "TIMESTAMP" ) );
379 m_nTEXTAtom = getAtom( OUString::createFromAscii( "TEXT" ) );
380 m_nINCRAtom = getAtom( OUString::createFromAscii( "INCR" ) );
381 m_nCOMPOUNDAtom = getAtom( OUString::createFromAscii( "COMPOUND_TEXT" ) );
382 m_nMULTIPLEAtom = getAtom( OUString::createFromAscii( "MULTIPLE" ) );
383 m_nUTF16Atom = getAtom( OUString::createFromAscii( "ISO10646-1" ) );
384 // m_nUTF16Atom = getAtom( OUString::createFromAscii( "text/plain;charset=ISO-10646-UCS-2" ) );
385 m_nImageBmpAtom = getAtom( OUString::createFromAscii( "image/bmp" ) );
387 // Atoms for Xdnd protocol
388 m_nXdndAware = getAtom( OUString::createFromAscii( "XdndAware" ) );
389 m_nXdndEnter = getAtom( OUString::createFromAscii( "XdndEnter" ) );
390 m_nXdndLeave = getAtom( OUString::createFromAscii( "XdndLeave" ) );
391 m_nXdndPosition = getAtom( OUString::createFromAscii( "XdndPosition" ) );
392 m_nXdndStatus = getAtom( OUString::createFromAscii( "XdndStatus" ) );
393 m_nXdndDrop = getAtom( OUString::createFromAscii( "XdndDrop" ) );
394 m_nXdndFinished = getAtom( OUString::createFromAscii( "XdndFinished" ) );
395 m_nXdndSelection = getAtom( OUString::createFromAscii( "XdndSelection" ) );
396 m_nXdndTypeList = getAtom( OUString::createFromAscii( "XdndTypeList" ) );
397 m_nXdndProxy = getAtom( OUString::createFromAscii( "XdndProxy" ) );
398 m_nXdndActionCopy = getAtom( OUString::createFromAscii( "XdndActionCopy" ) );
399 m_nXdndActionMove = getAtom( OUString::createFromAscii( "XdndActionMove" ) );
400 m_nXdndActionLink = getAtom( OUString::createFromAscii( "XdndActionLink" ) );
401 m_nXdndActionAsk = getAtom( OUString::createFromAscii( "XdndActionAsk" ) );
402 m_nXdndActionPrivate= getAtom( OUString::createFromAscii( "XdndActionPrivate" ) );
404 // initialize map with member none
405 m_aAtomToString[ 0 ]= OUString::createFromAscii( "None" );
406 m_aAtomToString[ XA_PRIMARY ] = OUString::createFromAscii( "PRIMARY" );
408 // create a (invisible) message window
409 m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ),
410 10, 10, 10, 10, 0, 0, 1 );
412 // initialize threshold for incremetal transfers
413 // ICCCM says it should be smaller that the max request size
414 // which in turn is guaranteed to be at least 16k bytes
415 m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024;
417 if( m_aWindow )
419 // initialize default cursors
420 m_aMoveCursor = createCursor( movedata_curs_bits,
421 movedata_mask_bits,
422 movedata_curs_width,
423 movedata_curs_height,
424 movedata_curs_x_hot,
425 movedata_curs_y_hot );
426 m_aCopyCursor = createCursor( copydata_curs_bits,
427 copydata_mask_bits,
428 copydata_curs_width,
429 copydata_curs_height,
430 copydata_curs_x_hot,
431 copydata_curs_y_hot );
432 m_aLinkCursor = createCursor( linkdata_curs_bits,
433 linkdata_mask_bits,
434 linkdata_curs_width,
435 linkdata_curs_height,
436 linkdata_curs_x_hot,
437 linkdata_curs_y_hot );
438 m_aNoneCursor = createCursor( nodrop_curs_bits,
439 nodrop_mask_bits,
440 nodrop_curs_width,
441 nodrop_curs_height,
442 nodrop_curs_x_hot,
443 nodrop_curs_y_hot );
448 // just interested in SelectionClear/Notify/Request and PropertyChange
449 XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask );
450 // create the transferable for Drag operations
451 m_xDropTransferable = new X11Transferable( *this, static_cast< OWeakObject* >(this), m_nXdndSelection );
452 registerHandler( m_nXdndSelection, *this );
454 m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this );
455 if( m_aThread )
456 osl_resumeThread( m_aThread );
457 #if OSL_DEBUG_LEVEL > 1
458 else
459 fprintf( stderr, "SelectionManager::initialize: creation of dispatch thread failed !\n" );
460 #endif
466 // ------------------------------------------------------------------------
468 SelectionManager::~SelectionManager()
470 #if OSL_DEBUG_LEVEL > 1
471 fprintf( stderr, "SelectionManager::~SelectionManager (%s)\n", m_pDisplay ? DisplayString(m_pDisplay) : "no display" );
472 #endif
474 MutexGuard aGuard( *Mutex::getGlobalMutex() );
476 ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it;
477 for( it = getInstances().begin(); it != getInstances().end(); ++it )
478 if( it->second == this )
480 getInstances().erase( it );
481 break;
485 if( m_aThread )
487 osl_terminateThread( m_aThread );
488 osl_joinWithThread( m_aThread );
489 osl_destroyThread( m_aThread );
492 if( m_aDragExecuteThread )
494 osl_terminateThread( m_aDragExecuteThread );
495 osl_joinWithThread( m_aDragExecuteThread );
496 m_aDragExecuteThread = NULL;
497 // thread handle is freed in dragDoDispatch()
500 MutexGuard aGuard(m_aMutex);
502 #if OSL_DEBUG_LEVEL > 1
503 fprintf( stderr, "shutting down SelectionManager\n" );
504 #endif
506 if( m_xDisplayConnection.is() )
508 m_xDisplayConnection->removeEventHandler( Any(), this );
509 m_xDisplayConnection.clear();
512 if( m_pDisplay )
514 deregisterHandler( m_nXdndSelection );
515 // destroy message window
516 if( m_aWindow )
517 XDestroyWindow( m_pDisplay, m_aWindow );
518 // release cursors
519 if (m_aMoveCursor != None)
520 XFreeCursor(m_pDisplay, m_aMoveCursor);
521 if (m_aCopyCursor != None)
522 XFreeCursor(m_pDisplay, m_aCopyCursor);
523 if (m_aLinkCursor != None)
524 XFreeCursor(m_pDisplay, m_aLinkCursor);
525 if (m_aNoneCursor != None)
526 XFreeCursor(m_pDisplay, m_aNoneCursor);
528 // paranoia setting, the drag thread should have
529 // done that already
530 XUngrabPointer( m_pDisplay, CurrentTime );
531 XUngrabKeyboard( m_pDisplay, CurrentTime );
533 XCloseDisplay( m_pDisplay );
537 // ------------------------------------------------------------------------
539 SelectionAdaptor* SelectionManager::getAdaptor( Atom selection )
541 ::std::hash_map< Atom, Selection* >::iterator it =
542 m_aSelections.find( selection );
543 return it != m_aSelections.end() ? it->second->m_pAdaptor : NULL;
546 // ------------------------------------------------------------------------
548 OUString SelectionManager::convertFromCompound( const char* pText, int nLen )
550 MutexGuard aGuard( m_aMutex );
551 OUString aRet;
552 if( nLen < 0 )
553 nLen = strlen( pText );
555 char** pTextList = NULL;
556 int nTexts = 0;
558 XTextProperty aProp;
559 aProp.value = (unsigned char*)pText;
560 aProp.encoding = m_nCOMPOUNDAtom;
561 aProp.format = 8;
562 aProp.nitems = nLen;
563 XmbTextPropertyToTextList( m_pDisplay,
564 &aProp,
565 &pTextList,
566 &nTexts );
567 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
568 for( int i = 0; i < nTexts; i++ )
569 aRet += OStringToOUString( pTextList[i], aEncoding );
571 if( pTextList )
572 XFreeStringList( pTextList );
574 return aRet;
577 // ------------------------------------------------------------------------
579 OString SelectionManager::convertToCompound( const OUString& rText )
581 MutexGuard aGuard( m_aMutex );
582 XTextProperty aProp;
583 aProp.value = NULL;
584 aProp.encoding = XA_STRING;
585 aProp.format = 8;
586 aProp.nitems = 0;
588 OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() );
589 char* pT = const_cast<char*>(aRet.getStr());
591 XmbTextListToTextProperty( m_pDisplay,
592 &pT,
594 XCompoundTextStyle,
595 &aProp );
596 if( aProp.value )
598 aRet = (char*)aProp.value;
599 XFree( aProp.value );
600 #ifdef SOLARIS
601 /* #97070#
602 * for currently unknown reasons XmbTextListToTextProperty on Solaris returns
603 * no data in ISO8859-n encodings (at least for n = 1, 15)
604 * in these encodings the directly converted text does the
605 * trick, also.
607 if( ! aRet.getLength() && rText.getLength() )
608 aRet = OUStringToOString( rText, osl_getThreadTextEncoding() );
609 #endif
611 else
612 aRet = OString();
614 return aRet;
617 // ------------------------------------------------------------------------
619 bool SelectionManager::convertData(
620 const Reference< XTransferable >& xTransferable,
621 Atom nType,
622 Atom nSelection,
623 int& rFormat,
624 Sequence< sal_Int8 >& rData )
626 bool bSuccess = false;
628 if( ! xTransferable.is() )
629 return bSuccess;
634 DataFlavor aFlavor;
635 aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat );
637 sal_Int32 nIndex = 0;
638 if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "text/plain" ) == 0 )
640 if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "charset=utf-16" ) == 0 )
641 aFlavor.DataType = getCppuType( (OUString *) 0 );
642 else
643 aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
645 else
646 aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
648 if( xTransferable->isDataFlavorSupported( aFlavor ) )
650 Any aValue( xTransferable->getTransferData( aFlavor ) );
651 if( aValue.getValueTypeClass() == TypeClass_STRING )
653 OUString aString;
654 aValue >>= aString;
655 rData = Sequence< sal_Int8 >( (sal_Int8*)aString.getStr(), aString.getLength() * sizeof( sal_Unicode ) );
656 bSuccess = true;
658 else if( aValue.getValueType() == getCppuType( (Sequence< sal_Int8 >*)0 ) )
660 aValue >>= rData;
661 bSuccess = true;
664 else if( aFlavor.MimeType.compareToAscii( "text/plain", 10 ) == 0 )
666 rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
667 bool bCompoundText = false;
668 if( nType == m_nCOMPOUNDAtom )
669 bCompoundText = true;
670 else
671 aEncoding = getTextPlainEncoding( aFlavor.MimeType );
672 if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText )
674 aFlavor.MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" );
675 aFlavor.DataType = getCppuType( (OUString *) 0 );
676 if( xTransferable->isDataFlavorSupported( aFlavor ) )
678 Any aValue( xTransferable->getTransferData( aFlavor ) );
679 OUString aString;
680 aValue >>= aString;
681 OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) );
682 rData = Sequence< sal_Int8 >( (sal_Int8*)aByteString.getStr(), aByteString.getLength() * sizeof( sal_Char ) );
683 bSuccess = true;
688 // various exceptions possible ... which all lead to a failed conversion
689 // so simplify here to a catch all
690 catch(...)
694 return bSuccess;
697 // ------------------------------------------------------------------------
699 SelectionManager& SelectionManager::get( const OUString& rDisplayName )
701 MutexGuard aGuard( *Mutex::getGlobalMutex() );
703 OUString aDisplayName( rDisplayName );
704 if( ! aDisplayName.getLength() )
705 aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 );
706 SelectionManager* pInstance = NULL;
708 ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it = getInstances().find( aDisplayName );
709 if( it != getInstances().end() )
710 pInstance = it->second;
711 else pInstance = getInstances()[ aDisplayName ] = new SelectionManager();
713 return *pInstance;
716 // ------------------------------------------------------------------------
718 const OUString& SelectionManager::getString( Atom aAtom )
720 MutexGuard aGuard(m_aMutex);
722 ::std::hash_map< Atom, OUString >::const_iterator it;
723 if( ( it = m_aAtomToString.find( aAtom ) ) == m_aAtomToString.end() )
725 static OUString aEmpty;
726 char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : NULL;
727 if( ! pAtom )
728 return aEmpty;
729 OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) );
730 XFree( pAtom );
731 m_aStringToAtom[ aString ] = aAtom;
732 m_aAtomToString[ aAtom ] = aString;
734 return m_aAtomToString[ aAtom ];
737 // ------------------------------------------------------------------------
739 Atom SelectionManager::getAtom( const OUString& rString )
741 MutexGuard aGuard(m_aMutex);
743 ::std::hash_map< OUString, Atom, OUStringHash >::const_iterator it;
744 if( ( it = m_aStringToAtom.find( rString ) ) == m_aStringToAtom.end() )
746 static Atom nNoDisplayAtoms = 1;
747 Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ), False ) : nNoDisplayAtoms++;
748 m_aStringToAtom[ rString ] = aAtom;
749 m_aAtomToString[ aAtom ] = rString;
751 return m_aStringToAtom[ rString ];
754 // ------------------------------------------------------------------------
756 bool SelectionManager::requestOwnership( Atom selection )
758 bool bSuccess = false;
759 if( m_pDisplay && m_aWindow )
761 MutexGuard aGuard(m_aMutex);
763 SelectionAdaptor* pAdaptor = getAdaptor( selection );
764 if( pAdaptor )
766 XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime );
767 if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow )
768 bSuccess = true;
769 #if OSL_DEBUG_LEVEL > 1
770 fprintf( stderr, "%s ownership for selection %s\n",
771 bSuccess ? "acquired" : "failed to acquire",
772 OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
773 #endif
774 Selection* pSel = m_aSelections[ selection ];
775 pSel->m_bOwner = bSuccess;
776 delete pSel->m_pPixmap;
777 pSel->m_pPixmap = NULL;
778 pSel->m_nOrigTimestamp = m_nSelectionTimestamp;
780 #if OSL_DEBUG_LEVEL > 1
781 else
782 fprintf( stderr, "no adaptor for selection %s\n",
783 OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
785 if( pAdaptor->getTransferable().is() )
787 Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors();
788 for( int i = 0; i < aTypes.getLength(); i++ )
790 fprintf( stderr, " %s\n", OUStringToOString( aTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
793 #endif
795 return bSuccess;
798 // ------------------------------------------------------------------------
800 void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront )
802 NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
803 int nTabEntries = selection == m_nXdndSelection
804 ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) :
805 sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]);
807 OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) );
808 rFormat = 0;
809 for( int i = 0; i < nTabEntries; i++ )
811 if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) )
813 if( ! pTab[i].nAtom )
814 pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
815 rFormat = pTab[i].nFormat;
816 if( bPushFront )
817 rConversions.push_front( pTab[i].nAtom );
818 else
819 rConversions.push_back( pTab[i].nAtom );
820 if( pTab[i].nFormat == XA_PIXMAP )
822 if( bPushFront )
824 rConversions.push_front( XA_VISUALID );
825 rConversions.push_front( XA_COLORMAP );
827 else
829 rConversions.push_back( XA_VISUALID );
830 rConversions.push_back( XA_COLORMAP );
835 if( ! rFormat )
836 rFormat = 8; // byte buffer
837 if( bPushFront )
838 rConversions.push_front( getAtom( rType ) );
839 else
840 rConversions.push_back( getAtom( rType ) );
843 // ------------------------------------------------------------------------
845 void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection )
847 rOutTypeList.clear();
849 int nFormat;
850 int nFlavors = rTypes.getLength();
851 const DataFlavor* pFlavors = rTypes.getConstArray();
852 bool bHaveText = false;
853 for( int i = 0; i < nFlavors; i++ )
855 if( pFlavors[i].MimeType.compareToAscii( "text/plain", 10 ) == 0)
856 bHaveText = true;
857 else
858 convertTypeToNative( pFlavors[i].MimeType, targetselection, nFormat, rOutTypeList );
860 if( bHaveText )
862 if( targetselection != m_nXdndSelection )
864 // only mimetypes should go into Xdnd type list
865 rOutTypeList.push_front( XA_STRING );
866 rOutTypeList.push_front( m_nCOMPOUNDAtom );
868 convertTypeToNative( OUString::createFromAscii( "text/plain;charset=utf-8" ), targetselection, nFormat, rOutTypeList, true );
870 if( targetselection != m_nXdndSelection )
871 rOutTypeList.push_back( m_nMULTIPLEAtom );
874 // ------------------------------------------------------------------------
876 OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, int& rFormat )
878 NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
879 int nTabEntries = selection == m_nXdndSelection
880 ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) :
881 sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]);
883 for( int i = 0; i < nTabEntries; i++ )
885 if( ! pTab[i].nAtom )
886 pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
887 if( nType == pTab[i].nAtom )
889 rFormat = pTab[i].nFormat;
890 return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 );
893 rFormat = 8;
894 return getString( nType );
897 // ------------------------------------------------------------------------
899 bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData )
901 ResettableMutexGuard aGuard(m_aMutex);
902 ::std::hash_map< Atom, Selection* >::iterator it;
903 bool bSuccess = false;
905 #if OSL_DEBUG_LEVEL > 1
906 OUString aSelection( getString( selection ) );
907 OUString aType( getString( type ) );
908 fprintf( stderr, "getPasteData( %s, native: %s )\n",
909 OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
910 OUStringToOString( aType, RTL_TEXTENCODING_ISO_8859_1 ).getStr()
912 #endif
914 if( ! m_pDisplay )
915 return false;
917 it = m_aSelections.find( selection );
918 if( it == m_aSelections.end() )
919 return false;
921 Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection );
922 if( aSelectionOwner == None )
923 return false;
924 if( aSelectionOwner == m_aWindow )
926 // probably bad timing led us here
927 #if OSL_DEBUG_LEVEL > 1
928 fprintf( stderr, "Innere Nabelschau\n" );
929 #endif
930 return false;
933 // ICCCM recommends to destroy property before convert request unless
934 // parameters are transported; we do only in case of MULTIPLE,
935 // so destroy property unless target is MULTIPLE
936 if( type != m_nMULTIPLEAtom )
937 XDeleteProperty( m_pDisplay, m_aWindow, selection );
939 XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime );
940 it->second->m_eState = Selection::WaitingForResponse;
941 it->second->m_aRequestedType = type;
942 it->second->m_aData = Sequence< sal_Int8 >();
943 it->second->m_aDataArrived.reset();
944 // really start the request; if we don't flush the
945 // queue the request won't leave it because there are no more
946 // X calls after this until the data arrived or timeout
947 XFlush( m_pDisplay );
949 // do a reschedule
950 struct timeval tv_last, tv_current;
951 gettimeofday( &tv_last, NULL );
952 tv_current = tv_last;
954 XEvent aEvent;
957 bool bAdjustTime = false;
959 bool bHandle = false;
961 if( XCheckTypedEvent( m_pDisplay,
962 PropertyNotify,
963 &aEvent
966 bHandle = true;
967 if( aEvent.xproperty.window == m_aWindow
968 && aEvent.xproperty.atom == selection )
969 bAdjustTime = true;
971 else
972 if( XCheckTypedEvent( m_pDisplay,
973 SelectionClear,
974 &aEvent
977 bHandle = true;
979 else
980 if( XCheckTypedEvent( m_pDisplay,
981 SelectionRequest,
982 &aEvent
984 bHandle = true;
985 else
986 if( XCheckTypedEvent( m_pDisplay,
987 SelectionNotify,
988 &aEvent
991 bHandle = true;
992 if( aEvent.xselection.selection == selection
993 && ( aEvent.xselection.requestor == m_aWindow ||
994 aEvent.xselection.requestor == m_aCurrentDropWindow )
996 bAdjustTime = true;
998 else
1000 TimeValue aTVal;
1001 aTVal.Seconds = 0;
1002 aTVal.Nanosec = 100000000;
1003 aGuard.clear();
1004 osl_waitThread( &aTVal );
1005 aGuard.reset();
1007 if( bHandle )
1009 aGuard.clear();
1010 handleXEvent( aEvent );
1011 aGuard.reset();
1014 gettimeofday( &tv_current, NULL );
1015 if( bAdjustTime )
1016 tv_last = tv_current;
1017 } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() );
1019 #if OSL_DEBUG_LEVEL > 1
1020 if( (tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout() )
1021 fprintf( stderr, "timed out\n" );
1022 #endif
1023 if( it->second->m_aDataArrived.check() &&
1024 it->second->m_aData.getLength() )
1026 rData = it->second->m_aData;
1027 bSuccess = true;
1029 #if OSL_DEBUG_LEVEL > 1
1030 else
1031 fprintf( stderr, "conversion unsuccessfull\n" );
1032 #endif
1033 return bSuccess;
1036 // ------------------------------------------------------------------------
1038 bool SelectionManager::getPasteData( Atom selection, const ::rtl::OUString& rType, Sequence< sal_Int8 >& rData )
1040 int nFormat;
1041 bool bSuccess = false;
1043 ::std::hash_map< Atom, Selection* >::iterator it;
1045 MutexGuard aGuard(m_aMutex);
1047 it = m_aSelections.find( selection );
1048 if( it == m_aSelections.end() )
1049 return false;
1052 if( it->second->m_aTypes.getLength() == 0 )
1054 Sequence< DataFlavor > aFlavors;
1055 getPasteDataTypes( selection, aFlavors );
1056 if( it->second->m_aTypes.getLength() == 0 )
1057 return false;
1060 const Sequence< DataFlavor >& rTypes( it->second->m_aTypes );
1061 const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes );
1062 #if OSL_DEBUG_LEVEL > 1
1063 fprintf( stderr, "getPasteData( \"%s\", \"%s\" )\n",
1064 OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1065 OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1066 #endif
1068 if( rType.equalsAsciiL( "text/plain;charset=utf-16", 25 ) )
1070 // lets see if we have UTF16 else try to find something convertible
1071 if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 )
1073 Sequence< sal_Int8 > aData;
1074 if( it->second->m_aUTF8Type != None &&
1075 getPasteData( selection,
1076 it->second->m_aUTF8Type,
1077 aData )
1080 OUString aRet( (const sal_Char*)aData.getConstArray(), aData.getLength(), RTL_TEXTENCODING_UTF8 );
1081 rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1082 bSuccess = true;
1084 else if( it->second->m_bHaveCompound &&
1085 getPasteData( selection,
1086 m_nCOMPOUNDAtom,
1087 aData )
1090 OUString aRet( convertFromCompound( (const char*)aData.getConstArray(), aData.getLength() ) );
1091 rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1092 bSuccess = true;
1094 else
1096 for( int i = 0; i < rTypes.getLength(); i++ )
1098 rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType );
1099 if( aEncoding != RTL_TEXTENCODING_DONTKNOW &&
1100 aEncoding != RTL_TEXTENCODING_UNICODE &&
1101 getPasteData( selection,
1102 rNativeTypes[i],
1103 aData )
1106 #if OSL_DEBUG_LEVEL > 1
1107 fprintf( stderr, "using \"%s\" instead of \"%s\"\n",
1108 OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1109 OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1111 #endif
1112 OString aConvert( (sal_Char*)aData.getConstArray(), aData.getLength() );
1113 OUString aUTF( OStringToOUString( aConvert, aEncoding ) );
1114 rData = Sequence< sal_Int8 >( (sal_Int8*)aUTF.getStr(), (aUTF.getLength()+1)*sizeof( sal_Unicode ) );
1115 bSuccess = true;
1116 break;
1122 else if( rType.equalsAsciiL( "image/bmp", 9 ) )
1124 // #i83376# try if someone has the data in image/bmp already before
1125 // doing the PIXMAP stuff (e.g. the gimp has this)
1126 bSuccess = getPasteData( selection, m_nImageBmpAtom, rData );
1127 #if OSL_DEBUG_LEVEL > 1
1128 if( bSuccess )
1129 fprintf( stderr, "got %d bytes of image/bmp\n", (int)rData.getLength() );
1130 #endif
1131 if( ! bSuccess )
1133 Pixmap aPixmap = None;
1134 Colormap aColormap = None;
1136 // prepare property for MULTIPLE request
1137 Sequence< sal_Int8 > aData;
1138 Atom pTypes[4] = { XA_PIXMAP, XA_PIXMAP,
1139 XA_COLORMAP, XA_COLORMAP };
1141 MutexGuard aGuard(m_aMutex);
1143 XChangeProperty( m_pDisplay,
1144 m_aWindow,
1145 selection,
1146 XA_ATOM,
1148 PropModeReplace,
1149 (unsigned char*)pTypes,
1150 4 );
1153 // try MULTIPLE request
1154 if( getPasteData( selection, m_nMULTIPLEAtom, aData ) )
1156 Atom* pReturnedTypes = (Atom*)aData.getArray();
1157 if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP )
1159 MutexGuard aGuard(m_aMutex);
1161 Atom type = None;
1162 int format = 0;
1163 unsigned long nItems = 0;
1164 unsigned long nBytes = 0;
1165 unsigned char* pReturn = NULL;
1166 XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems, &nBytes, &pReturn );
1167 if( pReturn )
1169 if( type == XA_PIXMAP )
1170 aPixmap = *(Pixmap*)pReturn;
1171 XFree( pReturn );
1172 pReturn = NULL;
1173 if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP )
1175 XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format, &nItems, &nBytes, &pReturn );
1176 if( pReturn )
1178 if( type == XA_COLORMAP )
1179 aColormap = *(Colormap*)pReturn;
1180 XFree( pReturn );
1184 #if OSL_DEBUG_LEVEL > 1
1185 else
1187 fprintf( stderr, "could not get PIXMAP property: type=%s, format=%d, items=%ld, bytes=%ld, ret=0x%p\n", OUStringToOString( getString( type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), format, nItems, nBytes, pReturn );
1189 #endif
1193 if( aPixmap == None )
1195 // perhaps two normal requests will work
1196 if( getPasteData( selection, XA_PIXMAP, aData ) )
1198 aPixmap = *(Pixmap*)aData.getArray();
1199 if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) )
1200 aColormap = *(Colormap*)aData.getArray();
1204 // convert data if possible
1205 if( aPixmap != None )
1207 MutexGuard aGuard(m_aMutex);
1209 sal_Int32 nOutSize = 0;
1210 sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize );
1211 if( pBytes && nOutSize )
1213 rData = Sequence< sal_Int8 >( nOutSize );
1214 memcpy( rData.getArray(), pBytes, nOutSize );
1215 X11_freeBmp( pBytes );
1216 bSuccess = true;
1222 if( ! bSuccess )
1224 ::std::list< Atom > aTypes;
1225 convertTypeToNative( rType, selection, nFormat, aTypes );
1226 ::std::list< Atom >::const_iterator type_it;
1227 Atom nSelectedType = None;
1228 for( type_it = aTypes.begin(); type_it != aTypes.end() && nSelectedType == None; ++type_it )
1230 for( unsigned int i = 0; i < rNativeTypes.size() && nSelectedType == None; i++ )
1231 if( rNativeTypes[i] == *type_it )
1232 nSelectedType = *type_it;
1234 if( nSelectedType != None )
1235 bSuccess = getPasteData( selection, nSelectedType, rData );
1237 #if OSL_DEBUG_LEVEL > 1
1238 fprintf( stderr, "getPasteData for selection %s and data type %s returns %s, returned sequence has length %ld\n",
1239 OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1240 OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1241 bSuccess ? "true" : "false",
1242 rData.getLength()
1244 #endif
1245 return bSuccess;
1248 // ------------------------------------------------------------------------
1250 bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes )
1252 ::std::hash_map< Atom, Selection* >::iterator it;
1254 MutexGuard aGuard(m_aMutex);
1256 it = m_aSelections.find( selection );
1257 if( it != m_aSelections.end() &&
1258 it->second->m_aTypes.getLength() &&
1259 abs( it->second->m_nLastTimestamp - time( NULL ) ) < 2
1262 rTypes = it->second->m_aTypes;
1263 return true;
1267 bool bSuccess = false;
1268 bool bHaveUTF16 = false;
1269 Atom aUTF8Type = None;
1270 bool bHaveCompound = false;
1271 bool bHaveText = false;
1272 Sequence< sal_Int8 > aAtoms;
1274 if( selection == m_nXdndSelection )
1276 // xdnd sends first three types with XdndEnter
1277 // if more than three types are supported then the XDndTypeList
1278 // property on the source window is used
1279 if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
1281 if( m_aDropEnterEvent.data.l[1] & 1 )
1283 const unsigned int atomcount = 256;
1284 // more than three types; look in property
1285 MutexGuard aGuard(m_aMutex);
1287 Atom nType;
1288 int nFormat;
1289 unsigned long nItems, nBytes;
1290 unsigned char* pBytes = NULL;
1292 XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1293 m_nXdndTypeList, 0, atomcount, False,
1294 XA_ATOM,
1295 &nType, &nFormat, &nItems, &nBytes, &pBytes );
1296 #if OSL_DEBUG_LEVEL > 1
1297 fprintf( stderr, "have %ld data types in XdndTypeList\n", nItems );
1298 #endif
1299 if( nItems == atomcount && nBytes > 0 )
1301 // wow ... more than 256 types !
1302 aAtoms.realloc( sizeof( Atom )*atomcount+nBytes );
1303 memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount );
1304 XFree( pBytes );
1305 pBytes = NULL;
1306 XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1307 m_nXdndTypeList, atomcount, nBytes/sizeof(Atom),
1308 False, XA_ATOM,
1309 &nType, &nFormat, &nItems, &nBytes, &pBytes );
1311 memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) );
1312 XFree( pBytes );
1315 else
1317 aAtoms.realloc( sizeof(Atom)*nItems );
1318 memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) );
1319 XFree( pBytes );
1322 else
1324 // one to three types
1325 int n = 0, i;
1326 for( i = 0; i < 3; i++ )
1327 if( m_aDropEnterEvent.data.l[2+i] )
1328 n++;
1329 #if OSL_DEBUG_LEVEL > 1
1330 fprintf( stderr, "have %d data types in XdndEnter\n", n );
1331 #endif
1332 aAtoms.realloc( sizeof(Atom)*n );
1333 for( i = 0, n = 0; i < 3; i++ )
1334 if( m_aDropEnterEvent.data.l[2+i] )
1335 ((Atom*)aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i];
1339 // get data of type TARGETS
1340 else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) )
1341 aAtoms = Sequence< sal_Int8 >();
1343 std::vector< Atom > aNativeTypes;
1344 if( aAtoms.getLength() )
1346 sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom);
1347 Atom* pAtoms = (Atom*)aAtoms.getArray();
1348 rTypes.realloc( nAtoms );
1349 aNativeTypes.resize( nAtoms );
1350 DataFlavor* pFlavors = rTypes.getArray();
1351 sal_Int32 nNativeTypesIndex = 0;
1352 while( nAtoms-- )
1354 #if OSL_DEBUG_LEVEL > 1
1355 if( *pAtoms && *pAtoms < 0x01000000 )
1356 fprintf( stderr, "native type: %s\n", OUStringToOString( getString( *pAtoms ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1357 #endif
1358 if( *pAtoms == m_nCOMPOUNDAtom )
1359 bHaveText = bHaveCompound = true;
1360 else if( *pAtoms && *pAtoms < 0x01000000 )
1362 int nFormat;
1363 pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat );
1364 pFlavors->DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
1365 sal_Int32 nIndex = 0;
1366 if( pFlavors->MimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain", 10 ) )
1368 OUString aToken(pFlavors->MimeType.getToken( 0, ';', nIndex ));
1369 // omit text/plain;charset=unicode since it is not well defined
1370 if( aToken.compareToAscii( "charset=unicode" ) == 0 )
1372 pAtoms++;
1373 continue;
1375 bHaveText = true;
1376 if( aToken.compareToAscii( "charset=utf-16" ) == 0 )
1378 bHaveUTF16 = true;
1379 pFlavors->DataType = getCppuType( (OUString*)0 );
1381 else if( aToken.compareToAscii( "charset=utf-8" ) == 0 )
1383 aUTF8Type = *pAtoms;
1386 pFlavors++;
1387 aNativeTypes[ nNativeTypesIndex ] = *pAtoms;
1388 nNativeTypesIndex++;
1390 pAtoms++;
1392 if( (pFlavors - rTypes.getArray()) < rTypes.getLength() )
1393 rTypes.realloc(pFlavors - rTypes.getArray());
1394 bSuccess = rTypes.getLength() ? true : false;
1395 if( bHaveText && ! bHaveUTF16 )
1397 int i = 0;
1399 int nNewFlavors = rTypes.getLength()+1;
1400 Sequence< DataFlavor > aTemp( nNewFlavors );
1401 for( i = 0; i < nNewFlavors-1; i++ )
1402 aTemp.getArray()[i+1] = rTypes.getConstArray()[i];
1403 aTemp.getArray()[0].MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" );
1404 aTemp.getArray()[0].DataType = getCppuType( (OUString*)0 );
1405 rTypes = aTemp;
1407 std::vector< Atom > aNativeTemp( nNewFlavors );
1408 for( i = 0; i < nNewFlavors-1; i++ )
1409 aNativeTemp[ i + 1 ] = aNativeTypes[ i ];
1410 aNativeTemp[0] = None;
1411 aNativeTypes = aNativeTemp;
1416 MutexGuard aGuard(m_aMutex);
1418 it = m_aSelections.find( selection );
1419 if( it != m_aSelections.end() )
1421 if( bSuccess )
1423 it->second->m_aTypes = rTypes;
1424 it->second->m_aNativeTypes = aNativeTypes;
1425 it->second->m_nLastTimestamp = time( NULL );
1426 it->second->m_bHaveUTF16 = bHaveUTF16;
1427 it->second->m_aUTF8Type = aUTF8Type;
1428 it->second->m_bHaveCompound = bHaveCompound;
1430 else
1432 it->second->m_aTypes = Sequence< DataFlavor >();
1433 it->second->m_aNativeTypes = std::vector< Atom >();
1434 it->second->m_nLastTimestamp = 0;
1435 it->second->m_bHaveUTF16 = false;
1436 it->second->m_aUTF8Type = None;
1437 it->second->m_bHaveCompound = false;
1442 #if OSL_DEBUG_LEVEL > 1
1443 // if( selection != m_nCLIPBOARDAtom )
1445 fprintf( stderr, "SelectionManager::getPasteDataTypes( %s ) = %s\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), bSuccess ? "true" : "false" );
1446 for( int i = 0; i < rTypes.getLength(); i++ )
1447 fprintf( stderr, "type: %s\n", OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1449 #endif
1451 return bSuccess;
1454 // ------------------------------------------------------------------------
1456 PixmapHolder* SelectionManager::getPixmapHolder( Atom selection )
1458 std::hash_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection );
1459 if( it == m_aSelections.end() )
1460 return NULL;
1461 if( ! it->second->m_pPixmap )
1462 it->second->m_pPixmap = new PixmapHolder( m_pDisplay );
1463 return it->second->m_pPixmap;
1466 static sal_Size GetTrueFormatSize(int nFormat)
1468 // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
1469 return nFormat == 32 ? sizeof(long) : nFormat/8;
1472 bool SelectionManager::sendData( SelectionAdaptor* pAdaptor,
1473 Window requestor,
1474 Atom target,
1475 Atom property,
1476 Atom selection )
1478 ResettableMutexGuard aGuard( m_aMutex );
1480 // handle targets related to image/bmp
1481 if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID )
1483 PixmapHolder* pPixmap = getPixmapHolder( selection );
1484 if( ! pPixmap ) return false;
1485 XID nValue = None;
1487 // handle colormap request
1488 if( target == XA_COLORMAP )
1489 nValue = (XID)pPixmap->getColormap();
1490 else if( target == XA_VISUALID )
1491 nValue = (XID)pPixmap->getVisualID();
1492 else if( target == XA_PIXMAP || target == XA_BITMAP )
1494 nValue = (XID)pPixmap->getPixmap();
1495 if( nValue == None )
1497 // first conversion
1498 Sequence< sal_Int8 > aData;
1499 int nFormat;
1500 aGuard.clear();
1501 bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1502 aGuard.reset();
1503 if( bConverted )
1505 // get pixmap again since clearing the guard could have invalidated
1506 // the pixmap in another thread
1507 pPixmap = getPixmapHolder( selection );
1508 // conversion succeeded, so aData contains image/bmp now
1509 if( pPixmap->needsConversion( (const sal_uInt8*)aData.getConstArray() )
1510 && m_xBitmapConverter.is() )
1512 #if OSL_DEBUG_LEVEL > 1
1513 fprintf( stderr, "trying bitmap conversion\n" );
1514 #endif
1515 Reference<XBitmap> xBM( new BmpTransporter( aData ) );
1516 Sequence<Any> aArgs(2), aOutArgs;
1517 Sequence<sal_Int16> aOutIndex;
1518 aArgs.getArray()[0] = makeAny( xBM );
1519 aArgs.getArray()[1] = makeAny( (sal_uInt16)pPixmap->getDepth() );
1520 aGuard.clear();
1523 Any aResult =
1524 m_xBitmapConverter->invoke( OUString::createFromAscii( "convert-bitmap-depth" ),
1525 aArgs, aOutIndex, aOutArgs );
1526 if( aResult >>= xBM )
1527 aData = xBM->getDIB();
1529 catch(...)
1531 #if OSL_DEBUG_LEVEL > 1
1532 fprintf( stderr, "exception in bitmap converter\n" );
1533 #endif
1535 aGuard.reset();
1537 // get pixmap again since clearing the guard could have invalidated
1538 // the pixmap in another thread
1539 pPixmap = getPixmapHolder( selection );
1540 nValue = (XID)pPixmap->setBitmapData( (const sal_uInt8*)aData.getConstArray() );
1542 if( nValue == None )
1543 return false;
1545 if( target == XA_BITMAP )
1546 nValue = (XID)pPixmap->getBitmap();
1549 XChangeProperty( m_pDisplay,
1550 requestor,
1551 property,
1552 target,
1554 PropModeReplace,
1555 (const unsigned char*)&nValue,
1557 return true;
1561 * special target TEXT allows us to transfer
1562 * the data in an encoding of our choice
1563 * COMPOUND_TEXT will work with most applications
1565 if( target == m_nTEXTAtom )
1566 target = m_nCOMPOUNDAtom;
1568 Sequence< sal_Int8 > aData;
1569 int nFormat;
1570 aGuard.clear();
1571 bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1572 aGuard.reset();
1573 if( bConverted )
1575 // conversion succeeded
1576 if( aData.getLength() > m_nIncrementalThreshold )
1578 #if OSL_DEBUG_LEVEL > 1
1579 fprintf( stderr, "using INCR protocol\n" );
1580 std::hash_map< Window, std::hash_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor );
1581 if( win_it != m_aIncrementals.end() )
1583 std::hash_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property );
1584 if( inc_it != win_it->second.end() )
1586 const IncrementalTransfer& rInc = inc_it->second;
1587 fprintf( stderr, "premature end and new start for INCR transfer for window 0x%lx, property %s, type %s\n",
1588 rInc.m_aRequestor,
1589 OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1590 OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1594 #endif
1596 // insert IncrementalTransfer
1597 IncrementalTransfer& rInc = m_aIncrementals[ requestor ][ property ];
1598 rInc.m_aData = aData;
1599 rInc.m_nBufferPos = 0;
1600 rInc.m_aRequestor = requestor;
1601 rInc.m_aProperty = property;
1602 rInc.m_aTarget = target;
1603 rInc.m_nFormat = nFormat;
1604 rInc.m_nTransferStartTime = time( NULL );
1606 // use incr protocol, signal start to requestor
1607 long nMinSize = m_nIncrementalThreshold;
1608 XSelectInput( m_pDisplay, requestor, PropertyChangeMask );
1609 XChangeProperty( m_pDisplay, requestor, property,
1610 m_nINCRAtom, 32, PropModeReplace, (unsigned char*)&nMinSize, 1 );
1611 XFlush( m_pDisplay );
1613 else
1615 sal_Size nUnitSize = GetTrueFormatSize(nFormat);
1616 XChangeProperty( m_pDisplay,
1617 requestor,
1618 property,
1619 target,
1620 nFormat,
1621 PropModeReplace,
1622 (const unsigned char*)aData.getConstArray(),
1623 aData.getLength()/nUnitSize );
1626 #if OSL_DEBUG_LEVEL > 1
1627 else
1628 fprintf( stderr, "convertData failed for type: %s \n",
1629 OUStringToOString( convertTypeFromNative( target, selection, nFormat ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1630 #endif
1631 return bConverted;
1634 // ------------------------------------------------------------------------
1636 bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest )
1638 ResettableMutexGuard aGuard( m_aMutex );
1639 #if OSL_DEBUG_LEVEL > 1
1640 fprintf( stderr, "handleSelectionRequest for selection %s and target %s\n",
1641 OUStringToOString( getString( rRequest.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1642 OUStringToOString( getString( rRequest.target ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1644 #endif
1646 XEvent aNotify;
1648 aNotify.type = SelectionNotify;
1649 aNotify.xselection.display = rRequest.display;
1650 aNotify.xselection.send_event = True;
1651 aNotify.xselection.requestor = rRequest.requestor;
1652 aNotify.xselection.selection = rRequest.selection;
1653 aNotify.xselection.time = rRequest.time;
1654 aNotify.xselection.target = rRequest.target;
1655 aNotify.xselection.property = None;
1657 SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection );
1658 // ensure that we still own that selection
1659 if( pAdaptor &&
1660 XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow )
1662 Reference< XTransferable > xTrans( pAdaptor->getTransferable() );
1663 if( rRequest.target == m_nTARGETSAtom )
1665 // someone requests our types
1666 if( xTrans.is() )
1668 aGuard.clear();
1669 Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors();
1670 aGuard.reset();
1672 ::std::list< Atom > aConversions;
1673 getNativeTypeList( aFlavors, aConversions, rRequest.selection );
1675 int i, nTypes = aConversions.size();
1676 Atom* pTypes = (Atom*)alloca( nTypes * sizeof( Atom ) );
1677 std::list< Atom >::const_iterator it;
1678 for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it )
1679 pTypes[i] = *it;
1680 XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1681 XA_ATOM, 32, PropModeReplace, (const unsigned char*)pTypes, nTypes );
1682 aNotify.xselection.property = rRequest.property;
1683 #if OSL_DEBUG_LEVEL > 1
1684 fprintf( stderr, "sending type list:\n" );
1685 for( int k = 0; k < nTypes; k++ )
1686 fprintf( stderr, " %s\n", pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) : "<None>" );
1687 #endif
1690 else if( rRequest.target == m_nTIMESTAMPAtom )
1692 long nTimeStamp = (long)m_aSelections[rRequest.selection]->m_nOrigTimestamp;
1693 XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1694 XA_INTEGER, 32, PropModeReplace, (const unsigned char*)&nTimeStamp, 1 );
1695 aNotify.xselection.property = rRequest.property;
1696 #if OSL_DEBUG_LEVEL > 1
1697 fprintf( stderr, "sending timestamp: %d\n", (int)nTimeStamp );
1698 #endif
1700 else
1702 bool bEventSuccess = false;
1703 if( rRequest.target == m_nMULTIPLEAtom )
1705 // get all targets
1706 Atom nType = None;
1707 int nFormat = 0;
1708 unsigned long nItems = 0, nBytes = 0;
1709 unsigned char* pData = NULL;
1711 // get number of atoms
1712 XGetWindowProperty( m_pDisplay,
1713 rRequest.requestor,
1714 rRequest.property,
1715 0, 0,
1716 False,
1717 AnyPropertyType,
1718 &nType, &nFormat,
1719 &nItems, &nBytes,
1720 &pData );
1721 if( nFormat == 32 && nBytes/4 )
1723 if( pData ) // ?? should not happen
1725 XFree( pData );
1726 pData = NULL;
1728 XGetWindowProperty( m_pDisplay,
1729 rRequest.requestor,
1730 rRequest.property,
1731 0, nBytes/4,
1732 False,
1733 nType,
1734 &nType, &nFormat,
1735 &nItems, &nBytes,
1736 &pData );
1737 if( pData && nItems )
1739 #if OSL_DEBUG_LEVEL > 1
1740 fprintf( stderr, "found %ld atoms in MULTIPLE request\n", nItems );
1741 #endif
1742 bEventSuccess = true;
1743 bool bResetAtoms = false;
1744 Atom* pAtoms = (Atom*)pData;
1745 aGuard.clear();
1746 for( unsigned int i = 0; i < nItems; i += 2 )
1748 #if OSL_DEBUG_LEVEL > 1
1749 fprintf( stderr, " %s => %s: ",
1750 OUStringToOString( getString( pAtoms[i] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1751 OUStringToOString( getString( pAtoms[i+1] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1752 #endif
1753 bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection );
1754 #if OSL_DEBUG_LEVEL > 1
1755 fprintf( stderr, "%s\n", bSuccess ? "succeeded" : "failed" );
1756 #endif
1757 if( ! bSuccess )
1759 pAtoms[i] = None;
1760 bResetAtoms = true;
1763 aGuard.reset();
1764 if( bResetAtoms )
1765 XChangeProperty( m_pDisplay,
1766 rRequest.requestor,
1767 rRequest.property,
1768 XA_ATOM,
1770 PropModeReplace,
1771 pData,
1772 nBytes/4 );
1774 if( pData )
1775 XFree( pData );
1777 #if OSL_DEBUG_LEVEL > 1
1778 else
1780 fprintf( stderr, "could not get type list from \"%s\" of type \"%s\" on requestor 0x%lx, requestor has properties:",
1781 OUStringToOString( getString( rRequest.property ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1782 OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1783 rRequest.requestor );
1784 int nProps = 0;
1785 Atom* pProps = XListProperties( m_pDisplay, rRequest.requestor, &nProps );
1786 if( pProps )
1788 for( int i = 0; i < nProps; i++ )
1789 fprintf( stderr, " \"%s\"", OUStringToOString( getString( pProps[i]), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1790 XFree( pProps );
1793 #endif
1795 else
1797 aGuard.clear();
1798 bEventSuccess = sendData( pAdaptor, rRequest.requestor, rRequest.target, rRequest.property, rRequest.selection );
1799 aGuard.reset();
1801 if( bEventSuccess )
1803 aNotify.xselection.target = rRequest.target;
1804 aNotify.xselection.property = rRequest.property;
1807 aGuard.clear();
1808 xTrans.clear();
1809 aGuard.reset();
1811 XSendEvent( m_pDisplay, rRequest.requestor, False, 0, &aNotify );
1813 if( rRequest.selection == XA_PRIMARY &&
1814 m_bWaitingForPrimaryConversion &&
1815 m_xDragSourceListener.is() )
1817 DragSourceDropEvent dsde;
1818 dsde.Source = static_cast< OWeakObject* >(this);
1819 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, rRequest.time, *this );
1820 dsde.DragSource = static_cast< XDragSource* >(this);
1821 if( aNotify.xselection.property != None )
1823 dsde.DropAction = DNDConstants::ACTION_COPY;
1824 dsde.DropSuccess = sal_True;
1826 else
1828 dsde.DropAction = DNDConstants::ACTION_NONE;
1829 dsde.DropSuccess = sal_False;
1831 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
1832 m_xDragSourceListener.clear();
1833 aGuard.clear();
1834 if( xListener.is() )
1835 xListener->dragDropEnd( dsde );
1838 // we handled the event in any case by answering
1839 return true;
1842 // ------------------------------------------------------------------------
1844 bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent& rNotify )
1846 MutexGuard aGuard( m_aMutex );
1847 // data we requested arrived
1848 #if OSL_DEBUG_LEVEL > 1
1849 fprintf( stderr, "handleReceivePropertyNotify for property %s\n",
1850 OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1851 #endif
1852 bool bHandled = false;
1854 ::std::hash_map< Atom, Selection* >::iterator it =
1855 m_aSelections.find( rNotify.atom );
1856 if( it != m_aSelections.end() &&
1857 rNotify.state == PropertyNewValue &&
1858 ( it->second->m_eState == Selection::WaitingForResponse ||
1859 it->second->m_eState == Selection::WaitingForData ||
1860 it->second->m_eState == Selection::IncrementalTransfer
1864 // MULTIPLE requests are only complete after selection notify
1865 if( it->second->m_aRequestedType == m_nMULTIPLEAtom &&
1866 ( it->second->m_eState == Selection::WaitingForResponse ||
1867 it->second->m_eState == Selection::WaitingForData ) )
1868 return false;
1870 bHandled = true;
1872 Atom nType = None;
1873 int nFormat = 0;
1874 unsigned long nItems = 0, nBytes = 0;
1875 unsigned char* pData = NULL;
1877 // get type and length
1878 XGetWindowProperty( m_pDisplay,
1879 rNotify.window,
1880 rNotify.atom,
1881 0, 0,
1882 False,
1883 AnyPropertyType,
1884 &nType, &nFormat,
1885 &nItems, &nBytes,
1886 &pData );
1887 #if OSL_DEBUG_LEVEL > 1
1888 fprintf( stderr, "found %ld bytes data of type %s and format %d, items = %ld\n",
1889 nBytes,
1890 OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1891 nFormat, nItems );
1892 #endif
1893 if( pData )
1895 XFree( pData );
1896 pData = NULL;
1899 if( nType == m_nINCRAtom )
1901 // start data transfer
1902 XDeleteProperty( m_pDisplay, rNotify.window, rNotify.atom );
1903 it->second->m_eState = Selection::IncrementalTransfer;
1905 else if( nType != None )
1907 XGetWindowProperty( m_pDisplay,
1908 rNotify.window,
1909 rNotify.atom,
1910 0, nBytes/4 +1,
1911 True,
1912 nType,
1913 &nType, &nFormat,
1914 &nItems, &nBytes,
1915 &pData );
1916 #if OSL_DEBUG_LEVEL > 1
1917 fprintf( stderr, "read %ld items data of type %s and format %d, %ld bytes left in property\n",
1918 nItems,
1919 OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1920 nFormat, nBytes );
1921 #endif
1923 sal_Size nUnitSize = GetTrueFormatSize(nFormat);
1925 if( it->second->m_eState == Selection::WaitingForData ||
1926 it->second->m_eState == Selection::WaitingForResponse )
1928 // copy data
1929 it->second->m_aData = Sequence< sal_Int8 >( (sal_Int8*)pData, nItems*nUnitSize );
1930 it->second->m_eState = Selection::Inactive;
1931 it->second->m_aDataArrived.set();
1933 else if( it->second->m_eState == Selection::IncrementalTransfer )
1935 if( nItems )
1937 // append data
1938 Sequence< sal_Int8 > aData( it->second->m_aData.getLength() + nItems*nUnitSize );
1939 memcpy( aData.getArray(), it->second->m_aData.getArray(), it->second->m_aData.getLength() );
1940 memcpy( aData.getArray() + it->second->m_aData.getLength(), pData, nItems*nUnitSize );
1941 it->second->m_aData = aData;
1943 else
1945 it->second->m_eState = Selection::Inactive;
1946 it->second->m_aDataArrived.set();
1949 if( pData )
1950 XFree( pData );
1952 else if( it->second->m_eState == Selection::IncrementalTransfer )
1954 it->second->m_eState = Selection::Inactive;
1955 it->second->m_aDataArrived.set();
1958 return bHandled;
1961 // ------------------------------------------------------------------------
1963 bool SelectionManager::handleSendPropertyNotify( XPropertyEvent& rNotify )
1965 MutexGuard aGuard( m_aMutex );
1967 // ready for next part of a IncrementalTransfer
1968 #if OSL_DEBUG_LEVEL > 1
1969 fprintf( stderr, "handleSendPropertyNotify for property %s (%s)\n",
1970 OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1971 rNotify.state == PropertyNewValue ? "new value" : ( rNotify.state == PropertyDelete ? "deleted" : "unknown")
1973 #endif
1975 bool bHandled = false;
1976 // feed incrementals
1977 if( rNotify.state == PropertyDelete )
1979 std::hash_map< Window, std::hash_map< Atom, IncrementalTransfer > >::iterator it;
1980 it = m_aIncrementals.find( rNotify.window );
1981 if( it != m_aIncrementals.end() )
1983 bHandled = true;
1984 int nCurrentTime = time( NULL );
1985 std::hash_map< Atom, IncrementalTransfer >::iterator inc_it;
1986 // throw out aborted transfers
1987 std::list< Atom > aTimeouts;
1988 for( inc_it = it->second.begin(); inc_it != it->second.end(); ++inc_it )
1990 if( (nCurrentTime - inc_it->second.m_nTransferStartTime) > (getSelectionTimeout()+2) )
1992 aTimeouts.push_back( inc_it->first );
1993 #if OSL_DEBUG_LEVEL > 1
1994 const IncrementalTransfer& rInc = inc_it->second;
1995 fprintf( stderr, "timeout on INCR transfer for window 0x%lx, property %s, type %s\n",
1996 rInc.m_aRequestor,
1997 OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1998 OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
2000 #endif
2004 while( aTimeouts.begin() != aTimeouts.end() )
2006 // transfer broken, might even be a new client with the
2007 // same window id
2008 it->second.erase( aTimeouts.front() );
2009 aTimeouts.pop_front();
2012 inc_it = it->second.find( rNotify.atom );
2013 if( inc_it != it->second.end() )
2015 IncrementalTransfer& rInc = inc_it->second;
2017 int nBytes = rInc.m_aData.getLength() - rInc.m_nBufferPos;
2018 nBytes = (nBytes > m_nIncrementalThreshold) ? m_nIncrementalThreshold : nBytes;
2019 if( nBytes < 0 ) // sanity check
2020 nBytes = 0;
2021 #if OSL_DEBUG_LEVEL > 1
2022 fprintf( stderr, "pushing %d bytes: \"%.*s\"...\n",
2023 nBytes, nBytes > 32 ? 32 : nBytes,
2024 (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos );
2025 #endif
2027 sal_Size nUnitSize = GetTrueFormatSize(rInc.m_nFormat);
2029 XChangeProperty( m_pDisplay,
2030 rInc.m_aRequestor,
2031 rInc.m_aProperty,
2032 rInc.m_aTarget,
2033 rInc.m_nFormat,
2034 PropModeReplace,
2035 (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos,
2036 nBytes/nUnitSize );
2037 rInc.m_nBufferPos += nBytes;
2038 rInc.m_nTransferStartTime = nCurrentTime;
2040 if( nBytes == 0 ) // transfer finished
2042 #if OSL_DEBUG_LEVEL > 1
2043 fprintf( stderr, "finished INCR transfer for window 0x%lx, property %s, type %s\n",
2044 rInc.m_aRequestor,
2045 OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2046 OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
2048 #endif
2049 it->second.erase( inc_it );
2053 // eventually clean up the hash map
2054 if( it->second.begin() == it->second.end() )
2055 m_aIncrementals.erase( it );
2058 return bHandled;
2061 // ------------------------------------------------------------------------
2063 bool SelectionManager::handleSelectionNotify( XSelectionEvent& rNotify )
2065 MutexGuard aGuard( m_aMutex );
2067 bool bHandled = false;
2069 // notification about success/failure of one of our conversion requests
2070 #if OSL_DEBUG_LEVEL > 1
2071 OUString aSelection( getString( rNotify.selection ) );
2072 OUString aProperty( OUString::createFromAscii( "None" ) );
2073 if( rNotify.property )
2074 aProperty = getString( rNotify.property );
2075 fprintf( stderr, "handleSelectionNotify for selection %s and property %s (0x%lx)\n",
2076 OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2077 OUStringToOString( aProperty, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2078 rNotify.property
2080 if( rNotify.requestor != m_aWindow && rNotify.requestor != m_aCurrentDropWindow )
2081 fprintf( stderr, "Warning: selection notify for unknown window 0x%lx\n", rNotify.requestor );
2082 #endif
2083 ::std::hash_map< Atom, Selection* >::iterator it =
2084 m_aSelections.find( rNotify.selection );
2085 if (
2086 (rNotify.requestor == m_aWindow || rNotify.requestor == m_aCurrentDropWindow) &&
2087 it != m_aSelections.end() &&
2089 (it->second->m_eState == Selection::WaitingForResponse) ||
2090 (it->second->m_eState == Selection::WaitingForData)
2094 bHandled = true;
2095 if( it->second->m_aRequestedType == m_nMULTIPLEAtom )
2097 Atom nType = None;
2098 int nFormat = 0;
2099 unsigned long nItems = 0, nBytes = 0;
2100 unsigned char* pData = NULL;
2102 // get type and length
2103 XGetWindowProperty( m_pDisplay,
2104 rNotify.requestor,
2105 rNotify.property,
2106 0, 256,
2107 False,
2108 AnyPropertyType,
2109 &nType, &nFormat,
2110 &nItems, &nBytes,
2111 &pData );
2112 if( nBytes ) // HUGE request !!!
2114 if( pData )
2115 XFree( pData );
2116 XGetWindowProperty( m_pDisplay,
2117 rNotify.requestor,
2118 rNotify.property,
2119 0, 256+(nBytes+3)/4,
2120 False,
2121 AnyPropertyType,
2122 &nType, &nFormat,
2123 &nItems, &nBytes,
2124 &pData );
2126 it->second->m_eState = Selection::Inactive;
2127 sal_Size nUnitSize = GetTrueFormatSize(nFormat);
2128 it->second->m_aData = Sequence< sal_Int8 >((sal_Int8*)pData, nItems * nUnitSize);
2129 it->second->m_aDataArrived.set();
2130 if( pData )
2131 XFree( pData );
2133 // WaitingForData can actually happen; some
2134 // applications (e.g. cmdtool on Solaris) first send
2135 // a success and then cancel it. Weird !
2136 else if( rNotify.property == None )
2138 // conversion failed, stop transfer
2139 it->second->m_eState = Selection::Inactive;
2140 it->second->m_aData = Sequence< sal_Int8 >();
2141 it->second->m_aDataArrived.set();
2143 // get the bytes, by INCR if necessary
2144 else
2145 it->second->m_eState = Selection::WaitingForData;
2147 #if OSL_DEBUG_LEVEL > 1
2148 else if( it != m_aSelections.end() )
2149 fprintf( stderr, "Warning: selection in state %d\n", it->second->m_eState );
2150 #endif
2151 return bHandled;
2154 // ------------------------------------------------------------------------
2156 bool SelectionManager::handleDropEvent( XClientMessageEvent& rMessage )
2158 ResettableMutexGuard aGuard(m_aMutex);
2160 // handle drop related events
2161 Window aSource = rMessage.data.l[0];
2162 Window aTarget = rMessage.window;
2164 bool bHandled = false;
2166 ::std::hash_map< Window, DropTargetEntry >::iterator it =
2167 m_aDropTargets.find( aTarget );
2169 #if OSL_DEBUG_LEVEL > 1
2170 if( rMessage.message_type == m_nXdndEnter ||
2171 rMessage.message_type == m_nXdndLeave ||
2172 rMessage.message_type == m_nXdndDrop ||
2173 rMessage.message_type == m_nXdndPosition )
2175 fprintf( stderr, "got drop event %s, ", OUStringToOString( getString( rMessage.message_type ), RTL_TEXTENCODING_ASCII_US).getStr() );
2176 if( it == m_aDropTargets.end() )
2177 fprintf( stderr, "but no target found\n" );
2178 else if( ! it->second.m_pTarget->m_bActive )
2179 fprintf( stderr, "but target is inactive\n" );
2180 else if( m_aDropEnterEvent.data.l[0] != None && (Window)m_aDropEnterEvent.data.l[0] != aSource )
2181 fprintf( stderr, "but source 0x%lx is unknown (expected 0x%lx or 0)\n", aSource, m_aDropEnterEvent.data.l[0] );
2182 else
2183 fprintf( stderr, "processing.\n" );
2185 #endif
2187 if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive &&
2188 m_bDropWaitingForCompletion && m_aDropEnterEvent.data.l[0] )
2190 bHandled = true;
2191 OSL_ENSURE( 0, "someone forgot to call dropComplete ?" );
2192 // some listener forgot to call dropComplete in the last operation
2193 // let us end it now and accept the new enter event
2194 aGuard.clear();
2195 dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime );
2196 aGuard.reset();
2199 if( it != m_aDropTargets.end() &&
2200 it->second.m_pTarget->m_bActive &&
2201 ( m_aDropEnterEvent.data.l[0] == None || Window(m_aDropEnterEvent.data.l[0]) == aSource )
2204 if( rMessage.message_type == m_nXdndEnter )
2206 bHandled = true;
2207 m_aDropEnterEvent = rMessage;
2208 m_bDropEnterSent = false;
2209 m_aCurrentDropWindow = aTarget;
2210 m_nCurrentProtocolVersion = m_aDropEnterEvent.data.l[1] >> 24;
2211 #if OSL_DEBUG_LEVEL > 1
2212 fprintf( stderr, "received XdndEnter on 0x%lx\n", aTarget );
2213 #endif
2215 else if(
2216 rMessage.message_type == m_nXdndPosition &&
2217 aSource == Window(m_aDropEnterEvent.data.l[0])
2220 bHandled = true;
2221 m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[3] : CurrentTime;
2222 if( ! m_bDropEnterSent )
2223 m_nDropTimestamp = m_nDropTime;
2225 Window aChild;
2226 XTranslateCoordinates( m_pDisplay,
2227 it->second.m_aRootWindow,
2228 it->first,
2229 rMessage.data.l[2] >> 16,
2230 rMessage.data.l[2] & 0xffff,
2231 &m_nLastX, &m_nLastY,
2232 &aChild );
2234 #if OSL_DEBUG_LEVEL > 1
2235 fprintf( stderr, "received XdndPosition on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY );
2236 #endif
2237 DropTargetDragEnterEvent aEvent;
2238 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2239 aEvent.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2240 aEvent.LocationX = m_nLastX;
2241 aEvent.LocationY = m_nLastY;
2242 aEvent.SourceActions = m_nSourceActions;
2243 if( m_nCurrentProtocolVersion < 2 )
2244 aEvent.DropAction = DNDConstants::ACTION_COPY;
2245 else if( Atom(rMessage.data.l[4]) == m_nXdndActionCopy )
2246 aEvent.DropAction = DNDConstants::ACTION_COPY;
2247 else if( Atom(rMessage.data.l[4]) == m_nXdndActionMove )
2248 aEvent.DropAction = DNDConstants::ACTION_MOVE;
2249 else if( Atom(rMessage.data.l[4]) == m_nXdndActionLink )
2250 aEvent.DropAction = DNDConstants::ACTION_LINK;
2251 else if( Atom(rMessage.data.l[4]) == m_nXdndActionAsk )
2252 // currently no interface to implement ask
2253 aEvent.DropAction = ~0;
2254 else
2255 aEvent.DropAction = DNDConstants::ACTION_NONE;
2257 m_nLastDropAction = aEvent.DropAction;
2258 if( ! m_bDropEnterSent )
2260 m_bDropEnterSent = true;
2261 aEvent.SupportedDataFlavors = m_xDropTransferable->getTransferDataFlavors();
2262 aGuard.clear();
2263 it->second->dragEnter( aEvent );
2265 else
2267 aGuard.clear();
2268 it->second->dragOver( aEvent );
2271 else if(
2272 rMessage.message_type == m_nXdndLeave &&
2273 aSource == Window(m_aDropEnterEvent.data.l[0])
2276 bHandled = true;
2277 #if OSL_DEBUG_LEVEL > 1
2278 fprintf( stderr, "received XdndLeave on 0x%lx\n", aTarget );
2279 #endif
2280 DropTargetEvent aEvent;
2281 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2282 m_aDropEnterEvent.data.l[0] = None;
2283 if( m_aCurrentDropWindow == aTarget )
2284 m_aCurrentDropWindow = None;
2285 m_nCurrentProtocolVersion = nXdndProtocolRevision;
2286 aGuard.clear();
2287 it->second->dragExit( aEvent );
2289 else if(
2290 rMessage.message_type == m_nXdndDrop &&
2291 aSource == Window(m_aDropEnterEvent.data.l[0])
2294 bHandled = true;
2295 m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[2] : CurrentTime;
2297 #if OSL_DEBUG_LEVEL > 1
2298 fprintf( stderr, "received XdndDrop on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY );
2299 #endif
2300 if( m_bLastDropAccepted )
2302 DropTargetDropEvent aEvent;
2303 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2304 aEvent.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2305 aEvent.LocationX = m_nLastX;
2306 aEvent.LocationY = m_nLastY;
2307 aEvent.DropAction = m_nLastDropAction;
2308 // there is nothing corresponding to source supported actions
2309 // every source can do link, copy and move
2310 aEvent.SourceActions= m_nLastDropAction;
2311 aEvent.Transferable = m_xDropTransferable;
2313 m_bDropWaitingForCompletion = true;
2314 aGuard.clear();
2315 it->second->drop( aEvent );
2317 else
2319 #if OSL_DEBUG_LEVEL > 1
2320 fprintf( stderr, "XdndDrop canceled due to m_bLastDropAccepted = fale\n" );
2321 #endif
2322 DropTargetEvent aEvent;
2323 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2324 aGuard.clear();
2325 it->second->dragExit( aEvent );
2326 // reset the drop status, notify source
2327 dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime );
2331 return bHandled;
2335 * methods for XDropTargetDropContext
2338 void SelectionManager::dropComplete( sal_Bool bSuccess, Window aDropWindow, Time )
2340 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, m_nDragTimestamp, *this );
2349 dsde.DragSource = static_cast< XDragSource* >(this);
2350 dsde.DropAction = getUserDragAction();
2351 dsde.DropSuccess = bSuccess;
2352 Reference< XDragSourceListener > xListener = m_xDragSourceListener;
2353 m_xDragSourceListener.clear();
2355 aGuard.clear();
2356 xListener->dragDropEnd( dsde );
2358 else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2360 XEvent aEvent;
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;
2371 if( bSuccess )
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;
2381 #if OSL_DEBUG_LEVEL > 1
2382 fprintf( stderr, "Sending XdndFinished to 0x%lx\n",
2383 m_aDropEnterEvent.data.l[0]
2385 #endif
2387 XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2388 False, NoEventMask, & aEvent );
2390 m_aDropEnterEvent.data.l[0] = None;
2391 m_aCurrentDropWindow = None;
2392 m_nCurrentProtocolVersion = nXdndProtocolRevision;
2394 m_bDropWaitingForCompletion = false;
2396 else
2397 OSL_ASSERT( "dropComplete from invalid DropTargetDropContext" );
2401 * methods for XDropTargetDragContext
2404 // ------------------------------------------------------------------------
2406 void SelectionManager::sendDragStatus( Atom nDropAction )
2408 ClearableMutexGuard aGuard(m_aMutex);
2410 if( m_xDragSourceListener.is() )
2412 sal_Int8 nNewDragAction;
2413 if( nDropAction == m_nXdndActionMove )
2414 nNewDragAction = DNDConstants::ACTION_MOVE;
2415 else if( nDropAction == m_nXdndActionCopy )
2416 nNewDragAction = DNDConstants::ACTION_COPY;
2417 else if( nDropAction == m_nXdndActionLink )
2418 nNewDragAction = DNDConstants::ACTION_LINK;
2419 else
2420 nNewDragAction = DNDConstants::ACTION_NONE;
2421 nNewDragAction &= m_nSourceActions;
2423 if( nNewDragAction != m_nTargetAcceptAction )
2425 setCursor( getDefaultCursor( nNewDragAction ), m_aDropWindow, m_nDragTimestamp );
2426 m_nTargetAcceptAction = nNewDragAction;
2429 DragSourceDragEvent dsde;
2430 dsde.Source = static_cast< OWeakObject* >(this);
2431 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2432 dsde.DragSource = static_cast< XDragSource* >(this);
2433 dsde.DropAction = m_nSourceActions;
2434 dsde.UserAction = getUserDragAction();
2436 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2437 // caution: do not change anything after this
2438 aGuard.clear();
2439 if( xListener.is() )
2440 xListener->dragOver( dsde );
2442 else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2444 XEvent aEvent;
2445 aEvent.xclient.type = ClientMessage;
2446 aEvent.xclient.display = m_pDisplay;
2447 aEvent.xclient.window = m_aDropEnterEvent.data.l[0];
2448 aEvent.xclient.message_type = m_nXdndStatus;
2449 aEvent.xclient.format = 32;
2450 aEvent.xclient.data.l[0] = m_aCurrentDropWindow;
2451 aEvent.xclient.data.l[1] = 2;
2452 if( nDropAction == m_nXdndActionMove ||
2453 nDropAction == m_nXdndActionLink ||
2454 nDropAction == m_nXdndActionCopy )
2455 aEvent.xclient.data.l[1] |= 1;
2456 aEvent.xclient.data.l[2] = 0;
2457 aEvent.xclient.data.l[3] = 0;
2458 aEvent.xclient.data.l[4] = m_nCurrentProtocolVersion > 1 ? nDropAction : 0;
2460 #if OSL_DEBUG_LEVEL > 1
2461 fprintf( stderr, "Sending XdndStatus to 0x%lx with action %s\n",
2462 m_aDropEnterEvent.data.l[0],
2463 OUStringToOString( getString( nDropAction ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
2465 #endif
2467 XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2468 False, NoEventMask, & aEvent );
2469 XFlush( m_pDisplay );
2473 // ------------------------------------------------------------------------
2475 sal_Int8 SelectionManager::getUserDragAction() const
2477 return (m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT) ? m_nTargetAcceptAction : m_nUserDragAction;
2480 // ------------------------------------------------------------------------
2482 bool SelectionManager::updateDragAction( int modifierState )
2484 bool bRet = false;
2486 sal_Int8 nNewDropAction = DNDConstants::ACTION_MOVE;
2487 if( ( modifierState & ShiftMask ) && ! ( modifierState & ControlMask ) )
2488 nNewDropAction = DNDConstants::ACTION_MOVE;
2489 else if( ( modifierState & ControlMask ) && ! ( modifierState & ShiftMask ) )
2490 nNewDropAction = DNDConstants::ACTION_COPY;
2491 else if( ( modifierState & ShiftMask ) && ( modifierState & ControlMask ) )
2492 nNewDropAction = DNDConstants::ACTION_LINK;
2493 if( m_nCurrentProtocolVersion < 0 && m_aDropWindow != None )
2494 nNewDropAction = DNDConstants::ACTION_COPY;
2495 nNewDropAction &= m_nSourceActions;
2497 if( ! ( modifierState & ( ControlMask | ShiftMask ) ) )
2499 if( ! nNewDropAction )
2501 // default to an action so the user does not have to press
2502 // keys explicitly
2503 if( m_nSourceActions & DNDConstants::ACTION_MOVE )
2504 nNewDropAction = DNDConstants::ACTION_MOVE;
2505 else if( m_nSourceActions & DNDConstants::ACTION_COPY )
2506 nNewDropAction = DNDConstants::ACTION_COPY;
2507 else if( m_nSourceActions & DNDConstants::ACTION_LINK )
2508 nNewDropAction = DNDConstants::ACTION_LINK;
2510 nNewDropAction |= DNDConstants::ACTION_DEFAULT;
2513 if( nNewDropAction != m_nUserDragAction || m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT )
2515 #if OSL_DEBUG_LEVEL > 1
2516 fprintf( stderr, "updateDragAction: %x -> %x\n", (int)m_nUserDragAction, (int)nNewDropAction );
2517 #endif
2518 bRet = true;
2519 m_nUserDragAction = nNewDropAction;
2521 DragSourceDragEvent dsde;
2522 dsde.Source = static_cast< OWeakObject* >(this);
2523 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2524 dsde.DragSource = static_cast< XDragSource* >(this);
2525 dsde.DropAction = m_nUserDragAction;
2526 dsde.UserAction = m_nUserDragAction;
2527 m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; // invalidate last accept
2528 m_xDragSourceListener->dropActionChanged( dsde );
2530 return bRet;
2533 // ------------------------------------------------------------------------
2535 void SelectionManager::sendDropPosition( bool bForce, Time eventTime )
2537 ClearableMutexGuard aGuard(m_aMutex);
2539 if( m_bDropSent )
2540 return;
2542 ::std::hash_map< Window, DropTargetEntry >::const_iterator it =
2543 m_aDropTargets.find( m_aDropWindow );
2544 if( it != m_aDropTargets.end() )
2546 if( it->second.m_pTarget->m_bActive )
2548 int x, y;
2549 Window aChild;
2550 XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, m_aDropWindow, m_nLastDragX, m_nLastDragY, &x, &y, &aChild );
2551 DropTargetDragEvent dtde;
2552 dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget );
2553 dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2554 dtde.LocationX = x;
2555 dtde.LocationY = y;
2556 dtde.DropAction = getUserDragAction();
2557 dtde.SourceActions = m_nSourceActions;
2558 aGuard.clear();
2559 it->second->dragOver( dtde );
2562 else if( bForce ||
2564 m_nLastDragX < m_nNoPosX || m_nLastDragX >= m_nNoPosX+m_nNoPosWidth ||
2565 m_nLastDragY < m_nNoPosY || m_nLastDragY >= m_nNoPosY+m_nNoPosHeight
2568 // send XdndPosition
2569 XEvent aEvent;
2570 aEvent.type = ClientMessage;
2571 aEvent.xclient.display = m_pDisplay;
2572 aEvent.xclient.format = 32;
2573 aEvent.xclient.message_type = m_nXdndPosition;
2574 aEvent.xclient.window = m_aDropWindow;
2575 aEvent.xclient.data.l[0] = m_aWindow;
2576 aEvent.xclient.data.l[1] = 0;
2577 aEvent.xclient.data.l[2] = m_nLastDragX << 16 | (m_nLastDragY&0xffff);
2578 aEvent.xclient.data.l[3] = eventTime;
2580 if( m_nUserDragAction & DNDConstants::ACTION_COPY )
2581 aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2582 else if( m_nUserDragAction & DNDConstants::ACTION_MOVE )
2583 aEvent.xclient.data.l[4]=m_nXdndActionMove;
2584 else if( m_nUserDragAction & DNDConstants::ACTION_LINK )
2585 aEvent.xclient.data.l[4]=m_nXdndActionLink;
2586 else
2587 aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2588 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2589 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2593 // ------------------------------------------------------------------------
2595 bool SelectionManager::handleDragEvent( XEvent& rMessage )
2597 if( ! m_xDragSourceListener.is() )
2598 return false;
2600 ResettableMutexGuard aGuard(m_aMutex);
2602 bool bHandled = false;
2604 // for shortcut
2605 ::std::hash_map< Window, DropTargetEntry >::const_iterator it =
2606 m_aDropTargets.find( m_aDropWindow );
2607 #if OSL_DEBUG_LEVEL > 1
2608 switch( rMessage.type )
2610 case ClientMessage:
2611 fprintf( stderr, "handleDragEvent: %s\n", OUStringToOString( getString( rMessage.xclient.message_type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
2612 break;
2613 case MotionNotify:
2614 // fprintf( stderr, "handleDragEvent: MotionNotify\n" );
2615 break;
2616 case EnterNotify:
2617 fprintf( stderr, "handleDragEvent: EnterNotify\n" );
2618 break;
2619 case LeaveNotify:
2620 fprintf( stderr, "handleDragEvent: LeaveNotify\n" );
2621 break;
2622 case ButtonPress:
2623 fprintf( stderr, "handleDragEvent: ButtonPress %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton );
2624 break;
2625 case ButtonRelease:
2626 fprintf( stderr, "handleDragEvent: ButtonRelease %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton );
2627 break;
2628 case KeyPress:
2629 fprintf( stderr, "handleDragEvent: KeyPress\n" );
2630 break;
2631 case KeyRelease:
2632 fprintf( stderr, "handleDragEvent: KeyRelease\n" );
2633 break;
2634 default:
2635 fprintf( stderr, "handleDragEvent: <unknown type %d>\n", rMessage.type );
2636 break;
2638 #endif
2640 // handle drag related events
2641 if( rMessage.type == ClientMessage )
2643 if( Atom(rMessage.xclient.message_type) == m_nXdndStatus && Atom(rMessage.xclient.data.l[0]) == m_aDropWindow )
2645 bHandled = true;
2646 DragSourceDragEvent dsde;
2647 dsde.Source = static_cast< OWeakObject* >(this);
2648 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2649 dsde.DragSource = static_cast< XDragSource* >( this );
2650 dsde.UserAction = getUserDragAction();
2651 dsde.DropAction = DNDConstants::ACTION_NONE;
2652 m_bDropSuccess = rMessage.xclient.data.l[1] & 1 ? true : false;
2653 #if OSL_DEBUG_LEVEL > 1
2654 fprintf( stderr, "status drop action: accept = %s, %s\n",
2655 m_bDropSuccess ? "true" : "false",
2656 OUStringToOString( getString( rMessage.xclient.data.l[4] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
2657 #endif
2658 if( rMessage.xclient.data.l[1] & 1 )
2660 if( m_nCurrentProtocolVersion > 1 )
2662 if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionCopy )
2663 dsde.DropAction = DNDConstants::ACTION_COPY;
2664 else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionMove )
2665 dsde.DropAction = DNDConstants::ACTION_MOVE;
2666 else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionLink )
2667 dsde.DropAction = DNDConstants::ACTION_LINK;
2669 else
2670 dsde.DropAction = DNDConstants::ACTION_COPY;
2672 m_nTargetAcceptAction = dsde.DropAction;
2674 if( ! ( rMessage.xclient.data.l[1] & 2 ) )
2676 m_nNoPosX = rMessage.xclient.data.l[2] >> 16;
2677 m_nNoPosY = rMessage.xclient.data.l[2] & 0xffff;
2678 m_nNoPosWidth = rMessage.xclient.data.l[3] >> 16;
2679 m_nNoPosHeight = rMessage.xclient.data.l[3] & 0xffff;
2681 else
2682 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2684 setCursor( getDefaultCursor( dsde.DropAction ), m_aDropWindow, m_nDragTimestamp );
2685 aGuard.clear();
2686 m_xDragSourceListener->dragOver( dsde );
2688 else if( Atom(rMessage.xclient.message_type) == m_nXdndFinished && m_aDropWindow == Atom(rMessage.xclient.data.l[0]) )
2690 bHandled = true;
2691 // notify the listener
2692 DragSourceDropEvent dsde;
2693 dsde.Source = static_cast< OWeakObject* >(this);
2694 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2695 dsde.DragSource = static_cast< XDragSource* >(this);
2696 dsde.DropAction = m_nTargetAcceptAction;
2697 dsde.DropSuccess = m_bDropSuccess;
2698 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2699 m_xDragSourceListener.clear();
2700 aGuard.clear();
2701 xListener->dragDropEnd( dsde );
2704 else if( rMessage.type == MotionNotify ||
2705 rMessage.type == EnterNotify || rMessage.type == LeaveNotify
2708 bHandled = true;
2709 bool bForce = false;
2710 int root_x = rMessage.type == MotionNotify ? rMessage.xmotion.x_root : rMessage.xcrossing.x_root;
2711 int root_y = rMessage.type == MotionNotify ? rMessage.xmotion.y_root : rMessage.xcrossing.y_root;
2712 Window root = rMessage.type == MotionNotify ? rMessage.xmotion.root : rMessage.xcrossing.root;
2713 m_nDragTimestamp = rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time;
2715 aGuard.clear();
2716 if( rMessage.type == MotionNotify )
2718 bForce = updateDragAction( rMessage.xmotion.state );
2720 updateDragWindow( root_x, root_y, root );
2721 aGuard.reset();
2723 if( m_nCurrentProtocolVersion >= 0 && m_aDropProxy != None )
2725 aGuard.clear();
2726 sendDropPosition( bForce, rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time );
2729 else if( rMessage.type == KeyPress || rMessage.type == KeyRelease )
2731 bHandled = true;
2732 KeySym aKey = XKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0 );
2733 if( aKey == XK_Escape )
2735 // abort drag
2736 if( it != m_aDropTargets.end() )
2738 DropTargetEvent dte;
2739 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
2740 aGuard.clear();
2741 it->second.m_pTarget->dragExit( dte );
2743 else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
2745 // send XdndLeave
2746 XEvent aEvent;
2747 aEvent.type = ClientMessage;
2748 aEvent.xclient.display = m_pDisplay;
2749 aEvent.xclient.format = 32;
2750 aEvent.xclient.message_type = m_nXdndLeave;
2751 aEvent.xclient.window = m_aDropWindow;
2752 aEvent.xclient.data.l[0] = m_aWindow;
2753 memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
2754 m_aDropWindow = m_aDropProxy = None;
2755 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2757 // notify the listener
2758 DragSourceDropEvent dsde;
2759 dsde.Source = static_cast< OWeakObject* >(this);
2760 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2761 dsde.DragSource = static_cast< XDragSource* >(this);
2762 dsde.DropAction = DNDConstants::ACTION_NONE;
2763 dsde.DropSuccess = sal_False;
2764 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2765 m_xDragSourceListener.clear();
2766 aGuard.clear();
2767 xListener->dragDropEnd( dsde );
2769 else
2772 * man page says: state is state immediate PRIOR to the
2773 * event. It would seem that this is a somewhat arguable
2774 * design decision.
2776 int nState = rMessage.xkey.state;
2777 int nNewState = 0;
2778 switch( aKey )
2780 case XK_Shift_R:
2781 case XK_Shift_L: nNewState = ShiftMask;break;
2782 case XK_Control_R:
2783 case XK_Control_L: nNewState = ControlMask;break;
2784 // just interested in shift and ctrl for dnd
2786 if( rMessage.type == KeyPress )
2787 nState |= nNewState;
2788 else
2789 nState &= ~nNewState;
2790 aGuard.clear();
2791 if( updateDragAction( nState ) )
2792 sendDropPosition( true, rMessage.xkey.time );
2795 else if(
2796 ( rMessage.type == ButtonPress || rMessage.type == ButtonRelease ) &&
2797 rMessage.xbutton.button == m_nDragButton )
2799 bool bCancel = true;
2800 if( m_aDropWindow != None )
2802 if( it != m_aDropTargets.end() )
2804 if( it->second.m_pTarget->m_bActive && m_nUserDragAction != DNDConstants::ACTION_NONE && m_bLastDropAccepted )
2806 bHandled = true;
2807 int x, y;
2808 Window aChild;
2809 XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &x, &y, &aChild );
2810 DropTargetDropEvent dtde;
2811 dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget );
2812 dtde.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2813 dtde.LocationX = x;
2814 dtde.LocationY = y;
2815 dtde.DropAction = m_nUserDragAction;
2816 dtde.SourceActions = m_nSourceActions;
2817 dtde.Transferable = m_xDragSourceTransferable;
2818 m_bDropSent = true;
2819 m_nDropTimeout = time( NULL );
2820 m_bDropWaitingForCompletion = true;
2821 aGuard.clear();
2822 it->second->drop( dtde );
2823 bCancel = false;
2825 else bCancel = true;
2827 else if( m_nCurrentProtocolVersion >= 0 )
2829 bHandled = true;
2831 XEvent aEvent;
2832 aEvent.type = ClientMessage;
2833 aEvent.xclient.display = m_pDisplay;
2834 aEvent.xclient.format = 32;
2835 aEvent.xclient.message_type = m_nXdndDrop;
2836 aEvent.xclient.window = m_aDropWindow;
2837 aEvent.xclient.data.l[0] = m_aWindow;
2838 aEvent.xclient.data.l[1] = 0;
2839 aEvent.xclient.data.l[2] = rMessage.xbutton.time;
2840 aEvent.xclient.data.l[3] = 0;
2841 aEvent.xclient.data.l[4] = 0;
2843 m_bDropSent = true;
2844 m_nDropTimeout = time( NULL );
2845 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2846 bCancel = false;
2848 else
2850 // dropping on non XdndWindows: acquire ownership of
2851 // PRIMARY and send a middle mouse button click down/up to
2852 // target window
2853 SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY );
2854 if( pAdaptor )
2856 bHandled = true;
2858 Window aDummy;
2859 XEvent aEvent;
2860 aEvent.type = ButtonPress;
2861 aEvent.xbutton.display = m_pDisplay;
2862 aEvent.xbutton.window = m_aDropWindow;
2863 aEvent.xbutton.root = rMessage.xbutton.root;
2864 aEvent.xbutton.subwindow = m_aDropWindow;
2865 aEvent.xbutton.time = rMessage.xbutton.time+1;
2866 aEvent.xbutton.x_root = rMessage.xbutton.x_root;
2867 aEvent.xbutton.y_root = rMessage.xbutton.y_root;
2868 aEvent.xbutton.state = rMessage.xbutton.state;
2869 aEvent.xbutton.button = Button2;
2870 aEvent.xbutton.same_screen = True;
2871 XTranslateCoordinates( m_pDisplay,
2872 rMessage.xbutton.root, m_aDropWindow,
2873 rMessage.xbutton.x_root, rMessage.xbutton.y_root,
2874 &aEvent.xbutton.x, &aEvent.xbutton.y,
2875 &aDummy );
2876 XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonPressMask, &aEvent );
2877 aEvent.xbutton.type = ButtonRelease;
2878 aEvent.xbutton.time++;
2879 aEvent.xbutton.state |= Button2Mask;
2880 XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonReleaseMask, &aEvent );
2882 m_bDropSent = true;
2883 m_nDropTimeout = time( NULL );
2884 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2885 m_bWaitingForPrimaryConversion = true;
2886 m_bDropSent = true;
2887 m_nDropTimeout = time( NULL );
2888 // HACK :-)
2889 aGuard.clear();
2890 static_cast< X11Clipboard* >( pAdaptor )->setContents( m_xDragSourceTransferable, Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >() );
2891 aGuard.reset();
2892 bCancel = false;
2896 if( bCancel )
2898 // cancel drag
2899 DragSourceDropEvent dsde;
2900 dsde.Source = static_cast< OWeakObject* >(this);
2901 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2902 dsde.DragSource = static_cast< XDragSource* >(this);
2903 dsde.DropAction = DNDConstants::ACTION_NONE;
2904 dsde.DropSuccess = sal_False;
2905 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2906 m_xDragSourceListener.clear();
2907 aGuard.clear();
2908 xListener->dragDropEnd( dsde );
2909 bHandled = true;
2912 return bHandled;
2915 // ------------------------------------------------------------------------
2917 void SelectionManager::accept( sal_Int8 dragOperation, Window aDropWindow, Time )
2919 if( aDropWindow == m_aCurrentDropWindow )
2921 #if OSL_DEBUG_LEVEL > 1
2922 fprintf( stderr, "accept: %x\n", dragOperation );
2923 #endif
2924 Atom nAction = None;
2925 dragOperation &= (DNDConstants::ACTION_MOVE | DNDConstants::ACTION_COPY | DNDConstants::ACTION_LINK);
2926 if( dragOperation & DNDConstants::ACTION_MOVE )
2927 nAction = m_nXdndActionMove;
2928 else if( dragOperation & DNDConstants::ACTION_COPY )
2929 nAction = m_nXdndActionCopy;
2930 else if( dragOperation & DNDConstants::ACTION_LINK )
2931 nAction = m_nXdndActionLink;
2932 m_bLastDropAccepted = true;
2933 sendDragStatus( nAction );
2937 // ------------------------------------------------------------------------
2939 void SelectionManager::reject( Window aDropWindow, Time )
2941 if( aDropWindow == m_aCurrentDropWindow )
2943 #if OSL_DEBUG_LEVEL > 1
2944 fprintf( stderr, "reject\n" );
2945 #endif
2946 m_bLastDropAccepted = false;
2947 sendDragStatus( None );
2948 if( m_bDropSent && m_xDragSourceListener.is() )
2950 DragSourceDropEvent dsde;
2951 dsde.Source = static_cast< OWeakObject* >(this);
2952 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2953 dsde.DragSource = static_cast< XDragSource* >(this);
2954 dsde.DropAction = DNDConstants::ACTION_NONE;
2955 dsde.DropSuccess = sal_False;
2956 m_xDragSourceListener->dragDropEnd( dsde );
2957 m_xDragSourceListener.clear();
2963 * XDragSource
2966 sal_Bool SelectionManager::isDragImageSupported() throw()
2968 return sal_False;
2971 // ------------------------------------------------------------------------
2973 sal_Int32 SelectionManager::getDefaultCursor( sal_Int8 dragAction ) throw()
2975 Cursor aCursor = m_aNoneCursor;
2976 if( dragAction & DNDConstants::ACTION_MOVE )
2977 aCursor = m_aMoveCursor;
2978 else if( dragAction & DNDConstants::ACTION_COPY )
2979 aCursor = m_aCopyCursor;
2980 else if( dragAction & DNDConstants::ACTION_LINK )
2981 aCursor = m_aLinkCursor;
2982 return aCursor;
2985 // ------------------------------------------------------------------------
2987 int SelectionManager::getXdndVersion( Window aWindow, Window& rProxy )
2989 Atom* pProperties = NULL;
2990 int nProperties = 0;
2991 Atom nType;
2992 int nFormat;
2993 unsigned long nItems, nBytes;
2994 unsigned char* pBytes = NULL;
2996 int nVersion = -1;
2997 rProxy = None;
3000 * XListProperties is used here to avoid unnecessary XGetWindowProperty calls
3001 * and therefore reducing latency penalty
3003 pProperties = XListProperties( m_pDisplay, aWindow, &nProperties );
3004 // first look for proxy
3005 int i;
3006 for( i = 0; i < nProperties; i++ )
3008 if( pProperties[i] == m_nXdndProxy )
3010 XGetWindowProperty( m_pDisplay, aWindow, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3011 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3012 if( pBytes )
3014 if( nType == XA_WINDOW )
3015 rProxy = *(Window*)pBytes;
3016 XFree( pBytes );
3017 pBytes = NULL;
3018 if( rProxy != None )
3020 // now check proxy wether it points to itself
3021 XGetWindowProperty( m_pDisplay, rProxy, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3022 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3023 if( pBytes )
3025 if( nType == XA_WINDOW && *(Window*)pBytes != rProxy )
3026 rProxy = None;
3027 XFree( pBytes );
3028 pBytes = NULL;
3030 else
3031 rProxy = None;
3034 break;
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 );
3041 if( pBytes )
3043 if( nType == XA_ATOM )
3044 nVersion = *(Atom*)pBytes;
3045 XFree( pBytes );
3048 nVersion = nVersion > nXdndProtocolRevision ? nXdndProtocolRevision : nVersion;
3050 return nVersion;
3053 // ------------------------------------------------------------------------
3055 void SelectionManager::updateDragWindow( int nX, int nY, Window aRoot )
3057 ResettableMutexGuard aGuard( m_aMutex );
3059 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3061 m_nLastDragX = nX;
3062 m_nLastDragY = nY;
3064 Window aParent = aRoot;
3065 Window aChild;
3066 Window aNewProxy = None, aNewCurrentWindow = None;
3067 int nNewProtocolVersion = -1;
3068 int nWinX, nWinY;
3070 // find the first XdndAware window or check if root window is
3071 // XdndAware or has XdndProxy
3074 XTranslateCoordinates( m_pDisplay, aRoot, aParent, nX, nY, &nWinX, &nWinY, &aChild );
3075 if( aChild != None )
3077 if( aChild == m_aCurrentDropWindow && aChild != aRoot && m_nCurrentProtocolVersion >= 0 )
3079 aParent = aChild;
3080 break;
3082 nNewProtocolVersion = getXdndVersion( aChild, aNewProxy );
3083 aParent = aChild;
3085 } while( aChild != None && nNewProtocolVersion < 0 );
3087 aNewCurrentWindow = aParent;
3088 if( aNewCurrentWindow == aRoot )
3090 // no children, try root drop
3091 nNewProtocolVersion = getXdndVersion( aNewCurrentWindow, aNewProxy );
3092 if( nNewProtocolVersion < 3 )
3094 aNewCurrentWindow = aNewProxy = None;
3095 nNewProtocolVersion = nXdndProtocolRevision;
3100 DragSourceDragEvent dsde;
3101 dsde.Source = static_cast< OWeakObject* >(this);
3102 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
3103 dsde.DragSource = static_cast< XDragSource* >(this);
3104 dsde.DropAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3105 dsde.UserAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3107 ::std::hash_map< Window, DropTargetEntry >::const_iterator it;
3108 if( aNewCurrentWindow != m_aDropWindow )
3110 #if OSL_DEBUG_LEVEL > 1
3111 fprintf( stderr, "drag left window 0x%lx (rev. %d), entered window 0x%lx (rev %d)\n", m_aDropWindow, m_nCurrentProtocolVersion, aNewCurrentWindow, nNewProtocolVersion );
3112 #endif
3114 if( m_aDropWindow != None )
3116 it = m_aDropTargets.find( m_aDropWindow );
3117 if( it != m_aDropTargets.end() )
3118 // shortcut for own drop targets
3120 DropTargetEvent dte;
3121 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
3122 aGuard.clear();
3123 it->second.m_pTarget->dragExit( dte );
3124 aGuard.reset();
3126 else
3128 // send old drop target a XdndLeave
3129 XEvent aEvent;
3130 aEvent.type = ClientMessage;
3131 aEvent.xclient.display = m_pDisplay;
3132 aEvent.xclient.format = 32;
3133 aEvent.xclient.message_type = m_nXdndLeave;
3134 aEvent.xclient.window = m_aDropWindow;
3135 aEvent.xclient.data.l[0] = m_aWindow;
3136 aEvent.xclient.data.l[1] = 0;
3137 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3139 if( xListener.is() )
3141 aGuard.clear();
3142 xListener->dragExit( dsde );
3143 aGuard.reset();
3147 m_nCurrentProtocolVersion = nNewProtocolVersion;
3148 m_aDropWindow = aNewCurrentWindow;
3149 m_aDropProxy = aNewProxy != None ? aNewProxy : m_aDropWindow;
3151 it = m_aDropTargets.find( m_aDropWindow );
3152 if( it != m_aDropTargets.end() && ! it->second.m_pTarget->m_bActive )
3153 m_aDropProxy = None;
3155 if( m_aDropProxy != None && xListener.is() )
3157 aGuard.clear();
3158 xListener->dragEnter( dsde );
3159 aGuard.reset();
3161 // send XdndEnter
3162 if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
3164 it = m_aDropTargets.find( m_aDropWindow );
3165 if( it != m_aDropTargets.end() )
3167 XTranslateCoordinates( m_pDisplay, aRoot, m_aDropWindow, nX, nY, &nWinX, &nWinY, &aChild );
3168 DropTargetDragEnterEvent dtde;
3169 dtde.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
3170 dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
3171 dtde.LocationX = nWinX;
3172 dtde.LocationY = nWinY;
3173 dtde.DropAction = m_nUserDragAction;
3174 dtde.SourceActions = m_nSourceActions;
3175 dtde.SupportedDataFlavors = m_xDragSourceTransferable->getTransferDataFlavors();
3176 aGuard.clear();
3177 it->second.m_pTarget->dragEnter( dtde );
3178 aGuard.reset();
3180 else
3182 XEvent aEvent;
3183 aEvent.type = ClientMessage;
3184 aEvent.xclient.display = m_pDisplay;
3185 aEvent.xclient.format = 32;
3186 aEvent.xclient.message_type = m_nXdndEnter;
3187 aEvent.xclient.window = m_aDropWindow;
3188 aEvent.xclient.data.l[0] = m_aWindow;
3189 aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24;
3190 memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3191 // fill in data types
3192 ::std::list< Atom > aConversions;
3193 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3194 if( aConversions.size() > 3 )
3195 aEvent.xclient.data.l[1] |= 1;
3196 ::std::list< Atom >::const_iterator type_it = aConversions.begin();
3197 for( int i = 0; type_it != aConversions.end() && i < 3; i++, ++type_it )
3198 aEvent.xclient.data.l[i+2] = *type_it;
3199 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3202 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
3204 else if( m_aDropProxy != None && xListener.is() )
3206 aGuard.clear();
3207 // drag over for XdndAware windows comes when receiving XdndStatus
3208 xListener->dragOver( dsde );
3212 // ------------------------------------------------------------------------
3214 void SelectionManager::startDrag(
3215 const DragGestureEvent& trigger,
3216 sal_Int8 sourceActions,
3217 sal_Int32,
3218 sal_Int32,
3219 const Reference< XTransferable >& transferable,
3220 const Reference< XDragSourceListener >& listener
3221 ) throw()
3223 #if OSL_DEBUG_LEVEL > 1
3224 fprintf( stderr, "startDrag( sourceActions = %x )\n", (int)sourceActions );
3225 #endif
3227 DragSourceDropEvent aDragFailedEvent;
3228 aDragFailedEvent.Source = static_cast< OWeakObject* >(this);
3229 aDragFailedEvent.DragSource = static_cast< XDragSource* >(this);
3230 aDragFailedEvent.DragSourceContext = new DragSourceContext( None, CurrentTime, *this );
3231 aDragFailedEvent.DropAction = DNDConstants::ACTION_NONE;
3232 aDragFailedEvent.DropSuccess = sal_False;
3234 if( m_aDragRunning.check() )
3236 if( listener.is() )
3237 listener->dragDropEnd( aDragFailedEvent );
3239 #if OSL_DEBUG_LEVEL > 1
3240 fprintf( stderr, "*** ERROR *** second drag and drop started.\n" );
3241 if( m_xDragSourceListener.is() )
3242 fprintf( stderr, "*** ERROR *** drag source listener already set.\n" );
3243 else
3244 fprintf( stderr, "*** ERROR *** drag thread already running.\n" );
3245 #endif
3246 return;
3250 ClearableMutexGuard aGuard(m_aMutex);
3252 // first get the current pointer position and the window that
3253 // the pointer is located in. since said window should be one
3254 // of our DropTargets at the time of executeDrag we can use
3255 // them for a start
3256 Window aRoot, aParent, aChild;
3257 int root_x, root_y, win_x, win_y;
3258 unsigned int mask;
3260 ::std::hash_map< Window, DropTargetEntry >::const_iterator it;
3261 it = m_aDropTargets.begin();
3262 while( it != m_aDropTargets.end() )
3264 if( XQueryPointer( m_pDisplay, it->second.m_aRootWindow,
3265 &aRoot, &aParent,
3266 &root_x, &root_y,
3267 &win_x, &win_y,
3268 &mask ) )
3270 aParent = it->second.m_aRootWindow;
3271 break;
3273 ++it;
3276 // don't start DnD if there is none of our windows on the same screen as
3277 // the pointer or if no mouse button is pressed
3278 if( it == m_aDropTargets.end() || (mask & (Button1Mask|Button2Mask|Button3Mask)) == 0 )
3280 aGuard.clear();
3281 if( listener.is() )
3282 listener->dragDropEnd( aDragFailedEvent );
3283 return;
3286 // try to find which of our drop targets is the drag source
3287 // if that drop target is deregistered we should stop executing
3288 // the drag (actually this is a poor substitute for an "endDrag"
3289 // method ).
3290 m_aDragSourceWindow = None;
3291 aParent = aRoot = it->second.m_aRootWindow;
3294 XTranslateCoordinates( m_pDisplay, aRoot, aParent, root_x, root_y, &win_x, &win_y, &aChild );
3295 if( aChild != None && m_aDropTargets.find( aChild ) != m_aDropTargets.end() )
3297 m_aDragSourceWindow = aChild;
3298 #if OSL_DEBUG_LEVEL > 1
3299 fprintf( stderr, "found drag source window 0x%lx\n", m_aDragSourceWindow );
3300 #endif
3301 break;
3303 aParent = aChild;
3304 } while( aChild != None );
3307 #if OSL_DEBUG_LEVEL > 1
3308 fprintf( stderr, "try to grab pointer ... " );
3309 #endif
3310 int nPointerGrabSuccess =
3311 XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True,
3312 DRAG_EVENT_MASK,
3313 GrabModeAsync, GrabModeAsync,
3314 None,
3315 None,
3316 CurrentTime );
3317 #if OSL_DEBUG_LEVEL > 1
3318 fprintf( stderr, "%d\n", nPointerGrabSuccess );
3319 #endif
3320 #if OSL_DEBUG_LEVEL > 1
3321 fprintf( stderr, "try to grab keyboard ... " );
3322 #endif
3323 int nKeyboardGrabSuccess =
3324 XGrabKeyboard( m_pDisplay, it->second.m_aRootWindow, True,
3325 GrabModeAsync, GrabModeAsync, CurrentTime );
3326 #if OSL_DEBUG_LEVEL > 1
3327 fprintf( stderr, "%d\n", nKeyboardGrabSuccess );
3328 #endif
3329 if( nPointerGrabSuccess != GrabSuccess || nKeyboardGrabSuccess != GrabSuccess )
3331 if( nPointerGrabSuccess == GrabSuccess )
3332 XUngrabPointer( m_pDisplay, CurrentTime );
3333 if( nKeyboardGrabSuccess == GrabSuccess )
3334 XUngrabKeyboard( m_pDisplay, CurrentTime );
3335 XFlush( m_pDisplay );
3336 aGuard.clear();
3337 if( listener.is() )
3338 listener->dragDropEnd( aDragFailedEvent );
3339 return;
3342 m_xDragSourceTransferable = transferable;
3343 m_xDragSourceListener = listener;
3344 m_aDragFlavors = transferable->getTransferDataFlavors();
3345 m_aCurrentCursor = None;
3347 requestOwnership( m_nXdndSelection );
3349 ::std::list< Atom > aConversions;
3350 ::std::list< Atom >::const_iterator type_it;
3351 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3353 int nTypes = aConversions.size();
3354 Atom* pTypes = (Atom*)alloca( sizeof(Atom)*nTypes );
3355 type_it = aConversions.begin();
3356 for( int n = 0; n < nTypes; n++, ++type_it )
3357 pTypes[n] = *type_it;
3359 XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes );
3361 m_nSourceActions = sourceActions | DNDConstants::ACTION_DEFAULT;
3362 m_nUserDragAction = DNDConstants::ACTION_MOVE & m_nSourceActions;
3363 if( ! m_nUserDragAction )
3364 m_nUserDragAction = DNDConstants::ACTION_COPY & m_nSourceActions;
3365 if( ! m_nUserDragAction )
3366 m_nUserDragAction = DNDConstants::ACTION_LINK & m_nSourceActions;
3367 m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT;
3368 m_bDropSent = false;
3369 m_bDropSuccess = false;
3370 m_bWaitingForPrimaryConversion = false;
3371 m_nDragButton = Button1; // default to left button
3372 if( trigger.Event.getValueTypeName().equalsAsciiL( "com.sun.star.awt.MouseEvent", 27 ) )
3374 MouseEvent aEvent;
3375 trigger.Event >>= aEvent;
3376 if( aEvent.Buttons & MouseButton::LEFT )
3377 m_nDragButton = Button1;
3378 else if( aEvent.Buttons & MouseButton::RIGHT )
3379 m_nDragButton = Button3;
3380 else if( aEvent.Buttons & MouseButton::MIDDLE )
3381 m_nDragButton = Button2;
3383 #if OSL_DEBUG_LEVEL > 1
3384 fprintf( stderr, "m_nUserDragAction = %x\n", (int)m_nUserDragAction );
3385 #endif
3386 updateDragWindow( root_x, root_y, aRoot );
3387 m_nUserDragAction = ~0;
3388 updateDragAction( mask );
3391 m_aDragRunning.set();
3392 m_aDragExecuteThread = osl_createSuspendedThread( call_SelectionManager_runDragExecute, this );
3393 if( m_aDragExecuteThread )
3394 osl_resumeThread( m_aDragExecuteThread );
3395 else
3397 #if OSL_DEBUG_LEVEL > 1
3398 fprintf( stderr, "osl_createSuspendedThread failed for drag execute\n" );
3399 #endif
3400 m_xDragSourceListener.clear();
3401 m_xDragSourceTransferable.clear();
3403 m_bDropSent = false;
3404 m_bDropSuccess = false;
3405 m_bWaitingForPrimaryConversion = false;
3406 m_aDropWindow = None;
3407 m_aDropProxy = None;
3408 m_nCurrentProtocolVersion = nXdndProtocolRevision;
3409 m_nNoPosX = 0;
3410 m_nNoPosY = 0;
3411 m_nNoPosWidth = 0;
3412 m_nNoPosHeight = 0;
3413 m_aCurrentCursor = None;
3415 XUngrabPointer( m_pDisplay, CurrentTime );
3416 XUngrabKeyboard( m_pDisplay, CurrentTime );
3417 XFlush( m_pDisplay );
3419 m_aDragRunning.reset();
3421 if( listener.is() )
3422 listener->dragDropEnd( aDragFailedEvent );
3426 void SelectionManager::runDragExecute( void* pThis )
3428 SelectionManager* This = (SelectionManager*)pThis;
3429 This->dragDoDispatch();
3432 void SelectionManager::dragDoDispatch()
3435 // do drag
3436 // m_xDragSourceListener will be cleared on finished drop
3437 #if OSL_DEBUG_LEVEL > 1
3438 fprintf( stderr, "begin executeDrag dispatching\n" );
3439 #endif
3440 TimeValue aTVal;
3441 aTVal.Seconds = 0;
3442 aTVal.Nanosec = 200000000;
3443 oslThread aThread = m_aDragExecuteThread;
3444 while( m_xDragSourceListener.is() && ( ! m_bDropSent || time(NULL)-m_nDropTimeout < 5 ) && osl_scheduleThread( aThread ) )
3446 // let the thread in the run method do the dispatching
3447 // just look occasionally here whether drop timed out or is completed
3448 osl_waitThread( &aTVal );
3450 #if OSL_DEBUG_LEVEL > 1
3451 fprintf( stderr, "end executeDrag dispatching\n" );
3452 #endif
3454 ClearableMutexGuard aGuard(m_aMutex);
3456 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3457 Reference< XTransferable > xTransferable( m_xDragSourceTransferable );
3458 m_xDragSourceListener.clear();
3459 m_xDragSourceTransferable.clear();
3461 DragSourceDropEvent dsde;
3462 dsde.Source = static_cast< OWeakObject* >(this);
3463 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
3464 dsde.DragSource = static_cast< XDragSource* >(this);
3465 dsde.DropAction = DNDConstants::ACTION_NONE;
3466 dsde.DropSuccess = sal_False;
3468 // cleanup after drag
3469 if( m_bWaitingForPrimaryConversion )
3470 getAdaptor( XA_PRIMARY )->clearTransferable();
3472 m_bDropSent = false;
3473 m_bDropSuccess = false;
3474 m_bWaitingForPrimaryConversion = false;
3475 m_aDropWindow = None;
3476 m_aDropProxy = None;
3477 m_nCurrentProtocolVersion = nXdndProtocolRevision;
3478 m_nNoPosX = 0;
3479 m_nNoPosY = 0;
3480 m_nNoPosWidth = 0;
3481 m_nNoPosHeight = 0;
3482 m_aCurrentCursor = None;
3484 XUngrabPointer( m_pDisplay, CurrentTime );
3485 XUngrabKeyboard( m_pDisplay, CurrentTime );
3486 XFlush( m_pDisplay );
3488 m_aDragExecuteThread = NULL;
3489 m_aDragRunning.reset();
3491 aGuard.clear();
3492 if( xListener.is() )
3494 xTransferable.clear();
3495 xListener->dragDropEnd( dsde );
3498 osl_destroyThread( aThread );
3502 * XDragSourceContext
3505 sal_Int32 SelectionManager::getCurrentCursor()
3507 return m_aCurrentCursor;
3510 // ------------------------------------------------------------------------
3512 void SelectionManager::setCursor( sal_Int32 cursor, Window aDropWindow, Time )
3514 MutexGuard aGuard( m_aMutex );
3515 if( aDropWindow == m_aDropWindow && Cursor(cursor) != m_aCurrentCursor )
3517 if( m_xDragSourceListener.is() && ! m_bDropSent )
3519 m_aCurrentCursor = cursor;
3520 XChangeActivePointerGrab( m_pDisplay, DRAG_EVENT_MASK, cursor, CurrentTime );
3521 XFlush( m_pDisplay );
3526 // ------------------------------------------------------------------------
3528 void SelectionManager::setImage( sal_Int32, Window, Time )
3532 // ------------------------------------------------------------------------
3534 void SelectionManager::transferablesFlavorsChanged()
3536 MutexGuard aGuard(m_aMutex);
3538 m_aDragFlavors = m_xDragSourceTransferable->getTransferDataFlavors();
3539 int i;
3541 std::list< Atom > aConversions;
3542 std::list< Atom >::const_iterator type_it;
3544 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3546 int nTypes = aConversions.size();
3547 Atom* pTypes = (Atom*)alloca( sizeof(Atom)*aConversions.size() );
3548 for( i = 0, type_it = aConversions.begin(); type_it != aConversions.end(); ++type_it, i++ )
3549 pTypes[i] = *type_it;
3550 XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes );
3552 if( m_aCurrentDropWindow != None && m_nCurrentProtocolVersion >= 0 )
3554 // send synthetic leave and enter events
3556 XEvent aEvent;
3558 aEvent.type = ClientMessage;
3559 aEvent.xclient.display = m_pDisplay;
3560 aEvent.xclient.format = 32;
3561 aEvent.xclient.window = m_aDropWindow;
3562 aEvent.xclient.data.l[0] = m_aWindow;
3564 aEvent.xclient.message_type = m_nXdndLeave;
3565 aEvent.xclient.data.l[1] = 0;
3566 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3568 aEvent.xclient.message_type = m_nXdndEnter;
3569 aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24;
3570 memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3571 // fill in data types
3572 if( nTypes > 3 )
3573 aEvent.xclient.data.l[1] |= 1;
3574 for( int j = 0; j < nTypes && j < 3; j++ )
3575 aEvent.xclient.data.l[j+2] = pTypes[j];
3577 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3582 * dispatch loop
3585 // ------------------------------------------------------------------------
3587 bool SelectionManager::handleXEvent( XEvent& rEvent )
3590 * since we are XConnectionListener to a second X display
3591 * to get client messages it is essential not to dispatch
3592 * events twice that we get on both connections
3594 * #95201# between dispatching ButtonPress and startDrag
3595 * the user can already have released the mouse. The ButtonRelease
3596 * will then be dispatched in VCLs queue and never turn up here.
3597 * Which is not so good, since startDrag will XGrabPointer and
3598 * XGrabKeyboard -> solid lock.
3600 if( rEvent.xany.display != m_pDisplay
3601 && rEvent.type != ClientMessage
3602 && rEvent.type != ButtonPress
3603 && rEvent.type != ButtonRelease
3605 return false;
3607 bool bHandled = false;
3608 switch (rEvent.type)
3610 case SelectionClear:
3612 ClearableMutexGuard aGuard(m_aMutex);
3613 #if OSL_DEBUG_LEVEL > 1
3614 fprintf( stderr, "SelectionClear for selection %s\n",
3615 OUStringToOString( getString( rEvent.xselectionclear.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
3617 #endif
3618 SelectionAdaptor* pAdaptor = getAdaptor( rEvent.xselectionclear.selection );
3619 std::hash_map< Atom, Selection* >::iterator it( m_aSelections.find( rEvent.xselectionclear.selection ) );
3620 if( it != m_aSelections.end() )
3621 it->second->m_bOwner = false;
3622 aGuard.clear();
3623 if ( pAdaptor )
3624 pAdaptor->clearTransferable();
3626 break;
3628 case SelectionRequest:
3629 bHandled = handleSelectionRequest( rEvent.xselectionrequest );
3630 break;
3631 case PropertyNotify:
3632 if( rEvent.xproperty.window == m_aWindow ||
3633 rEvent.xproperty.window == m_aCurrentDropWindow
3635 bHandled = handleReceivePropertyNotify( rEvent.xproperty );
3636 else
3637 bHandled = handleSendPropertyNotify( rEvent.xproperty );
3638 break;
3639 case SelectionNotify:
3640 bHandled = handleSelectionNotify( rEvent.xselection );
3641 break;
3642 case ClientMessage:
3643 // messages from drag target
3644 if( rEvent.xclient.message_type == m_nXdndStatus ||
3645 rEvent.xclient.message_type == m_nXdndFinished )
3646 bHandled = handleDragEvent( rEvent );
3647 // messages from drag source
3648 else if(
3649 rEvent.xclient.message_type == m_nXdndEnter ||
3650 rEvent.xclient.message_type == m_nXdndLeave ||
3651 rEvent.xclient.message_type == m_nXdndPosition ||
3652 rEvent.xclient.message_type == m_nXdndDrop
3654 bHandled = handleDropEvent( rEvent.xclient );
3655 break;
3656 case EnterNotify:
3657 case LeaveNotify:
3658 case MotionNotify:
3659 case ButtonPress:
3660 case ButtonRelease:
3661 case KeyPress:
3662 case KeyRelease:
3663 bHandled = handleDragEvent( rEvent );
3664 break;
3665 default:
3668 return bHandled;
3671 // ------------------------------------------------------------------------
3673 void SelectionManager::dispatchEvent( int millisec )
3675 pollfd aPollFD;
3676 XEvent event;
3678 // query socket handle to poll on
3679 aPollFD.fd = ConnectionNumber( m_pDisplay );
3680 aPollFD.events = POLLIN;
3681 aPollFD.revents = 0;
3683 // wait for activity (outside the xlib)
3684 if( poll( &aPollFD, 1, millisec ) > 0 )
3686 // now acquire the mutex to prevent other threads
3687 // from using the same X connection
3688 ResettableMutexGuard aGuard(m_aMutex);
3690 // prevent that another thread already ate the input
3691 // this can happen if e.g. another thread does
3692 // an X request getting a response. the response
3693 // would be removed from the queue and we would end up
3694 // with an empty socket here
3695 if( poll( &aPollFD, 1, 0 ) > 0 )
3697 int nPending = 1;
3698 while( nPending )
3700 nPending = XPending( m_pDisplay );
3701 if( nPending )
3703 XNextEvent( m_pDisplay, &event );
3704 aGuard.clear();
3705 handleXEvent( event );
3706 aGuard.reset();
3713 // ------------------------------------------------------------------------
3715 void SelectionManager::run( void* pThis )
3717 #if OSL_DEBUG_LEVEL > 1
3718 fprintf(stderr, "SelectionManager::run\n" );
3719 #endif
3720 // dispatch until the cows come home
3722 SelectionManager* This = (SelectionManager*)pThis;
3724 timeval aLast;
3725 gettimeofday( &aLast, 0 );
3727 while( osl_scheduleThread(This->m_aThread) )
3729 This->dispatchEvent( 1000 );
3731 timeval aNow;
3732 gettimeofday( &aNow, 0 );
3734 if( (aNow.tv_sec - aLast.tv_sec) > 0 )
3736 ClearableMutexGuard aGuard(This->m_aMutex);
3737 std::list< std::pair< SelectionAdaptor*, Reference< XInterface > > > aChangeList;
3739 for( std::hash_map< Atom, Selection* >::iterator it = This->m_aSelections.begin(); it != This->m_aSelections.end(); ++it )
3741 if( it->first != This->m_nXdndSelection && ! it->second->m_bOwner )
3743 Window aOwner = XGetSelectionOwner( This->m_pDisplay, it->first );
3744 if( aOwner != it->second->m_aLastOwner )
3746 it->second->m_aLastOwner = aOwner;
3747 std::pair< SelectionAdaptor*, Reference< XInterface > >
3748 aKeep( it->second->m_pAdaptor, it->second->m_pAdaptor->getReference() );
3749 aChangeList.push_back( aKeep );
3753 aGuard.clear();
3754 while( aChangeList.begin() != aChangeList.end() )
3756 aChangeList.front().first->fireContentsChanged();
3757 aChangeList.pop_front();
3759 aLast = aNow;
3762 #if OSL_DEBUG_LEVEL > 1
3763 fprintf(stderr, "SelectionManager::run end\n" );
3764 #endif
3767 // ------------------------------------------------------------------------
3769 sal_Bool SelectionManager::handleEvent( const Any& event ) throw()
3771 Sequence< sal_Int8 > aSeq;
3772 if( (event >>= aSeq) )
3774 XEvent* pEvent = (XEvent*)aSeq.getArray();
3775 Time nTimestamp = CurrentTime;
3776 if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease )
3777 nTimestamp = pEvent->xbutton.time;
3778 else if( pEvent->type == KeyPress || pEvent->type == KeyRelease )
3779 nTimestamp = pEvent->xkey.time;
3780 else if( pEvent->type == MotionNotify )
3781 nTimestamp = pEvent->xmotion.time;
3782 else if( pEvent->type == PropertyNotify )
3783 nTimestamp = pEvent->xproperty.time;
3785 if( nTimestamp != CurrentTime )
3787 MutexGuard aGuard(m_aMutex);
3789 m_nSelectionTimestamp = nTimestamp;
3792 return sal_Bool( handleXEvent( *pEvent ) );
3794 else
3796 #if OSL_DEBUG_LEVEL > 1
3797 fprintf( stderr, "SelectionManager got downing event\n" );
3798 #endif
3799 MutexGuard aGuard(m_aMutex);
3800 // stop dispatching
3801 if( m_aThread )
3802 osl_terminateThread( m_aThread );
3803 m_xDisplayConnection->removeEventHandler( Any(), this );
3804 m_xDisplayConnection.clear();
3806 return sal_True;
3809 // ------------------------------------------------------------------------
3811 void SelectionManager::registerHandler( Atom selection, SelectionAdaptor& rAdaptor )
3813 MutexGuard aGuard(m_aMutex);
3815 Selection* pNewSelection = new Selection();
3816 pNewSelection->m_pAdaptor = &rAdaptor;
3817 pNewSelection->m_aAtom = selection;
3818 m_aSelections[ selection ] = pNewSelection;
3821 // ------------------------------------------------------------------------
3823 void SelectionManager::deregisterHandler( Atom selection )
3825 MutexGuard aGuard(m_aMutex);
3827 ::std::hash_map< Atom, Selection* >::iterator it =
3828 m_aSelections.find( selection );
3829 if( it != m_aSelections.end() )
3831 delete it->second->m_pPixmap;
3832 delete it->second;
3833 m_aSelections.erase( it );
3837 // ------------------------------------------------------------------------
3839 void SelectionManager::registerDropTarget( Window aWindow, DropTarget* pTarget )
3841 MutexGuard aGuard(m_aMutex);
3843 // sanity check
3844 ::std::hash_map< Window, DropTargetEntry >::const_iterator it =
3845 m_aDropTargets.find( aWindow );
3846 if( it != m_aDropTargets.end() )
3847 OSL_ASSERT( "attempt to register window as drop target twice" );
3848 else if( aWindow && m_pDisplay )
3850 XSelectInput( m_pDisplay, aWindow, PropertyChangeMask );
3852 // set XdndAware
3853 XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&nXdndProtocolRevision, 1 );
3855 DropTargetEntry aEntry( pTarget );
3856 // get root window of window (in 99.999% of all cases this will be
3857 // DefaultRootWindow( m_pDisplay )
3858 int x, y;
3859 unsigned int w, h, bw, d;
3860 XGetGeometry( m_pDisplay, aWindow, &aEntry.m_aRootWindow,
3861 &x, &y, &w, &h, &bw, &d );
3862 m_aDropTargets[ aWindow ] = aEntry;
3864 else
3865 OSL_ASSERT( "attempt to register None as drop target" );
3868 // ------------------------------------------------------------------------
3870 void SelectionManager::deregisterDropTarget( Window aWindow )
3872 ClearableMutexGuard aGuard(m_aMutex);
3874 m_aDropTargets.erase( aWindow );
3875 if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() )
3877 // abort drag
3878 std::hash_map< Window, DropTargetEntry >::const_iterator it =
3879 m_aDropTargets.find( m_aDropWindow );
3880 if( it != m_aDropTargets.end() )
3882 DropTargetEvent dte;
3883 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
3884 aGuard.clear();
3885 it->second.m_pTarget->dragExit( dte );
3887 else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
3889 // send XdndLeave
3890 XEvent aEvent;
3891 aEvent.type = ClientMessage;
3892 aEvent.xclient.display = m_pDisplay;
3893 aEvent.xclient.format = 32;
3894 aEvent.xclient.message_type = m_nXdndLeave;
3895 aEvent.xclient.window = m_aDropWindow;
3896 aEvent.xclient.data.l[0] = m_aWindow;
3897 memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
3898 m_aDropWindow = m_aDropProxy = None;
3899 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3901 // notify the listener
3902 DragSourceDropEvent dsde;
3903 dsde.Source = static_cast< OWeakObject* >(this);
3904 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
3905 dsde.DragSource = static_cast< XDragSource* >(this);
3906 dsde.DropAction = DNDConstants::ACTION_NONE;
3907 dsde.DropSuccess = sal_False;
3908 Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3909 m_xDragSourceListener.clear();
3910 aGuard.clear();
3911 xListener->dragDropEnd( dsde );
3916 * SelectionAdaptor
3919 Reference< XTransferable > SelectionManager::getTransferable() throw()
3921 return m_xDragSourceTransferable;
3924 // ------------------------------------------------------------------------
3926 void SelectionManager::clearTransferable() throw()
3928 m_xDragSourceTransferable.clear();
3931 // ------------------------------------------------------------------------
3933 void SelectionManager::fireContentsChanged() throw()
3937 // ------------------------------------------------------------------------
3939 Reference< XInterface > SelectionManager::getReference() throw()
3941 return Reference< XInterface >( static_cast<OWeakObject*>(this) );
3944 // ------------------------------------------------------------------------
3947 * SelectionManagerHolder
3950 SelectionManagerHolder::SelectionManagerHolder() :
3951 ::cppu::WeakComponentImplHelper3<
3952 XDragSource,
3953 XInitialization,
3954 XServiceInfo > (m_aMutex)
3958 // ------------------------------------------------------------------------
3960 SelectionManagerHolder::~SelectionManagerHolder()
3964 // ------------------------------------------------------------------------
3966 void SelectionManagerHolder::initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception )
3968 OUString aDisplayName;
3970 if( arguments.getLength() > 0 )
3972 Reference< XDisplayConnection > xConn;
3973 arguments.getConstArray()[0] >>= xConn;
3974 if( xConn.is() )
3976 Any aIdentifier;
3977 aIdentifier >>= aDisplayName;
3981 SelectionManager& rManager = SelectionManager::get( aDisplayName );
3982 rManager.initialize( arguments );
3983 m_xRealDragSource = static_cast< XDragSource* >(&rManager);
3987 * XDragSource
3990 sal_Bool SelectionManagerHolder::isDragImageSupported() throw()
3992 return m_xRealDragSource.is() ? m_xRealDragSource->isDragImageSupported() : sal_False;
3995 // ------------------------------------------------------------------------
3997 sal_Int32 SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction ) throw()
3999 return m_xRealDragSource.is() ? m_xRealDragSource->getDefaultCursor( dragAction ) : 0;
4002 // ------------------------------------------------------------------------
4004 void SelectionManagerHolder::startDrag(
4005 const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger,
4006 sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image,
4007 const Reference< ::com::sun::star::datatransfer::XTransferable >& transferable,
4008 const Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener
4009 ) throw()
4011 if( m_xRealDragSource.is() )
4012 m_xRealDragSource->startDrag( trigger, sourceActions, cursor, image, transferable, listener );
4015 // ------------------------------------------------------------------------
4018 * XServiceInfo
4021 // ------------------------------------------------------------------------
4023 OUString SelectionManagerHolder::getImplementationName() throw()
4025 return OUString::createFromAscii(XDND_IMPLEMENTATION_NAME);
4028 // ------------------------------------------------------------------------
4030 sal_Bool SelectionManagerHolder::supportsService( const OUString& ServiceName ) throw()
4032 Sequence < OUString > SupportedServicesNames = Xdnd_getSupportedServiceNames();
4034 for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; )
4035 if (SupportedServicesNames[n].compareTo(ServiceName) == 0)
4036 return sal_True;
4038 return sal_False;
4041 // ------------------------------------------------------------------------
4043 Sequence< OUString > SelectionManagerHolder::getSupportedServiceNames() throw()
4045 return Xdnd_getSupportedServiceNames();
4049 // ------------------------------------------------------------------------