xref: /trunk/main/vcl/unx/generic/dtrans/X11_selection.cxx (revision 0c4cad538f83b30756fa17f65fffdbec2517f7fc)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include "unx/saldisp.hxx"
26 #include "unx/saldata.hxx"
27 
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/time.h>
32 
33 #include "tools/prex.h"
34 #include <X11/Xatom.h>
35 #include <X11/keysym.h>
36 #include <X11/Xutil.h>
37 #include "tools/postx.h"
38 #if defined(LINUX) || defined(NETBSD) || defined (FREEBSD)
39 #include <sys/poll.h>
40 #else
41 #include <poll.h>
42 #endif
43 #include <sal/alloca.h>
44 
45 #include <X11_selection.hxx>
46 #include <X11_clipboard.hxx>
47 #include <X11_transferable.hxx>
48 #include <X11_dndcontext.hxx>
49 #include <bmp.hxx>
50 
51 #include "vcl/svapp.hxx"
52 
53 // pointer bitmaps
54 #include <copydata_curs.h>
55 #include <copydata_mask.h>
56 #include <movedata_curs.h>
57 #include <movedata_mask.h>
58 #include <linkdata_curs.h>
59 #include <linkdata_mask.h>
60 #include <nodrop_curs.h>
61 #include <nodrop_mask.h>
62 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
63 #include <com/sun/star/awt/MouseEvent.hpp>
64 #include <com/sun/star/awt/MouseButton.hpp>
65 #include <rtl/tencinfo.h>
66 #include <osl/process.h>
67 
68 #include <comphelper/processfactory.hxx>
69 #include <vos/mutex.hxx>
70 
71 #define DRAG_EVENT_MASK ButtonPressMask         |\
72                             ButtonReleaseMask       |\
73                             PointerMotionMask       |\
74                             EnterWindowMask         |\
75                             LeaveWindowMask
76 
77 namespace {
78 
79 namespace css = com::sun::star;
80 
81 }
82 
83 using namespace com::sun::star::datatransfer;
84 using namespace com::sun::star::datatransfer::dnd;
85 using namespace com::sun::star::lang;
86 using namespace com::sun::star::awt;
87 using namespace com::sun::star::uno;
88 using namespace com::sun::star::frame;
89 using namespace cppu;
90 using namespace osl;
91 using namespace rtl;
92 
93 using namespace x11;
94 
95 // stubs to satisfy Solaris compiler's rather rigid linking warning
96 extern "C"
97 {
call_SelectionManager_run(void * pMgr)98     static void call_SelectionManager_run( void * pMgr )
99     {
100         SelectionManager::run( pMgr );
101     }
102 
call_SelectionManager_runDragExecute(void * pMgr)103     static void call_SelectionManager_runDragExecute( void * pMgr )
104     {
105         SelectionManager::runDragExecute( pMgr );
106     }
107 }
108 
109 static const long nXdndProtocolRevision = 5;
110 
111 // mapping between mime types (or what the office thinks of mime types)
112 // and X convention types
113 struct NativeTypeEntry
114 {
115     Atom            nAtom;
116     const char*     pType;              // Mime encoding on our side
117     const char*     pNativeType;        // string corresponding to nAtom for the case of nAtom being uninitialized
118     int             nFormat;            // the corresponding format
119 };
120 
121 // the convention for Xdnd is mime types as specified by the corresponding
122 // RFC's with the addition that text/plain without charset tag contains iso8859-1
123 // sadly some applications (e.g. gtk) do not honor the mimetype only rule,
124 // so for compatibility add UTF8_STRING
125 static NativeTypeEntry aXdndConversionTab[] =
126 {
127     { 0, "text/plain;charset=iso8859-1", "text/plain", 8 },
128     { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }
129 };
130 
131 // for clipboard and primary selections there is only a convention for text
132 // that the encoding name of the text is taken as type in all capitalized letters
133 static NativeTypeEntry aNativeConversionTab[] =
134 {
135     { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 },
136     { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 },
137     { 0, "text/plain;charset=utf-8", "UTF-8", 8 },
138     { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 },
139     // ISO encodings
140     { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 },
141     { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 },
142     { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 },
143     { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 },
144     { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 },
145     { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 },
146     { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 },
147     { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 },
148     { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 },
149     { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 },
150     { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 },
151     { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 },
152     // Asian encodings
153     { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 },
154     { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 },
155     { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 },
156     { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 },
157     { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 },
158     { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 },
159     // eastern European encodings
160     { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 },
161     { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 },
162     // String (== iso8859-1)
163     { XA_STRING, "text/plain;charset=iso8859-1", "STRING", 8 },
164     // special for compound text
165     { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 },
166 
167     // PIXMAP
168     { XA_PIXMAP, "image/bmp", "PIXMAP", 32 }
169 };
170 
getTextPlainEncoding(const OUString & rMimeType)171 rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType )
172 {
173     rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
174     OUString aMimeType( rMimeType.toAsciiLowerCase() );
175     sal_Int32 nIndex = 0;
176     if( aMimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain" , 10 ) )
177     {
178         if( aMimeType.getLength() == 10 ) // only "text/plain"
179             aEncoding = RTL_TEXTENCODING_ISO_8859_1;
180         else
181         {
182             while( nIndex != -1 )
183             {
184                 OUString aToken = aMimeType.getToken( 0, ';', nIndex );
185                 sal_Int32 nPos = 0;
186                 if( aToken.getToken( 0, '=', nPos ).equalsAsciiL( "charset", 7 ) )
187                 {
188                     OString aEncToken = OUStringToOString( aToken.getToken( 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 );
189                     aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() );
190                     if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
191                     {
192                         if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) )
193                             aEncoding = RTL_TEXTENCODING_UTF8;
194                     }
195                     if( aEncoding != RTL_TEXTENCODING_DONTKNOW )
196                         break;
197                 }
198             }
199         }
200     }
201 #if OSL_DEBUG_LEVEL > 1
202     if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
203         fprintf( stderr, "getTextPlainEncoding( %s ) failed\n", OUStringToOString( rMimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
204 #endif
205     return aEncoding;
206 }
207 
208 // ------------------------------------------------------------------------
209 
getInstances()210 ::std::hash_map< OUString, SelectionManager*, OUStringHash >& SelectionManager::getInstances()
211 {
212     static ::std::hash_map< OUString, SelectionManager*, OUStringHash > aInstances;
213     return aInstances;
214 }
215 
216 // ------------------------------------------------------------------------
217 
SelectionManager()218 SelectionManager::SelectionManager() :
219         m_nIncrementalThreshold( 15*1024 ),
220         m_pDisplay( NULL ),
221         m_aThread( NULL ),
222         m_aDragExecuteThread( NULL ),
223         m_aWindow( None ),
224         m_nSelectionTimeout( 0 ),
225         m_nSelectionTimestamp( CurrentTime ),
226         m_bDropEnterSent( true ),
227         m_aCurrentDropWindow( None ),
228         m_nDropTime( None ),
229         m_nLastDropAction( 0 ),
230         m_nLastX( 0 ),
231         m_nLastY( 0 ),
232         m_nDropTimestamp( 0 ),
233         m_bDropWaitingForCompletion( false ),
234         m_aDropWindow( None ),
235         m_aDropProxy( None ),
236         m_aDragSourceWindow( None ),
237         m_nLastDragX( 0 ),
238         m_nLastDragY( 0 ),
239         m_nNoPosX( 0 ),
240         m_nNoPosY( 0 ),
241         m_nNoPosWidth( 0 ),
242         m_nNoPosHeight( 0 ),
243         m_nDragButton( 0 ),
244         m_nUserDragAction( 0 ),
245         m_nTargetAcceptAction( 0 ),
246         m_nSourceActions( 0 ),
247         m_bLastDropAccepted( false ),
248         m_bDropSuccess( false ),
249         m_bDropSent( false ),
250         m_bWaitingForPrimaryConversion( false ),
251         m_nDragTimestamp( None ),
252         m_aMoveCursor( None ),
253         m_aCopyCursor( None ),
254         m_aLinkCursor( None ),
255         m_aNoneCursor( None ),
256         m_aCurrentCursor( None ),
257         m_nCurrentProtocolVersion( nXdndProtocolRevision ),
258         m_nCLIPBOARDAtom( None ),
259         m_nTARGETSAtom( None ),
260         m_nTIMESTAMPAtom( None ),
261         m_nTEXTAtom( None ),
262         m_nINCRAtom( None ),
263         m_nCOMPOUNDAtom( None ),
264         m_nMULTIPLEAtom( None ),
265         m_nUTF16Atom( None ),
266         m_nImageBmpAtom( None ),
267         m_nXdndAware( None ),
268         m_nXdndEnter( None ),
269         m_nXdndLeave( None ),
270         m_nXdndPosition( None ),
271         m_nXdndStatus( None ),
272         m_nXdndDrop( None ),
273         m_nXdndFinished( None ),
274         m_nXdndSelection( None ),
275         m_nXdndTypeList( None ),
276         m_nXdndProxy( None ),
277         m_nXdndActionCopy( None ),
278         m_nXdndActionMove( None ),
279         m_nXdndActionLink( None ),
280         m_nXdndActionAsk( None ),
281         m_nXdndActionPrivate( None ),
282         m_bShutDown( false )
283 {
284     m_aDropEnterEvent.data.l[0] = None;
285     m_aDragRunning.reset();
286 }
287 
createCursor(const char * pPointerData,const char * pMaskData,int width,int height,int hotX,int hotY)288 XLIB_Cursor SelectionManager::createCursor( const char* pPointerData, const char* pMaskData, int width, int height, int hotX, int hotY )
289 {
290     Pixmap aPointer;
291     Pixmap aMask;
292     XColor aBlack, aWhite;
293 
294     aBlack.pixel = BlackPixel( m_pDisplay, 0 );
295     aBlack.red = aBlack.green = aBlack.blue = 0;
296     aBlack.flags = DoRed | DoGreen | DoBlue;
297 
298     aWhite.pixel = WhitePixel( m_pDisplay, 0 );
299     aWhite.red = aWhite.green = aWhite.blue = 0xffff;
300     aWhite.flags = DoRed | DoGreen | DoBlue;
301 
302     aPointer =
303         XCreateBitmapFromData( m_pDisplay,
304                                m_aWindow,
305                                pPointerData,
306                                width,
307                                height );
308     aMask
309         = XCreateBitmapFromData( m_pDisplay,
310                                  m_aWindow,
311                                  pMaskData,
312                                  width,
313                                  height );
314     XLIB_Cursor aCursor =
315         XCreatePixmapCursor( m_pDisplay, aPointer, aMask,
316                              &aBlack, &aWhite,
317                              hotX,
318                              hotY );
319     XFreePixmap( m_pDisplay, aPointer );
320     XFreePixmap( m_pDisplay, aMask );
321 
322     return aCursor;
323 }
324 
initialize(const Sequence<Any> & arguments)325 void SelectionManager::initialize( const Sequence< Any >& arguments ) throw (::com::sun::star::uno::Exception)
326 {
327     MutexGuard aGuard(m_aMutex);
328 
329     if( ! m_xDisplayConnection.is() )
330     {
331         /*
332          *  first argument must be a ::com::sun::star::awt::XDisplayConnection
333          *  from this we will get the XEvents of the vcl event loop by
334          *  registering us as XEventHandler on it.
335          *
336          *  implementor's note:
337          *  FIXME:
338          *  finally the clipboard and XDND service is back in the module it belongs
339          *  now cleanup and sharing of resources with the normal vcl event loop
340          *  needs to be added. The display used would be that of the normal event loop
341          *  and synchronization should be done via the SolarMutex.
342          */
343         if( arguments.getLength() > 0 )
344             arguments.getConstArray()[0] >>= m_xDisplayConnection;
345         if( ! m_xDisplayConnection.is() )
346         {
347 #if 0
348             // for the time being try to live without XDisplayConnection
349             // for the sake of clipboard service
350             // clipboard service should be initialized with a XDisplayConnection
351             // in the future
352             Exception aExc;
353             aExc.Message = OUString::createFromAscii( "initialize me with a valid XDisplayConnection" );
354             aExc.Context = static_cast< OWeakObject* >(this);
355             throw aExc;
356 #endif
357         }
358         else
359             m_xDisplayConnection->addEventHandler( Any(), this, ~0 );
360     }
361 
362     if( !m_xBitmapConverter.is() )
363     {
364         if( arguments.getLength() > 2 )
365             arguments.getConstArray()[2] >>= m_xBitmapConverter;
366     }
367 
368     OUString aParam;
369     if( ! m_pDisplay )
370     {
371         OUString aUDisplay;
372         if( m_xDisplayConnection.is() )
373         {
374             Any aIdentifier;
375             aIdentifier = m_xDisplayConnection->getIdentifier();
376             aIdentifier >>= aUDisplay;
377         }
378 
379         OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) );
380 
381         m_pDisplay = XOpenDisplay( aDisplayName.getLength() ? aDisplayName.getStr() : NULL );
382 
383         if( m_pDisplay )
384         {
385 #ifdef SYNCHRONIZE
386             XSynchronize( m_pDisplay, True );
387 #endif
388             // clipboard selection
389             m_nCLIPBOARDAtom    = getAtom( OUString::createFromAscii( "CLIPBOARD" ) );
390 
391             // special targets
392             m_nTARGETSAtom      = getAtom( OUString::createFromAscii( "TARGETS" ) );
393             m_nTIMESTAMPAtom    = getAtom( OUString::createFromAscii( "TIMESTAMP" ) );
394             m_nTEXTAtom         = getAtom( OUString::createFromAscii( "TEXT" ) );
395             m_nINCRAtom         = getAtom( OUString::createFromAscii( "INCR" ) );
396             m_nCOMPOUNDAtom     = getAtom( OUString::createFromAscii( "COMPOUND_TEXT" ) );
397             m_nMULTIPLEAtom     = getAtom( OUString::createFromAscii( "MULTIPLE" ) );
398             m_nUTF16Atom        = getAtom( OUString::createFromAscii( "ISO10646-1" ) );
399 //          m_nUTF16Atom        = getAtom( OUString::createFromAscii( "text/plain;charset=ISO-10646-UCS-2" ) );
400             m_nImageBmpAtom     = getAtom( OUString::createFromAscii( "image/bmp" ) );
401 
402             // Atoms for Xdnd protocol
403             m_nXdndAware        = getAtom( OUString::createFromAscii( "XdndAware" ) );
404             m_nXdndEnter        = getAtom( OUString::createFromAscii( "XdndEnter" ) );
405             m_nXdndLeave        = getAtom( OUString::createFromAscii( "XdndLeave" ) );
406             m_nXdndPosition     = getAtom( OUString::createFromAscii( "XdndPosition" ) );
407             m_nXdndStatus       = getAtom( OUString::createFromAscii( "XdndStatus" ) );
408             m_nXdndDrop         = getAtom( OUString::createFromAscii( "XdndDrop" ) );
409             m_nXdndFinished     = getAtom( OUString::createFromAscii( "XdndFinished" ) );
410             m_nXdndSelection    = getAtom( OUString::createFromAscii( "XdndSelection" ) );
411             m_nXdndTypeList     = getAtom( OUString::createFromAscii( "XdndTypeList" ) );
412             m_nXdndProxy        = getAtom( OUString::createFromAscii( "XdndProxy" ) );
413             m_nXdndActionCopy   = getAtom( OUString::createFromAscii( "XdndActionCopy" ) );
414             m_nXdndActionMove   = getAtom( OUString::createFromAscii( "XdndActionMove" ) );
415             m_nXdndActionLink   = getAtom( OUString::createFromAscii( "XdndActionLink" ) );
416             m_nXdndActionAsk    = getAtom( OUString::createFromAscii( "XdndActionAsk" ) );
417             m_nXdndActionPrivate= getAtom( OUString::createFromAscii( "XdndActionPrivate" ) );
418 
419             // initialize map with member none
420             m_aAtomToString[ 0 ]= OUString::createFromAscii( "None" );
421             m_aAtomToString[ XA_PRIMARY ] = OUString::createFromAscii( "PRIMARY" );
422 
423             // create a (invisible) message window
424             m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ),
425                                              10, 10, 10, 10, 0, 0, 1 );
426 
427             // initialize threshold for incremental transfers
428             // ICCCM says it should be smaller that the max request size
429             // which in turn is guaranteed to be at least 16k bytes
430             m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024;
431 
432             if( m_aWindow )
433             {
434 #define createCursorFromXPM(name) createCursor((const char*)name##curs##_bits, (const char*)name##mask##_bits, name##curs_width, name##curs_height, name##curs_x_hot, name##curs_y_hot );
435                 // initialize default cursors
436                 m_aMoveCursor = createCursorFromXPM( movedata_);
437                 m_aCopyCursor = createCursorFromXPM( copydata_);
438                 m_aLinkCursor = createCursorFromXPM( linkdata_);
439                 m_aNoneCursor = createCursorFromXPM( nodrop_);
440 
441                 // just interested in SelectionClear/Notify/Request and PropertyChange
442                 XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask );
443                 // create the transferable for Drag operations
444                 m_xDropTransferable = new X11Transferable( *this, static_cast< OWeakObject* >(this), m_nXdndSelection );
445                 registerHandler( m_nXdndSelection, *this );
446 
447                 m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this );
448                 if( m_aThread )
449                     osl_resumeThread( m_aThread );
450 #if OSL_DEBUG_LEVEL > 1
451                 else
452                     fprintf( stderr, "SelectionManager::initialize: creation of dispatch thread failed !\n" );
453 #endif
454             }
455         }
456     }
457 }
458 
459 // ------------------------------------------------------------------------
460 
~SelectionManager()461 SelectionManager::~SelectionManager()
462 {
463 #if OSL_DEBUG_LEVEL > 1
464     fprintf( stderr, "SelectionManager::~SelectionManager (%s)\n", m_pDisplay ? DisplayString(m_pDisplay) : "no display" );
465 #endif
466     {
467         MutexGuard aGuard( *Mutex::getGlobalMutex() );
468 
469         ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it;
470         for( it = getInstances().begin(); it != getInstances().end(); ++it )
471             if( it->second == this )
472             {
473                 getInstances().erase( it );
474                 break;
475             }
476     }
477 
478     if( m_aThread )
479     {
480         osl_terminateThread( m_aThread );
481         osl_joinWithThread( m_aThread );
482         osl_destroyThread( m_aThread );
483     }
484 
485     if( m_aDragExecuteThread )
486     {
487         osl_terminateThread( m_aDragExecuteThread );
488         osl_joinWithThread( m_aDragExecuteThread );
489         m_aDragExecuteThread = NULL;
490         // thread handle is freed in dragDoDispatch()
491     }
492 
493     MutexGuard aGuard(m_aMutex);
494 
495 #if OSL_DEBUG_LEVEL > 1
496     fprintf( stderr, "shutting down SelectionManager\n" );
497 #endif
498 
499     if( m_xDisplayConnection.is() )
500     {
501         m_xDisplayConnection->removeEventHandler( Any(), this );
502         m_xDisplayConnection.clear();
503     }
504 
505     if( m_pDisplay )
506     {
507         deregisterHandler( m_nXdndSelection );
508         // destroy message window
509         if( m_aWindow )
510             XDestroyWindow( m_pDisplay, m_aWindow );
511         // release cursors
512         if (m_aMoveCursor != None)
513             XFreeCursor(m_pDisplay, m_aMoveCursor);
514         if (m_aCopyCursor != None)
515             XFreeCursor(m_pDisplay, m_aCopyCursor);
516         if (m_aLinkCursor != None)
517             XFreeCursor(m_pDisplay, m_aLinkCursor);
518         if (m_aNoneCursor != None)
519             XFreeCursor(m_pDisplay, m_aNoneCursor);
520 
521         // paranoia setting, the drag thread should have
522         // done that already
523         XUngrabPointer( m_pDisplay, CurrentTime );
524         XUngrabKeyboard( m_pDisplay, CurrentTime );
525 
526         XCloseDisplay( m_pDisplay );
527     }
528 }
529 
530 // ------------------------------------------------------------------------
531 
getAdaptor(Atom selection)532 SelectionAdaptor* SelectionManager::getAdaptor( Atom selection )
533 {
534     ::std::hash_map< Atom, Selection* >::iterator it =
535           m_aSelections.find( selection );
536     return it != m_aSelections.end() ? it->second->m_pAdaptor : NULL;
537 }
538 
539 // ------------------------------------------------------------------------
540 
convertFromCompound(const char * pText,int nLen)541 OUString SelectionManager::convertFromCompound( const char* pText, int nLen )
542 {
543     MutexGuard aGuard( m_aMutex );
544     OUString aRet;
545     if( nLen < 0 )
546         nLen = strlen( pText );
547 
548     char** pTextList = NULL;
549     int nTexts = 0;
550 
551     XTextProperty aProp;
552     aProp.value     = (unsigned char*)pText;
553     aProp.encoding  = m_nCOMPOUNDAtom;
554     aProp.format    = 8;
555     aProp.nitems    = nLen;
556     XmbTextPropertyToTextList( m_pDisplay,
557                                &aProp,
558                                &pTextList,
559                                &nTexts );
560     rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
561     for( int i = 0; i < nTexts; i++ )
562         aRet += OStringToOUString( pTextList[i], aEncoding );
563 
564     if( pTextList )
565         XFreeStringList( pTextList );
566 
567     return aRet;
568 }
569 
570 // ------------------------------------------------------------------------
571 
convertToCompound(const OUString & rText)572 OString SelectionManager::convertToCompound( const OUString& rText )
573 {
574     MutexGuard aGuard( m_aMutex );
575     XTextProperty aProp;
576     aProp.value = NULL;
577     aProp.encoding = XA_STRING;
578     aProp.format = 8;
579     aProp.nitems = 0;
580 
581     OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() );
582     char* pT = const_cast<char*>(aRet.getStr());
583 
584     XmbTextListToTextProperty( m_pDisplay,
585                                &pT,
586                                1,
587                                XCompoundTextStyle,
588                                &aProp );
589     if( aProp.value )
590     {
591         aRet = (char*)aProp.value;
592         XFree( aProp.value );
593 #ifdef SOLARIS
594         /*  #97070#
595          *  for currently unknown reasons XmbTextListToTextProperty on Solaris returns
596          *  no data in ISO8859-n encodings (at least for n = 1, 15)
597          *  in these encodings the directly converted text does the
598          *  trick, also.
599          */
600         if( ! aRet.getLength() && rText.getLength() )
601             aRet = OUStringToOString( rText, osl_getThreadTextEncoding() );
602 #endif
603     }
604     else
605         aRet = OString();
606 
607     return aRet;
608 }
609 
610 // ------------------------------------------------------------------------
611 
convertData(const css::uno::Reference<XTransferable> & xTransferable,Atom nType,Atom nSelection,int & rFormat,Sequence<sal_Int8> & rData)612 bool SelectionManager::convertData(
613                                    const css::uno::Reference< XTransferable >& xTransferable,
614                                    Atom nType,
615                                    Atom nSelection,
616                                    int& rFormat,
617                                    Sequence< sal_Int8 >& rData )
618 {
619     bool bSuccess = false;
620 
621     if( ! xTransferable.is() )
622         return bSuccess;
623 
624     try
625     {
626 
627         DataFlavor aFlavor;
628         aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat );
629 
630         sal_Int32 nIndex = 0;
631         if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "text/plain" ) == 0 )
632         {
633             if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "charset=utf-16" ) == 0 )
634                 aFlavor.DataType = getCppuType( (OUString *) 0 );
635             else
636                 aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
637         }
638         else
639             aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
640 
641         if( xTransferable->isDataFlavorSupported( aFlavor ) )
642         {
643             Any aValue( xTransferable->getTransferData( aFlavor ) );
644             if( aValue.getValueTypeClass() == TypeClass_STRING )
645             {
646                 OUString aString;
647                 aValue >>= aString;
648                 rData = Sequence< sal_Int8 >( (sal_Int8*)aString.getStr(), aString.getLength() * sizeof( sal_Unicode ) );
649                 bSuccess = true;
650             }
651             else if( aValue.getValueType() == getCppuType( (Sequence< sal_Int8 >*)0 ) )
652             {
653                 aValue >>= rData;
654                 bSuccess = true;
655             }
656         }
657         else if( aFlavor.MimeType.compareToAscii( "text/plain", 10 ) == 0 )
658         {
659             rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
660             bool bCompoundText = false;
661             if( nType == m_nCOMPOUNDAtom )
662                 bCompoundText = true;
663             else
664                 aEncoding = getTextPlainEncoding( aFlavor.MimeType );
665             if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText )
666             {
667                 aFlavor.MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" );
668                 aFlavor.DataType = getCppuType( (OUString *) 0 );
669                 if( xTransferable->isDataFlavorSupported( aFlavor ) )
670                 {
671                     Any aValue( xTransferable->getTransferData( aFlavor ) );
672                     OUString aString;
673                     aValue >>= aString;
674                     OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) );
675                     rData = Sequence< sal_Int8 >( (sal_Int8*)aByteString.getStr(), aByteString.getLength() * sizeof( sal_Char ) );
676                     bSuccess = true;
677                 }
678             }
679         }
680     }
681     // various exceptions possible ... which all lead to a failed conversion
682     // so simplify here to a catch all
683     catch(...)
684     {
685     }
686 
687     return bSuccess;
688 }
689 
690 // ------------------------------------------------------------------------
691 
get(const OUString & rDisplayName)692 SelectionManager& SelectionManager::get( const OUString& rDisplayName )
693 {
694     MutexGuard aGuard( *Mutex::getGlobalMutex() );
695 
696     OUString aDisplayName( rDisplayName );
697     if( ! aDisplayName.getLength() )
698         aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 );
699     SelectionManager* pInstance = NULL;
700 
701     ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it = getInstances().find( aDisplayName );
702     if( it != getInstances().end() )
703         pInstance = it->second;
704     else pInstance = getInstances()[ aDisplayName ] = new SelectionManager();
705 
706     return *pInstance;
707 }
708 
709 // ------------------------------------------------------------------------
710 
getString(Atom aAtom)711 const OUString& SelectionManager::getString( Atom aAtom )
712 {
713     MutexGuard aGuard(m_aMutex);
714 
715     ::std::hash_map< Atom, OUString >::const_iterator it;
716     if( ( it = m_aAtomToString.find( aAtom ) ) == m_aAtomToString.end() )
717     {
718         static OUString aEmpty;
719         char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : NULL;
720         if( ! pAtom )
721             return aEmpty;
722         OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) );
723         XFree( pAtom );
724         m_aStringToAtom[ aString ] = aAtom;
725         m_aAtomToString[ aAtom ] = aString;
726     }
727     return m_aAtomToString[ aAtom ];
728 }
729 
730 // ------------------------------------------------------------------------
731 
getAtom(const OUString & rString)732 Atom SelectionManager::getAtom( const OUString& rString )
733 {
734     MutexGuard aGuard(m_aMutex);
735 
736     ::std::hash_map< OUString, Atom, OUStringHash >::const_iterator it;
737     if( ( it = m_aStringToAtom.find( rString ) ) == m_aStringToAtom.end() )
738     {
739         static Atom nNoDisplayAtoms = 1;
740         Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1).getStr(), False ) : nNoDisplayAtoms++;
741         m_aStringToAtom[ rString ] = aAtom;
742         m_aAtomToString[ aAtom ] = rString;
743     }
744     return m_aStringToAtom[ rString ];
745 }
746 
747 // ------------------------------------------------------------------------
748 
requestOwnership(Atom selection)749 bool SelectionManager::requestOwnership( Atom selection )
750 {
751     bool bSuccess = false;
752     if( m_pDisplay && m_aWindow )
753     {
754         MutexGuard aGuard(m_aMutex);
755 
756         SelectionAdaptor* pAdaptor = getAdaptor( selection );
757         if( pAdaptor )
758         {
759             XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime );
760             if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow )
761                 bSuccess = true;
762 #if OSL_DEBUG_LEVEL > 1
763             fprintf( stderr, "%s ownership for selection %s\n",
764                      bSuccess ? "acquired" : "failed to acquire",
765                      OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
766 #endif
767             Selection* pSel = m_aSelections[ selection ];
768             pSel->m_bOwner = bSuccess;
769             delete pSel->m_pPixmap;
770             pSel->m_pPixmap = NULL;
771             pSel->m_nOrigTimestamp = m_nSelectionTimestamp;
772         }
773 #if OSL_DEBUG_LEVEL > 1
774         else
775             fprintf( stderr, "no adaptor for selection %s\n",
776                      OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
777 
778         if( pAdaptor->getTransferable().is() )
779         {
780             Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors();
781             for( int i = 0; i < aTypes.getLength(); i++ )
782             {
783                 fprintf( stderr, "   %s\n", OUStringToOString( aTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
784             }
785         }
786 #endif
787     }
788     return bSuccess;
789 }
790 
791 // ------------------------------------------------------------------------
792 
convertTypeToNative(const OUString & rType,Atom selection,int & rFormat,::std::list<Atom> & rConversions,bool bPushFront)793 void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront )
794 {
795     NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
796     int nTabEntries = selection == m_nXdndSelection
797         ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) :
798         sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]);
799 
800     OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) );
801     rFormat = 0;
802     for( int i = 0; i < nTabEntries; i++ )
803     {
804         if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) )
805         {
806             if( ! pTab[i].nAtom )
807                 pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
808             rFormat = pTab[i].nFormat;
809             if( bPushFront )
810                 rConversions.push_front( pTab[i].nAtom );
811             else
812                 rConversions.push_back( pTab[i].nAtom );
813             if( pTab[i].nFormat == XA_PIXMAP )
814             {
815                 if( bPushFront )
816                 {
817                     rConversions.push_front( XA_VISUALID );
818                     rConversions.push_front( XA_COLORMAP );
819                 }
820                 else
821                 {
822                     rConversions.push_back( XA_VISUALID );
823                     rConversions.push_back( XA_COLORMAP );
824                 }
825             }
826         }
827     }
828     if( ! rFormat )
829         rFormat = 8; // byte buffer
830     if( bPushFront )
831         rConversions.push_front( getAtom( rType ) );
832     else
833         rConversions.push_back( getAtom( rType ) );
834 };
835 
836 // ------------------------------------------------------------------------
837 
getNativeTypeList(const Sequence<DataFlavor> & rTypes,std::list<Atom> & rOutTypeList,Atom targetselection)838 void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection )
839 {
840     rOutTypeList.clear();
841 
842     int nFormat;
843     int nFlavors = rTypes.getLength();
844     const DataFlavor* pFlavors = rTypes.getConstArray();
845     bool bHaveText = false;
846     for( int i = 0; i < nFlavors; i++ )
847     {
848         if( pFlavors[i].MimeType.compareToAscii( "text/plain", 10 ) == 0)
849             bHaveText = true;
850         else
851             convertTypeToNative( pFlavors[i].MimeType, targetselection, nFormat, rOutTypeList );
852     }
853     if( bHaveText )
854     {
855         if( targetselection != m_nXdndSelection )
856         {
857             // only mimetypes should go into Xdnd type list
858             rOutTypeList.push_front( XA_STRING );
859             rOutTypeList.push_front( m_nCOMPOUNDAtom );
860         }
861         convertTypeToNative( OUString::createFromAscii( "text/plain;charset=utf-8" ), targetselection, nFormat, rOutTypeList, true );
862     }
863     if( targetselection != m_nXdndSelection )
864         rOutTypeList.push_back( m_nMULTIPLEAtom );
865 }
866 
867 // ------------------------------------------------------------------------
868 
convertTypeFromNative(Atom nType,Atom selection,int & rFormat)869 OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, int& rFormat )
870 {
871     NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
872     int nTabEntries = selection == m_nXdndSelection
873         ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) :
874         sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]);
875 
876     for( int i = 0; i < nTabEntries; i++ )
877     {
878         if( ! pTab[i].nAtom )
879             pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
880         if( nType == pTab[i].nAtom )
881         {
882             rFormat = pTab[i].nFormat;
883             return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 );
884         }
885     }
886     rFormat = 8;
887     return getString( nType );
888 }
889 
890 // ------------------------------------------------------------------------
891 
getPasteData(Atom selection,Atom type,Sequence<sal_Int8> & rData)892 bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData )
893 {
894     ResettableMutexGuard aGuard(m_aMutex);
895     ::std::hash_map< Atom, Selection* >::iterator it;
896     bool bSuccess = false;
897 
898 #if OSL_DEBUG_LEVEL > 1
899     OUString aSelection( getString( selection ) );
900     OUString aType( getString( type ) );
901     fprintf( stderr, "getPasteData( %s, native: %s )\n",
902              OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
903              OUStringToOString( aType, RTL_TEXTENCODING_ISO_8859_1 ).getStr()
904              );
905 #endif
906 
907     if( ! m_pDisplay )
908         return false;
909 
910     it = m_aSelections.find( selection );
911     if( it == m_aSelections.end() )
912         return false;
913 
914     XLIB_Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection );
915     if( aSelectionOwner == None )
916         return false;
917     if( aSelectionOwner == m_aWindow )
918     {
919         // probably bad timing led us here
920 #if OSL_DEBUG_LEVEL > 1
921         fprintf( stderr, "Innere Nabelschau\n" );
922 #endif
923         return false;
924     }
925 
926     // ICCCM recommends to destroy property before convert request unless
927     // parameters are transported; we do only in case of MULTIPLE,
928     // so destroy property unless target is MULTIPLE
929     if( type != m_nMULTIPLEAtom )
930         XDeleteProperty( m_pDisplay, m_aWindow, selection );
931 
932     XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime );
933     it->second->m_eState            = Selection::WaitingForResponse;
934     it->second->m_aRequestedType    = type;
935     it->second->m_aData             = Sequence< sal_Int8 >();
936     it->second->m_aDataArrived.reset();
937     // really start the request; if we don't flush the
938     // queue the request won't leave it because there are no more
939     // X calls after this until the data arrived or timeout
940     XFlush( m_pDisplay );
941 
942     // do a reschedule
943     struct timeval tv_last, tv_current;
944     gettimeofday( &tv_last, NULL );
945     tv_current = tv_last;
946 
947     XEvent aEvent;
948     do
949     {
950         bool bAdjustTime = false;
951         {
952             bool bHandle = false;
953 
954             if( XCheckTypedEvent( m_pDisplay,
955                                   PropertyNotify,
956                                   &aEvent
957                                   ) )
958             {
959                 bHandle = true;
960                 if( aEvent.xproperty.window == m_aWindow
961                     && aEvent.xproperty.atom == selection )
962                     bAdjustTime = true;
963             }
964             else
965             if( XCheckTypedEvent( m_pDisplay,
966                                   SelectionClear,
967                                   &aEvent
968                                   ) )
969             {
970                 bHandle = true;
971             }
972             else
973             if( XCheckTypedEvent( m_pDisplay,
974                                   SelectionRequest,
975                                   &aEvent
976                                   ) )
977                 bHandle = true;
978             else
979             if( XCheckTypedEvent( m_pDisplay,
980                                   SelectionNotify,
981                                   &aEvent
982                                   ) )
983             {
984                 bHandle = true;
985                 if( aEvent.xselection.selection == selection
986                     && ( aEvent.xselection.requestor == m_aWindow ||
987                          aEvent.xselection.requestor == m_aCurrentDropWindow )
988                     )
989                     bAdjustTime = true;
990             }
991             else
992             {
993                 TimeValue aTVal;
994                 aTVal.Seconds = 0;
995                 aTVal.Nanosec = 100000000;
996                 aGuard.clear();
997                 osl_waitThread( &aTVal );
998                 aGuard.reset();
999             }
1000             if( bHandle )
1001             {
1002                 aGuard.clear();
1003                 handleXEvent( aEvent );
1004                 aGuard.reset();
1005             }
1006         }
1007         gettimeofday( &tv_current, NULL );
1008         if( bAdjustTime )
1009             tv_last = tv_current;
1010     } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() );
1011 
1012 #if OSL_DEBUG_LEVEL > 1
1013     if( (tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout() )
1014         fprintf( stderr, "timed out\n" );
1015 #endif
1016     if( it->second->m_aDataArrived.check() &&
1017         it->second->m_aData.getLength() )
1018     {
1019         rData = it->second->m_aData;
1020         bSuccess = true;
1021     }
1022 #if OSL_DEBUG_LEVEL > 1
1023     else
1024         fprintf( stderr, "conversion unsuccessful\n" );
1025 #endif
1026     return bSuccess;
1027 }
1028 
1029 // ------------------------------------------------------------------------
1030 
getPasteData(Atom selection,const::rtl::OUString & rType,Sequence<sal_Int8> & rData)1031 bool SelectionManager::getPasteData( Atom selection, const ::rtl::OUString& rType, Sequence< sal_Int8 >& rData )
1032 {
1033     int nFormat;
1034     bool bSuccess = false;
1035 
1036     ::std::hash_map< Atom, Selection* >::iterator it;
1037     {
1038         MutexGuard aGuard(m_aMutex);
1039 
1040         it = m_aSelections.find( selection );
1041         if( it == m_aSelections.end() )
1042             return false;
1043     }
1044 
1045     if( it->second->m_aTypes.getLength() == 0 )
1046     {
1047         Sequence< DataFlavor > aFlavors;
1048         getPasteDataTypes( selection, aFlavors );
1049         if( it->second->m_aTypes.getLength() == 0 )
1050             return false;
1051     }
1052 
1053     const Sequence< DataFlavor >& rTypes( it->second->m_aTypes );
1054     const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes );
1055 #if OSL_DEBUG_LEVEL > 1
1056     fprintf( stderr, "getPasteData( \"%s\", \"%s\" )\n",
1057              OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1058              OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1059 #endif
1060 
1061     if( rType.equalsAsciiL( "text/plain;charset=utf-16", 25 ) )
1062     {
1063         // let's see if we have UTF16 else try to find something convertible
1064         if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 )
1065         {
1066             Sequence< sal_Int8 > aData;
1067             if( it->second->m_aUTF8Type != None &&
1068                 getPasteData( selection,
1069                               it->second->m_aUTF8Type,
1070                               aData )
1071                 )
1072             {
1073               OUString aRet( (const sal_Char*)aData.getConstArray(), aData.getLength(), RTL_TEXTENCODING_UTF8 );
1074               rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1075               bSuccess = true;
1076             }
1077             else if( it->second->m_bHaveCompound &&
1078                 getPasteData( selection,
1079                               m_nCOMPOUNDAtom,
1080                               aData )
1081                 )
1082             {
1083                 OUString aRet( convertFromCompound( (const char*)aData.getConstArray(), aData.getLength() ) );
1084                 rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1085                 bSuccess = true;
1086             }
1087             else
1088             {
1089                 for( int i = 0; i < rTypes.getLength(); i++ )
1090                 {
1091                     rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType );
1092                     if( aEncoding != RTL_TEXTENCODING_DONTKNOW  &&
1093                         aEncoding != RTL_TEXTENCODING_UNICODE   &&
1094                         getPasteData( selection,
1095                                       rNativeTypes[i],
1096                                       aData )
1097                         )
1098                     {
1099 #if OSL_DEBUG_LEVEL > 1
1100                         fprintf( stderr, "using \"%s\" instead of \"%s\"\n",
1101                                  OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1102                                  OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1103                                  );
1104 #endif
1105                         OString aConvert( (sal_Char*)aData.getConstArray(), aData.getLength() );
1106                         OUString aUTF( OStringToOUString( aConvert, aEncoding ) );
1107                         rData = Sequence< sal_Int8 >( (sal_Int8*)aUTF.getStr(), (aUTF.getLength()+1)*sizeof( sal_Unicode ) );
1108                         bSuccess = true;
1109                         break;
1110                     }
1111                 }
1112             }
1113         }
1114     }
1115     else if( rType.equalsAsciiL( "image/bmp", 9 ) )
1116     {
1117         // #i83376# try if someone has the data in image/bmp already before
1118         // doing the PIXMAP stuff (e.g. the gimp has this)
1119         bSuccess = getPasteData( selection, m_nImageBmpAtom, rData );
1120         #if OSL_DEBUG_LEVEL > 1
1121         if( bSuccess )
1122             fprintf( stderr, "got %d bytes of image/bmp\n", (int)rData.getLength() );
1123         #endif
1124         if( ! bSuccess )
1125         {
1126             Pixmap aPixmap = None;
1127             Colormap aColormap = None;
1128 
1129             // prepare property for MULTIPLE request
1130             Sequence< sal_Int8 > aData;
1131             Atom pTypes[4] = { XA_PIXMAP, XA_PIXMAP,
1132             XA_COLORMAP, XA_COLORMAP };
1133             {
1134                 MutexGuard aGuard(m_aMutex);
1135 
1136                 XChangeProperty( m_pDisplay,
1137                     m_aWindow,
1138                     selection,
1139                     XA_ATOM,
1140                     32,
1141                     PropModeReplace,
1142                     (unsigned char*)pTypes,
1143                     4 );
1144             }
1145 
1146             // try MULTIPLE request
1147             if( getPasteData( selection, m_nMULTIPLEAtom, aData ) )
1148             {
1149                 Atom* pReturnedTypes = (Atom*)aData.getArray();
1150                 if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP )
1151                 {
1152                     MutexGuard aGuard(m_aMutex);
1153 
1154                     Atom type = None;
1155                     int format = 0;
1156                     unsigned long nItems = 0;
1157                     unsigned long nBytes = 0;
1158                     unsigned char* pReturn = NULL;
1159                     XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems, &nBytes, &pReturn );
1160                     if( pReturn )
1161                     {
1162                         if( type == XA_PIXMAP )
1163                             aPixmap = *(Pixmap*)pReturn;
1164                         XFree( pReturn );
1165                         pReturn = NULL;
1166                         if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP )
1167                         {
1168                             XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format, &nItems, &nBytes, &pReturn );
1169                             if( pReturn )
1170                             {
1171                                 if( type == XA_COLORMAP )
1172                                     aColormap = *(Colormap*)pReturn;
1173                                 XFree( pReturn );
1174                             }
1175                         }
1176                     }
1177                     #if OSL_DEBUG_LEVEL > 1
1178                     else
1179                     {
1180                         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 );
1181                     }
1182                     #endif
1183                 }
1184             }
1185 
1186             if( aPixmap == None )
1187             {
1188                 // perhaps two normal requests will work
1189                 if( getPasteData( selection, XA_PIXMAP, aData ) )
1190                 {
1191                     aPixmap = *(Pixmap*)aData.getArray();
1192                     if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) )
1193                         aColormap = *(Colormap*)aData.getArray();
1194                 }
1195             }
1196 
1197             // convert data if possible
1198             if( aPixmap != None )
1199             {
1200                 MutexGuard aGuard(m_aMutex);
1201 
1202                 sal_Int32 nOutSize = 0;
1203                 sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize );
1204                 if( pBytes && nOutSize )
1205                 {
1206                     rData = Sequence< sal_Int8 >( nOutSize );
1207                     memcpy( rData.getArray(), pBytes, nOutSize );
1208                     X11_freeBmp( pBytes );
1209                     bSuccess = true;
1210                 }
1211             }
1212         }
1213     }
1214 
1215     if( ! bSuccess )
1216     {
1217         ::std::list< Atom > aTypes;
1218         convertTypeToNative( rType, selection, nFormat, aTypes );
1219         ::std::list< Atom >::const_iterator type_it;
1220         Atom nSelectedType = None;
1221         for( type_it = aTypes.begin(); type_it != aTypes.end() && nSelectedType == None; ++type_it )
1222         {
1223             for( unsigned int i = 0; i < rNativeTypes.size() && nSelectedType == None; i++ )
1224                 if( rNativeTypes[i] == *type_it )
1225                     nSelectedType = *type_it;
1226         }
1227         if( nSelectedType != None )
1228             bSuccess = getPasteData( selection, nSelectedType, rData );
1229     }
1230 #if OSL_DEBUG_LEVEL > 1
1231     fprintf( stderr, "getPasteData for selection %s and data type %s returns %s, returned sequence has length %ld\n",
1232              OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1233              OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1234              bSuccess ? "true" : "false",
1235              rData.getLength()
1236              );
1237 #endif
1238     return bSuccess;
1239 }
1240 
1241 // ------------------------------------------------------------------------
1242 
getPasteDataTypes(Atom selection,Sequence<DataFlavor> & rTypes)1243 bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes )
1244 {
1245     ::std::hash_map< Atom, Selection* >::iterator it;
1246     {
1247         MutexGuard aGuard(m_aMutex);
1248 
1249         it = m_aSelections.find( selection );
1250         if( it != m_aSelections.end()                           &&
1251             it->second->m_aTypes.getLength()                    &&
1252             abs( it->second->m_nLastTimestamp - time( NULL ) ) < 2
1253             )
1254         {
1255             rTypes = it->second->m_aTypes;
1256             return true;
1257         }
1258     }
1259 
1260     bool bSuccess = false;
1261     bool bHaveUTF16 = false;
1262     Atom aUTF8Type = None;
1263     bool bHaveCompound = false;
1264     bool bHaveText = false;
1265     Sequence< sal_Int8 > aAtoms;
1266 
1267     if( selection == m_nXdndSelection )
1268     {
1269         // xdnd sends first three types with XdndEnter
1270         // if more than three types are supported then the XDndTypeList
1271         // property on the source window is used
1272         if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
1273         {
1274             if( m_aDropEnterEvent.data.l[1] & 1 )
1275             {
1276                 const unsigned int atomcount = 256;
1277                 // more than three types; look in property
1278                 MutexGuard aGuard(m_aMutex);
1279 
1280                 Atom nType;
1281                 int nFormat;
1282                 unsigned long nItems, nBytes;
1283                 unsigned char* pBytes = NULL;
1284 
1285                 XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1286                                     m_nXdndTypeList, 0, atomcount, False,
1287                                     XA_ATOM,
1288                                     &nType, &nFormat, &nItems, &nBytes, &pBytes );
1289 #if OSL_DEBUG_LEVEL > 1
1290                 fprintf( stderr, "have %ld data types in XdndTypeList\n", nItems );
1291 #endif
1292                 if( nItems == atomcount && nBytes > 0 )
1293                 {
1294                     // wow ... more than 256 types !
1295                     aAtoms.realloc( sizeof( Atom )*atomcount+nBytes );
1296                     memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount );
1297                     XFree( pBytes );
1298                     pBytes = NULL;
1299                     XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1300                                         m_nXdndTypeList, atomcount, nBytes/sizeof(Atom),
1301                                         False, XA_ATOM,
1302                                         &nType, &nFormat, &nItems, &nBytes, &pBytes );
1303                     {
1304                         memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) );
1305                         XFree( pBytes );
1306                     }
1307                 }
1308                 else
1309                 {
1310                     aAtoms.realloc( sizeof(Atom)*nItems );
1311                     memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) );
1312                     XFree( pBytes );
1313                 }
1314             }
1315             else
1316             {
1317                 // one to three types
1318                 int n = 0, i;
1319                 for( i = 0; i < 3; i++ )
1320                     if( m_aDropEnterEvent.data.l[2+i] )
1321                         n++;
1322 #if OSL_DEBUG_LEVEL > 1
1323                 fprintf( stderr, "have %d data types in XdndEnter\n", n );
1324 #endif
1325                 aAtoms.realloc( sizeof(Atom)*n );
1326                 for( i = 0, n = 0; i < 3; i++ )
1327                     if( m_aDropEnterEvent.data.l[2+i] )
1328                         ((Atom*)aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i];
1329             }
1330         }
1331     }
1332     // get data of type TARGETS
1333     else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) )
1334         aAtoms = Sequence< sal_Int8 >();
1335 
1336     std::vector< Atom > aNativeTypes;
1337     if( aAtoms.getLength() )
1338     {
1339         sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom);
1340         Atom* pAtoms = (Atom*)aAtoms.getArray();
1341         rTypes.realloc( nAtoms );
1342         aNativeTypes.resize( nAtoms );
1343         DataFlavor* pFlavors = rTypes.getArray();
1344         sal_Int32 nNativeTypesIndex = 0;
1345         while( nAtoms-- )
1346         {
1347 #if OSL_DEBUG_LEVEL > 1
1348             if( *pAtoms && *pAtoms < 0x01000000 )
1349                 fprintf( stderr, "native type: %s\n", OUStringToOString( getString( *pAtoms ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1350 #endif
1351             if( *pAtoms == m_nCOMPOUNDAtom )
1352                 bHaveText = bHaveCompound = true;
1353             else if( *pAtoms && *pAtoms < 0x01000000 )
1354             {
1355                 int nFormat;
1356                 pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat );
1357                 pFlavors->DataType = getCppuType( (Sequence< sal_Int8 >*)0 );
1358                 sal_Int32 nIndex = 0;
1359                 if( pFlavors->MimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain", 10 ) )
1360                 {
1361                     OUString aToken(pFlavors->MimeType.getToken( 0, ';', nIndex ));
1362                     // omit text/plain;charset=unicode since it is not well defined
1363                     if( aToken.compareToAscii( "charset=unicode" ) == 0 )
1364                     {
1365                         pAtoms++;
1366                         continue;
1367                     }
1368                     bHaveText = true;
1369                     if( aToken.compareToAscii( "charset=utf-16" ) == 0 )
1370                     {
1371                         bHaveUTF16 = true;
1372                         pFlavors->DataType = getCppuType( (OUString*)0 );
1373                     }
1374                     else if( aToken.compareToAscii( "charset=utf-8" ) == 0 )
1375                     {
1376                         aUTF8Type = *pAtoms;
1377                     }
1378                 }
1379                 pFlavors++;
1380                 aNativeTypes[ nNativeTypesIndex ] = *pAtoms;
1381                 nNativeTypesIndex++;
1382             }
1383             pAtoms++;
1384         }
1385         if( (pFlavors - rTypes.getArray()) < rTypes.getLength() )
1386             rTypes.realloc(pFlavors - rTypes.getArray());
1387         bSuccess = rTypes.getLength() ? true : false;
1388         if( bHaveText && ! bHaveUTF16 )
1389         {
1390             int i = 0;
1391 
1392             int nNewFlavors = rTypes.getLength()+1;
1393             Sequence< DataFlavor > aTemp( nNewFlavors );
1394             for( i = 0; i < nNewFlavors-1; i++ )
1395                 aTemp.getArray()[i+1] = rTypes.getConstArray()[i];
1396             aTemp.getArray()[0].MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" );
1397             aTemp.getArray()[0].DataType = getCppuType( (OUString*)0 );
1398             rTypes = aTemp;
1399 
1400             std::vector< Atom > aNativeTemp( nNewFlavors );
1401             for( i = 0; i < nNewFlavors-1; i++ )
1402                 aNativeTemp[ i + 1 ] = aNativeTypes[ i ];
1403             aNativeTemp[0] = None;
1404             aNativeTypes = aNativeTemp;
1405         }
1406     }
1407 
1408     {
1409         MutexGuard aGuard(m_aMutex);
1410 
1411         it = m_aSelections.find( selection );
1412         if( it != m_aSelections.end() )
1413         {
1414             if( bSuccess )
1415             {
1416                 it->second->m_aTypes            = rTypes;
1417                 it->second->m_aNativeTypes      = aNativeTypes;
1418                 it->second->m_nLastTimestamp    = time( NULL );
1419                 it->second->m_bHaveUTF16        = bHaveUTF16;
1420                 it->second->m_aUTF8Type         = aUTF8Type;
1421                 it->second->m_bHaveCompound     = bHaveCompound;
1422             }
1423             else
1424             {
1425                 it->second->m_aTypes            = Sequence< DataFlavor >();
1426                 it->second->m_aNativeTypes      = std::vector< Atom >();
1427                 it->second->m_nLastTimestamp    = 0;
1428                 it->second->m_bHaveUTF16        = false;
1429                 it->second->m_aUTF8Type         = None;
1430                 it->second->m_bHaveCompound     = false;
1431             }
1432         }
1433     }
1434 
1435 #if OSL_DEBUG_LEVEL > 1
1436 //  if( selection != m_nCLIPBOARDAtom )
1437     {
1438         fprintf( stderr, "SelectionManager::getPasteDataTypes( %s ) = %s\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), bSuccess ? "true" : "false" );
1439         for( int i = 0; i < rTypes.getLength(); i++ )
1440             fprintf( stderr, "type: %s\n", OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1441     }
1442 #endif
1443 
1444     return bSuccess;
1445 }
1446 
1447 // ------------------------------------------------------------------------
1448 
getPixmapHolder(Atom selection)1449 PixmapHolder* SelectionManager::getPixmapHolder( Atom selection )
1450 {
1451     std::hash_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection );
1452     if( it == m_aSelections.end() )
1453         return NULL;
1454     if( ! it->second->m_pPixmap )
1455         it->second->m_pPixmap = new PixmapHolder( m_pDisplay );
1456     return it->second->m_pPixmap;
1457 }
1458 
GetTrueFormatSize(int nFormat)1459 static sal_Size GetTrueFormatSize(int nFormat)
1460 {
1461     // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
1462     return nFormat == 32 ? sizeof(long) : nFormat/8;
1463 }
1464 
sendData(SelectionAdaptor * pAdaptor,XLIB_Window requestor,Atom target,Atom property,Atom selection)1465 bool SelectionManager::sendData( SelectionAdaptor* pAdaptor,
1466                                  XLIB_Window requestor,
1467                                  Atom target,
1468                                  Atom property,
1469                                  Atom selection )
1470 {
1471     ResettableMutexGuard aGuard( m_aMutex );
1472 
1473     // handle targets related to image/bmp
1474     if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID )
1475     {
1476         PixmapHolder* pPixmap = getPixmapHolder( selection );
1477         if( ! pPixmap ) return false;
1478         XID nValue = None;
1479 
1480         // handle colormap request
1481         if( target == XA_COLORMAP )
1482             nValue = (XID)pPixmap->getColormap();
1483         else if( target == XA_VISUALID )
1484             nValue = (XID)pPixmap->getVisualID();
1485         else if( target == XA_PIXMAP || target == XA_BITMAP )
1486         {
1487             nValue = (XID)pPixmap->getPixmap();
1488             if( nValue == None )
1489             {
1490                 // first conversion
1491                 Sequence< sal_Int8 > aData;
1492                 int nFormat;
1493                 aGuard.clear();
1494                 bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1495                 aGuard.reset();
1496                 if( bConverted )
1497                 {
1498                     // get pixmap again since clearing the guard could have invalidated
1499                     // the pixmap in another thread
1500                     pPixmap = getPixmapHolder( selection );
1501                     // conversion succeeded, so aData contains image/bmp now
1502                     if( pPixmap->needsConversion( (const sal_uInt8*)aData.getConstArray() )
1503                         && m_xBitmapConverter.is() )
1504                     {
1505 #if OSL_DEBUG_LEVEL > 1
1506                         fprintf( stderr, "trying bitmap conversion\n" );
1507 #endif
1508                         css::uno::Reference<XBitmap> xBM( new BmpTransporter( aData ) );
1509                         Sequence<Any> aArgs(2), aOutArgs;
1510                         Sequence<sal_Int16> aOutIndex;
1511                         aArgs.getArray()[0] = makeAny( xBM );
1512                         aArgs.getArray()[1] = makeAny( (sal_uInt16)pPixmap->getDepth() );
1513                         aGuard.clear();
1514                         try
1515                         {
1516                             Any aResult =
1517                                 m_xBitmapConverter->invoke( OUString::createFromAscii( "convert-bitmap-depth" ),
1518                                                             aArgs, aOutIndex, aOutArgs );
1519                             if( aResult >>= xBM )
1520                                 aData = xBM->getDIB();
1521                         }
1522                         catch(...)
1523                         {
1524 #if OSL_DEBUG_LEVEL > 1
1525                             fprintf( stderr, "exception in bitmap converter\n" );
1526 #endif
1527                         }
1528                         aGuard.reset();
1529                     }
1530                     // get pixmap again since clearing the guard could have invalidated
1531                     // the pixmap in another thread
1532                     pPixmap = getPixmapHolder( selection );
1533                     nValue = (XID)pPixmap->setBitmapData( (const sal_uInt8*)aData.getConstArray() );
1534                 }
1535                 if( nValue == None )
1536                     return false;
1537             }
1538             if( target == XA_BITMAP )
1539                 nValue = (XID)pPixmap->getBitmap();
1540         }
1541 
1542         XChangeProperty( m_pDisplay,
1543                          requestor,
1544                          property,
1545                          target,
1546                          32,
1547                          PropModeReplace,
1548                          (const unsigned char*)&nValue,
1549                          1);
1550         return true;
1551     }
1552 
1553     /*
1554      * special target TEXT allows us to transfer
1555      * the data in an encoding of our choice
1556      * COMPOUND_TEXT will work with most applications
1557      */
1558     if( target == m_nTEXTAtom )
1559         target = m_nCOMPOUNDAtom;
1560 
1561     Sequence< sal_Int8 > aData;
1562     int nFormat;
1563     aGuard.clear();
1564     bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1565     aGuard.reset();
1566     if( bConverted )
1567     {
1568         // conversion succeeded
1569         if( aData.getLength() > m_nIncrementalThreshold )
1570         {
1571 #if OSL_DEBUG_LEVEL > 1
1572             fprintf( stderr, "using INCR protocol\n" );
1573             std::hash_map< XLIB_Window, std::hash_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor );
1574             if( win_it != m_aIncrementals.end() )
1575             {
1576                 std::hash_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property );
1577                 if( inc_it != win_it->second.end() )
1578                 {
1579                     const IncrementalTransfer& rInc = inc_it->second;
1580                     fprintf( stderr, "premature end and new start for INCR transfer for window 0x%lx, property %s, type %s\n",
1581                              rInc.m_aRequestor,
1582                              OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1583                              OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1584                              );
1585                 }
1586             }
1587 #endif
1588 
1589             // insert IncrementalTransfer
1590             IncrementalTransfer& rInc   = m_aIncrementals[ requestor ][ property ];
1591             rInc.m_aData                = aData;
1592             rInc.m_nBufferPos           = 0;
1593             rInc.m_aRequestor           = requestor;
1594             rInc.m_aProperty            = property;
1595             rInc.m_aTarget              = target;
1596             rInc.m_nFormat              = nFormat;
1597             rInc.m_nTransferStartTime   = time( NULL );
1598 
1599             // use incr protocol, signal start to requestor
1600             long nMinSize = m_nIncrementalThreshold;
1601             XSelectInput( m_pDisplay, requestor, PropertyChangeMask );
1602             XChangeProperty( m_pDisplay, requestor, property,
1603                              m_nINCRAtom, 32, PropModeReplace, (unsigned char*)&nMinSize, 1 );
1604             XFlush( m_pDisplay );
1605         }
1606         else
1607         {
1608             sal_Size nUnitSize = GetTrueFormatSize(nFormat);
1609             XChangeProperty( m_pDisplay,
1610                              requestor,
1611                              property,
1612                              target,
1613                              nFormat,
1614                              PropModeReplace,
1615                              (const unsigned char*)aData.getConstArray(),
1616                              aData.getLength()/nUnitSize );
1617             }
1618     }
1619 #if OSL_DEBUG_LEVEL > 1
1620     else
1621         fprintf( stderr, "convertData failed for type: %s \n",
1622                  OUStringToOString( convertTypeFromNative( target, selection, nFormat ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1623 #endif
1624     return bConverted;
1625 }
1626 
1627 // ------------------------------------------------------------------------
1628 
handleSelectionRequest(XSelectionRequestEvent & rRequest)1629 bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest )
1630 {
1631     ResettableMutexGuard aGuard( m_aMutex );
1632 #if OSL_DEBUG_LEVEL > 1
1633     fprintf( stderr, "handleSelectionRequest for selection %s and target %s\n",
1634              OUStringToOString( getString( rRequest.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1635              OUStringToOString( getString( rRequest.target ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1636              );
1637 #endif
1638 
1639     XEvent aNotify;
1640 
1641     aNotify.type                  = SelectionNotify;
1642     aNotify.xselection.display    = rRequest.display;
1643     aNotify.xselection.send_event = True;
1644     aNotify.xselection.requestor  = rRequest.requestor;
1645     aNotify.xselection.selection  = rRequest.selection;
1646     aNotify.xselection.time       = rRequest.time;
1647     aNotify.xselection.target     = rRequest.target;
1648     aNotify.xselection.property   = None;
1649 
1650     SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection );
1651     // ensure that we still own that selection
1652     if( pAdaptor &&
1653         XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow )
1654     {
1655         css::uno::Reference< XTransferable > xTrans( pAdaptor->getTransferable() );
1656         if( rRequest.target == m_nTARGETSAtom )
1657         {
1658             // someone requests our types
1659             if( xTrans.is() )
1660             {
1661                 aGuard.clear();
1662                 Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors();
1663                 aGuard.reset();
1664 
1665                 ::std::list< Atom > aConversions;
1666                 getNativeTypeList( aFlavors, aConversions, rRequest.selection );
1667 
1668                 int i, nTypes = aConversions.size();
1669                 Atom* pTypes = (Atom*)alloca( nTypes * sizeof( Atom ) );
1670                 std::list< Atom >::const_iterator it;
1671                 for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it )
1672                     pTypes[i] = *it;
1673                 XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1674                                  XA_ATOM, 32, PropModeReplace, (const unsigned char*)pTypes, nTypes );
1675                 aNotify.xselection.property = rRequest.property;
1676 #if OSL_DEBUG_LEVEL > 1
1677                 fprintf( stderr, "sending type list:\n" );
1678                 for( int k = 0; k < nTypes; k++ )
1679                     fprintf( stderr, "   %s\n", pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) : "<None>" );
1680 #endif
1681             }
1682         }
1683         else if( rRequest.target == m_nTIMESTAMPAtom )
1684         {
1685             long nTimeStamp = (long)m_aSelections[rRequest.selection]->m_nOrigTimestamp;
1686             XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1687                              XA_INTEGER, 32, PropModeReplace, (const unsigned char*)&nTimeStamp, 1 );
1688             aNotify.xselection.property = rRequest.property;
1689 #if OSL_DEBUG_LEVEL > 1
1690                 fprintf( stderr, "sending timestamp: %d\n", (int)nTimeStamp );
1691 #endif
1692         }
1693         else
1694         {
1695             bool bEventSuccess = false;
1696             if( rRequest.target == m_nMULTIPLEAtom )
1697             {
1698                 // get all targets
1699                 Atom nType = None;
1700                 int nFormat = 0;
1701                 unsigned long nItems = 0, nBytes = 0;
1702                 unsigned char* pData = NULL;
1703 
1704                 // get number of atoms
1705                 XGetWindowProperty( m_pDisplay,
1706                                     rRequest.requestor,
1707                                     rRequest.property,
1708                                     0, 0,
1709                                     False,
1710                                     AnyPropertyType,
1711                                     &nType, &nFormat,
1712                                     &nItems, &nBytes,
1713                                     &pData );
1714                 if( nFormat == 32 && nBytes/4 )
1715                 {
1716                     if( pData ) // ?? should not happen
1717                     {
1718                         XFree( pData );
1719                         pData = NULL;
1720                     }
1721                     XGetWindowProperty( m_pDisplay,
1722                                         rRequest.requestor,
1723                                         rRequest.property,
1724                                         0, nBytes/4,
1725                                         False,
1726                                         nType,
1727                                         &nType, &nFormat,
1728                                         &nItems, &nBytes,
1729                                         &pData );
1730                     if( pData && nItems )
1731                     {
1732 #if OSL_DEBUG_LEVEL > 1
1733                         fprintf( stderr, "found %ld atoms in MULTIPLE request\n", nItems );
1734 #endif
1735                         bEventSuccess = true;
1736                         bool bResetAtoms = false;
1737                         Atom* pAtoms = (Atom*)pData;
1738                         aGuard.clear();
1739                         for( unsigned int i = 0; i < nItems; i += 2 )
1740                         {
1741 #if OSL_DEBUG_LEVEL > 1
1742                             fprintf( stderr, "   %s => %s: ",
1743                                      OUStringToOString( getString( pAtoms[i] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1744                                      OUStringToOString( getString( pAtoms[i+1] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1745 #endif
1746                             bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection );
1747 #if OSL_DEBUG_LEVEL > 1
1748                             fprintf( stderr, "%s\n", bSuccess ? "succeeded" : "failed" );
1749 #endif
1750                             if( ! bSuccess )
1751                             {
1752                                 pAtoms[i] = None;
1753                                 bResetAtoms = true;
1754                             }
1755                         }
1756                         aGuard.reset();
1757                         if( bResetAtoms )
1758                             XChangeProperty( m_pDisplay,
1759                                              rRequest.requestor,
1760                                              rRequest.property,
1761                                              XA_ATOM,
1762                                              32,
1763                                              PropModeReplace,
1764                                              pData,
1765                                              nBytes/4 );
1766                     }
1767                     if( pData )
1768                         XFree( pData );
1769                 }
1770 #if OSL_DEBUG_LEVEL > 1
1771                 else
1772                 {
1773                     fprintf( stderr, "could not get type list from \"%s\" of type \"%s\" on requestor 0x%lx, requestor has properties:",
1774                              OUStringToOString( getString( rRequest.property ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1775                              OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1776                              rRequest.requestor );
1777                     int nProps = 0;
1778                     Atom* pProps = XListProperties( m_pDisplay, rRequest.requestor, &nProps );
1779                     if( pProps )
1780                     {
1781                         for( int i = 0; i < nProps; i++ )
1782                             fprintf( stderr, " \"%s\"", OUStringToOString( getString( pProps[i]), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1783                         XFree( pProps );
1784                     }
1785                 }
1786 #endif
1787             }
1788             else
1789             {
1790                 aGuard.clear();
1791                 bEventSuccess = sendData( pAdaptor, rRequest.requestor, rRequest.target, rRequest.property, rRequest.selection );
1792                 aGuard.reset();
1793             }
1794             if( bEventSuccess )
1795             {
1796                 aNotify.xselection.target = rRequest.target;
1797                 aNotify.xselection.property = rRequest.property;
1798             }
1799         }
1800         aGuard.clear();
1801         xTrans.clear();
1802         aGuard.reset();
1803     }
1804     XSendEvent( m_pDisplay, rRequest.requestor, False, 0, &aNotify );
1805 
1806     if( rRequest.selection == XA_PRIMARY    &&
1807         m_bWaitingForPrimaryConversion      &&
1808         m_xDragSourceListener.is() )
1809     {
1810         DragSourceDropEvent dsde;
1811         dsde.Source                 = static_cast< OWeakObject* >(this);
1812         dsde.DragSourceContext      = new DragSourceContext( m_aDropWindow, rRequest.time, *this );
1813         dsde.DragSource             = static_cast< XDragSource* >(this);
1814         if( aNotify.xselection.property != None )
1815         {
1816             dsde.DropAction         = DNDConstants::ACTION_COPY;
1817             dsde.DropSuccess        = sal_True;
1818         }
1819         else
1820         {
1821             dsde.DropAction         = DNDConstants::ACTION_NONE;
1822             dsde.DropSuccess        = sal_False;
1823         }
1824         css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
1825         m_xDragSourceListener.clear();
1826         aGuard.clear();
1827         if( xListener.is() )
1828             xListener->dragDropEnd( dsde );
1829     }
1830 
1831     // we handled the event in any case by answering
1832     return true;
1833 }
1834 
1835 // ------------------------------------------------------------------------
1836 
handleReceivePropertyNotify(XPropertyEvent & rNotify)1837 bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent& rNotify )
1838 {
1839     MutexGuard aGuard( m_aMutex );
1840     // data we requested arrived
1841 #if OSL_DEBUG_LEVEL > 1
1842     fprintf( stderr, "handleReceivePropertyNotify for property %s\n",
1843              OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1844 #endif
1845     bool bHandled = false;
1846 
1847     ::std::hash_map< Atom, Selection* >::iterator it =
1848           m_aSelections.find( rNotify.atom );
1849     if( it != m_aSelections.end() &&
1850         rNotify.state == PropertyNewValue &&
1851         ( it->second->m_eState == Selection::WaitingForResponse     ||
1852           it->second->m_eState == Selection::WaitingForData         ||
1853           it->second->m_eState == Selection::IncrementalTransfer
1854           )
1855         )
1856     {
1857         // MULTIPLE requests are only complete after selection notify
1858         if( it->second->m_aRequestedType == m_nMULTIPLEAtom &&
1859             ( it->second->m_eState == Selection::WaitingForResponse ||
1860               it->second->m_eState == Selection::WaitingForData ) )
1861             return false;
1862 
1863         bHandled = true;
1864 
1865         Atom nType = None;
1866         int nFormat = 0;
1867         unsigned long nItems = 0, nBytes = 0;
1868         unsigned char* pData = NULL;
1869 
1870         // get type and length
1871         XGetWindowProperty( m_pDisplay,
1872                             rNotify.window,
1873                             rNotify.atom,
1874                             0, 0,
1875                             False,
1876                             AnyPropertyType,
1877                             &nType, &nFormat,
1878                             &nItems, &nBytes,
1879                             &pData );
1880 #if OSL_DEBUG_LEVEL > 1
1881         fprintf( stderr, "found %ld bytes data of type %s and format %d, items = %ld\n",
1882                  nBytes,
1883                  OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1884                  nFormat, nItems );
1885 #endif
1886         if( pData )
1887         {
1888             XFree( pData );
1889             pData = NULL;
1890         }
1891 
1892         if( nType == m_nINCRAtom )
1893         {
1894             // start data transfer
1895             XDeleteProperty( m_pDisplay, rNotify.window, rNotify.atom );
1896             it->second->m_eState = Selection::IncrementalTransfer;
1897         }
1898         else if( nType != None )
1899         {
1900             XGetWindowProperty( m_pDisplay,
1901                                 rNotify.window,
1902                                 rNotify.atom,
1903                                 0, nBytes/4 +1,
1904                                 True,
1905                                 nType,
1906                                 &nType, &nFormat,
1907                                 &nItems, &nBytes,
1908                                 &pData );
1909 #if OSL_DEBUG_LEVEL > 1
1910             fprintf( stderr, "read %ld items data of type %s and format %d, %ld bytes left in property\n",
1911                      nItems,
1912                      OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1913                      nFormat, nBytes );
1914 #endif
1915 
1916             sal_Size nUnitSize = GetTrueFormatSize(nFormat);
1917 
1918             if( it->second->m_eState == Selection::WaitingForData ||
1919                 it->second->m_eState == Selection::WaitingForResponse )
1920             {
1921                 // copy data
1922                 it->second->m_aData = Sequence< sal_Int8 >( (sal_Int8*)pData, nItems*nUnitSize );
1923                 it->second->m_eState = Selection::Inactive;
1924                 it->second->m_aDataArrived.set();
1925             }
1926             else if( it->second->m_eState == Selection::IncrementalTransfer )
1927             {
1928                 if( nItems )
1929                 {
1930                     // append data
1931                     Sequence< sal_Int8 > aData( it->second->m_aData.getLength() + nItems*nUnitSize );
1932                     memcpy( aData.getArray(), it->second->m_aData.getArray(), it->second->m_aData.getLength() );
1933                     memcpy( aData.getArray() + it->second->m_aData.getLength(), pData, nItems*nUnitSize );
1934                     it->second->m_aData = aData;
1935                 }
1936                 else
1937                 {
1938                     it->second->m_eState = Selection::Inactive;
1939                     it->second->m_aDataArrived.set();
1940                 }
1941             }
1942             if( pData )
1943                 XFree( pData );
1944         }
1945         else if( it->second->m_eState == Selection::IncrementalTransfer )
1946         {
1947             it->second->m_eState = Selection::Inactive;
1948             it->second->m_aDataArrived.set();
1949         }
1950     }
1951     return bHandled;
1952 }
1953 
1954 // ------------------------------------------------------------------------
1955 
handleSendPropertyNotify(XPropertyEvent & rNotify)1956 bool SelectionManager::handleSendPropertyNotify( XPropertyEvent& rNotify )
1957 {
1958     MutexGuard aGuard( m_aMutex );
1959 
1960     // ready for next part of an IncrementalTransfer
1961 #if OSL_DEBUG_LEVEL > 1
1962     fprintf( stderr, "handleSendPropertyNotify for property %s (%s)\n",
1963              OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1964              rNotify.state == PropertyNewValue ? "new value" : ( rNotify.state == PropertyDelete ? "deleted" : "unknown")
1965              );
1966 #endif
1967 
1968     bool bHandled = false;
1969     // feed incrementals
1970     if( rNotify.state == PropertyDelete )
1971     {
1972         std::hash_map< XLIB_Window, std::hash_map< Atom, IncrementalTransfer > >::iterator it;
1973         it = m_aIncrementals.find( rNotify.window );
1974         if( it != m_aIncrementals.end() )
1975         {
1976             bHandled = true;
1977             int nCurrentTime = time( NULL );
1978             std::hash_map< Atom, IncrementalTransfer >::iterator inc_it;
1979             // throw out aborted transfers
1980             std::list< Atom > aTimeouts;
1981             for( inc_it = it->second.begin(); inc_it != it->second.end(); ++inc_it )
1982             {
1983                 if( (nCurrentTime - inc_it->second.m_nTransferStartTime) > (getSelectionTimeout()+2) )
1984                 {
1985                     aTimeouts.push_back( inc_it->first );
1986 #if OSL_DEBUG_LEVEL > 1
1987                     const IncrementalTransfer& rInc = inc_it->second;
1988                     fprintf( stderr, "timeout on INCR transfer for window 0x%lx, property %s, type %s\n",
1989                              rInc.m_aRequestor,
1990                              OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1991                              OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
1992                              );
1993 #endif
1994                 }
1995             }
1996 
1997             while( aTimeouts.begin() != aTimeouts.end() )
1998             {
1999                 // transfer broken, might even be a new client with the
2000                 // same window id
2001                 it->second.erase( aTimeouts.front() );
2002                 aTimeouts.pop_front();
2003             }
2004 
2005             inc_it = it->second.find( rNotify.atom );
2006             if( inc_it != it->second.end() )
2007             {
2008                 IncrementalTransfer& rInc = inc_it->second;
2009 
2010                 int nBytes = rInc.m_aData.getLength() - rInc.m_nBufferPos;
2011                 nBytes = (nBytes > m_nIncrementalThreshold) ? m_nIncrementalThreshold : nBytes;
2012                 if( nBytes < 0 )  // sanity check
2013                     nBytes = 0;
2014 #if OSL_DEBUG_LEVEL > 1
2015                 fprintf( stderr, "pushing %d bytes: \"%.*s\"...\n",
2016                          nBytes, nBytes > 32 ? 32 : nBytes,
2017                          (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos );
2018 #endif
2019 
2020                 sal_Size nUnitSize = GetTrueFormatSize(rInc.m_nFormat);
2021 
2022                 XChangeProperty( m_pDisplay,
2023                                  rInc.m_aRequestor,
2024                                  rInc.m_aProperty,
2025                                  rInc.m_aTarget,
2026                                  rInc.m_nFormat,
2027                                  PropModeReplace,
2028                                  (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos,
2029                                  nBytes/nUnitSize );
2030                 rInc.m_nBufferPos += nBytes;
2031                 rInc.m_nTransferStartTime = nCurrentTime;
2032 
2033                 if( nBytes == 0 ) // transfer finished
2034                 {
2035 #if OSL_DEBUG_LEVEL > 1
2036                     fprintf( stderr, "finished INCR transfer for window 0x%lx, property %s, type %s\n",
2037                              rInc.m_aRequestor,
2038                              OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2039                              OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
2040                              );
2041 #endif
2042                     it->second.erase( inc_it );
2043                 }
2044 
2045             }
2046             // eventually clean up the hash map
2047             if( it->second.begin() == it->second.end() )
2048                 m_aIncrementals.erase( it );
2049         }
2050     }
2051     return bHandled;
2052 }
2053 
2054 // ------------------------------------------------------------------------
2055 
handleSelectionNotify(XSelectionEvent & rNotify)2056 bool SelectionManager::handleSelectionNotify( XSelectionEvent& rNotify )
2057 {
2058     MutexGuard aGuard( m_aMutex );
2059 
2060     bool bHandled = false;
2061 
2062     // notification about success/failure of one of our conversion requests
2063 #if OSL_DEBUG_LEVEL > 1
2064     OUString aSelection( getString( rNotify.selection ) );
2065     OUString aProperty( OUString::createFromAscii( "None" ) );
2066     if( rNotify.property )
2067         aProperty = getString( rNotify.property );
2068     fprintf( stderr, "handleSelectionNotify for selection %s and property %s (0x%lx)\n",
2069              OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2070              OUStringToOString( aProperty, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
2071              rNotify.property
2072              );
2073     if( rNotify.requestor != m_aWindow && rNotify.requestor != m_aCurrentDropWindow )
2074         fprintf( stderr, "Warning: selection notify for unknown window 0x%lx\n", rNotify.requestor );
2075 #endif
2076     ::std::hash_map< Atom, Selection* >::iterator it =
2077           m_aSelections.find( rNotify.selection );
2078     if (
2079         (rNotify.requestor == m_aWindow || rNotify.requestor == m_aCurrentDropWindow) &&
2080         it != m_aSelections.end() &&
2081         (
2082         (it->second->m_eState == Selection::WaitingForResponse) ||
2083         (it->second->m_eState == Selection::WaitingForData)
2084         )
2085        )
2086     {
2087         bHandled = true;
2088         if( it->second->m_aRequestedType == m_nMULTIPLEAtom )
2089         {
2090             Atom nType = None;
2091             int nFormat = 0;
2092             unsigned long nItems = 0, nBytes = 0;
2093             unsigned char* pData = NULL;
2094 
2095             // get type and length
2096             XGetWindowProperty( m_pDisplay,
2097                                 rNotify.requestor,
2098                                 rNotify.property,
2099                                 0, 256,
2100                                 False,
2101                                 AnyPropertyType,
2102                                 &nType, &nFormat,
2103                                 &nItems, &nBytes,
2104                                 &pData );
2105             if( nBytes ) // HUGE request !!!
2106             {
2107                 if( pData )
2108                     XFree( pData );
2109                 XGetWindowProperty( m_pDisplay,
2110                                     rNotify.requestor,
2111                                     rNotify.property,
2112                                     0, 256+(nBytes+3)/4,
2113                                     False,
2114                                     AnyPropertyType,
2115                                     &nType, &nFormat,
2116                                     &nItems, &nBytes,
2117                                     &pData );
2118             }
2119             it->second->m_eState        = Selection::Inactive;
2120             sal_Size nUnitSize = GetTrueFormatSize(nFormat);
2121             it->second->m_aData         = Sequence< sal_Int8 >((sal_Int8*)pData, nItems * nUnitSize);
2122             it->second->m_aDataArrived.set();
2123             if( pData )
2124                 XFree( pData );
2125         }
2126         // WaitingForData can actually happen; some
2127         // applications (e.g. cmdtool on Solaris) first send
2128         // a success and then cancel it. Weird !
2129         else if( rNotify.property == None )
2130         {
2131             // conversion failed, stop transfer
2132             it->second->m_eState        = Selection::Inactive;
2133             it->second->m_aData         = Sequence< sal_Int8 >();
2134             it->second->m_aDataArrived.set();
2135         }
2136         // get the bytes, by INCR if necessary
2137         else
2138             it->second->m_eState = Selection::WaitingForData;
2139     }
2140 #if OSL_DEBUG_LEVEL > 1
2141     else if( it != m_aSelections.end() )
2142         fprintf( stderr, "Warning: selection in state %d\n", it->second->m_eState );
2143 #endif
2144     return bHandled;
2145 }
2146 
2147 // ------------------------------------------------------------------------
2148 
handleDropEvent(XClientMessageEvent & rMessage)2149 bool SelectionManager::handleDropEvent( XClientMessageEvent& rMessage )
2150 {
2151     ResettableMutexGuard aGuard(m_aMutex);
2152 
2153     // handle drop related events
2154     XLIB_Window aSource = rMessage.data.l[0];
2155     XLIB_Window aTarget = rMessage.window;
2156 
2157     bool bHandled = false;
2158 
2159     ::std::hash_map< XLIB_Window, DropTargetEntry >::iterator it =
2160           m_aDropTargets.find( aTarget );
2161 
2162 #if OSL_DEBUG_LEVEL > 1
2163     if( rMessage.message_type == m_nXdndEnter     ||
2164         rMessage.message_type == m_nXdndLeave     ||
2165         rMessage.message_type == m_nXdndDrop      ||
2166         rMessage.message_type == m_nXdndPosition  )
2167     {
2168         fprintf( stderr, "got drop event %s, ", OUStringToOString( getString( rMessage.message_type ), RTL_TEXTENCODING_ASCII_US).getStr() );
2169         if( it == m_aDropTargets.end() )
2170             fprintf( stderr, "but no target found\n" );
2171         else if( ! it->second.m_pTarget->m_bActive )
2172             fprintf( stderr, "but target is inactive\n" );
2173         else if( m_aDropEnterEvent.data.l[0] != None && (XLIB_Window)m_aDropEnterEvent.data.l[0] != aSource )
2174             fprintf( stderr, "but source 0x%lx is unknown (expected 0x%lx or 0)\n", aSource, m_aDropEnterEvent.data.l[0] );
2175         else
2176             fprintf( stderr, "processing.\n" );
2177     }
2178 #endif
2179 
2180     if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive &&
2181         m_bDropWaitingForCompletion && m_aDropEnterEvent.data.l[0] )
2182     {
2183         bHandled = true;
2184         OSL_ENSURE( 0, "someone forgot to call dropComplete ?" );
2185         // some listener forgot to call dropComplete in the last operation
2186         // let us end it now and accept the new enter event
2187         aGuard.clear();
2188         dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime );
2189         aGuard.reset();
2190     }
2191 
2192     if( it != m_aDropTargets.end() &&
2193         it->second.m_pTarget->m_bActive &&
2194         ( m_aDropEnterEvent.data.l[0] == None || XLIB_Window(m_aDropEnterEvent.data.l[0]) == aSource )
2195       )
2196     {
2197         if( rMessage.message_type == m_nXdndEnter )
2198         {
2199             bHandled = true;
2200             m_aDropEnterEvent           = rMessage;
2201             m_bDropEnterSent            = false;
2202             m_aCurrentDropWindow        = aTarget;
2203             m_nCurrentProtocolVersion   = m_aDropEnterEvent.data.l[1] >> 24;
2204 #if OSL_DEBUG_LEVEL > 1
2205             fprintf( stderr, "received XdndEnter on 0x%lx\n", aTarget );
2206 #endif
2207         }
2208         else if(
2209                 rMessage.message_type == m_nXdndPosition &&
2210                 aSource == XLIB_Window(m_aDropEnterEvent.data.l[0])
2211                 )
2212         {
2213             bHandled = true;
2214             m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[3] : CurrentTime;
2215             if( ! m_bDropEnterSent )
2216                 m_nDropTimestamp = m_nDropTime;
2217 
2218             XLIB_Window aChild;
2219             XTranslateCoordinates( m_pDisplay,
2220                                    it->second.m_aRootWindow,
2221                                    it->first,
2222                                    rMessage.data.l[2] >> 16,
2223                                    rMessage.data.l[2] & 0xffff,
2224                                    &m_nLastX, &m_nLastY,
2225                                    &aChild );
2226 
2227 #if OSL_DEBUG_LEVEL > 1
2228             fprintf( stderr, "received XdndPosition on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY );
2229 #endif
2230             DropTargetDragEnterEvent aEvent;
2231             aEvent.Source       = static_cast< XDropTarget* >(it->second.m_pTarget);
2232             aEvent.Context      = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2233             aEvent.LocationX    = m_nLastX;
2234             aEvent.LocationY    = m_nLastY;
2235             aEvent.SourceActions = m_nSourceActions;
2236             if( m_nCurrentProtocolVersion < 2 )
2237                 aEvent.DropAction = DNDConstants::ACTION_COPY;
2238             else if( Atom(rMessage.data.l[4]) == m_nXdndActionCopy )
2239                 aEvent.DropAction = DNDConstants::ACTION_COPY;
2240             else if( Atom(rMessage.data.l[4]) == m_nXdndActionMove )
2241                 aEvent.DropAction = DNDConstants::ACTION_MOVE;
2242             else if( Atom(rMessage.data.l[4]) == m_nXdndActionLink )
2243                 aEvent.DropAction = DNDConstants::ACTION_LINK;
2244             else if( Atom(rMessage.data.l[4]) == m_nXdndActionAsk )
2245                 // currently no interface to implement ask
2246                 aEvent.DropAction = ~0;
2247             else
2248                 aEvent.DropAction = DNDConstants::ACTION_NONE;
2249 
2250             m_nLastDropAction   = aEvent.DropAction;
2251             if( ! m_bDropEnterSent )
2252             {
2253                 m_bDropEnterSent = true;
2254                 aEvent.SupportedDataFlavors = m_xDropTransferable->getTransferDataFlavors();
2255                 aGuard.clear();
2256                 it->second->dragEnter( aEvent );
2257             }
2258             else
2259             {
2260                 aGuard.clear();
2261                 it->second->dragOver( aEvent );
2262             }
2263         }
2264         else if(
2265                 rMessage.message_type == m_nXdndLeave  &&
2266                 aSource == XLIB_Window(m_aDropEnterEvent.data.l[0])
2267                 )
2268         {
2269             bHandled = true;
2270 #if OSL_DEBUG_LEVEL > 1
2271             fprintf( stderr, "received XdndLeave on 0x%lx\n", aTarget );
2272 #endif
2273             DropTargetEvent aEvent;
2274             aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2275             m_aDropEnterEvent.data.l[0] = None;
2276             if( m_aCurrentDropWindow == aTarget )
2277                 m_aCurrentDropWindow = None;
2278             m_nCurrentProtocolVersion = nXdndProtocolRevision;
2279             aGuard.clear();
2280             it->second->dragExit( aEvent );
2281         }
2282         else if(
2283                 rMessage.message_type == m_nXdndDrop &&
2284                 aSource == XLIB_Window(m_aDropEnterEvent.data.l[0])
2285                 )
2286         {
2287             bHandled = true;
2288             m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[2] : CurrentTime;
2289 
2290 #if OSL_DEBUG_LEVEL > 1
2291             fprintf( stderr, "received XdndDrop on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY );
2292 #endif
2293             if( m_bLastDropAccepted )
2294             {
2295                 DropTargetDropEvent aEvent;
2296                 aEvent.Source       = static_cast< XDropTarget* >(it->second.m_pTarget);
2297                 aEvent.Context      = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2298                 aEvent.LocationX    = m_nLastX;
2299                 aEvent.LocationY    = m_nLastY;
2300                 aEvent.DropAction   = m_nLastDropAction;
2301                 // there is nothing corresponding to source supported actions
2302                 // every source can do link, copy and move
2303                 aEvent.SourceActions= m_nLastDropAction;
2304                 aEvent.Transferable = m_xDropTransferable;
2305 
2306                 m_bDropWaitingForCompletion = true;
2307                 aGuard.clear();
2308                 it->second->drop( aEvent );
2309             }
2310             else
2311             {
2312 #if OSL_DEBUG_LEVEL > 1
2313                 fprintf( stderr, "XdndDrop canceled due to m_bLastDropAccepted = false\n" );
2314 #endif
2315                 DropTargetEvent aEvent;
2316                 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2317                 aGuard.clear();
2318                 it->second->dragExit( aEvent );
2319                 // reset the drop status, notify source
2320                 dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime );
2321             }
2322         }
2323     }
2324     return bHandled;
2325 }
2326 
2327 /*
2328  *  methods for XDropTargetDropContext
2329  */
2330 
dropComplete(sal_Bool bSuccess,XLIB_Window aDropWindow,XLIB_Time)2331 void SelectionManager::dropComplete( sal_Bool bSuccess, XLIB_Window aDropWindow, XLIB_Time )
2332 {
2333     ClearableMutexGuard aGuard(m_aMutex);
2334 
2335     if( aDropWindow == m_aCurrentDropWindow )
2336     {
2337         if( m_xDragSourceListener.is() )
2338         {
2339             DragSourceDropEvent dsde;
2340             dsde.Source             = static_cast< OWeakObject* >(this);
2341             dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2342             dsde.DragSource         = static_cast< XDragSource* >(this);
2343             dsde.DropAction         = getUserDragAction();
2344             dsde.DropSuccess        = bSuccess;
2345             css::uno::Reference< XDragSourceListener > xListener = m_xDragSourceListener;
2346             m_xDragSourceListener.clear();
2347 
2348             aGuard.clear();
2349             xListener->dragDropEnd( dsde );
2350         }
2351         else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2352         {
2353             XEvent aEvent;
2354             aEvent.xclient.type         = ClientMessage;
2355             aEvent.xclient.display      = m_pDisplay;
2356             aEvent.xclient.window       = m_aDropEnterEvent.data.l[0];
2357             aEvent.xclient.message_type = m_nXdndFinished;
2358             aEvent.xclient.format       = 32;
2359             aEvent.xclient.data.l[0]    = m_aCurrentDropWindow;
2360             aEvent.xclient.data.l[1]    = bSuccess ? 1 : 0;
2361             aEvent.xclient.data.l[2]    = 0;
2362             aEvent.xclient.data.l[3]    = 0;
2363             aEvent.xclient.data.l[4]    = 0;
2364             if( bSuccess )
2365             {
2366                 if( m_nLastDropAction & DNDConstants::ACTION_MOVE )
2367                     aEvent.xclient.data.l[2] = m_nXdndActionMove;
2368                 else if( m_nLastDropAction & DNDConstants::ACTION_COPY )
2369                     aEvent.xclient.data.l[2] = m_nXdndActionCopy;
2370                 else if( m_nLastDropAction & DNDConstants::ACTION_LINK )
2371                     aEvent.xclient.data.l[2] = m_nXdndActionLink;
2372             }
2373 
2374 #if OSL_DEBUG_LEVEL > 1
2375             fprintf( stderr, "Sending XdndFinished to 0x%lx\n",
2376                      m_aDropEnterEvent.data.l[0]
2377                      );
2378 #endif
2379 
2380             XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2381                         False, NoEventMask, & aEvent );
2382 
2383             m_aDropEnterEvent.data.l[0] = None;
2384             m_aCurrentDropWindow        = None;
2385             m_nCurrentProtocolVersion   = nXdndProtocolRevision;
2386         }
2387         m_bDropWaitingForCompletion = false;
2388     }
2389     else
2390         OSL_ASSERT( "dropComplete from invalid DropTargetDropContext" );
2391 }
2392 
2393 /*
2394  *  methods for XDropTargetDragContext
2395  */
2396 
2397 // ------------------------------------------------------------------------
2398 
sendDragStatus(Atom nDropAction)2399 void SelectionManager::sendDragStatus( Atom nDropAction )
2400 {
2401     ClearableMutexGuard aGuard(m_aMutex);
2402 
2403     if( m_xDragSourceListener.is() )
2404     {
2405         sal_Int8 nNewDragAction;
2406         if( nDropAction == m_nXdndActionMove )
2407             nNewDragAction = DNDConstants::ACTION_MOVE;
2408         else if( nDropAction == m_nXdndActionCopy )
2409             nNewDragAction = DNDConstants::ACTION_COPY;
2410         else if( nDropAction == m_nXdndActionLink )
2411             nNewDragAction = DNDConstants::ACTION_LINK;
2412         else
2413             nNewDragAction = DNDConstants::ACTION_NONE;
2414         nNewDragAction &= m_nSourceActions;
2415 
2416         if( nNewDragAction != m_nTargetAcceptAction )
2417         {
2418             setCursor( getDefaultCursor( nNewDragAction ), m_aDropWindow, m_nDragTimestamp );
2419             m_nTargetAcceptAction = nNewDragAction;
2420         }
2421 
2422         DragSourceDragEvent dsde;
2423         dsde.Source             = static_cast< OWeakObject* >(this);
2424         dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2425         dsde.DragSource         = static_cast< XDragSource* >(this);
2426         dsde.DropAction         = m_nSourceActions;
2427         dsde.UserAction         = getUserDragAction();
2428 
2429         css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2430         // caution: do not change anything after this
2431         aGuard.clear();
2432         if( xListener.is() )
2433             xListener->dragOver( dsde );
2434     }
2435     else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2436     {
2437         XEvent aEvent;
2438         aEvent.xclient.type         = ClientMessage;
2439         aEvent.xclient.display      = m_pDisplay;
2440         aEvent.xclient.window       = m_aDropEnterEvent.data.l[0];
2441         aEvent.xclient.message_type = m_nXdndStatus;
2442         aEvent.xclient.format       = 32;
2443         aEvent.xclient.data.l[0]    = m_aCurrentDropWindow;
2444         aEvent.xclient.data.l[1]    = 2;
2445         if( nDropAction == m_nXdndActionMove    ||
2446             nDropAction == m_nXdndActionLink    ||
2447             nDropAction == m_nXdndActionCopy )
2448             aEvent.xclient.data.l[1] |= 1;
2449         aEvent.xclient.data.l[2] = 0;
2450         aEvent.xclient.data.l[3] = 0;
2451         aEvent.xclient.data.l[4] = m_nCurrentProtocolVersion > 1 ? nDropAction : 0;
2452 
2453 #if OSL_DEBUG_LEVEL > 1
2454         fprintf( stderr, "Sending XdndStatus to 0x%lx with action %s\n",
2455                  m_aDropEnterEvent.data.l[0],
2456                  OUStringToOString( getString( nDropAction ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
2457                  );
2458 #endif
2459 
2460         XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2461                     False, NoEventMask, & aEvent );
2462         XFlush( m_pDisplay );
2463     }
2464 }
2465 
2466 // ------------------------------------------------------------------------
2467 
getUserDragAction() const2468 sal_Int8 SelectionManager::getUserDragAction() const
2469 {
2470     return (m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT) ? m_nTargetAcceptAction : m_nUserDragAction;
2471 }
2472 
2473 // ------------------------------------------------------------------------
2474 
updateDragAction(int modifierState)2475 bool SelectionManager::updateDragAction( int modifierState )
2476 {
2477     bool bRet = false;
2478 
2479     sal_Int8 nNewDropAction = DNDConstants::ACTION_MOVE;
2480     if( ( modifierState & ShiftMask ) && ! ( modifierState & ControlMask ) )
2481         nNewDropAction = DNDConstants::ACTION_MOVE;
2482     else if( ( modifierState & ControlMask ) && ! ( modifierState & ShiftMask ) )
2483         nNewDropAction = DNDConstants::ACTION_COPY;
2484     else if( ( modifierState & ShiftMask ) && ( modifierState & ControlMask ) )
2485         nNewDropAction = DNDConstants::ACTION_LINK;
2486     if( m_nCurrentProtocolVersion < 0 && m_aDropWindow != None )
2487         nNewDropAction = DNDConstants::ACTION_COPY;
2488     nNewDropAction &= m_nSourceActions;
2489 
2490     if( ! ( modifierState & ( ControlMask | ShiftMask ) ) )
2491     {
2492         if( ! nNewDropAction )
2493         {
2494             // default to an action so the user does not have to press
2495             // keys explicitly
2496             if( m_nSourceActions & DNDConstants::ACTION_MOVE )
2497                 nNewDropAction = DNDConstants::ACTION_MOVE;
2498             else if( m_nSourceActions & DNDConstants::ACTION_COPY )
2499                 nNewDropAction = DNDConstants::ACTION_COPY;
2500             else if( m_nSourceActions & DNDConstants::ACTION_LINK )
2501                 nNewDropAction = DNDConstants::ACTION_LINK;
2502         }
2503         nNewDropAction |= DNDConstants::ACTION_DEFAULT;
2504     }
2505 
2506     if( nNewDropAction != m_nUserDragAction || m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT )
2507     {
2508 #if OSL_DEBUG_LEVEL > 1
2509         fprintf( stderr, "updateDragAction: %x -> %x\n", (int)m_nUserDragAction, (int)nNewDropAction );
2510 #endif
2511         bRet = true;
2512         m_nUserDragAction = nNewDropAction;
2513 
2514         DragSourceDragEvent dsde;
2515         dsde.Source             = static_cast< OWeakObject* >(this);
2516         dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2517         dsde.DragSource         = static_cast< XDragSource* >(this);
2518         dsde.DropAction         = m_nUserDragAction;
2519         dsde.UserAction         = m_nUserDragAction;
2520         m_nTargetAcceptAction   = DNDConstants::ACTION_DEFAULT; // invalidate last accept
2521         m_xDragSourceListener->dropActionChanged( dsde );
2522     }
2523     return bRet;
2524 }
2525 
2526 // ------------------------------------------------------------------------
2527 
sendDropPosition(bool bForce,XLIB_Time eventTime)2528 void SelectionManager::sendDropPosition( bool bForce, XLIB_Time eventTime )
2529 {
2530     ClearableMutexGuard aGuard(m_aMutex);
2531 
2532     if( m_bDropSent )
2533         return;
2534 
2535     ::std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it =
2536           m_aDropTargets.find( m_aDropWindow );
2537     if( it != m_aDropTargets.end() )
2538     {
2539         if( it->second.m_pTarget->m_bActive )
2540         {
2541             int x, y;
2542             XLIB_Window aChild;
2543             XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, m_aDropWindow, m_nLastDragX, m_nLastDragY, &x, &y, &aChild );
2544             DropTargetDragEvent dtde;
2545             dtde.Source         = static_cast< OWeakObject* >(it->second.m_pTarget );
2546             dtde.Context        = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2547             dtde.LocationX      = x;
2548             dtde.LocationY      = y;
2549             dtde.DropAction     = getUserDragAction();
2550             dtde.SourceActions  = m_nSourceActions;
2551             aGuard.clear();
2552             it->second->dragOver( dtde );
2553         }
2554     }
2555     else if( bForce ||
2556 
2557              m_nLastDragX < m_nNoPosX || m_nLastDragX >= m_nNoPosX+m_nNoPosWidth ||
2558              m_nLastDragY < m_nNoPosY || m_nLastDragY >= m_nNoPosY+m_nNoPosHeight
2559              )
2560     {
2561         // send XdndPosition
2562         XEvent aEvent;
2563         aEvent.type = ClientMessage;
2564         aEvent.xclient.display      = m_pDisplay;
2565         aEvent.xclient.format       = 32;
2566         aEvent.xclient.message_type = m_nXdndPosition;
2567         aEvent.xclient.window       = m_aDropWindow;
2568         aEvent.xclient.data.l[0]    = m_aWindow;
2569         aEvent.xclient.data.l[1]    = 0;
2570         aEvent.xclient.data.l[2]    = m_nLastDragX << 16 | (m_nLastDragY&0xffff);
2571         aEvent.xclient.data.l[3]    = eventTime;
2572 
2573         if( m_nUserDragAction & DNDConstants::ACTION_COPY )
2574             aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2575         else if( m_nUserDragAction & DNDConstants::ACTION_MOVE )
2576             aEvent.xclient.data.l[4]=m_nXdndActionMove;
2577         else if( m_nUserDragAction & DNDConstants::ACTION_LINK )
2578             aEvent.xclient.data.l[4]=m_nXdndActionLink;
2579         else
2580             aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2581         XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2582         m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2583     }
2584 }
2585 
2586 // ------------------------------------------------------------------------
2587 
handleDragEvent(XEvent & rMessage)2588 bool SelectionManager::handleDragEvent( XEvent& rMessage )
2589 {
2590     if( ! m_xDragSourceListener.is() )
2591         return false;
2592 
2593     ResettableMutexGuard aGuard(m_aMutex);
2594 
2595     bool bHandled = false;
2596 
2597     // for shortcut
2598     ::std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it =
2599           m_aDropTargets.find( m_aDropWindow );
2600 #if OSL_DEBUG_LEVEL > 1
2601     switch( rMessage.type )
2602     {
2603         case ClientMessage:
2604             fprintf( stderr, "handleDragEvent: %s\n", OUStringToOString( getString( rMessage.xclient.message_type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
2605             break;
2606         case MotionNotify:
2607 //          fprintf( stderr, "handleDragEvent: MotionNotify\n" );
2608             break;
2609         case EnterNotify:
2610             fprintf( stderr, "handleDragEvent: EnterNotify\n" );
2611             break;
2612         case LeaveNotify:
2613             fprintf( stderr, "handleDragEvent: LeaveNotify\n" );
2614             break;
2615         case ButtonPress:
2616             fprintf( stderr, "handleDragEvent: ButtonPress %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton );
2617             break;
2618         case ButtonRelease:
2619             fprintf( stderr, "handleDragEvent: ButtonRelease %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton );
2620             break;
2621         case XLIB_KeyPress:
2622             fprintf( stderr, "handleDragEvent: KeyPress\n" );
2623             break;
2624         case KeyRelease:
2625             fprintf( stderr, "handleDragEvent: KeyRelease\n" );
2626             break;
2627         default:
2628             fprintf( stderr, "handleDragEvent: <unknown type %d>\n", rMessage.type );
2629             break;
2630     }
2631 #endif
2632 
2633     // handle drag related events
2634     if( rMessage.type == ClientMessage )
2635     {
2636         if( Atom(rMessage.xclient.message_type) == m_nXdndStatus && Atom(rMessage.xclient.data.l[0]) == m_aDropWindow )
2637         {
2638             bHandled = true;
2639             DragSourceDragEvent dsde;
2640             dsde.Source                 = static_cast< OWeakObject* >(this);
2641             dsde.DragSourceContext      = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2642             dsde.DragSource             = static_cast< XDragSource* >( this );
2643             dsde.UserAction = getUserDragAction();
2644             dsde.DropAction = DNDConstants::ACTION_NONE;
2645             m_bDropSuccess = rMessage.xclient.data.l[1] & 1 ? true : false;
2646 #if OSL_DEBUG_LEVEL > 1
2647             fprintf( stderr, "status drop action: accept = %s, %s\n",
2648                      m_bDropSuccess ? "true" : "false",
2649                      OUStringToOString( getString( rMessage.xclient.data.l[4] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
2650 #endif
2651             if( rMessage.xclient.data.l[1] & 1 )
2652             {
2653                 if( m_nCurrentProtocolVersion > 1 )
2654                 {
2655                     if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionCopy )
2656                         dsde.DropAction = DNDConstants::ACTION_COPY;
2657                     else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionMove )
2658                         dsde.DropAction = DNDConstants::ACTION_MOVE;
2659                     else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionLink )
2660                         dsde.DropAction = DNDConstants::ACTION_LINK;
2661                 }
2662                 else
2663                     dsde.DropAction = DNDConstants::ACTION_COPY;
2664             }
2665             m_nTargetAcceptAction = dsde.DropAction;
2666 
2667             if( ! ( rMessage.xclient.data.l[1] & 2 ) )
2668             {
2669                 m_nNoPosX       = rMessage.xclient.data.l[2] >> 16;
2670                 m_nNoPosY       = rMessage.xclient.data.l[2] & 0xffff;
2671                 m_nNoPosWidth   = rMessage.xclient.data.l[3] >> 16;
2672                 m_nNoPosHeight  = rMessage.xclient.data.l[3] & 0xffff;
2673             }
2674             else
2675                 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2676 
2677             setCursor( getDefaultCursor( dsde.DropAction ), m_aDropWindow, m_nDragTimestamp );
2678             aGuard.clear();
2679             m_xDragSourceListener->dragOver( dsde );
2680         }
2681         else if( Atom(rMessage.xclient.message_type) == m_nXdndFinished && m_aDropWindow == Atom(rMessage.xclient.data.l[0]) )
2682         {
2683             bHandled = true;
2684             // notify the listener
2685             DragSourceDropEvent dsde;
2686             dsde.Source             = static_cast< OWeakObject* >(this);
2687             dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2688             dsde.DragSource         = static_cast< XDragSource* >(this);
2689             dsde.DropAction         = m_nTargetAcceptAction;
2690             dsde.DropSuccess        = m_bDropSuccess;
2691             css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2692             m_xDragSourceListener.clear();
2693             aGuard.clear();
2694             xListener->dragDropEnd( dsde );
2695         }
2696     }
2697     else if( rMessage.type == MotionNotify ||
2698              rMessage.type == EnterNotify || rMessage.type == LeaveNotify
2699              )
2700     {
2701         bHandled = true;
2702         bool bForce = false;
2703         int root_x  = rMessage.type == MotionNotify ? rMessage.xmotion.x_root : rMessage.xcrossing.x_root;
2704         int root_y  = rMessage.type == MotionNotify ? rMessage.xmotion.y_root : rMessage.xcrossing.y_root;
2705         XLIB_Window root = rMessage.type == MotionNotify ? rMessage.xmotion.root : rMessage.xcrossing.root;
2706         m_nDragTimestamp = rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time;
2707 
2708         aGuard.clear();
2709         if( rMessage.type == MotionNotify )
2710         {
2711             bForce = updateDragAction( rMessage.xmotion.state );
2712         }
2713         updateDragWindow( root_x, root_y, root );
2714         aGuard.reset();
2715 
2716         if( m_nCurrentProtocolVersion >= 0 && m_aDropProxy != None )
2717         {
2718             aGuard.clear();
2719             sendDropPosition( bForce, rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time );
2720         }
2721     }
2722     else if( rMessage.type == XLIB_KeyPress || rMessage.type == KeyRelease )
2723     {
2724         bHandled = true;
2725         const KeySym aKey = XkbKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0, 0 );
2726         if( aKey == XK_Escape )
2727         {
2728             // abort drag
2729             if( it != m_aDropTargets.end() )
2730             {
2731                 DropTargetEvent dte;
2732                 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
2733                 aGuard.clear();
2734                 it->second.m_pTarget->dragExit( dte );
2735             }
2736             else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
2737             {
2738                 // send XdndLeave
2739                 XEvent aEvent;
2740                 aEvent.type = ClientMessage;
2741                 aEvent.xclient.display      = m_pDisplay;
2742                 aEvent.xclient.format       = 32;
2743                 aEvent.xclient.message_type = m_nXdndLeave;
2744                 aEvent.xclient.window       = m_aDropWindow;
2745                 aEvent.xclient.data.l[0]    = m_aWindow;
2746                 memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
2747                 m_aDropWindow = m_aDropProxy = None;
2748                 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2749             }
2750             // notify the listener
2751             DragSourceDropEvent dsde;
2752             dsde.Source             = static_cast< OWeakObject* >(this);
2753             dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2754             dsde.DragSource         = static_cast< XDragSource* >(this);
2755             dsde.DropAction         = DNDConstants::ACTION_NONE;
2756             dsde.DropSuccess        = sal_False;
2757             css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2758             m_xDragSourceListener.clear();
2759             aGuard.clear();
2760             xListener->dragDropEnd( dsde );
2761         }
2762         else
2763         {
2764             /*
2765              *  man page says: state is state immediate PRIOR to the
2766              *  event. It would seem that this is a somewhat arguable
2767              *  design decision.
2768              */
2769             int nState = rMessage.xkey.state;
2770             int nNewState = 0;
2771             switch( aKey )
2772             {
2773                 case XK_Shift_R:
2774                 case XK_Shift_L: nNewState = ShiftMask;break;
2775                 case XK_Control_R:
2776                 case XK_Control_L: nNewState = ControlMask;break;
2777                     // just interested in shift and ctrl for dnd
2778             }
2779             if( rMessage.type == XLIB_KeyPress )
2780                 nState |= nNewState;
2781             else
2782                 nState &= ~nNewState;
2783             aGuard.clear();
2784             if( updateDragAction( nState ) )
2785                 sendDropPosition( true, rMessage.xkey.time );
2786         }
2787     }
2788     else if(
2789             ( rMessage.type == ButtonPress || rMessage.type == ButtonRelease ) &&
2790             rMessage.xbutton.button == m_nDragButton )
2791     {
2792         bool bCancel = true;
2793         if( m_aDropWindow != None )
2794         {
2795             if( it != m_aDropTargets.end() )
2796             {
2797                 if( it->second.m_pTarget->m_bActive && m_nUserDragAction != DNDConstants::ACTION_NONE && m_bLastDropAccepted )
2798                 {
2799                     bHandled = true;
2800                     int x, y;
2801                     XLIB_Window aChild;
2802                     XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &x, &y, &aChild );
2803                     DropTargetDropEvent dtde;
2804                     dtde.Source         = static_cast< OWeakObject* >(it->second.m_pTarget );
2805                     dtde.Context        = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
2806                     dtde.LocationX      = x;
2807                     dtde.LocationY      = y;
2808                     dtde.DropAction     = m_nUserDragAction;
2809                     dtde.SourceActions  = m_nSourceActions;
2810                     dtde.Transferable   = m_xDragSourceTransferable;
2811                     m_bDropSent                 = true;
2812                     m_nDropTimeout              = time( NULL );
2813                     m_bDropWaitingForCompletion = true;
2814                     aGuard.clear();
2815                     it->second->drop( dtde );
2816                     bCancel = false;
2817                 }
2818                 else bCancel = true;
2819             }
2820             else if( m_nCurrentProtocolVersion >= 0 )
2821             {
2822                 bHandled = true;
2823 
2824                 XEvent aEvent;
2825                 aEvent.type = ClientMessage;
2826                 aEvent.xclient.display      = m_pDisplay;
2827                 aEvent.xclient.format       = 32;
2828                 aEvent.xclient.message_type = m_nXdndDrop;
2829                 aEvent.xclient.window       = m_aDropWindow;
2830                 aEvent.xclient.data.l[0]    = m_aWindow;
2831                 aEvent.xclient.data.l[1]    = 0;
2832                 aEvent.xclient.data.l[2]    = rMessage.xbutton.time;
2833                 aEvent.xclient.data.l[3]    = 0;
2834                 aEvent.xclient.data.l[4]    = 0;
2835 
2836                 m_bDropSent                 = true;
2837                 m_nDropTimeout              = time( NULL );
2838                 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2839                 bCancel = false;
2840             }
2841             else
2842             {
2843                 // dropping on non XdndWindows: acquire ownership of
2844                 // PRIMARY and send a middle mouse button click down/up to
2845                 // target window
2846                 SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY );
2847                 if( pAdaptor )
2848                 {
2849                     bHandled = true;
2850 
2851                     XLIB_Window aDummy;
2852                     XEvent aEvent;
2853                     aEvent.type = ButtonPress;
2854                     aEvent.xbutton.display      = m_pDisplay;
2855                     aEvent.xbutton.window       = m_aDropWindow;
2856                     aEvent.xbutton.root         = rMessage.xbutton.root;
2857                     aEvent.xbutton.subwindow    = m_aDropWindow;
2858                     aEvent.xbutton.time         = rMessage.xbutton.time+1;
2859                     aEvent.xbutton.x_root       = rMessage.xbutton.x_root;
2860                     aEvent.xbutton.y_root       = rMessage.xbutton.y_root;
2861                     aEvent.xbutton.state        = rMessage.xbutton.state;
2862                     aEvent.xbutton.button       = Button2;
2863                     aEvent.xbutton.same_screen  = True;
2864                     XTranslateCoordinates( m_pDisplay,
2865                                            rMessage.xbutton.root, m_aDropWindow,
2866                                            rMessage.xbutton.x_root, rMessage.xbutton.y_root,
2867                                            &aEvent.xbutton.x, &aEvent.xbutton.y,
2868                                            &aDummy );
2869                     XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonPressMask, &aEvent );
2870                     aEvent.xbutton.type   = ButtonRelease;
2871                     aEvent.xbutton.time++;
2872                     aEvent.xbutton.state |= Button2Mask;
2873                     XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonReleaseMask, &aEvent );
2874 
2875                     m_bDropSent                 = true;
2876                     m_nDropTimeout              = time( NULL );
2877                     XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2878                     m_bWaitingForPrimaryConversion  = true;
2879                     m_bDropSent                     = true;
2880                     m_nDropTimeout                  = time( NULL );
2881                     // HACK :-)
2882                     aGuard.clear();
2883                     static_cast< X11Clipboard* >( pAdaptor )->setContents( m_xDragSourceTransferable, css::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >() );
2884                     aGuard.reset();
2885                     bCancel = false;
2886                 }
2887             }
2888         }
2889         if( bCancel )
2890         {
2891             // cancel drag
2892             DragSourceDropEvent dsde;
2893             dsde.Source             = static_cast< OWeakObject* >(this);
2894             dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2895             dsde.DragSource         = static_cast< XDragSource* >(this);
2896             dsde.DropAction         = DNDConstants::ACTION_NONE;
2897             dsde.DropSuccess        = sal_False;
2898             css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2899             m_xDragSourceListener.clear();
2900             aGuard.clear();
2901             xListener->dragDropEnd( dsde );
2902             bHandled = true;
2903         }
2904     }
2905     return bHandled;
2906 }
2907 
2908 // ------------------------------------------------------------------------
2909 
accept(sal_Int8 dragOperation,XLIB_Window aDropWindow,XLIB_Time)2910 void SelectionManager::accept( sal_Int8 dragOperation, XLIB_Window aDropWindow, XLIB_Time )
2911 {
2912     if( aDropWindow == m_aCurrentDropWindow )
2913     {
2914 #if OSL_DEBUG_LEVEL > 1
2915         fprintf( stderr, "accept: %x\n", dragOperation );
2916 #endif
2917         Atom nAction = None;
2918         dragOperation &= (DNDConstants::ACTION_MOVE | DNDConstants::ACTION_COPY | DNDConstants::ACTION_LINK);
2919         if( dragOperation & DNDConstants::ACTION_MOVE )
2920             nAction = m_nXdndActionMove;
2921         else if( dragOperation & DNDConstants::ACTION_COPY )
2922             nAction = m_nXdndActionCopy;
2923         else if( dragOperation & DNDConstants::ACTION_LINK )
2924             nAction = m_nXdndActionLink;
2925         m_bLastDropAccepted = true;
2926         sendDragStatus( nAction );
2927     }
2928 }
2929 
2930 // ------------------------------------------------------------------------
2931 
reject(XLIB_Window aDropWindow,XLIB_Time)2932 void SelectionManager::reject( XLIB_Window aDropWindow, XLIB_Time )
2933 {
2934     if( aDropWindow == m_aCurrentDropWindow )
2935     {
2936 #if OSL_DEBUG_LEVEL > 1
2937         fprintf( stderr, "reject\n" );
2938 #endif
2939         m_bLastDropAccepted = false;
2940         sendDragStatus( None );
2941         if( m_bDropSent && m_xDragSourceListener.is() )
2942         {
2943             DragSourceDropEvent dsde;
2944             dsde.Source             = static_cast< OWeakObject* >(this);
2945             dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
2946             dsde.DragSource         = static_cast< XDragSource* >(this);
2947             dsde.DropAction         = DNDConstants::ACTION_NONE;
2948             dsde.DropSuccess        = sal_False;
2949             m_xDragSourceListener->dragDropEnd( dsde );
2950             m_xDragSourceListener.clear();
2951         }
2952     }
2953 }
2954 
2955 /*
2956  *  XDragSource
2957  */
2958 
isDragImageSupported()2959 sal_Bool SelectionManager::isDragImageSupported() throw()
2960 {
2961     return sal_False;
2962 }
2963 
2964 // ------------------------------------------------------------------------
2965 
getDefaultCursor(sal_Int8 dragAction)2966 sal_Int32 SelectionManager::getDefaultCursor( sal_Int8 dragAction ) throw()
2967 {
2968     XLIB_Cursor aCursor = m_aNoneCursor;
2969     if( dragAction & DNDConstants::ACTION_MOVE )
2970         aCursor = m_aMoveCursor;
2971     else if( dragAction & DNDConstants::ACTION_COPY )
2972         aCursor = m_aCopyCursor;
2973     else if( dragAction & DNDConstants::ACTION_LINK )
2974         aCursor = m_aLinkCursor;
2975     return aCursor;
2976 }
2977 
2978 // ------------------------------------------------------------------------
2979 
getXdndVersion(XLIB_Window aWindow,XLIB_Window & rProxy)2980 int SelectionManager::getXdndVersion( XLIB_Window aWindow, XLIB_Window& rProxy )
2981 {
2982     Atom* pProperties = NULL;
2983     int nProperties = 0;
2984     Atom nType;
2985     int nFormat;
2986     unsigned long nItems, nBytes;
2987     unsigned char* pBytes = NULL;
2988 
2989     int nVersion = -1;
2990     rProxy = None;
2991 
2992     /*
2993      *  XListProperties is used here to avoid unnecessary XGetWindowProperty calls
2994      *  and therefore reducing latency penalty
2995      */
2996     pProperties = XListProperties( m_pDisplay, aWindow, &nProperties );
2997     // first look for proxy
2998     int i;
2999     for( i = 0; i < nProperties; i++ )
3000     {
3001         if( pProperties[i] == m_nXdndProxy )
3002         {
3003             XGetWindowProperty( m_pDisplay, aWindow, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3004                                 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3005             if( pBytes )
3006             {
3007                 if( nType == XA_WINDOW )
3008                     rProxy = *(XLIB_Window*)pBytes;
3009                 XFree( pBytes );
3010                 pBytes = NULL;
3011                 if( rProxy != None )
3012                 {
3013                     // now check proxy whether it points to itself
3014                     XGetWindowProperty( m_pDisplay, rProxy, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3015                                         &nType, &nFormat, &nItems, &nBytes, &pBytes );
3016                     if( pBytes )
3017                     {
3018                         if( nType == XA_WINDOW && *(XLIB_Window*)pBytes != rProxy )
3019                             rProxy = None;
3020                         XFree( pBytes );
3021                         pBytes = NULL;
3022                     }
3023                     else
3024                         rProxy = None;
3025                 }
3026             }
3027             break;
3028         }
3029     }
3030     XLIB_Window aAwareWindow = rProxy != None ? rProxy : aWindow;
3031 
3032     XGetWindowProperty( m_pDisplay, aAwareWindow, m_nXdndAware, 0, 1, False, XA_ATOM,
3033                         &nType, &nFormat, &nItems, &nBytes, &pBytes );
3034     if( pBytes )
3035     {
3036         if( nType == XA_ATOM )
3037             nVersion = *(Atom*)pBytes;
3038         XFree( pBytes );
3039     }
3040 
3041     nVersion = nVersion > nXdndProtocolRevision ? nXdndProtocolRevision : nVersion;
3042 
3043     return nVersion;
3044 }
3045 
3046 // ------------------------------------------------------------------------
3047 
updateDragWindow(int nX,int nY,XLIB_Window aRoot)3048 void SelectionManager::updateDragWindow( int nX, int nY, XLIB_Window aRoot )
3049 {
3050     ResettableMutexGuard aGuard( m_aMutex );
3051 
3052     css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3053 
3054     m_nLastDragX = nX;
3055     m_nLastDragY = nY;
3056 
3057     XLIB_Window aParent = aRoot;
3058     XLIB_Window aChild;
3059     XLIB_Window aNewProxy = None, aNewCurrentWindow = None;
3060     int nNewProtocolVersion = -1;
3061     int nWinX, nWinY;
3062 
3063     // find the first XdndAware window or check if root window is
3064     // XdndAware or has XdndProxy
3065     do
3066     {
3067         XTranslateCoordinates( m_pDisplay, aRoot, aParent, nX, nY, &nWinX, &nWinY, &aChild );
3068         if( aChild != None )
3069         {
3070             if( aChild == m_aCurrentDropWindow && aChild != aRoot && m_nCurrentProtocolVersion >= 0 )
3071             {
3072                 aParent = aChild;
3073                 break;
3074             }
3075             nNewProtocolVersion = getXdndVersion( aChild, aNewProxy );
3076             aParent = aChild;
3077         }
3078     } while( aChild != None && nNewProtocolVersion < 0 );
3079 
3080     aNewCurrentWindow = aParent;
3081     if( aNewCurrentWindow == aRoot )
3082     {
3083         // no children, try root drop
3084         nNewProtocolVersion = getXdndVersion( aNewCurrentWindow, aNewProxy );
3085         if( nNewProtocolVersion < 3 )
3086         {
3087             aNewCurrentWindow = aNewProxy = None;
3088             nNewProtocolVersion = nXdndProtocolRevision;
3089         }
3090     }
3091 
3092     DragSourceDragEvent dsde;
3093     dsde.Source             = static_cast< OWeakObject* >(this);
3094     dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
3095     dsde.DragSource         = static_cast< XDragSource* >(this);
3096     dsde.DropAction         = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3097     dsde.UserAction         = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3098 
3099     ::std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it;
3100     if( aNewCurrentWindow != m_aDropWindow )
3101     {
3102 #if OSL_DEBUG_LEVEL > 1
3103         fprintf( stderr, "drag left window 0x%lx (rev. %d), entered window 0x%lx (rev %d)\n", m_aDropWindow, m_nCurrentProtocolVersion, aNewCurrentWindow, nNewProtocolVersion );
3104 #endif
3105 
3106         if( m_aDropWindow != None )
3107         {
3108             it = m_aDropTargets.find( m_aDropWindow );
3109             if( it != m_aDropTargets.end() )
3110                 // shortcut for own drop targets
3111             {
3112                 DropTargetEvent dte;
3113                 dte.Source  = static_cast< OWeakObject* >( it->second.m_pTarget );
3114                 aGuard.clear();
3115                 it->second.m_pTarget->dragExit( dte );
3116                 aGuard.reset();
3117             }
3118             else
3119             {
3120                 // send old drop target a XdndLeave
3121                 XEvent aEvent;
3122                 aEvent.type = ClientMessage;
3123                 aEvent.xclient.display          = m_pDisplay;
3124                 aEvent.xclient.format           = 32;
3125                 aEvent.xclient.message_type     = m_nXdndLeave;
3126                 aEvent.xclient.window           = m_aDropWindow;
3127                 aEvent.xclient.data.l[0]        = m_aWindow;
3128                 aEvent.xclient.data.l[1]        = 0;
3129                 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3130             }
3131             if( xListener.is() )
3132             {
3133                 aGuard.clear();
3134                 xListener->dragExit( dsde );
3135                 aGuard.reset();
3136             }
3137         }
3138 
3139         m_nCurrentProtocolVersion   = nNewProtocolVersion;
3140         m_aDropWindow               = aNewCurrentWindow;
3141         m_aDropProxy                = aNewProxy != None ? aNewProxy : m_aDropWindow;
3142 
3143         it = m_aDropTargets.find( m_aDropWindow );
3144         if( it != m_aDropTargets.end() && ! it->second.m_pTarget->m_bActive )
3145             m_aDropProxy = None;
3146 
3147         if( m_aDropProxy != None && xListener.is() )
3148         {
3149             aGuard.clear();
3150             xListener->dragEnter( dsde );
3151             aGuard.reset();
3152         }
3153         // send XdndEnter
3154         if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
3155         {
3156             it = m_aDropTargets.find( m_aDropWindow );
3157             if( it != m_aDropTargets.end() )
3158             {
3159                 XTranslateCoordinates( m_pDisplay, aRoot, m_aDropWindow, nX, nY, &nWinX, &nWinY, &aChild );
3160                 DropTargetDragEnterEvent dtde;
3161                 dtde.Source                 = static_cast< OWeakObject* >( it->second.m_pTarget );
3162                 dtde.Context                = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this );
3163                 dtde.LocationX              = nWinX;
3164                 dtde.LocationY              = nWinY;
3165                 dtde.DropAction             = m_nUserDragAction;
3166                 dtde.SourceActions          = m_nSourceActions;
3167                 dtde.SupportedDataFlavors   = m_xDragSourceTransferable->getTransferDataFlavors();
3168                 aGuard.clear();
3169                 it->second.m_pTarget->dragEnter( dtde );
3170                 aGuard.reset();
3171             }
3172             else
3173             {
3174                 XEvent aEvent;
3175                 aEvent.type = ClientMessage;
3176                 aEvent.xclient.display          = m_pDisplay;
3177                 aEvent.xclient.format           = 32;
3178                 aEvent.xclient.message_type = m_nXdndEnter;
3179                 aEvent.xclient.window       = m_aDropWindow;
3180                 aEvent.xclient.data.l[0]    = m_aWindow;
3181                 aEvent.xclient.data.l[1]    = m_nCurrentProtocolVersion << 24;
3182                 memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3183                 // fill in data types
3184                 ::std::list< Atom > aConversions;
3185                 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3186                 if( aConversions.size() > 3 )
3187                     aEvent.xclient.data.l[1] |= 1;
3188                 ::std::list< Atom >::const_iterator type_it = aConversions.begin();
3189                 for( int i = 0; type_it != aConversions.end() && i < 3; i++, ++type_it )
3190                     aEvent.xclient.data.l[i+2] = *type_it;
3191                 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3192             }
3193         }
3194         m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
3195     }
3196     else if( m_aDropProxy != None && xListener.is() )
3197     {
3198         aGuard.clear();
3199         // drag over for XdndAware windows comes when receiving XdndStatus
3200         xListener->dragOver( dsde );
3201     }
3202 }
3203 
3204 // ------------------------------------------------------------------------
3205 
startDrag(const DragGestureEvent & trigger,sal_Int8 sourceActions,sal_Int32,sal_Int32,const css::uno::Reference<XTransferable> & transferable,const css::uno::Reference<XDragSourceListener> & listener)3206 void SelectionManager::startDrag(
3207                                  const DragGestureEvent& trigger,
3208                                  sal_Int8 sourceActions,
3209                                  sal_Int32,
3210                                  sal_Int32,
3211                                  const css::uno::Reference< XTransferable >& transferable,
3212                                  const css::uno::Reference< XDragSourceListener >& listener
3213                                  ) throw()
3214 {
3215 #if OSL_DEBUG_LEVEL > 1
3216     fprintf( stderr, "startDrag( sourceActions = %x )\n", (int)sourceActions );
3217 #endif
3218 
3219     DragSourceDropEvent aDragFailedEvent;
3220     aDragFailedEvent.Source             = static_cast< OWeakObject* >(this);
3221     aDragFailedEvent.DragSource         = static_cast< XDragSource* >(this);
3222     aDragFailedEvent.DragSourceContext  = new DragSourceContext( None, CurrentTime, *this );
3223     aDragFailedEvent.DropAction         = DNDConstants::ACTION_NONE;
3224     aDragFailedEvent.DropSuccess        = sal_False;
3225 
3226     if( m_aDragRunning.check() )
3227     {
3228         if( listener.is() )
3229             listener->dragDropEnd( aDragFailedEvent );
3230 
3231 #if OSL_DEBUG_LEVEL > 1
3232         fprintf( stderr, "*** ERROR *** second drag and drop started.\n" );
3233         if( m_xDragSourceListener.is() )
3234             fprintf( stderr, "*** ERROR *** drag source listener already set.\n" );
3235         else
3236             fprintf( stderr, "*** ERROR *** drag thread already running.\n" );
3237 #endif
3238         return;
3239     }
3240 
3241     SalFrame* pCaptureFrame = NULL;
3242 
3243     {
3244         ClearableMutexGuard aGuard(m_aMutex);
3245 
3246         // first get the current pointer position and the window that
3247         // the pointer is located in. since said window should be one
3248         // of our DropTargets at the time of executeDrag we can use
3249         // them for a start
3250         XLIB_Window aRoot, aParent, aChild;
3251         int root_x(0), root_y(0), win_x, win_y;
3252         unsigned int mask(0);
3253 
3254         ::std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it;
3255         it = m_aDropTargets.begin();
3256         while( it != m_aDropTargets.end() )
3257         {
3258             if( XQueryPointer( m_pDisplay, it->second.m_aRootWindow,
3259                                &aRoot, &aParent,
3260                                &root_x, &root_y,
3261                                &win_x, &win_y,
3262                                &mask ) )
3263             {
3264                 aParent = it->second.m_aRootWindow;
3265                 break;
3266             }
3267             ++it;
3268         }
3269 
3270         // don't start DnD if there is none of our windows on the same screen as
3271         // the pointer or if no mouse button is pressed
3272         if( it == m_aDropTargets.end() || (mask & (Button1Mask|Button2Mask|Button3Mask)) == 0 )
3273         {
3274             aGuard.clear();
3275             if( listener.is() )
3276                 listener->dragDropEnd( aDragFailedEvent );
3277             return;
3278         }
3279 
3280         // try to find which of our drop targets is the drag source
3281         // if that drop target is deregistered we should stop executing
3282         // the drag (actually this is a poor substitute for an "endDrag"
3283         // method ).
3284         m_aDragSourceWindow = None;
3285         aParent = aRoot = it->second.m_aRootWindow;
3286         do
3287         {
3288             XTranslateCoordinates( m_pDisplay, aRoot, aParent, root_x, root_y, &win_x, &win_y, &aChild );
3289             if( aChild != None && m_aDropTargets.find( aChild ) != m_aDropTargets.end() )
3290             {
3291                 m_aDragSourceWindow = aChild;
3292 #if OSL_DEBUG_LEVEL > 1
3293                 fprintf( stderr, "found drag source window 0x%lx\n", m_aDragSourceWindow );
3294 #endif
3295                 break;
3296             }
3297             aParent = aChild;
3298         } while( aChild != None );
3299 
3300 #if OSL_DEBUG_LEVEL > 1
3301         fprintf( stderr, "try to grab pointer ... " );
3302 #endif
3303         int nPointerGrabSuccess =
3304             XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True,
3305                           DRAG_EVENT_MASK,
3306                           GrabModeAsync, GrabModeAsync,
3307                           None,
3308                           None,
3309                           CurrentTime );
3310         /* if we could not grab the pointer here, there is a chance
3311            that the pointer is grabbed by the other vcl display (the main loop)
3312            so let's break that grab and reset it later
3313 
3314            remark: this whole code should really be molten into normal vcl so only
3315            one display is used...
3316         */
3317         if( nPointerGrabSuccess != GrabSuccess )
3318         {
3319             vos::IMutex& rSolarMutex( Application::GetSolarMutex() );
3320             if( rSolarMutex.tryToAcquire() )
3321             {
3322                 pCaptureFrame = GetX11SalData()->GetDisplay()->GetCaptureFrame();
3323                 if( pCaptureFrame )
3324                 {
3325                     GetX11SalData()->GetDisplay()->CaptureMouse( NULL );
3326                     nPointerGrabSuccess =
3327                                 XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True,
3328                                               DRAG_EVENT_MASK,
3329                                               GrabModeAsync, GrabModeAsync,
3330                                               None,
3331                                               None,
3332                                               CurrentTime );
3333                 }
3334             }
3335         }
3336 #if OSL_DEBUG_LEVEL > 1
3337         fprintf( stderr, "%d\n", nPointerGrabSuccess );
3338 #endif
3339 #if OSL_DEBUG_LEVEL > 1
3340         fprintf( stderr, "try to grab keyboard ... " );
3341 #endif
3342         int nKeyboardGrabSuccess =
3343             XGrabKeyboard( m_pDisplay, it->second.m_aRootWindow, True,
3344                            GrabModeAsync, GrabModeAsync, CurrentTime );
3345 #if OSL_DEBUG_LEVEL > 1
3346         fprintf( stderr, "%d\n", nKeyboardGrabSuccess );
3347 #endif
3348         if( nPointerGrabSuccess != GrabSuccess || nKeyboardGrabSuccess != GrabSuccess )
3349         {
3350             if( nPointerGrabSuccess == GrabSuccess )
3351                 XUngrabPointer( m_pDisplay, CurrentTime );
3352             if( nKeyboardGrabSuccess == GrabSuccess )
3353                 XUngrabKeyboard( m_pDisplay, CurrentTime );
3354             XFlush( m_pDisplay );
3355             aGuard.clear();
3356             if( listener.is() )
3357                 listener->dragDropEnd( aDragFailedEvent );
3358             if( pCaptureFrame )
3359             {
3360                 vos::IMutex& rSolarMutex( Application::GetSolarMutex() );
3361                 if( rSolarMutex.tryToAcquire() )
3362                     GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame );
3363 #if OSL_DEBUG_LEVEL > 0
3364                 else
3365                     OSL_ENSURE( 0, "failed to acquire SolarMutex to reset capture frame" );
3366 #endif
3367             }
3368             return;
3369         }
3370 
3371         m_xDragSourceTransferable   = transferable;
3372         m_xDragSourceListener       = listener;
3373         m_aDragFlavors              = transferable->getTransferDataFlavors();
3374         m_aCurrentCursor            = None;
3375 
3376         requestOwnership( m_nXdndSelection );
3377 
3378         ::std::list< Atom > aConversions;
3379         ::std::list< Atom >::const_iterator type_it;
3380         getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3381 
3382         int nTypes = aConversions.size();
3383         Atom* pTypes = (Atom*)alloca( sizeof(Atom)*nTypes );
3384         type_it = aConversions.begin();
3385         for( int n = 0; n < nTypes; n++, ++type_it )
3386             pTypes[n] = *type_it;
3387 
3388         XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes );
3389 
3390         m_nSourceActions                = sourceActions | DNDConstants::ACTION_DEFAULT;
3391         m_nUserDragAction               = DNDConstants::ACTION_MOVE & m_nSourceActions;
3392         if( ! m_nUserDragAction )
3393             m_nUserDragAction           = DNDConstants::ACTION_COPY & m_nSourceActions;
3394         if( ! m_nUserDragAction )
3395             m_nUserDragAction           = DNDConstants::ACTION_LINK & m_nSourceActions;
3396         m_nTargetAcceptAction           = DNDConstants::ACTION_DEFAULT;
3397         m_bDropSent                     = false;
3398         m_bDropSuccess                  = false;
3399         m_bWaitingForPrimaryConversion  = false;
3400         m_nDragButton                   = Button1; // default to left button
3401         com::sun::star::awt::MouseEvent aEvent;
3402         if( trigger.Event >>= aEvent )
3403         {
3404             if( aEvent.Buttons & MouseButton::LEFT )
3405                 m_nDragButton = Button1;
3406             else if( aEvent.Buttons & MouseButton::RIGHT )
3407                 m_nDragButton = Button3;
3408             else if( aEvent.Buttons & MouseButton::MIDDLE )
3409                 m_nDragButton = Button2;
3410         }
3411 #if OSL_DEBUG_LEVEL > 1
3412         fprintf( stderr, "m_nUserDragAction = %x\n", (int)m_nUserDragAction );
3413 #endif
3414         updateDragWindow( root_x, root_y, aRoot );
3415         m_nUserDragAction = ~0;
3416         updateDragAction( mask );
3417     }
3418 
3419     m_aDragRunning.set();
3420     m_aDragExecuteThread = osl_createSuspendedThread( call_SelectionManager_runDragExecute, this );
3421     if( m_aDragExecuteThread )
3422         osl_resumeThread( m_aDragExecuteThread );
3423     else
3424     {
3425 #if OSL_DEBUG_LEVEL > 1
3426         fprintf( stderr, "osl_createSuspendedThread failed for drag execute\n" );
3427 #endif
3428         m_xDragSourceListener.clear();
3429         m_xDragSourceTransferable.clear();
3430 
3431         m_bDropSent                         = false;
3432         m_bDropSuccess                      = false;
3433         m_bWaitingForPrimaryConversion      = false;
3434         m_aDropWindow                       = None;
3435         m_aDropProxy                        = None;
3436         m_nCurrentProtocolVersion           = nXdndProtocolRevision;
3437         m_nNoPosX                           = 0;
3438         m_nNoPosY                           = 0;
3439         m_nNoPosWidth                       = 0;
3440         m_nNoPosHeight                      = 0;
3441         m_aCurrentCursor                    = None;
3442 
3443         XUngrabPointer( m_pDisplay, CurrentTime );
3444         XUngrabKeyboard( m_pDisplay, CurrentTime );
3445         XFlush( m_pDisplay );
3446 
3447         if( pCaptureFrame )
3448         {
3449             vos::IMutex& rSolarMutex( Application::GetSolarMutex() );
3450             if( rSolarMutex.tryToAcquire() )
3451                 GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame );
3452 #if OSL_DEBUG_LEVEL > 0
3453             else
3454                 OSL_ENSURE( 0, "failed to acquire SolarMutex to reset capture frame" );
3455 #endif
3456         }
3457 
3458         m_aDragRunning.reset();
3459 
3460         if( listener.is() )
3461             listener->dragDropEnd( aDragFailedEvent );
3462     }
3463 }
3464 
runDragExecute(void * pThis)3465 void SelectionManager::runDragExecute( void* pThis )
3466 {
3467     SelectionManager* This = (SelectionManager*)pThis;
3468     This->dragDoDispatch();
3469 }
3470 
dragDoDispatch()3471 void SelectionManager::dragDoDispatch()
3472 {
3473 
3474     // do drag
3475     // m_xDragSourceListener will be cleared on finished drop
3476 #if OSL_DEBUG_LEVEL > 1
3477     fprintf( stderr, "begin executeDrag dispatching\n" );
3478 #endif
3479     TimeValue aTVal;
3480     aTVal.Seconds = 0;
3481     aTVal.Nanosec = 200000000;
3482     oslThread aThread = m_aDragExecuteThread;
3483     while( m_xDragSourceListener.is() && ( ! m_bDropSent || time(NULL)-m_nDropTimeout < 5 ) && osl_scheduleThread( aThread ) )
3484     {
3485         // let the thread in the run method do the dispatching
3486         // just look occasionally here whether drop timed out or is completed
3487         osl_waitThread( &aTVal );
3488     }
3489 #if OSL_DEBUG_LEVEL > 1
3490     fprintf( stderr, "end executeDrag dispatching\n" );
3491 #endif
3492     {
3493         ClearableMutexGuard aGuard(m_aMutex);
3494 
3495         css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3496         css::uno::Reference< XTransferable > xTransferable( m_xDragSourceTransferable );
3497         m_xDragSourceListener.clear();
3498         m_xDragSourceTransferable.clear();
3499 
3500         DragSourceDropEvent dsde;
3501         dsde.Source             = static_cast< OWeakObject* >(this);
3502         dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
3503         dsde.DragSource         = static_cast< XDragSource* >(this);
3504         dsde.DropAction         = DNDConstants::ACTION_NONE;
3505         dsde.DropSuccess        = sal_False;
3506 
3507         // cleanup after drag
3508         if( m_bWaitingForPrimaryConversion )
3509             getAdaptor( XA_PRIMARY )->clearTransferable();
3510 
3511         m_bDropSent                         = false;
3512         m_bDropSuccess                      = false;
3513         m_bWaitingForPrimaryConversion      = false;
3514         m_aDropWindow                       = None;
3515         m_aDropProxy                        = None;
3516         m_nCurrentProtocolVersion           = nXdndProtocolRevision;
3517         m_nNoPosX                           = 0;
3518         m_nNoPosY                           = 0;
3519         m_nNoPosWidth                       = 0;
3520         m_nNoPosHeight                      = 0;
3521         m_aCurrentCursor                    = None;
3522 
3523         XUngrabPointer( m_pDisplay, CurrentTime );
3524         XUngrabKeyboard( m_pDisplay, CurrentTime );
3525         XFlush( m_pDisplay );
3526 
3527         m_aDragExecuteThread = NULL;
3528         m_aDragRunning.reset();
3529 
3530         aGuard.clear();
3531         if( xListener.is() )
3532         {
3533             xTransferable.clear();
3534             xListener->dragDropEnd( dsde );
3535         }
3536     }
3537     osl_destroyThread( aThread );
3538 }
3539 
3540 /*
3541  *  XDragSourceContext
3542  */
3543 
getCurrentCursor()3544 sal_Int32 SelectionManager::getCurrentCursor()
3545 {
3546     return m_aCurrentCursor;
3547 }
3548 
3549 // ------------------------------------------------------------------------
3550 
setCursor(sal_Int32 cursor,XLIB_Window aDropWindow,XLIB_Time)3551 void SelectionManager::setCursor( sal_Int32 cursor, XLIB_Window aDropWindow, XLIB_Time )
3552 {
3553     MutexGuard aGuard( m_aMutex );
3554     if( aDropWindow == m_aDropWindow && XLIB_Cursor(cursor) != m_aCurrentCursor )
3555     {
3556         if( m_xDragSourceListener.is() && ! m_bDropSent )
3557         {
3558             m_aCurrentCursor = cursor;
3559             XChangeActivePointerGrab( m_pDisplay, DRAG_EVENT_MASK, cursor, CurrentTime );
3560             XFlush( m_pDisplay );
3561         }
3562     }
3563 }
3564 
3565 // ------------------------------------------------------------------------
3566 
setImage(sal_Int32,XLIB_Window,XLIB_Time)3567 void SelectionManager::setImage( sal_Int32, XLIB_Window, XLIB_Time )
3568 {
3569 }
3570 
3571 // ------------------------------------------------------------------------
3572 
transferablesFlavorsChanged()3573 void SelectionManager::transferablesFlavorsChanged()
3574 {
3575     MutexGuard aGuard(m_aMutex);
3576 
3577     m_aDragFlavors = m_xDragSourceTransferable->getTransferDataFlavors();
3578     int i;
3579 
3580     std::list< Atom > aConversions;
3581     std::list< Atom >::const_iterator type_it;
3582 
3583     getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3584 
3585     int nTypes = aConversions.size();
3586     Atom* pTypes = (Atom*)alloca( sizeof(Atom)*aConversions.size() );
3587     for( i = 0, type_it = aConversions.begin(); type_it != aConversions.end(); ++type_it, i++ )
3588         pTypes[i] = *type_it;
3589     XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes );
3590 
3591     if( m_aCurrentDropWindow != None && m_nCurrentProtocolVersion >= 0 )
3592     {
3593         // send synthetic leave and enter events
3594 
3595         XEvent aEvent;
3596 
3597         aEvent.type = ClientMessage;
3598         aEvent.xclient.display          = m_pDisplay;
3599         aEvent.xclient.format           = 32;
3600         aEvent.xclient.window           = m_aDropWindow;
3601         aEvent.xclient.data.l[0]        = m_aWindow;
3602 
3603         aEvent.xclient.message_type     = m_nXdndLeave;
3604         aEvent.xclient.data.l[1]        = 0;
3605         XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3606 
3607         aEvent.xclient.message_type = m_nXdndEnter;
3608         aEvent.xclient.data.l[1]    = m_nCurrentProtocolVersion << 24;
3609         memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3610         // fill in data types
3611         if( nTypes > 3 )
3612             aEvent.xclient.data.l[1] |= 1;
3613         for( int j = 0; j < nTypes && j < 3; j++ )
3614             aEvent.xclient.data.l[j+2] = pTypes[j];
3615 
3616         XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3617     }
3618 }
3619 
3620 /*
3621  *  dispatch loop
3622  */
3623 
3624 // ------------------------------------------------------------------------
3625 
handleXEvent(XEvent & rEvent)3626 bool SelectionManager::handleXEvent( XEvent& rEvent )
3627 {
3628     /*
3629      *  since we are XConnectionListener to a second X display
3630      *  to get client messages it is essential not to dispatch
3631      *  events twice that we get on both connections
3632      *
3633      *  #95201# between dispatching ButtonPress and startDrag
3634      *  the user can already have released the mouse. The ButtonRelease
3635      *  will then be dispatched in VCLs queue and never turn up here.
3636      *  Which is not so good, since startDrag will XGrabPointer and
3637      *  XGrabKeyboard -> solid lock.
3638      */
3639     if( rEvent.xany.display != m_pDisplay
3640         && rEvent.type != ClientMessage
3641         && rEvent.type != ButtonPress
3642         && rEvent.type != ButtonRelease
3643         )
3644         return false;
3645 
3646     bool bHandled = false;
3647     switch (rEvent.type)
3648     {
3649         case SelectionClear:
3650         {
3651             ClearableMutexGuard aGuard(m_aMutex);
3652 #if OSL_DEBUG_LEVEL > 1
3653             fprintf( stderr, "SelectionClear for selection %s\n",
3654                      OUStringToOString( getString( rEvent.xselectionclear.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr()
3655                      );
3656 #endif
3657             SelectionAdaptor* pAdaptor = getAdaptor( rEvent.xselectionclear.selection );
3658             std::hash_map< Atom, Selection* >::iterator it( m_aSelections.find( rEvent.xselectionclear.selection ) );
3659             if( it != m_aSelections.end() )
3660                 it->second->m_bOwner = false;
3661             aGuard.clear();
3662             if ( pAdaptor )
3663                 pAdaptor->clearTransferable();
3664         }
3665         break;
3666 
3667         case SelectionRequest:
3668             bHandled = handleSelectionRequest( rEvent.xselectionrequest );
3669             break;
3670         case PropertyNotify:
3671             if( rEvent.xproperty.window == m_aWindow ||
3672                 rEvent.xproperty.window == m_aCurrentDropWindow
3673                 )
3674                 bHandled = handleReceivePropertyNotify( rEvent.xproperty );
3675             else
3676                 bHandled = handleSendPropertyNotify( rEvent.xproperty );
3677             break;
3678         case SelectionNotify:
3679             bHandled = handleSelectionNotify( rEvent.xselection );
3680             break;
3681         case ClientMessage:
3682             // messages from drag target
3683             if( rEvent.xclient.message_type == m_nXdndStatus ||
3684                 rEvent.xclient.message_type == m_nXdndFinished )
3685                 bHandled = handleDragEvent( rEvent );
3686             // messages from drag source
3687             else if(
3688                     rEvent.xclient.message_type == m_nXdndEnter     ||
3689                     rEvent.xclient.message_type == m_nXdndLeave     ||
3690                     rEvent.xclient.message_type == m_nXdndPosition  ||
3691                     rEvent.xclient.message_type == m_nXdndDrop
3692                     )
3693                 bHandled = handleDropEvent( rEvent.xclient );
3694             break;
3695         case EnterNotify:
3696         case LeaveNotify:
3697         case MotionNotify:
3698         case ButtonPress:
3699         case ButtonRelease:
3700         case XLIB_KeyPress:
3701         case KeyRelease:
3702             bHandled = handleDragEvent( rEvent );
3703             break;
3704         default:
3705             ;
3706     }
3707     return bHandled;
3708 }
3709 
3710 // ------------------------------------------------------------------------
3711 
dispatchEvent(int millisec)3712 void SelectionManager::dispatchEvent( int millisec )
3713 {
3714     pollfd aPollFD;
3715     XEvent event;
3716 
3717     // query socket handle to poll on
3718     aPollFD.fd      = ConnectionNumber( m_pDisplay );
3719     aPollFD.events  = POLLIN;
3720     aPollFD.revents = 0;
3721 
3722     // wait for activity (outside the xlib)
3723     if( poll( &aPollFD, 1, millisec ) > 0 )
3724     {
3725         // now acquire the mutex to prevent other threads
3726         // from using the same X connection
3727         ResettableMutexGuard aGuard(m_aMutex);
3728 
3729         // prevent that another thread already ate the input
3730         // this can happen if e.g. another thread does
3731         // an X request getting a response. the response
3732         // would be removed from the queue and we would end up
3733         // with an empty socket here
3734         if( poll( &aPollFD, 1, 0 ) > 0 )
3735         {
3736             int nPending = 1;
3737             while( nPending )
3738             {
3739                 nPending = XPending( m_pDisplay );
3740                 if( nPending )
3741                 {
3742                     XNextEvent( m_pDisplay, &event );
3743                     aGuard.clear();
3744                     handleXEvent( event );
3745                     aGuard.reset();
3746                 }
3747             }
3748         }
3749     }
3750 }
3751 
3752 // ------------------------------------------------------------------------
3753 
run(void * pThis)3754 void SelectionManager::run( void* pThis )
3755 {
3756 #if OSL_DEBUG_LEVEL > 1
3757     fprintf(stderr, "SelectionManager::run\n" );
3758 #endif
3759     // dispatch until the cows come home
3760 
3761     SelectionManager* This = (SelectionManager*)pThis;
3762 
3763     timeval aLast;
3764     gettimeofday( &aLast, 0 );
3765 
3766     css::uno::Reference< XMultiServiceFactory > xFact( ::comphelper::getProcessServiceFactory() );
3767     if( xFact.is() )
3768     {
3769         css::uno::Reference< XDesktop > xDesktop( xFact->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.frame.Desktop" ) ), UNO_QUERY );
3770         if( xDesktop.is() )
3771             xDesktop->addTerminateListener(This);
3772     }
3773 
3774     while( osl_scheduleThread(This->m_aThread) )
3775     {
3776         This->dispatchEvent( 1000 );
3777 
3778         timeval aNow;
3779         gettimeofday( &aNow, 0 );
3780 
3781         if( (aNow.tv_sec - aLast.tv_sec) > 0 )
3782         {
3783             ClearableMutexGuard aGuard(This->m_aMutex);
3784             std::list< std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > > > aChangeList;
3785 
3786             for( std::hash_map< Atom, Selection* >::iterator it = This->m_aSelections.begin(); it != This->m_aSelections.end(); ++it )
3787             {
3788                 if( it->first != This->m_nXdndSelection && ! it->second->m_bOwner )
3789                 {
3790                     XLIB_Window aOwner = XGetSelectionOwner( This->m_pDisplay, it->first );
3791                     if( aOwner != it->second->m_aLastOwner )
3792                     {
3793                         it->second->m_aLastOwner = aOwner;
3794                         std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > >
3795                             aKeep( it->second->m_pAdaptor, it->second->m_pAdaptor->getReference() );
3796                         aChangeList.push_back( aKeep );
3797                     }
3798                 }
3799             }
3800             aGuard.clear();
3801             while( aChangeList.begin() != aChangeList.end() )
3802             {
3803                 aChangeList.front().first->fireContentsChanged();
3804                 aChangeList.pop_front();
3805             }
3806             aLast = aNow;
3807         }
3808     }
3809 #if OSL_DEBUG_LEVEL > 1
3810     fprintf(stderr, "SelectionManager::run end\n" );
3811 #endif
3812 }
3813 
shutdown()3814 void SelectionManager::shutdown() throw()
3815 {
3816     ResettableMutexGuard aGuard(m_aMutex);
3817     if( m_bShutDown )
3818     {
3819         return;
3820     }
3821     m_bShutDown = true;
3822     // stop dispatching
3823     if( m_aThread )
3824     {
3825         osl_terminateThread( m_aThread );
3826         /*
3827          * Allow thread to finish before app exits to avoid pulling the carpet
3828          * out from under it if pasting is occurring during shutdown
3829          *
3830          * a) allow it to have the Mutex and
3831          * b) reschedule to allow it to complete callbacks to any
3832          * Application::GetSolarMutex protected regions, etc. e.g.
3833          * TransferableHelper::getTransferDataFlavors (via
3834          * SelectionManager::handleSelectionRequest) which it might
3835          * currently be trying to enter.
3836          *
3837          * Otherwise the thread may be left still waiting on a GlobalMutex
3838          * when that gets destroyed, letting the thread blow up and die
3839          * when enters the section in a now dead AOO instance.
3840          */
3841         aGuard.clear();
3842         while (osl_isThreadRunning(m_aThread))
3843         {
3844             vos::OGuard guard2(Application::GetSolarMutex());
3845             Application::Reschedule();
3846         }
3847         osl_joinWithThread( m_aThread );
3848         osl_destroyThread( m_aThread );
3849         m_aThread = NULL;
3850         aGuard.reset();
3851     }
3852     m_xDisplayConnection->removeEventHandler( Any(), this );
3853     m_xDisplayConnection.clear();
3854 }
3855 
3856 // ------------------------------------------------------------------------
3857 
handleEvent(const Any & event)3858 sal_Bool SelectionManager::handleEvent( const Any& event ) throw()
3859 {
3860     Sequence< sal_Int8 > aSeq;
3861     if( (event >>= aSeq) )
3862     {
3863         XEvent* pEvent = (XEvent*)aSeq.getArray();
3864         XLIB_Time nTimestamp = CurrentTime;
3865         if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease )
3866             nTimestamp = pEvent->xbutton.time;
3867         else if( pEvent->type == XLIB_KeyPress || pEvent->type == KeyRelease )
3868             nTimestamp = pEvent->xkey.time;
3869         else if( pEvent->type == MotionNotify )
3870             nTimestamp = pEvent->xmotion.time;
3871         else if( pEvent->type == PropertyNotify )
3872             nTimestamp = pEvent->xproperty.time;
3873 
3874         if( nTimestamp != CurrentTime )
3875         {
3876             MutexGuard aGuard(m_aMutex);
3877 
3878             m_nSelectionTimestamp = nTimestamp;
3879         }
3880 
3881         return sal_Bool( handleXEvent( *pEvent ) );
3882     }
3883     else
3884     {
3885         #if OSL_DEBUG_LEVEL > 1
3886         fprintf( stderr, "SelectionManager got downing event\n" );
3887         #endif
3888         shutdown();
3889     }
3890     return sal_True;
3891 }
3892 
disposing(const::com::sun::star::lang::EventObject &)3893 void SAL_CALL SelectionManager::disposing( const ::com::sun::star::lang::EventObject& )
3894     throw( ::com::sun::star::uno::RuntimeException )
3895 {
3896 }
3897 
queryTermination(const::com::sun::star::lang::EventObject &)3898 void SAL_CALL SelectionManager::queryTermination( const ::com::sun::star::lang::EventObject& )
3899     throw( ::com::sun::star::frame::TerminationVetoException, ::com::sun::star::uno::RuntimeException )
3900 {
3901 }
3902 
3903 /*
3904  * To be safe, shutdown needs to be called before the ~SfxApplication is called, waiting until
3905  * the downing event can be too late if paste are requested during shutdown and ~SfxApplication
3906  * has been called before vcl is shutdown
3907  */
notifyTermination(const::com::sun::star::lang::EventObject & rEvent)3908 void SAL_CALL SelectionManager::notifyTermination( const ::com::sun::star::lang::EventObject& rEvent )
3909     throw( ::com::sun::star::uno::RuntimeException )
3910 {
3911     css::uno::Reference< XDesktop > xDesktop( rEvent.Source, UNO_QUERY );
3912     if( xDesktop.is() == sal_True )
3913         xDesktop->removeTerminateListener( this );
3914     #if OSL_DEBUG_LEVEL > 1
3915     fprintf( stderr, "SelectionManager got app termination event\n" );
3916     #endif
3917     shutdown();
3918 }
3919 
3920 // ------------------------------------------------------------------------
3921 
registerHandler(Atom selection,SelectionAdaptor & rAdaptor)3922 void SelectionManager::registerHandler( Atom selection, SelectionAdaptor& rAdaptor )
3923 {
3924     MutexGuard aGuard(m_aMutex);
3925 
3926     Selection* pNewSelection    = new Selection();
3927     pNewSelection->m_pAdaptor   = &rAdaptor;
3928     pNewSelection->m_aAtom      = selection;
3929     m_aSelections[ selection ]  = pNewSelection;
3930 }
3931 
3932 // ------------------------------------------------------------------------
3933 
deregisterHandler(Atom selection)3934 void SelectionManager::deregisterHandler( Atom selection )
3935 {
3936     MutexGuard aGuard(m_aMutex);
3937 
3938     ::std::hash_map< Atom, Selection* >::iterator it =
3939           m_aSelections.find( selection );
3940     if( it != m_aSelections.end() )
3941     {
3942         delete it->second->m_pPixmap;
3943         delete it->second;
3944         m_aSelections.erase( it );
3945     }
3946 }
3947 
3948 // ------------------------------------------------------------------------
3949 
3950 static bool bWasError = false;
3951 
3952 extern "C"
3953 {
local_xerror_handler(Display *,XErrorEvent *)3954     int local_xerror_handler(Display* , XErrorEvent*)
3955     {
3956         bWasError = true;
3957         return 0;
3958     }
3959     typedef int(*xerror_hdl_t)(Display*,XErrorEvent*);
3960 }
3961 
registerDropTarget(XLIB_Window aWindow,DropTarget * pTarget)3962 void SelectionManager::registerDropTarget( XLIB_Window aWindow, DropTarget* pTarget )
3963 {
3964     MutexGuard aGuard(m_aMutex);
3965 
3966     // sanity check
3967     ::std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it =
3968           m_aDropTargets.find( aWindow );
3969     if( it != m_aDropTargets.end() )
3970         OSL_ASSERT( "attempt to register window as drop target twice" );
3971     else if( aWindow && m_pDisplay )
3972     {
3973         DropTargetEntry aEntry( pTarget );
3974         bWasError=false;
3975         /* #i100000# ugly workaround: gtk sets its own XErrorHandler which is not suitable for us
3976            unfortunately XErrorHandler is not per display, so this is just and ugly hack
3977            Need to remove separate display and integrate clipboard/dnd into vcl's unx code ASAP
3978         */
3979         xerror_hdl_t pOldHandler = XSetErrorHandler( local_xerror_handler );
3980         XSelectInput( m_pDisplay, aWindow, PropertyChangeMask );
3981         if( ! bWasError )
3982         {
3983             // set XdndAware
3984             XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&nXdndProtocolRevision, 1 );
3985             if( ! bWasError )
3986             {
3987                 // get root window of window (in 99.999% of all cases this will be
3988                 // DefaultRootWindow( m_pDisplay )
3989                 int x, y;
3990                 unsigned int w, h, bw, d;
3991                 XGetGeometry( m_pDisplay, aWindow, &aEntry.m_aRootWindow,
3992                               &x, &y, &w, &h, &bw, &d );
3993             }
3994         }
3995         XSetErrorHandler( pOldHandler );
3996         if(bWasError)
3997             return;
3998         m_aDropTargets[ aWindow ] = aEntry;
3999     }
4000     else
4001         OSL_ASSERT( "attempt to register None as drop target" );
4002 }
4003 
4004 // ------------------------------------------------------------------------
4005 
deregisterDropTarget(XLIB_Window aWindow)4006 void SelectionManager::deregisterDropTarget( XLIB_Window aWindow )
4007 {
4008     ClearableMutexGuard aGuard(m_aMutex);
4009 
4010     m_aDropTargets.erase( aWindow );
4011     if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() )
4012     {
4013         // abort drag
4014         std::hash_map< XLIB_Window, DropTargetEntry >::const_iterator it =
4015             m_aDropTargets.find( m_aDropWindow );
4016         if( it != m_aDropTargets.end() )
4017         {
4018             DropTargetEvent dte;
4019             dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
4020             aGuard.clear();
4021             it->second.m_pTarget->dragExit( dte );
4022         }
4023         else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
4024         {
4025             // send XdndLeave
4026             XEvent aEvent;
4027             aEvent.type = ClientMessage;
4028             aEvent.xclient.display      = m_pDisplay;
4029             aEvent.xclient.format       = 32;
4030             aEvent.xclient.message_type = m_nXdndLeave;
4031             aEvent.xclient.window       = m_aDropWindow;
4032             aEvent.xclient.data.l[0]    = m_aWindow;
4033             memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
4034             m_aDropWindow = m_aDropProxy = None;
4035             XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
4036         }
4037         // notify the listener
4038         DragSourceDropEvent dsde;
4039         dsde.Source             = static_cast< OWeakObject* >(this);
4040         dsde.DragSourceContext  = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
4041         dsde.DragSource         = static_cast< XDragSource* >(this);
4042         dsde.DropAction         = DNDConstants::ACTION_NONE;
4043         dsde.DropSuccess        = sal_False;
4044         css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
4045         m_xDragSourceListener.clear();
4046         aGuard.clear();
4047         xListener->dragDropEnd( dsde );
4048     }
4049 }
4050 
4051 /*
4052  *  SelectionAdaptor
4053  */
4054 
getTransferable()4055 css::uno::Reference< XTransferable > SelectionManager::getTransferable() throw()
4056 {
4057     return m_xDragSourceTransferable;
4058 }
4059 
4060 // ------------------------------------------------------------------------
4061 
clearTransferable()4062 void SelectionManager::clearTransferable() throw()
4063 {
4064     m_xDragSourceTransferable.clear();
4065 }
4066 
4067 // ------------------------------------------------------------------------
4068 
fireContentsChanged()4069 void SelectionManager::fireContentsChanged() throw()
4070 {
4071 }
4072 
4073 // ------------------------------------------------------------------------
4074 
getReference()4075 css::uno::Reference< XInterface > SelectionManager::getReference() throw()
4076 {
4077     return css::uno::Reference< XInterface >( static_cast<OWeakObject*>(this) );
4078 }
4079 
4080 // ------------------------------------------------------------------------
4081 
4082 /*
4083  *  SelectionManagerHolder
4084  */
4085 
SelectionManagerHolder()4086 SelectionManagerHolder::SelectionManagerHolder() :
4087         ::cppu::WeakComponentImplHelper3<
4088     XDragSource,
4089     XInitialization,
4090     XServiceInfo > (m_aMutex)
4091 {
4092 }
4093 
4094 // ------------------------------------------------------------------------
4095 
~SelectionManagerHolder()4096 SelectionManagerHolder::~SelectionManagerHolder()
4097 {
4098 }
4099 
4100 // ------------------------------------------------------------------------
4101 
initialize(const Sequence<Any> & arguments)4102 void SelectionManagerHolder::initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception )
4103 {
4104     OUString aDisplayName;
4105 
4106     if( arguments.getLength() > 0 )
4107     {
4108         css::uno::Reference< XDisplayConnection > xConn;
4109         arguments.getConstArray()[0] >>= xConn;
4110         if( xConn.is() )
4111         {
4112             Any aIdentifier;
4113             aIdentifier >>= aDisplayName;
4114         }
4115     }
4116 
4117     SelectionManager& rManager = SelectionManager::get( aDisplayName );
4118     rManager.initialize( arguments );
4119     m_xRealDragSource = static_cast< XDragSource* >(&rManager);
4120 }
4121 
4122 /*
4123  *  XDragSource
4124  */
4125 
isDragImageSupported()4126 sal_Bool SelectionManagerHolder::isDragImageSupported() throw()
4127 {
4128     return m_xRealDragSource.is() ? m_xRealDragSource->isDragImageSupported() : sal_False;
4129 }
4130 
4131 // ------------------------------------------------------------------------
4132 
getDefaultCursor(sal_Int8 dragAction)4133 sal_Int32 SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction ) throw()
4134 {
4135     return m_xRealDragSource.is() ? m_xRealDragSource->getDefaultCursor( dragAction ) : 0;
4136 }
4137 
4138 // ------------------------------------------------------------------------
4139 
startDrag(const::com::sun::star::datatransfer::dnd::DragGestureEvent & trigger,sal_Int8 sourceActions,sal_Int32 cursor,sal_Int32 image,const css::uno::Reference<::com::sun::star::datatransfer::XTransferable> & transferable,const css::uno::Reference<::com::sun::star::datatransfer::dnd::XDragSourceListener> & listener)4140 void SelectionManagerHolder::startDrag(
4141                                        const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger,
4142                                        sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image,
4143                                        const css::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& transferable,
4144                                        const css::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener
4145                                        ) throw()
4146 {
4147     if( m_xRealDragSource.is() )
4148         m_xRealDragSource->startDrag( trigger, sourceActions, cursor, image, transferable, listener );
4149 }
4150 
4151 // ------------------------------------------------------------------------
4152 
4153 /*
4154  *  XServiceInfo
4155  */
4156 
4157 // ------------------------------------------------------------------------
4158 
getImplementationName()4159 OUString SelectionManagerHolder::getImplementationName() throw()
4160 {
4161     return OUString::createFromAscii(XDND_IMPLEMENTATION_NAME);
4162 }
4163 
4164 // ------------------------------------------------------------------------
4165 
supportsService(const OUString & ServiceName)4166 sal_Bool SelectionManagerHolder::supportsService( const OUString& ServiceName ) throw()
4167 {
4168     Sequence < OUString > SupportedServicesNames = Xdnd_getSupportedServiceNames();
4169 
4170     for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; )
4171         if (SupportedServicesNames[n].compareTo(ServiceName) == 0)
4172             return sal_True;
4173 
4174     return sal_False;
4175 }
4176 
4177 // ------------------------------------------------------------------------
4178 
getSupportedServiceNames()4179 Sequence< OUString > SelectionManagerHolder::getSupportedServiceNames() throw()
4180 {
4181     return Xdnd_getSupportedServiceNames();
4182 }
4183 
4184 /* vim: set noet sw=4 ts=4: */
4185