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