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