/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: X11_selection.cxx,v $ * $Revision: 1.85 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_dtrans.hxx" #include #include #include #include #include #include #include #include #include #if defined(LINUX) || defined(NETBSD) || defined (FREEBSD) #include #else #include #endif #include #include #include #include #include #include // pointer bitmaps #include #include #include #include #include #include #include #include #include #include #include #include #ifndef OSL_PROCESS_H #include #endif #define DRAG_EVENT_MASK ButtonPressMask |\ ButtonReleaseMask |\ PointerMotionMask |\ EnterWindowMask |\ LeaveWindowMask using namespace com::sun::star::datatransfer; using namespace com::sun::star::datatransfer::dnd; using namespace com::sun::star::lang; using namespace com::sun::star::awt; using namespace com::sun::star::uno; using namespace cppu; using namespace osl; using namespace rtl; using namespace x11; // stubs to satisfy solaris compiler's rather rigid linking warning extern "C" { static void call_SelectionManager_run( void * pMgr ) { SelectionManager::run( pMgr ); } static void call_SelectionManager_runDragExecute( void * pMgr ) { SelectionManager::runDragExecute( pMgr ); } } static const long nXdndProtocolRevision = 5; // mapping between mime types (or what the office thinks of mime types) // and X convention types struct NativeTypeEntry { Atom nAtom; const char* pType; // Mime encoding on our side const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized int nFormat; // the corresponding format }; // the convention for Xdnd is mime types as specified by the corresponding // RFC's with the addition that text/plain without charset tag contains iso8859-1 // sadly some applications (e.g. gtk) do not honor the mimetype only rule, // so for compatibility add UTF8_STRING static NativeTypeEntry aXdndConversionTab[] = { { 0, "text/plain;charset=iso8859-1", "text/plain", 8 }, { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 } }; // for clipboard and primary selections there is only a convention for text // that the encoding name of the text is taken as type in all capitalized letters static NativeTypeEntry aNativeConversionTab[] = { { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 }, { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }, { 0, "text/plain;charset=utf-8", "UTF-8", 8 }, { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 }, // ISO encodings { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 }, { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 }, { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 }, { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 }, { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 }, { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 }, { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 }, { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 }, { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 }, { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 }, { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 }, { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 }, // asian encodings { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 }, { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 }, { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 }, { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 }, { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 }, { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 }, // eastern european encodings { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 }, { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 }, // String (== iso8859-1) { XA_STRING, "text/plain;charset=iso8859-1", "STRING", 8 }, // special for compound text { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 }, // PIXMAP { XA_PIXMAP, "image/bmp", "PIXMAP", 32 } }; rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType ) { rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW; OUString aMimeType( rMimeType.toAsciiLowerCase() ); sal_Int32 nIndex = 0; if( aMimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain" , 10 ) ) { if( aMimeType.getLength() == 10 ) // only "text/plain" aEncoding = RTL_TEXTENCODING_ISO_8859_1; else { while( nIndex != -1 ) { OUString aToken = aMimeType.getToken( 0, ';', nIndex ); sal_Int32 nPos = 0; if( aToken.getToken( 0, '=', nPos ).equalsAsciiL( "charset", 7 ) ) { OString aEncToken = OUStringToOString( aToken.getToken( 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 ); aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() ); if( aEncoding == RTL_TEXTENCODING_DONTKNOW ) { if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) ) aEncoding = RTL_TEXTENCODING_UTF8; } if( aEncoding != RTL_TEXTENCODING_DONTKNOW ) break; } } } } #if OSL_DEBUG_LEVEL > 1 if( aEncoding == RTL_TEXTENCODING_DONTKNOW ) fprintf( stderr, "getTextPlainEncoding( %s ) failed\n", OUStringToOString( rMimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif return aEncoding; } // ------------------------------------------------------------------------ ::std::hash_map< OUString, SelectionManager*, OUStringHash >& SelectionManager::getInstances() { static ::std::hash_map< OUString, SelectionManager*, OUStringHash > aInstances; return aInstances; } // ------------------------------------------------------------------------ SelectionManager::SelectionManager() : m_nIncrementalThreshold( 15*1024 ), m_pDisplay( NULL ), m_aThread( NULL ), m_aDragExecuteThread( NULL ), m_aWindow( None ), m_nSelectionTimeout( 0 ), m_nSelectionTimestamp( CurrentTime ), m_aCurrentDropWindow( None ), m_bDropWaitingForCompletion( false ), m_aDropWindow( None ), m_aDropProxy( None ), m_aDragSourceWindow( None ), m_nNoPosX( 0 ), m_nNoPosY( 0 ), m_nNoPosWidth( 0 ), m_nNoPosHeight( 0 ), m_bLastDropAccepted( false ), m_bDropSuccess( false ), m_bDropSent( false ), m_bWaitingForPrimaryConversion( false ), m_aMoveCursor( None ), m_aCopyCursor( None ), m_aLinkCursor( None ), m_aNoneCursor( None ), m_aCurrentCursor( None ), m_nCurrentProtocolVersion( nXdndProtocolRevision ) { m_aDropEnterEvent.data.l[0] = None; m_bDropEnterSent = true; m_aDragRunning.reset(); } Cursor SelectionManager::createCursor( const char* pPointerData, const char* pMaskData, int width, int height, int hotX, int hotY ) { Pixmap aPointer; Pixmap aMask; XColor aBlack, aWhite; aBlack.pixel = BlackPixel( m_pDisplay, 0 ); aBlack.red = aBlack.green = aBlack.blue = 0; aBlack.flags = DoRed | DoGreen | DoBlue; aWhite.pixel = WhitePixel( m_pDisplay, 0 ); aWhite.red = aWhite.green = aWhite.blue = 0xffff; aWhite.flags = DoRed | DoGreen | DoBlue; aPointer = XCreateBitmapFromData( m_pDisplay, m_aWindow, pPointerData, width, height ); aMask = XCreateBitmapFromData( m_pDisplay, m_aWindow, pMaskData, width, height ); Cursor aCursor = XCreatePixmapCursor( m_pDisplay, aPointer, aMask, &aBlack, &aWhite, hotX, hotY ); XFreePixmap( m_pDisplay, aPointer ); XFreePixmap( m_pDisplay, aMask ); return aCursor; } void SelectionManager::initialize( const Sequence< Any >& arguments ) throw (::com::sun::star::uno::Exception) { MutexGuard aGuard(m_aMutex); if( ! m_xDisplayConnection.is() ) { /* * first argument must be a ::com::sun::star::awt::XDisplayConnection * from this we will get the XEvents of the vcl event loop by * registering us as XEventHandler on it. * * implementor's note: * * Because XDND implementations send their ClientMessage events with * XSendEvent and event mask 0 (which is perfectly sensible) * these events get only sent to the connection that created * the window in question: the vcl display. * * So this whole service should be implemented in vcl where * it belongs. But as usual the dogmatics win and the dogma says: * "vcl bad, service good". So instead of making vcl a service * providing library we make a new one that is "independent" of vcl, * because it is not linked against it and dispatch every event of * the vcl event loop to here via uno mechanisms (that is copying it, * locking and unlocking mutexes). Well, the user has fast computers * these days, so why do we need performance where more memory and * a faster CPU will do the job ? * And that is why this runs under the title: * How NOT to implement a service * or the victory of dogma over common sense. * */ if( arguments.getLength() > 0 ) arguments.getConstArray()[0] >>= m_xDisplayConnection; if( ! m_xDisplayConnection.is() ) { #if 0 // for the time being try to live without XDisplayConnection // for the sake of clipboard service // clipboard service should be initialized with a XDisplayConnection // in the future Exception aExc; aExc.Message = OUString::createFromAscii( "initialize me with a valid XDisplayConnection" ); aExc.Context = static_cast< OWeakObject* >(this); throw aExc; #endif } else m_xDisplayConnection->addEventHandler( Any(), this, ~0 ); } if( !m_xBitmapConverter.is() ) { if( arguments.getLength() > 2 ) arguments.getConstArray()[2] >>= m_xBitmapConverter; } int nParams = osl_getCommandArgCount(); OUString aParam; bool bIsHeadless = false; for( int i = 0; i < nParams; i++ ) { osl_getCommandArg( i, &aParam.pData ); if( aParam.equalsAscii( "-headless" ) ) { bIsHeadless = true; break; } } if( ! m_pDisplay && ! bIsHeadless ) { OUString aUDisplay; if( m_xDisplayConnection.is() ) { Any aIdentifier; aIdentifier = m_xDisplayConnection->getIdentifier(); aIdentifier >>= aUDisplay; } OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) ); m_pDisplay = XOpenDisplay( aDisplayName.getLength() ? aDisplayName.getStr() : NULL ); if( m_pDisplay ) { #ifdef SYNCHRONIZE XSynchronize( m_pDisplay, True ); #endif // clipboard selection m_nCLIPBOARDAtom = getAtom( OUString::createFromAscii( "CLIPBOARD" ) ); // special targets m_nTARGETSAtom = getAtom( OUString::createFromAscii( "TARGETS" ) ); m_nTIMESTAMPAtom = getAtom( OUString::createFromAscii( "TIMESTAMP" ) ); m_nTEXTAtom = getAtom( OUString::createFromAscii( "TEXT" ) ); m_nINCRAtom = getAtom( OUString::createFromAscii( "INCR" ) ); m_nCOMPOUNDAtom = getAtom( OUString::createFromAscii( "COMPOUND_TEXT" ) ); m_nMULTIPLEAtom = getAtom( OUString::createFromAscii( "MULTIPLE" ) ); m_nUTF16Atom = getAtom( OUString::createFromAscii( "ISO10646-1" ) ); // m_nUTF16Atom = getAtom( OUString::createFromAscii( "text/plain;charset=ISO-10646-UCS-2" ) ); m_nImageBmpAtom = getAtom( OUString::createFromAscii( "image/bmp" ) ); // Atoms for Xdnd protocol m_nXdndAware = getAtom( OUString::createFromAscii( "XdndAware" ) ); m_nXdndEnter = getAtom( OUString::createFromAscii( "XdndEnter" ) ); m_nXdndLeave = getAtom( OUString::createFromAscii( "XdndLeave" ) ); m_nXdndPosition = getAtom( OUString::createFromAscii( "XdndPosition" ) ); m_nXdndStatus = getAtom( OUString::createFromAscii( "XdndStatus" ) ); m_nXdndDrop = getAtom( OUString::createFromAscii( "XdndDrop" ) ); m_nXdndFinished = getAtom( OUString::createFromAscii( "XdndFinished" ) ); m_nXdndSelection = getAtom( OUString::createFromAscii( "XdndSelection" ) ); m_nXdndTypeList = getAtom( OUString::createFromAscii( "XdndTypeList" ) ); m_nXdndProxy = getAtom( OUString::createFromAscii( "XdndProxy" ) ); m_nXdndActionCopy = getAtom( OUString::createFromAscii( "XdndActionCopy" ) ); m_nXdndActionMove = getAtom( OUString::createFromAscii( "XdndActionMove" ) ); m_nXdndActionLink = getAtom( OUString::createFromAscii( "XdndActionLink" ) ); m_nXdndActionAsk = getAtom( OUString::createFromAscii( "XdndActionAsk" ) ); m_nXdndActionPrivate= getAtom( OUString::createFromAscii( "XdndActionPrivate" ) ); // initialize map with member none m_aAtomToString[ 0 ]= OUString::createFromAscii( "None" ); m_aAtomToString[ XA_PRIMARY ] = OUString::createFromAscii( "PRIMARY" ); // create a (invisible) message window m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ), 10, 10, 10, 10, 0, 0, 1 ); // initialize threshold for incremetal transfers // ICCCM says it should be smaller that the max request size // which in turn is guaranteed to be at least 16k bytes m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024; if( m_aWindow ) { // initialize default cursors m_aMoveCursor = createCursor( movedata_curs_bits, movedata_mask_bits, movedata_curs_width, movedata_curs_height, movedata_curs_x_hot, movedata_curs_y_hot ); m_aCopyCursor = createCursor( copydata_curs_bits, copydata_mask_bits, copydata_curs_width, copydata_curs_height, copydata_curs_x_hot, copydata_curs_y_hot ); m_aLinkCursor = createCursor( linkdata_curs_bits, linkdata_mask_bits, linkdata_curs_width, linkdata_curs_height, linkdata_curs_x_hot, linkdata_curs_y_hot ); m_aNoneCursor = createCursor( nodrop_curs_bits, nodrop_mask_bits, nodrop_curs_width, nodrop_curs_height, nodrop_curs_x_hot, nodrop_curs_y_hot ); // just interested in SelectionClear/Notify/Request and PropertyChange XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask ); // create the transferable for Drag operations m_xDropTransferable = new X11Transferable( *this, static_cast< OWeakObject* >(this), m_nXdndSelection ); registerHandler( m_nXdndSelection, *this ); m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this ); if( m_aThread ) osl_resumeThread( m_aThread ); #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "SelectionManager::initialize: creation of dispatch thread failed !\n" ); #endif } } } } // ------------------------------------------------------------------------ SelectionManager::~SelectionManager() { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "SelectionManager::~SelectionManager (%s)\n", m_pDisplay ? DisplayString(m_pDisplay) : "no display" ); #endif { MutexGuard aGuard( *Mutex::getGlobalMutex() ); ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it; for( it = getInstances().begin(); it != getInstances().end(); ++it ) if( it->second == this ) { getInstances().erase( it ); break; } } if( m_aThread ) { osl_terminateThread( m_aThread ); osl_joinWithThread( m_aThread ); osl_destroyThread( m_aThread ); } if( m_aDragExecuteThread ) { osl_terminateThread( m_aDragExecuteThread ); osl_joinWithThread( m_aDragExecuteThread ); m_aDragExecuteThread = NULL; // thread handle is freed in dragDoDispatch() } MutexGuard aGuard(m_aMutex); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "shutting down SelectionManager\n" ); #endif if( m_xDisplayConnection.is() ) { m_xDisplayConnection->removeEventHandler( Any(), this ); m_xDisplayConnection.clear(); } if( m_pDisplay ) { deregisterHandler( m_nXdndSelection ); // destroy message window if( m_aWindow ) XDestroyWindow( m_pDisplay, m_aWindow ); // release cursors if (m_aMoveCursor != None) XFreeCursor(m_pDisplay, m_aMoveCursor); if (m_aCopyCursor != None) XFreeCursor(m_pDisplay, m_aCopyCursor); if (m_aLinkCursor != None) XFreeCursor(m_pDisplay, m_aLinkCursor); if (m_aNoneCursor != None) XFreeCursor(m_pDisplay, m_aNoneCursor); // paranoia setting, the drag thread should have // done that already XUngrabPointer( m_pDisplay, CurrentTime ); XUngrabKeyboard( m_pDisplay, CurrentTime ); XCloseDisplay( m_pDisplay ); } } // ------------------------------------------------------------------------ SelectionAdaptor* SelectionManager::getAdaptor( Atom selection ) { ::std::hash_map< Atom, Selection* >::iterator it = m_aSelections.find( selection ); return it != m_aSelections.end() ? it->second->m_pAdaptor : NULL; } // ------------------------------------------------------------------------ OUString SelectionManager::convertFromCompound( const char* pText, int nLen ) { MutexGuard aGuard( m_aMutex ); OUString aRet; if( nLen < 0 ) nLen = strlen( pText ); char** pTextList = NULL; int nTexts = 0; XTextProperty aProp; aProp.value = (unsigned char*)pText; aProp.encoding = m_nCOMPOUNDAtom; aProp.format = 8; aProp.nitems = nLen; XmbTextPropertyToTextList( m_pDisplay, &aProp, &pTextList, &nTexts ); rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); for( int i = 0; i < nTexts; i++ ) aRet += OStringToOUString( pTextList[i], aEncoding ); if( pTextList ) XFreeStringList( pTextList ); return aRet; } // ------------------------------------------------------------------------ OString SelectionManager::convertToCompound( const OUString& rText ) { MutexGuard aGuard( m_aMutex ); XTextProperty aProp; aProp.value = NULL; aProp.encoding = XA_STRING; aProp.format = 8; aProp.nitems = 0; OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() ); char* pT = const_cast(aRet.getStr()); XmbTextListToTextProperty( m_pDisplay, &pT, 1, XCompoundTextStyle, &aProp ); if( aProp.value ) { aRet = (char*)aProp.value; XFree( aProp.value ); #ifdef SOLARIS /* #97070# * for currently unknown reasons XmbTextListToTextProperty on Solaris returns * no data in ISO8859-n encodings (at least for n = 1, 15) * in these encodings the directly converted text does the * trick, also. */ if( ! aRet.getLength() && rText.getLength() ) aRet = OUStringToOString( rText, osl_getThreadTextEncoding() ); #endif } else aRet = OString(); return aRet; } // ------------------------------------------------------------------------ bool SelectionManager::convertData( const Reference< XTransferable >& xTransferable, Atom nType, Atom nSelection, int& rFormat, Sequence< sal_Int8 >& rData ) { bool bSuccess = false; if( ! xTransferable.is() ) return bSuccess; try { DataFlavor aFlavor; aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat ); sal_Int32 nIndex = 0; if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "text/plain" ) == 0 ) { if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "charset=utf-16" ) == 0 ) aFlavor.DataType = getCppuType( (OUString *) 0 ); else aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); } else aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); if( xTransferable->isDataFlavorSupported( aFlavor ) ) { Any aValue( xTransferable->getTransferData( aFlavor ) ); if( aValue.getValueTypeClass() == TypeClass_STRING ) { OUString aString; aValue >>= aString; rData = Sequence< sal_Int8 >( (sal_Int8*)aString.getStr(), aString.getLength() * sizeof( sal_Unicode ) ); bSuccess = true; } else if( aValue.getValueType() == getCppuType( (Sequence< sal_Int8 >*)0 ) ) { aValue >>= rData; bSuccess = true; } } else if( aFlavor.MimeType.compareToAscii( "text/plain", 10 ) == 0 ) { rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW; bool bCompoundText = false; if( nType == m_nCOMPOUNDAtom ) bCompoundText = true; else aEncoding = getTextPlainEncoding( aFlavor.MimeType ); if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText ) { aFlavor.MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" ); aFlavor.DataType = getCppuType( (OUString *) 0 ); if( xTransferable->isDataFlavorSupported( aFlavor ) ) { Any aValue( xTransferable->getTransferData( aFlavor ) ); OUString aString; aValue >>= aString; OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) ); rData = Sequence< sal_Int8 >( (sal_Int8*)aByteString.getStr(), aByteString.getLength() * sizeof( sal_Char ) ); bSuccess = true; } } } } // various exceptions possible ... which all lead to a failed conversion // so simplify here to a catch all catch(...) { } return bSuccess; } // ------------------------------------------------------------------------ SelectionManager& SelectionManager::get( const OUString& rDisplayName ) { MutexGuard aGuard( *Mutex::getGlobalMutex() ); OUString aDisplayName( rDisplayName ); if( ! aDisplayName.getLength() ) aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 ); SelectionManager* pInstance = NULL; ::std::hash_map< OUString, SelectionManager*, OUStringHash >::iterator it = getInstances().find( aDisplayName ); if( it != getInstances().end() ) pInstance = it->second; else pInstance = getInstances()[ aDisplayName ] = new SelectionManager(); return *pInstance; } // ------------------------------------------------------------------------ const OUString& SelectionManager::getString( Atom aAtom ) { MutexGuard aGuard(m_aMutex); ::std::hash_map< Atom, OUString >::const_iterator it; if( ( it = m_aAtomToString.find( aAtom ) ) == m_aAtomToString.end() ) { static OUString aEmpty; char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : NULL; if( ! pAtom ) return aEmpty; OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) ); XFree( pAtom ); m_aStringToAtom[ aString ] = aAtom; m_aAtomToString[ aAtom ] = aString; } return m_aAtomToString[ aAtom ]; } // ------------------------------------------------------------------------ Atom SelectionManager::getAtom( const OUString& rString ) { MutexGuard aGuard(m_aMutex); ::std::hash_map< OUString, Atom, OUStringHash >::const_iterator it; if( ( it = m_aStringToAtom.find( rString ) ) == m_aStringToAtom.end() ) { static Atom nNoDisplayAtoms = 1; Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ), False ) : nNoDisplayAtoms++; m_aStringToAtom[ rString ] = aAtom; m_aAtomToString[ aAtom ] = rString; } return m_aStringToAtom[ rString ]; } // ------------------------------------------------------------------------ bool SelectionManager::requestOwnership( Atom selection ) { bool bSuccess = false; if( m_pDisplay && m_aWindow ) { MutexGuard aGuard(m_aMutex); SelectionAdaptor* pAdaptor = getAdaptor( selection ); if( pAdaptor ) { XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime ); if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow ) bSuccess = true; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%s ownership for selection %s\n", bSuccess ? "acquired" : "failed to acquire", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif Selection* pSel = m_aSelections[ selection ]; pSel->m_bOwner = bSuccess; delete pSel->m_pPixmap; pSel->m_pPixmap = NULL; pSel->m_nOrigTimestamp = m_nSelectionTimestamp; } #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "no adaptor for selection %s\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); if( pAdaptor->getTransferable().is() ) { Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors(); for( int i = 0; i < aTypes.getLength(); i++ ) { fprintf( stderr, " %s\n", OUStringToOString( aTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); } } #endif } return bSuccess; } // ------------------------------------------------------------------------ void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront ) { NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab; int nTabEntries = selection == m_nXdndSelection ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) : sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]); OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) ); rFormat = 0; for( int i = 0; i < nTabEntries; i++ ) { if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) ) { if( ! pTab[i].nAtom ) pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) ); rFormat = pTab[i].nFormat; if( bPushFront ) rConversions.push_front( pTab[i].nAtom ); else rConversions.push_back( pTab[i].nAtom ); if( pTab[i].nFormat == XA_PIXMAP ) { if( bPushFront ) { rConversions.push_front( XA_VISUALID ); rConversions.push_front( XA_COLORMAP ); } else { rConversions.push_back( XA_VISUALID ); rConversions.push_back( XA_COLORMAP ); } } } } if( ! rFormat ) rFormat = 8; // byte buffer if( bPushFront ) rConversions.push_front( getAtom( rType ) ); else rConversions.push_back( getAtom( rType ) ); }; // ------------------------------------------------------------------------ void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection ) { rOutTypeList.clear(); int nFormat; int nFlavors = rTypes.getLength(); const DataFlavor* pFlavors = rTypes.getConstArray(); bool bHaveText = false; for( int i = 0; i < nFlavors; i++ ) { if( pFlavors[i].MimeType.compareToAscii( "text/plain", 10 ) == 0) bHaveText = true; else convertTypeToNative( pFlavors[i].MimeType, targetselection, nFormat, rOutTypeList ); } if( bHaveText ) { if( targetselection != m_nXdndSelection ) { // only mimetypes should go into Xdnd type list rOutTypeList.push_front( XA_STRING ); rOutTypeList.push_front( m_nCOMPOUNDAtom ); } convertTypeToNative( OUString::createFromAscii( "text/plain;charset=utf-8" ), targetselection, nFormat, rOutTypeList, true ); } if( targetselection != m_nXdndSelection ) rOutTypeList.push_back( m_nMULTIPLEAtom ); } // ------------------------------------------------------------------------ OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, int& rFormat ) { NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab; int nTabEntries = selection == m_nXdndSelection ? sizeof(aXdndConversionTab)/sizeof(aXdndConversionTab[0]) : sizeof(aNativeConversionTab)/sizeof(aNativeConversionTab[0]); for( int i = 0; i < nTabEntries; i++ ) { if( ! pTab[i].nAtom ) pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) ); if( nType == pTab[i].nAtom ) { rFormat = pTab[i].nFormat; return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 ); } } rFormat = 8; return getString( nType ); } // ------------------------------------------------------------------------ bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData ) { ResettableMutexGuard aGuard(m_aMutex); ::std::hash_map< Atom, Selection* >::iterator it; bool bSuccess = false; #if OSL_DEBUG_LEVEL > 1 OUString aSelection( getString( selection ) ); OUString aType( getString( type ) ); fprintf( stderr, "getPasteData( %s, native: %s )\n", OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( aType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif if( ! m_pDisplay ) return false; it = m_aSelections.find( selection ); if( it == m_aSelections.end() ) return false; Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection ); if( aSelectionOwner == None ) return false; if( aSelectionOwner == m_aWindow ) { // probably bad timing led us here #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "Innere Nabelschau\n" ); #endif return false; } // ICCCM recommends to destroy property before convert request unless // parameters are transported; we do only in case of MULTIPLE, // so destroy property unless target is MULTIPLE if( type != m_nMULTIPLEAtom ) XDeleteProperty( m_pDisplay, m_aWindow, selection ); XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime ); it->second->m_eState = Selection::WaitingForResponse; it->second->m_aRequestedType = type; it->second->m_aData = Sequence< sal_Int8 >(); it->second->m_aDataArrived.reset(); // really start the request; if we don't flush the // queue the request won't leave it because there are no more // X calls after this until the data arrived or timeout XFlush( m_pDisplay ); // do a reschedule struct timeval tv_last, tv_current; gettimeofday( &tv_last, NULL ); tv_current = tv_last; XEvent aEvent; do { bool bAdjustTime = false; { bool bHandle = false; if( XCheckTypedEvent( m_pDisplay, PropertyNotify, &aEvent ) ) { bHandle = true; if( aEvent.xproperty.window == m_aWindow && aEvent.xproperty.atom == selection ) bAdjustTime = true; } else if( XCheckTypedEvent( m_pDisplay, SelectionClear, &aEvent ) ) { bHandle = true; } else if( XCheckTypedEvent( m_pDisplay, SelectionRequest, &aEvent ) ) bHandle = true; else if( XCheckTypedEvent( m_pDisplay, SelectionNotify, &aEvent ) ) { bHandle = true; if( aEvent.xselection.selection == selection && ( aEvent.xselection.requestor == m_aWindow || aEvent.xselection.requestor == m_aCurrentDropWindow ) ) bAdjustTime = true; } else { TimeValue aTVal; aTVal.Seconds = 0; aTVal.Nanosec = 100000000; aGuard.clear(); osl_waitThread( &aTVal ); aGuard.reset(); } if( bHandle ) { aGuard.clear(); handleXEvent( aEvent ); aGuard.reset(); } } gettimeofday( &tv_current, NULL ); if( bAdjustTime ) tv_last = tv_current; } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() ); #if OSL_DEBUG_LEVEL > 1 if( (tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout() ) fprintf( stderr, "timed out\n" ); #endif if( it->second->m_aDataArrived.check() && it->second->m_aData.getLength() ) { rData = it->second->m_aData; bSuccess = true; } #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "conversion unsuccessfull\n" ); #endif return bSuccess; } // ------------------------------------------------------------------------ bool SelectionManager::getPasteData( Atom selection, const ::rtl::OUString& rType, Sequence< sal_Int8 >& rData ) { int nFormat; bool bSuccess = false; ::std::hash_map< Atom, Selection* >::iterator it; { MutexGuard aGuard(m_aMutex); it = m_aSelections.find( selection ); if( it == m_aSelections.end() ) return false; } if( it->second->m_aTypes.getLength() == 0 ) { Sequence< DataFlavor > aFlavors; getPasteDataTypes( selection, aFlavors ); if( it->second->m_aTypes.getLength() == 0 ) return false; } const Sequence< DataFlavor >& rTypes( it->second->m_aTypes ); const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "getPasteData( \"%s\", \"%s\" )\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif if( rType.equalsAsciiL( "text/plain;charset=utf-16", 25 ) ) { // lets see if we have UTF16 else try to find something convertible if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 ) { Sequence< sal_Int8 > aData; if( it->second->m_aUTF8Type != None && getPasteData( selection, it->second->m_aUTF8Type, aData ) ) { OUString aRet( (const sal_Char*)aData.getConstArray(), aData.getLength(), RTL_TEXTENCODING_UTF8 ); rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) ); bSuccess = true; } else if( it->second->m_bHaveCompound && getPasteData( selection, m_nCOMPOUNDAtom, aData ) ) { OUString aRet( convertFromCompound( (const char*)aData.getConstArray(), aData.getLength() ) ); rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) ); bSuccess = true; } else { for( int i = 0; i < rTypes.getLength(); i++ ) { rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType ); if( aEncoding != RTL_TEXTENCODING_DONTKNOW && aEncoding != RTL_TEXTENCODING_UNICODE && getPasteData( selection, rNativeTypes[i], aData ) ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "using \"%s\" instead of \"%s\"\n", OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif OString aConvert( (sal_Char*)aData.getConstArray(), aData.getLength() ); OUString aUTF( OStringToOUString( aConvert, aEncoding ) ); rData = Sequence< sal_Int8 >( (sal_Int8*)aUTF.getStr(), (aUTF.getLength()+1)*sizeof( sal_Unicode ) ); bSuccess = true; break; } } } } } else if( rType.equalsAsciiL( "image/bmp", 9 ) ) { // #i83376# try if someone has the data in image/bmp already before // doing the PIXMAP stuff (e.g. the gimp has this) bSuccess = getPasteData( selection, m_nImageBmpAtom, rData ); #if OSL_DEBUG_LEVEL > 1 if( bSuccess ) fprintf( stderr, "got %d bytes of image/bmp\n", (int)rData.getLength() ); #endif if( ! bSuccess ) { Pixmap aPixmap = None; Colormap aColormap = None; // prepare property for MULTIPLE request Sequence< sal_Int8 > aData; Atom pTypes[4] = { XA_PIXMAP, XA_PIXMAP, XA_COLORMAP, XA_COLORMAP }; { MutexGuard aGuard(m_aMutex); XChangeProperty( m_pDisplay, m_aWindow, selection, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, 4 ); } // try MULTIPLE request if( getPasteData( selection, m_nMULTIPLEAtom, aData ) ) { Atom* pReturnedTypes = (Atom*)aData.getArray(); if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP ) { MutexGuard aGuard(m_aMutex); Atom type = None; int format = 0; unsigned long nItems = 0; unsigned long nBytes = 0; unsigned char* pReturn = NULL; XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems, &nBytes, &pReturn ); if( pReturn ) { if( type == XA_PIXMAP ) aPixmap = *(Pixmap*)pReturn; XFree( pReturn ); pReturn = NULL; if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP ) { XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format, &nItems, &nBytes, &pReturn ); if( pReturn ) { if( type == XA_COLORMAP ) aColormap = *(Colormap*)pReturn; XFree( pReturn ); } } } #if OSL_DEBUG_LEVEL > 1 else { 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 ); } #endif } } if( aPixmap == None ) { // perhaps two normal requests will work if( getPasteData( selection, XA_PIXMAP, aData ) ) { aPixmap = *(Pixmap*)aData.getArray(); if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) ) aColormap = *(Colormap*)aData.getArray(); } } // convert data if possible if( aPixmap != None ) { MutexGuard aGuard(m_aMutex); sal_Int32 nOutSize = 0; sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize ); if( pBytes && nOutSize ) { rData = Sequence< sal_Int8 >( nOutSize ); memcpy( rData.getArray(), pBytes, nOutSize ); X11_freeBmp( pBytes ); bSuccess = true; } } } } if( ! bSuccess ) { ::std::list< Atom > aTypes; convertTypeToNative( rType, selection, nFormat, aTypes ); ::std::list< Atom >::const_iterator type_it; Atom nSelectedType = None; for( type_it = aTypes.begin(); type_it != aTypes.end() && nSelectedType == None; ++type_it ) { for( unsigned int i = 0; i < rNativeTypes.size() && nSelectedType == None; i++ ) if( rNativeTypes[i] == *type_it ) nSelectedType = *type_it; } if( nSelectedType != None ) bSuccess = getPasteData( selection, nSelectedType, rData ); } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "getPasteData for selection %s and data type %s returns %s, returned sequence has length %ld\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), bSuccess ? "true" : "false", rData.getLength() ); #endif return bSuccess; } // ------------------------------------------------------------------------ bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes ) { ::std::hash_map< Atom, Selection* >::iterator it; { MutexGuard aGuard(m_aMutex); it = m_aSelections.find( selection ); if( it != m_aSelections.end() && it->second->m_aTypes.getLength() && abs( it->second->m_nLastTimestamp - time( NULL ) ) < 2 ) { rTypes = it->second->m_aTypes; return true; } } bool bSuccess = false; bool bHaveUTF16 = false; Atom aUTF8Type = None; bool bHaveCompound = false; bool bHaveText = false; Sequence< sal_Int8 > aAtoms; if( selection == m_nXdndSelection ) { // xdnd sends first three types with XdndEnter // if more than three types are supported then the XDndTypeList // property on the source window is used if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) { if( m_aDropEnterEvent.data.l[1] & 1 ) { const unsigned int atomcount = 256; // more than three types; look in property MutexGuard aGuard(m_aMutex); Atom nType; int nFormat; unsigned long nItems, nBytes; unsigned char* pBytes = NULL; XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0], m_nXdndTypeList, 0, atomcount, False, XA_ATOM, &nType, &nFormat, &nItems, &nBytes, &pBytes ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "have %ld data types in XdndTypeList\n", nItems ); #endif if( nItems == atomcount && nBytes > 0 ) { // wow ... more than 256 types ! aAtoms.realloc( sizeof( Atom )*atomcount+nBytes ); memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount ); XFree( pBytes ); pBytes = NULL; XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0], m_nXdndTypeList, atomcount, nBytes/sizeof(Atom), False, XA_ATOM, &nType, &nFormat, &nItems, &nBytes, &pBytes ); { memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) ); XFree( pBytes ); } } else { aAtoms.realloc( sizeof(Atom)*nItems ); memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) ); XFree( pBytes ); } } else { // one to three types int n = 0, i; for( i = 0; i < 3; i++ ) if( m_aDropEnterEvent.data.l[2+i] ) n++; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "have %d data types in XdndEnter\n", n ); #endif aAtoms.realloc( sizeof(Atom)*n ); for( i = 0, n = 0; i < 3; i++ ) if( m_aDropEnterEvent.data.l[2+i] ) ((Atom*)aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i]; } } } // get data of type TARGETS else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) ) aAtoms = Sequence< sal_Int8 >(); std::vector< Atom > aNativeTypes; if( aAtoms.getLength() ) { sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom); Atom* pAtoms = (Atom*)aAtoms.getArray(); rTypes.realloc( nAtoms ); aNativeTypes.resize( nAtoms ); DataFlavor* pFlavors = rTypes.getArray(); sal_Int32 nNativeTypesIndex = 0; while( nAtoms-- ) { #if OSL_DEBUG_LEVEL > 1 if( *pAtoms && *pAtoms < 0x01000000 ) fprintf( stderr, "native type: %s\n", OUStringToOString( getString( *pAtoms ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif if( *pAtoms == m_nCOMPOUNDAtom ) bHaveText = bHaveCompound = true; else if( *pAtoms && *pAtoms < 0x01000000 ) { int nFormat; pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat ); pFlavors->DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); sal_Int32 nIndex = 0; if( pFlavors->MimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain", 10 ) ) { OUString aToken(pFlavors->MimeType.getToken( 0, ';', nIndex )); // omit text/plain;charset=unicode since it is not well defined if( aToken.compareToAscii( "charset=unicode" ) == 0 ) { pAtoms++; continue; } bHaveText = true; if( aToken.compareToAscii( "charset=utf-16" ) == 0 ) { bHaveUTF16 = true; pFlavors->DataType = getCppuType( (OUString*)0 ); } else if( aToken.compareToAscii( "charset=utf-8" ) == 0 ) { aUTF8Type = *pAtoms; } } pFlavors++; aNativeTypes[ nNativeTypesIndex ] = *pAtoms; nNativeTypesIndex++; } pAtoms++; } if( (pFlavors - rTypes.getArray()) < rTypes.getLength() ) rTypes.realloc(pFlavors - rTypes.getArray()); bSuccess = rTypes.getLength() ? true : false; if( bHaveText && ! bHaveUTF16 ) { int i = 0; int nNewFlavors = rTypes.getLength()+1; Sequence< DataFlavor > aTemp( nNewFlavors ); for( i = 0; i < nNewFlavors-1; i++ ) aTemp.getArray()[i+1] = rTypes.getConstArray()[i]; aTemp.getArray()[0].MimeType = OUString::createFromAscii( "text/plain;charset=utf-16" ); aTemp.getArray()[0].DataType = getCppuType( (OUString*)0 ); rTypes = aTemp; std::vector< Atom > aNativeTemp( nNewFlavors ); for( i = 0; i < nNewFlavors-1; i++ ) aNativeTemp[ i + 1 ] = aNativeTypes[ i ]; aNativeTemp[0] = None; aNativeTypes = aNativeTemp; } } { MutexGuard aGuard(m_aMutex); it = m_aSelections.find( selection ); if( it != m_aSelections.end() ) { if( bSuccess ) { it->second->m_aTypes = rTypes; it->second->m_aNativeTypes = aNativeTypes; it->second->m_nLastTimestamp = time( NULL ); it->second->m_bHaveUTF16 = bHaveUTF16; it->second->m_aUTF8Type = aUTF8Type; it->second->m_bHaveCompound = bHaveCompound; } else { it->second->m_aTypes = Sequence< DataFlavor >(); it->second->m_aNativeTypes = std::vector< Atom >(); it->second->m_nLastTimestamp = 0; it->second->m_bHaveUTF16 = false; it->second->m_aUTF8Type = None; it->second->m_bHaveCompound = false; } } } #if OSL_DEBUG_LEVEL > 1 // if( selection != m_nCLIPBOARDAtom ) { fprintf( stderr, "SelectionManager::getPasteDataTypes( %s ) = %s\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), bSuccess ? "true" : "false" ); for( int i = 0; i < rTypes.getLength(); i++ ) fprintf( stderr, "type: %s\n", OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); } #endif return bSuccess; } // ------------------------------------------------------------------------ PixmapHolder* SelectionManager::getPixmapHolder( Atom selection ) { std::hash_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection ); if( it == m_aSelections.end() ) return NULL; if( ! it->second->m_pPixmap ) it->second->m_pPixmap = new PixmapHolder( m_pDisplay ); return it->second->m_pPixmap; } static sal_Size GetTrueFormatSize(int nFormat) { // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html return nFormat == 32 ? sizeof(long) : nFormat/8; } bool SelectionManager::sendData( SelectionAdaptor* pAdaptor, Window requestor, Atom target, Atom property, Atom selection ) { ResettableMutexGuard aGuard( m_aMutex ); // handle targets related to image/bmp if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID ) { PixmapHolder* pPixmap = getPixmapHolder( selection ); if( ! pPixmap ) return false; XID nValue = None; // handle colormap request if( target == XA_COLORMAP ) nValue = (XID)pPixmap->getColormap(); else if( target == XA_VISUALID ) nValue = (XID)pPixmap->getVisualID(); else if( target == XA_PIXMAP || target == XA_BITMAP ) { nValue = (XID)pPixmap->getPixmap(); if( nValue == None ) { // first conversion Sequence< sal_Int8 > aData; int nFormat; aGuard.clear(); bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData ); aGuard.reset(); if( bConverted ) { // get pixmap again since clearing the guard could have invalidated // the pixmap in another thread pPixmap = getPixmapHolder( selection ); // conversion succeeded, so aData contains image/bmp now if( pPixmap->needsConversion( (const sal_uInt8*)aData.getConstArray() ) && m_xBitmapConverter.is() ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "trying bitmap conversion\n" ); #endif Reference xBM( new BmpTransporter( aData ) ); Sequence aArgs(2), aOutArgs; Sequence aOutIndex; aArgs.getArray()[0] = makeAny( xBM ); aArgs.getArray()[1] = makeAny( (sal_uInt16)pPixmap->getDepth() ); aGuard.clear(); try { Any aResult = m_xBitmapConverter->invoke( OUString::createFromAscii( "convert-bitmap-depth" ), aArgs, aOutIndex, aOutArgs ); if( aResult >>= xBM ) aData = xBM->getDIB(); } catch(...) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "exception in bitmap converter\n" ); #endif } aGuard.reset(); } // get pixmap again since clearing the guard could have invalidated // the pixmap in another thread pPixmap = getPixmapHolder( selection ); nValue = (XID)pPixmap->setBitmapData( (const sal_uInt8*)aData.getConstArray() ); } if( nValue == None ) return false; } if( target == XA_BITMAP ) nValue = (XID)pPixmap->getBitmap(); } XChangeProperty( m_pDisplay, requestor, property, target, 32, PropModeReplace, (const unsigned char*)&nValue, 1); return true; } /* * special target TEXT allows us to transfer * the data in an encoding of our choice * COMPOUND_TEXT will work with most applications */ if( target == m_nTEXTAtom ) target = m_nCOMPOUNDAtom; Sequence< sal_Int8 > aData; int nFormat; aGuard.clear(); bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData ); aGuard.reset(); if( bConverted ) { // conversion succeeded if( aData.getLength() > m_nIncrementalThreshold ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "using INCR protocol\n" ); std::hash_map< Window, std::hash_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor ); if( win_it != m_aIncrementals.end() ) { std::hash_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property ); if( inc_it != win_it->second.end() ) { const IncrementalTransfer& rInc = inc_it->second; fprintf( stderr, "premature end and new start for INCR transfer for window 0x%lx, property %s, type %s\n", rInc.m_aRequestor, OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); } } #endif // insert IncrementalTransfer IncrementalTransfer& rInc = m_aIncrementals[ requestor ][ property ]; rInc.m_aData = aData; rInc.m_nBufferPos = 0; rInc.m_aRequestor = requestor; rInc.m_aProperty = property; rInc.m_aTarget = target; rInc.m_nFormat = nFormat; rInc.m_nTransferStartTime = time( NULL ); // use incr protocol, signal start to requestor long nMinSize = m_nIncrementalThreshold; XSelectInput( m_pDisplay, requestor, PropertyChangeMask ); XChangeProperty( m_pDisplay, requestor, property, m_nINCRAtom, 32, PropModeReplace, (unsigned char*)&nMinSize, 1 ); XFlush( m_pDisplay ); } else { sal_Size nUnitSize = GetTrueFormatSize(nFormat); XChangeProperty( m_pDisplay, requestor, property, target, nFormat, PropModeReplace, (const unsigned char*)aData.getConstArray(), aData.getLength()/nUnitSize ); } } #if OSL_DEBUG_LEVEL > 1 else fprintf( stderr, "convertData failed for type: %s \n", OUStringToOString( convertTypeFromNative( target, selection, nFormat ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif return bConverted; } // ------------------------------------------------------------------------ bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest ) { ResettableMutexGuard aGuard( m_aMutex ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "handleSelectionRequest for selection %s and target %s\n", OUStringToOString( getString( rRequest.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( rRequest.target ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif XEvent aNotify; aNotify.type = SelectionNotify; aNotify.xselection.display = rRequest.display; aNotify.xselection.send_event = True; aNotify.xselection.requestor = rRequest.requestor; aNotify.xselection.selection = rRequest.selection; aNotify.xselection.time = rRequest.time; aNotify.xselection.target = rRequest.target; aNotify.xselection.property = None; SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection ); // ensure that we still own that selection if( pAdaptor && XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow ) { Reference< XTransferable > xTrans( pAdaptor->getTransferable() ); if( rRequest.target == m_nTARGETSAtom ) { // someone requests our types if( xTrans.is() ) { aGuard.clear(); Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors(); aGuard.reset(); ::std::list< Atom > aConversions; getNativeTypeList( aFlavors, aConversions, rRequest.selection ); int i, nTypes = aConversions.size(); Atom* pTypes = (Atom*)alloca( nTypes * sizeof( Atom ) ); std::list< Atom >::const_iterator it; for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it ) pTypes[i] = *it; XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property, XA_ATOM, 32, PropModeReplace, (const unsigned char*)pTypes, nTypes ); aNotify.xselection.property = rRequest.property; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "sending type list:\n" ); for( int k = 0; k < nTypes; k++ ) fprintf( stderr, " %s\n", pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) : "" ); #endif } } else if( rRequest.target == m_nTIMESTAMPAtom ) { long nTimeStamp = (long)m_aSelections[rRequest.selection]->m_nOrigTimestamp; XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property, XA_INTEGER, 32, PropModeReplace, (const unsigned char*)&nTimeStamp, 1 ); aNotify.xselection.property = rRequest.property; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "sending timestamp: %d\n", (int)nTimeStamp ); #endif } else { bool bEventSuccess = false; if( rRequest.target == m_nMULTIPLEAtom ) { // get all targets Atom nType = None; int nFormat = 0; unsigned long nItems = 0, nBytes = 0; unsigned char* pData = NULL; // get number of atoms XGetWindowProperty( m_pDisplay, rRequest.requestor, rRequest.property, 0, 0, False, AnyPropertyType, &nType, &nFormat, &nItems, &nBytes, &pData ); if( nFormat == 32 && nBytes/4 ) { if( pData ) // ?? should not happen { XFree( pData ); pData = NULL; } XGetWindowProperty( m_pDisplay, rRequest.requestor, rRequest.property, 0, nBytes/4, False, nType, &nType, &nFormat, &nItems, &nBytes, &pData ); if( pData && nItems ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found %ld atoms in MULTIPLE request\n", nItems ); #endif bEventSuccess = true; bool bResetAtoms = false; Atom* pAtoms = (Atom*)pData; aGuard.clear(); for( unsigned int i = 0; i < nItems; i += 2 ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, " %s => %s: ", OUStringToOString( getString( pAtoms[i] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( pAtoms[i+1] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%s\n", bSuccess ? "succeeded" : "failed" ); #endif if( ! bSuccess ) { pAtoms[i] = None; bResetAtoms = true; } } aGuard.reset(); if( bResetAtoms ) XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property, XA_ATOM, 32, PropModeReplace, pData, nBytes/4 ); } if( pData ) XFree( pData ); } #if OSL_DEBUG_LEVEL > 1 else { fprintf( stderr, "could not get type list from \"%s\" of type \"%s\" on requestor 0x%lx, requestor has properties:", OUStringToOString( getString( rRequest.property ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), rRequest.requestor ); int nProps = 0; Atom* pProps = XListProperties( m_pDisplay, rRequest.requestor, &nProps ); if( pProps ) { for( int i = 0; i < nProps; i++ ) fprintf( stderr, " \"%s\"", OUStringToOString( getString( pProps[i]), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); XFree( pProps ); } } #endif } else { aGuard.clear(); bEventSuccess = sendData( pAdaptor, rRequest.requestor, rRequest.target, rRequest.property, rRequest.selection ); aGuard.reset(); } if( bEventSuccess ) { aNotify.xselection.target = rRequest.target; aNotify.xselection.property = rRequest.property; } } aGuard.clear(); xTrans.clear(); aGuard.reset(); } XSendEvent( m_pDisplay, rRequest.requestor, False, 0, &aNotify ); if( rRequest.selection == XA_PRIMARY && m_bWaitingForPrimaryConversion && m_xDragSourceListener.is() ) { DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, rRequest.time, *this ); dsde.DragSource = static_cast< XDragSource* >(this); if( aNotify.xselection.property != None ) { dsde.DropAction = DNDConstants::ACTION_COPY; dsde.DropSuccess = sal_True; } else { dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; } Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_xDragSourceListener.clear(); aGuard.clear(); if( xListener.is() ) xListener->dragDropEnd( dsde ); } // we handled the event in any case by answering return true; } // ------------------------------------------------------------------------ bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent& rNotify ) { MutexGuard aGuard( m_aMutex ); // data we requested arrived #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "handleReceivePropertyNotify for property %s\n", OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif bool bHandled = false; ::std::hash_map< Atom, Selection* >::iterator it = m_aSelections.find( rNotify.atom ); if( it != m_aSelections.end() && rNotify.state == PropertyNewValue && ( it->second->m_eState == Selection::WaitingForResponse || it->second->m_eState == Selection::WaitingForData || it->second->m_eState == Selection::IncrementalTransfer ) ) { // MULTIPLE requests are only complete after selection notify if( it->second->m_aRequestedType == m_nMULTIPLEAtom && ( it->second->m_eState == Selection::WaitingForResponse || it->second->m_eState == Selection::WaitingForData ) ) return false; bHandled = true; Atom nType = None; int nFormat = 0; unsigned long nItems = 0, nBytes = 0; unsigned char* pData = NULL; // get type and length XGetWindowProperty( m_pDisplay, rNotify.window, rNotify.atom, 0, 0, False, AnyPropertyType, &nType, &nFormat, &nItems, &nBytes, &pData ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found %ld bytes data of type %s and format %d, items = %ld\n", nBytes, OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), nFormat, nItems ); #endif if( pData ) { XFree( pData ); pData = NULL; } if( nType == m_nINCRAtom ) { // start data transfer XDeleteProperty( m_pDisplay, rNotify.window, rNotify.atom ); it->second->m_eState = Selection::IncrementalTransfer; } else if( nType != None ) { XGetWindowProperty( m_pDisplay, rNotify.window, rNotify.atom, 0, nBytes/4 +1, True, nType, &nType, &nFormat, &nItems, &nBytes, &pData ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "read %ld items data of type %s and format %d, %ld bytes left in property\n", nItems, OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), nFormat, nBytes ); #endif sal_Size nUnitSize = GetTrueFormatSize(nFormat); if( it->second->m_eState == Selection::WaitingForData || it->second->m_eState == Selection::WaitingForResponse ) { // copy data it->second->m_aData = Sequence< sal_Int8 >( (sal_Int8*)pData, nItems*nUnitSize ); it->second->m_eState = Selection::Inactive; it->second->m_aDataArrived.set(); } else if( it->second->m_eState == Selection::IncrementalTransfer ) { if( nItems ) { // append data Sequence< sal_Int8 > aData( it->second->m_aData.getLength() + nItems*nUnitSize ); memcpy( aData.getArray(), it->second->m_aData.getArray(), it->second->m_aData.getLength() ); memcpy( aData.getArray() + it->second->m_aData.getLength(), pData, nItems*nUnitSize ); it->second->m_aData = aData; } else { it->second->m_eState = Selection::Inactive; it->second->m_aDataArrived.set(); } } if( pData ) XFree( pData ); } else if( it->second->m_eState == Selection::IncrementalTransfer ) { it->second->m_eState = Selection::Inactive; it->second->m_aDataArrived.set(); } } return bHandled; } // ------------------------------------------------------------------------ bool SelectionManager::handleSendPropertyNotify( XPropertyEvent& rNotify ) { MutexGuard aGuard( m_aMutex ); // ready for next part of a IncrementalTransfer #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "handleSendPropertyNotify for property %s (%s)\n", OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), rNotify.state == PropertyNewValue ? "new value" : ( rNotify.state == PropertyDelete ? "deleted" : "unknown") ); #endif bool bHandled = false; // feed incrementals if( rNotify.state == PropertyDelete ) { std::hash_map< Window, std::hash_map< Atom, IncrementalTransfer > >::iterator it; it = m_aIncrementals.find( rNotify.window ); if( it != m_aIncrementals.end() ) { bHandled = true; int nCurrentTime = time( NULL ); std::hash_map< Atom, IncrementalTransfer >::iterator inc_it; // throw out aborted transfers std::list< Atom > aTimeouts; for( inc_it = it->second.begin(); inc_it != it->second.end(); ++inc_it ) { if( (nCurrentTime - inc_it->second.m_nTransferStartTime) > (getSelectionTimeout()+2) ) { aTimeouts.push_back( inc_it->first ); #if OSL_DEBUG_LEVEL > 1 const IncrementalTransfer& rInc = inc_it->second; fprintf( stderr, "timeout on INCR transfer for window 0x%lx, property %s, type %s\n", rInc.m_aRequestor, OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif } } while( aTimeouts.begin() != aTimeouts.end() ) { // transfer broken, might even be a new client with the // same window id it->second.erase( aTimeouts.front() ); aTimeouts.pop_front(); } inc_it = it->second.find( rNotify.atom ); if( inc_it != it->second.end() ) { IncrementalTransfer& rInc = inc_it->second; int nBytes = rInc.m_aData.getLength() - rInc.m_nBufferPos; nBytes = (nBytes > m_nIncrementalThreshold) ? m_nIncrementalThreshold : nBytes; if( nBytes < 0 ) // sanity check nBytes = 0; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "pushing %d bytes: \"%.*s\"...\n", nBytes, nBytes > 32 ? 32 : nBytes, (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos ); #endif sal_Size nUnitSize = GetTrueFormatSize(rInc.m_nFormat); XChangeProperty( m_pDisplay, rInc.m_aRequestor, rInc.m_aProperty, rInc.m_aTarget, rInc.m_nFormat, PropModeReplace, (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos, nBytes/nUnitSize ); rInc.m_nBufferPos += nBytes; rInc.m_nTransferStartTime = nCurrentTime; if( nBytes == 0 ) // transfer finished { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "finished INCR transfer for window 0x%lx, property %s, type %s\n", rInc.m_aRequestor, OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif it->second.erase( inc_it ); } } // eventually clean up the hash map if( it->second.begin() == it->second.end() ) m_aIncrementals.erase( it ); } } return bHandled; } // ------------------------------------------------------------------------ bool SelectionManager::handleSelectionNotify( XSelectionEvent& rNotify ) { MutexGuard aGuard( m_aMutex ); bool bHandled = false; // notification about success/failure of one of our conversion requests #if OSL_DEBUG_LEVEL > 1 OUString aSelection( getString( rNotify.selection ) ); OUString aProperty( OUString::createFromAscii( "None" ) ); if( rNotify.property ) aProperty = getString( rNotify.property ); fprintf( stderr, "handleSelectionNotify for selection %s and property %s (0x%lx)\n", OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), OUStringToOString( aProperty, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), rNotify.property ); if( rNotify.requestor != m_aWindow && rNotify.requestor != m_aCurrentDropWindow ) fprintf( stderr, "Warning: selection notify for unknown window 0x%lx\n", rNotify.requestor ); #endif ::std::hash_map< Atom, Selection* >::iterator it = m_aSelections.find( rNotify.selection ); if ( (rNotify.requestor == m_aWindow || rNotify.requestor == m_aCurrentDropWindow) && it != m_aSelections.end() && ( (it->second->m_eState == Selection::WaitingForResponse) || (it->second->m_eState == Selection::WaitingForData) ) ) { bHandled = true; if( it->second->m_aRequestedType == m_nMULTIPLEAtom ) { Atom nType = None; int nFormat = 0; unsigned long nItems = 0, nBytes = 0; unsigned char* pData = NULL; // get type and length XGetWindowProperty( m_pDisplay, rNotify.requestor, rNotify.property, 0, 256, False, AnyPropertyType, &nType, &nFormat, &nItems, &nBytes, &pData ); if( nBytes ) // HUGE request !!! { if( pData ) XFree( pData ); XGetWindowProperty( m_pDisplay, rNotify.requestor, rNotify.property, 0, 256+(nBytes+3)/4, False, AnyPropertyType, &nType, &nFormat, &nItems, &nBytes, &pData ); } it->second->m_eState = Selection::Inactive; sal_Size nUnitSize = GetTrueFormatSize(nFormat); it->second->m_aData = Sequence< sal_Int8 >((sal_Int8*)pData, nItems * nUnitSize); it->second->m_aDataArrived.set(); if( pData ) XFree( pData ); } // WaitingForData can actually happen; some // applications (e.g. cmdtool on Solaris) first send // a success and then cancel it. Weird ! else if( rNotify.property == None ) { // conversion failed, stop transfer it->second->m_eState = Selection::Inactive; it->second->m_aData = Sequence< sal_Int8 >(); it->second->m_aDataArrived.set(); } // get the bytes, by INCR if necessary else it->second->m_eState = Selection::WaitingForData; } #if OSL_DEBUG_LEVEL > 1 else if( it != m_aSelections.end() ) fprintf( stderr, "Warning: selection in state %d\n", it->second->m_eState ); #endif return bHandled; } // ------------------------------------------------------------------------ bool SelectionManager::handleDropEvent( XClientMessageEvent& rMessage ) { ResettableMutexGuard aGuard(m_aMutex); // handle drop related events Window aSource = rMessage.data.l[0]; Window aTarget = rMessage.window; bool bHandled = false; ::std::hash_map< Window, DropTargetEntry >::iterator it = m_aDropTargets.find( aTarget ); #if OSL_DEBUG_LEVEL > 1 if( rMessage.message_type == m_nXdndEnter || rMessage.message_type == m_nXdndLeave || rMessage.message_type == m_nXdndDrop || rMessage.message_type == m_nXdndPosition ) { fprintf( stderr, "got drop event %s, ", OUStringToOString( getString( rMessage.message_type ), RTL_TEXTENCODING_ASCII_US).getStr() ); if( it == m_aDropTargets.end() ) fprintf( stderr, "but no target found\n" ); else if( ! it->second.m_pTarget->m_bActive ) fprintf( stderr, "but target is inactive\n" ); else if( m_aDropEnterEvent.data.l[0] != None && (Window)m_aDropEnterEvent.data.l[0] != aSource ) fprintf( stderr, "but source 0x%lx is unknown (expected 0x%lx or 0)\n", aSource, m_aDropEnterEvent.data.l[0] ); else fprintf( stderr, "processing.\n" ); } #endif if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive && m_bDropWaitingForCompletion && m_aDropEnterEvent.data.l[0] ) { bHandled = true; OSL_ENSURE( 0, "someone forgot to call dropComplete ?" ); // some listener forgot to call dropComplete in the last operation // let us end it now and accept the new enter event aGuard.clear(); dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime ); aGuard.reset(); } if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive && ( m_aDropEnterEvent.data.l[0] == None || Window(m_aDropEnterEvent.data.l[0]) == aSource ) ) { if( rMessage.message_type == m_nXdndEnter ) { bHandled = true; m_aDropEnterEvent = rMessage; m_bDropEnterSent = false; m_aCurrentDropWindow = aTarget; m_nCurrentProtocolVersion = m_aDropEnterEvent.data.l[1] >> 24; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "received XdndEnter on 0x%lx\n", aTarget ); #endif } else if( rMessage.message_type == m_nXdndPosition && aSource == Window(m_aDropEnterEvent.data.l[0]) ) { bHandled = true; m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[3] : CurrentTime; if( ! m_bDropEnterSent ) m_nDropTimestamp = m_nDropTime; Window aChild; XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, it->first, rMessage.data.l[2] >> 16, rMessage.data.l[2] & 0xffff, &m_nLastX, &m_nLastY, &aChild ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "received XdndPosition on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY ); #endif DropTargetDragEnterEvent aEvent; aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); aEvent.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); aEvent.LocationX = m_nLastX; aEvent.LocationY = m_nLastY; aEvent.SourceActions = m_nSourceActions; if( m_nCurrentProtocolVersion < 2 ) aEvent.DropAction = DNDConstants::ACTION_COPY; else if( Atom(rMessage.data.l[4]) == m_nXdndActionCopy ) aEvent.DropAction = DNDConstants::ACTION_COPY; else if( Atom(rMessage.data.l[4]) == m_nXdndActionMove ) aEvent.DropAction = DNDConstants::ACTION_MOVE; else if( Atom(rMessage.data.l[4]) == m_nXdndActionLink ) aEvent.DropAction = DNDConstants::ACTION_LINK; else if( Atom(rMessage.data.l[4]) == m_nXdndActionAsk ) // currently no interface to implement ask aEvent.DropAction = ~0; else aEvent.DropAction = DNDConstants::ACTION_NONE; m_nLastDropAction = aEvent.DropAction; if( ! m_bDropEnterSent ) { m_bDropEnterSent = true; aEvent.SupportedDataFlavors = m_xDropTransferable->getTransferDataFlavors(); aGuard.clear(); it->second->dragEnter( aEvent ); } else { aGuard.clear(); it->second->dragOver( aEvent ); } } else if( rMessage.message_type == m_nXdndLeave && aSource == Window(m_aDropEnterEvent.data.l[0]) ) { bHandled = true; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "received XdndLeave on 0x%lx\n", aTarget ); #endif DropTargetEvent aEvent; aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); m_aDropEnterEvent.data.l[0] = None; if( m_aCurrentDropWindow == aTarget ) m_aCurrentDropWindow = None; m_nCurrentProtocolVersion = nXdndProtocolRevision; aGuard.clear(); it->second->dragExit( aEvent ); } else if( rMessage.message_type == m_nXdndDrop && aSource == Window(m_aDropEnterEvent.data.l[0]) ) { bHandled = true; m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[2] : CurrentTime; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "received XdndDrop on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY ); #endif if( m_bLastDropAccepted ) { DropTargetDropEvent aEvent; aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); aEvent.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); aEvent.LocationX = m_nLastX; aEvent.LocationY = m_nLastY; aEvent.DropAction = m_nLastDropAction; // there is nothing corresponding to source supported actions // every source can do link, copy and move aEvent.SourceActions= m_nLastDropAction; aEvent.Transferable = m_xDropTransferable; m_bDropWaitingForCompletion = true; aGuard.clear(); it->second->drop( aEvent ); } else { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "XdndDrop canceled due to m_bLastDropAccepted = fale\n" ); #endif DropTargetEvent aEvent; aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); aGuard.clear(); it->second->dragExit( aEvent ); // reset the drop status, notify source dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime ); } } } return bHandled; } /* * methods for XDropTargetDropContext */ void SelectionManager::dropComplete( sal_Bool bSuccess, Window aDropWindow, Time ) { ClearableMutexGuard aGuard(m_aMutex); if( aDropWindow == m_aCurrentDropWindow ) { if( m_xDragSourceListener.is() ) { DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = getUserDragAction(); dsde.DropSuccess = bSuccess; Reference< XDragSourceListener > xListener = m_xDragSourceListener; m_xDragSourceListener.clear(); aGuard.clear(); xListener->dragDropEnd( dsde ); } else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) { XEvent aEvent; aEvent.xclient.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.window = m_aDropEnterEvent.data.l[0]; aEvent.xclient.message_type = m_nXdndFinished; aEvent.xclient.format = 32; aEvent.xclient.data.l[0] = m_aCurrentDropWindow; aEvent.xclient.data.l[1] = bSuccess ? 1 : 0; aEvent.xclient.data.l[2] = 0; aEvent.xclient.data.l[3] = 0; aEvent.xclient.data.l[4] = 0; if( bSuccess ) { if( m_nLastDropAction & DNDConstants::ACTION_MOVE ) aEvent.xclient.data.l[2] = m_nXdndActionMove; else if( m_nLastDropAction & DNDConstants::ACTION_COPY ) aEvent.xclient.data.l[2] = m_nXdndActionCopy; else if( m_nLastDropAction & DNDConstants::ACTION_LINK ) aEvent.xclient.data.l[2] = m_nXdndActionLink; } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "Sending XdndFinished to 0x%lx\n", m_aDropEnterEvent.data.l[0] ); #endif XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0], False, NoEventMask, & aEvent ); m_aDropEnterEvent.data.l[0] = None; m_aCurrentDropWindow = None; m_nCurrentProtocolVersion = nXdndProtocolRevision; } m_bDropWaitingForCompletion = false; } else OSL_ASSERT( "dropComplete from invalid DropTargetDropContext" ); } /* * methods for XDropTargetDragContext */ // ------------------------------------------------------------------------ void SelectionManager::sendDragStatus( Atom nDropAction ) { ClearableMutexGuard aGuard(m_aMutex); if( m_xDragSourceListener.is() ) { sal_Int8 nNewDragAction; if( nDropAction == m_nXdndActionMove ) nNewDragAction = DNDConstants::ACTION_MOVE; else if( nDropAction == m_nXdndActionCopy ) nNewDragAction = DNDConstants::ACTION_COPY; else if( nDropAction == m_nXdndActionLink ) nNewDragAction = DNDConstants::ACTION_LINK; else nNewDragAction = DNDConstants::ACTION_NONE; nNewDragAction &= m_nSourceActions; if( nNewDragAction != m_nTargetAcceptAction ) { setCursor( getDefaultCursor( nNewDragAction ), m_aDropWindow, m_nDragTimestamp ); m_nTargetAcceptAction = nNewDragAction; } DragSourceDragEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = m_nSourceActions; dsde.UserAction = getUserDragAction(); Reference< XDragSourceListener > xListener( m_xDragSourceListener ); // caution: do not change anything after this aGuard.clear(); if( xListener.is() ) xListener->dragOver( dsde ); } else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) { XEvent aEvent; aEvent.xclient.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.window = m_aDropEnterEvent.data.l[0]; aEvent.xclient.message_type = m_nXdndStatus; aEvent.xclient.format = 32; aEvent.xclient.data.l[0] = m_aCurrentDropWindow; aEvent.xclient.data.l[1] = 2; if( nDropAction == m_nXdndActionMove || nDropAction == m_nXdndActionLink || nDropAction == m_nXdndActionCopy ) aEvent.xclient.data.l[1] |= 1; aEvent.xclient.data.l[2] = 0; aEvent.xclient.data.l[3] = 0; aEvent.xclient.data.l[4] = m_nCurrentProtocolVersion > 1 ? nDropAction : 0; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "Sending XdndStatus to 0x%lx with action %s\n", m_aDropEnterEvent.data.l[0], OUStringToOString( getString( nDropAction ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0], False, NoEventMask, & aEvent ); XFlush( m_pDisplay ); } } // ------------------------------------------------------------------------ sal_Int8 SelectionManager::getUserDragAction() const { return (m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT) ? m_nTargetAcceptAction : m_nUserDragAction; } // ------------------------------------------------------------------------ bool SelectionManager::updateDragAction( int modifierState ) { bool bRet = false; sal_Int8 nNewDropAction = DNDConstants::ACTION_MOVE; if( ( modifierState & ShiftMask ) && ! ( modifierState & ControlMask ) ) nNewDropAction = DNDConstants::ACTION_MOVE; else if( ( modifierState & ControlMask ) && ! ( modifierState & ShiftMask ) ) nNewDropAction = DNDConstants::ACTION_COPY; else if( ( modifierState & ShiftMask ) && ( modifierState & ControlMask ) ) nNewDropAction = DNDConstants::ACTION_LINK; if( m_nCurrentProtocolVersion < 0 && m_aDropWindow != None ) nNewDropAction = DNDConstants::ACTION_COPY; nNewDropAction &= m_nSourceActions; if( ! ( modifierState & ( ControlMask | ShiftMask ) ) ) { if( ! nNewDropAction ) { // default to an action so the user does not have to press // keys explicitly if( m_nSourceActions & DNDConstants::ACTION_MOVE ) nNewDropAction = DNDConstants::ACTION_MOVE; else if( m_nSourceActions & DNDConstants::ACTION_COPY ) nNewDropAction = DNDConstants::ACTION_COPY; else if( m_nSourceActions & DNDConstants::ACTION_LINK ) nNewDropAction = DNDConstants::ACTION_LINK; } nNewDropAction |= DNDConstants::ACTION_DEFAULT; } if( nNewDropAction != m_nUserDragAction || m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "updateDragAction: %x -> %x\n", (int)m_nUserDragAction, (int)nNewDropAction ); #endif bRet = true; m_nUserDragAction = nNewDropAction; DragSourceDragEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = m_nUserDragAction; dsde.UserAction = m_nUserDragAction; m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; // invalidate last accept m_xDragSourceListener->dropActionChanged( dsde ); } return bRet; } // ------------------------------------------------------------------------ void SelectionManager::sendDropPosition( bool bForce, Time eventTime ) { ClearableMutexGuard aGuard(m_aMutex); if( m_bDropSent ) return; ::std::hash_map< Window, DropTargetEntry >::const_iterator it = m_aDropTargets.find( m_aDropWindow ); if( it != m_aDropTargets.end() ) { if( it->second.m_pTarget->m_bActive ) { int x, y; Window aChild; XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, m_aDropWindow, m_nLastDragX, m_nLastDragY, &x, &y, &aChild ); DropTargetDragEvent dtde; dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget ); dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); dtde.LocationX = x; dtde.LocationY = y; dtde.DropAction = getUserDragAction(); dtde.SourceActions = m_nSourceActions; aGuard.clear(); it->second->dragOver( dtde ); } } else if( bForce || m_nLastDragX < m_nNoPosX || m_nLastDragX >= m_nNoPosX+m_nNoPosWidth || m_nLastDragY < m_nNoPosY || m_nLastDragY >= m_nNoPosY+m_nNoPosHeight ) { // send XdndPosition XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndPosition; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; aEvent.xclient.data.l[1] = 0; aEvent.xclient.data.l[2] = m_nLastDragX << 16 | (m_nLastDragY&0xffff); aEvent.xclient.data.l[3] = eventTime; if( m_nUserDragAction & DNDConstants::ACTION_COPY ) aEvent.xclient.data.l[4]=m_nXdndActionCopy; else if( m_nUserDragAction & DNDConstants::ACTION_MOVE ) aEvent.xclient.data.l[4]=m_nXdndActionMove; else if( m_nUserDragAction & DNDConstants::ACTION_LINK ) aEvent.xclient.data.l[4]=m_nXdndActionLink; else aEvent.xclient.data.l[4]=m_nXdndActionCopy; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; } } // ------------------------------------------------------------------------ bool SelectionManager::handleDragEvent( XEvent& rMessage ) { if( ! m_xDragSourceListener.is() ) return false; ResettableMutexGuard aGuard(m_aMutex); bool bHandled = false; // for shortcut ::std::hash_map< Window, DropTargetEntry >::const_iterator it = m_aDropTargets.find( m_aDropWindow ); #if OSL_DEBUG_LEVEL > 1 switch( rMessage.type ) { case ClientMessage: fprintf( stderr, "handleDragEvent: %s\n", OUStringToOString( getString( rMessage.xclient.message_type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); break; case MotionNotify: // fprintf( stderr, "handleDragEvent: MotionNotify\n" ); break; case EnterNotify: fprintf( stderr, "handleDragEvent: EnterNotify\n" ); break; case LeaveNotify: fprintf( stderr, "handleDragEvent: LeaveNotify\n" ); break; case ButtonPress: fprintf( stderr, "handleDragEvent: ButtonPress %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton ); break; case ButtonRelease: fprintf( stderr, "handleDragEvent: ButtonRelease %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton ); break; case KeyPress: fprintf( stderr, "handleDragEvent: KeyPress\n" ); break; case KeyRelease: fprintf( stderr, "handleDragEvent: KeyRelease\n" ); break; default: fprintf( stderr, "handleDragEvent: \n", rMessage.type ); break; } #endif // handle drag related events if( rMessage.type == ClientMessage ) { if( Atom(rMessage.xclient.message_type) == m_nXdndStatus && Atom(rMessage.xclient.data.l[0]) == m_aDropWindow ) { bHandled = true; DragSourceDragEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >( this ); dsde.UserAction = getUserDragAction(); dsde.DropAction = DNDConstants::ACTION_NONE; m_bDropSuccess = rMessage.xclient.data.l[1] & 1 ? true : false; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "status drop action: accept = %s, %s\n", m_bDropSuccess ? "true" : "false", OUStringToOString( getString( rMessage.xclient.data.l[4] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif if( rMessage.xclient.data.l[1] & 1 ) { if( m_nCurrentProtocolVersion > 1 ) { if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionCopy ) dsde.DropAction = DNDConstants::ACTION_COPY; else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionMove ) dsde.DropAction = DNDConstants::ACTION_MOVE; else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionLink ) dsde.DropAction = DNDConstants::ACTION_LINK; } else dsde.DropAction = DNDConstants::ACTION_COPY; } m_nTargetAcceptAction = dsde.DropAction; if( ! ( rMessage.xclient.data.l[1] & 2 ) ) { m_nNoPosX = rMessage.xclient.data.l[2] >> 16; m_nNoPosY = rMessage.xclient.data.l[2] & 0xffff; m_nNoPosWidth = rMessage.xclient.data.l[3] >> 16; m_nNoPosHeight = rMessage.xclient.data.l[3] & 0xffff; } else m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; setCursor( getDefaultCursor( dsde.DropAction ), m_aDropWindow, m_nDragTimestamp ); aGuard.clear(); m_xDragSourceListener->dragOver( dsde ); } else if( Atom(rMessage.xclient.message_type) == m_nXdndFinished && m_aDropWindow == Atom(rMessage.xclient.data.l[0]) ) { bHandled = true; // notify the listener DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = m_nTargetAcceptAction; dsde.DropSuccess = m_bDropSuccess; Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_xDragSourceListener.clear(); aGuard.clear(); xListener->dragDropEnd( dsde ); } } else if( rMessage.type == MotionNotify || rMessage.type == EnterNotify || rMessage.type == LeaveNotify ) { bHandled = true; bool bForce = false; int root_x = rMessage.type == MotionNotify ? rMessage.xmotion.x_root : rMessage.xcrossing.x_root; int root_y = rMessage.type == MotionNotify ? rMessage.xmotion.y_root : rMessage.xcrossing.y_root; Window root = rMessage.type == MotionNotify ? rMessage.xmotion.root : rMessage.xcrossing.root; m_nDragTimestamp = rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time; aGuard.clear(); if( rMessage.type == MotionNotify ) { bForce = updateDragAction( rMessage.xmotion.state ); } updateDragWindow( root_x, root_y, root ); aGuard.reset(); if( m_nCurrentProtocolVersion >= 0 && m_aDropProxy != None ) { aGuard.clear(); sendDropPosition( bForce, rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time ); } } else if( rMessage.type == KeyPress || rMessage.type == KeyRelease ) { bHandled = true; KeySym aKey = XKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0 ); if( aKey == XK_Escape ) { // abort drag if( it != m_aDropTargets.end() ) { DropTargetEvent dte; dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); aGuard.clear(); it->second.m_pTarget->dragExit( dte ); } else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) { // send XdndLeave XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndLeave; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4); m_aDropWindow = m_aDropProxy = None; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); } // notify the listener DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_xDragSourceListener.clear(); aGuard.clear(); xListener->dragDropEnd( dsde ); } else { /* * man page says: state is state immediate PRIOR to the * event. It would seem that this is a somewhat arguable * design decision. */ int nState = rMessage.xkey.state; int nNewState = 0; switch( aKey ) { case XK_Shift_R: case XK_Shift_L: nNewState = ShiftMask;break; case XK_Control_R: case XK_Control_L: nNewState = ControlMask;break; // just interested in shift and ctrl for dnd } if( rMessage.type == KeyPress ) nState |= nNewState; else nState &= ~nNewState; aGuard.clear(); if( updateDragAction( nState ) ) sendDropPosition( true, rMessage.xkey.time ); } } else if( ( rMessage.type == ButtonPress || rMessage.type == ButtonRelease ) && rMessage.xbutton.button == m_nDragButton ) { bool bCancel = true; if( m_aDropWindow != None ) { if( it != m_aDropTargets.end() ) { if( it->second.m_pTarget->m_bActive && m_nUserDragAction != DNDConstants::ACTION_NONE && m_bLastDropAccepted ) { bHandled = true; int x, y; Window aChild; XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &x, &y, &aChild ); DropTargetDropEvent dtde; dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget ); dtde.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); dtde.LocationX = x; dtde.LocationY = y; dtde.DropAction = m_nUserDragAction; dtde.SourceActions = m_nSourceActions; dtde.Transferable = m_xDragSourceTransferable; m_bDropSent = true; m_nDropTimeout = time( NULL ); m_bDropWaitingForCompletion = true; aGuard.clear(); it->second->drop( dtde ); bCancel = false; } else bCancel = true; } else if( m_nCurrentProtocolVersion >= 0 ) { bHandled = true; XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndDrop; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; aEvent.xclient.data.l[1] = 0; aEvent.xclient.data.l[2] = rMessage.xbutton.time; aEvent.xclient.data.l[3] = 0; aEvent.xclient.data.l[4] = 0; m_bDropSent = true; m_nDropTimeout = time( NULL ); XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); bCancel = false; } else { // dropping on non XdndWindows: acquire ownership of // PRIMARY and send a middle mouse button click down/up to // target window SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY ); if( pAdaptor ) { bHandled = true; Window aDummy; XEvent aEvent; aEvent.type = ButtonPress; aEvent.xbutton.display = m_pDisplay; aEvent.xbutton.window = m_aDropWindow; aEvent.xbutton.root = rMessage.xbutton.root; aEvent.xbutton.subwindow = m_aDropWindow; aEvent.xbutton.time = rMessage.xbutton.time+1; aEvent.xbutton.x_root = rMessage.xbutton.x_root; aEvent.xbutton.y_root = rMessage.xbutton.y_root; aEvent.xbutton.state = rMessage.xbutton.state; aEvent.xbutton.button = Button2; aEvent.xbutton.same_screen = True; XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &aEvent.xbutton.x, &aEvent.xbutton.y, &aDummy ); XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonPressMask, &aEvent ); aEvent.xbutton.type = ButtonRelease; aEvent.xbutton.time++; aEvent.xbutton.state |= Button2Mask; XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonReleaseMask, &aEvent ); m_bDropSent = true; m_nDropTimeout = time( NULL ); XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); m_bWaitingForPrimaryConversion = true; m_bDropSent = true; m_nDropTimeout = time( NULL ); // HACK :-) aGuard.clear(); static_cast< X11Clipboard* >( pAdaptor )->setContents( m_xDragSourceTransferable, Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >() ); aGuard.reset(); bCancel = false; } } } if( bCancel ) { // cancel drag DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_xDragSourceListener.clear(); aGuard.clear(); xListener->dragDropEnd( dsde ); bHandled = true; } } return bHandled; } // ------------------------------------------------------------------------ void SelectionManager::accept( sal_Int8 dragOperation, Window aDropWindow, Time ) { if( aDropWindow == m_aCurrentDropWindow ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "accept: %x\n", dragOperation ); #endif Atom nAction = None; dragOperation &= (DNDConstants::ACTION_MOVE | DNDConstants::ACTION_COPY | DNDConstants::ACTION_LINK); if( dragOperation & DNDConstants::ACTION_MOVE ) nAction = m_nXdndActionMove; else if( dragOperation & DNDConstants::ACTION_COPY ) nAction = m_nXdndActionCopy; else if( dragOperation & DNDConstants::ACTION_LINK ) nAction = m_nXdndActionLink; m_bLastDropAccepted = true; sendDragStatus( nAction ); } } // ------------------------------------------------------------------------ void SelectionManager::reject( Window aDropWindow, Time ) { if( aDropWindow == m_aCurrentDropWindow ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "reject\n" ); #endif m_bLastDropAccepted = false; sendDragStatus( None ); if( m_bDropSent && m_xDragSourceListener.is() ) { DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; m_xDragSourceListener->dragDropEnd( dsde ); m_xDragSourceListener.clear(); } } } /* * XDragSource */ sal_Bool SelectionManager::isDragImageSupported() throw() { return sal_False; } // ------------------------------------------------------------------------ sal_Int32 SelectionManager::getDefaultCursor( sal_Int8 dragAction ) throw() { Cursor aCursor = m_aNoneCursor; if( dragAction & DNDConstants::ACTION_MOVE ) aCursor = m_aMoveCursor; else if( dragAction & DNDConstants::ACTION_COPY ) aCursor = m_aCopyCursor; else if( dragAction & DNDConstants::ACTION_LINK ) aCursor = m_aLinkCursor; return aCursor; } // ------------------------------------------------------------------------ int SelectionManager::getXdndVersion( Window aWindow, Window& rProxy ) { Atom* pProperties = NULL; int nProperties = 0; Atom nType; int nFormat; unsigned long nItems, nBytes; unsigned char* pBytes = NULL; int nVersion = -1; rProxy = None; /* * XListProperties is used here to avoid unnecessary XGetWindowProperty calls * and therefore reducing latency penalty */ pProperties = XListProperties( m_pDisplay, aWindow, &nProperties ); // first look for proxy int i; for( i = 0; i < nProperties; i++ ) { if( pProperties[i] == m_nXdndProxy ) { XGetWindowProperty( m_pDisplay, aWindow, m_nXdndProxy, 0, 1, False, XA_WINDOW, &nType, &nFormat, &nItems, &nBytes, &pBytes ); if( pBytes ) { if( nType == XA_WINDOW ) rProxy = *(Window*)pBytes; XFree( pBytes ); pBytes = NULL; if( rProxy != None ) { // now check proxy wether it points to itself XGetWindowProperty( m_pDisplay, rProxy, m_nXdndProxy, 0, 1, False, XA_WINDOW, &nType, &nFormat, &nItems, &nBytes, &pBytes ); if( pBytes ) { if( nType == XA_WINDOW && *(Window*)pBytes != rProxy ) rProxy = None; XFree( pBytes ); pBytes = NULL; } else rProxy = None; } } break; } } Window aAwareWindow = rProxy != None ? rProxy : aWindow; XGetWindowProperty( m_pDisplay, aAwareWindow, m_nXdndAware, 0, 1, False, XA_ATOM, &nType, &nFormat, &nItems, &nBytes, &pBytes ); if( pBytes ) { if( nType == XA_ATOM ) nVersion = *(Atom*)pBytes; XFree( pBytes ); } nVersion = nVersion > nXdndProtocolRevision ? nXdndProtocolRevision : nVersion; return nVersion; } // ------------------------------------------------------------------------ void SelectionManager::updateDragWindow( int nX, int nY, Window aRoot ) { ResettableMutexGuard aGuard( m_aMutex ); Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_nLastDragX = nX; m_nLastDragY = nY; Window aParent = aRoot; Window aChild; Window aNewProxy = None, aNewCurrentWindow = None; int nNewProtocolVersion = -1; int nWinX, nWinY; // find the first XdndAware window or check if root window is // XdndAware or has XdndProxy do { XTranslateCoordinates( m_pDisplay, aRoot, aParent, nX, nY, &nWinX, &nWinY, &aChild ); if( aChild != None ) { if( aChild == m_aCurrentDropWindow && aChild != aRoot && m_nCurrentProtocolVersion >= 0 ) { aParent = aChild; break; } nNewProtocolVersion = getXdndVersion( aChild, aNewProxy ); aParent = aChild; } } while( aChild != None && nNewProtocolVersion < 0 ); aNewCurrentWindow = aParent; if( aNewCurrentWindow == aRoot ) { // no children, try root drop nNewProtocolVersion = getXdndVersion( aNewCurrentWindow, aNewProxy ); if( nNewProtocolVersion < 3 ) { aNewCurrentWindow = aNewProxy = None; nNewProtocolVersion = nXdndProtocolRevision; } } DragSourceDragEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY; dsde.UserAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY; ::std::hash_map< Window, DropTargetEntry >::const_iterator it; if( aNewCurrentWindow != m_aDropWindow ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "drag left window 0x%lx (rev. %d), entered window 0x%lx (rev %d)\n", m_aDropWindow, m_nCurrentProtocolVersion, aNewCurrentWindow, nNewProtocolVersion ); #endif if( m_aDropWindow != None ) { it = m_aDropTargets.find( m_aDropWindow ); if( it != m_aDropTargets.end() ) // shortcut for own drop targets { DropTargetEvent dte; dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); aGuard.clear(); it->second.m_pTarget->dragExit( dte ); aGuard.reset(); } else { // send old drop target a XdndLeave XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndLeave; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; aEvent.xclient.data.l[1] = 0; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); } if( xListener.is() ) { aGuard.clear(); xListener->dragExit( dsde ); aGuard.reset(); } } m_nCurrentProtocolVersion = nNewProtocolVersion; m_aDropWindow = aNewCurrentWindow; m_aDropProxy = aNewProxy != None ? aNewProxy : m_aDropWindow; it = m_aDropTargets.find( m_aDropWindow ); if( it != m_aDropTargets.end() && ! it->second.m_pTarget->m_bActive ) m_aDropProxy = None; if( m_aDropProxy != None && xListener.is() ) { aGuard.clear(); xListener->dragEnter( dsde ); aGuard.reset(); } // send XdndEnter if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) { it = m_aDropTargets.find( m_aDropWindow ); if( it != m_aDropTargets.end() ) { XTranslateCoordinates( m_pDisplay, aRoot, m_aDropWindow, nX, nY, &nWinX, &nWinY, &aChild ); DropTargetDragEnterEvent dtde; dtde.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); dtde.LocationX = nWinX; dtde.LocationY = nWinY; dtde.DropAction = m_nUserDragAction; dtde.SourceActions = m_nSourceActions; dtde.SupportedDataFlavors = m_xDragSourceTransferable->getTransferDataFlavors(); aGuard.clear(); it->second.m_pTarget->dragEnter( dtde ); aGuard.reset(); } else { XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndEnter; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24; memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 ); // fill in data types ::std::list< Atom > aConversions; getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); if( aConversions.size() > 3 ) aEvent.xclient.data.l[1] |= 1; ::std::list< Atom >::const_iterator type_it = aConversions.begin(); for( int i = 0; type_it != aConversions.end() && i < 3; i++, ++type_it ) aEvent.xclient.data.l[i+2] = *type_it; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); } } m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; } else if( m_aDropProxy != None && xListener.is() ) { aGuard.clear(); // drag over for XdndAware windows comes when receiving XdndStatus xListener->dragOver( dsde ); } } // ------------------------------------------------------------------------ void SelectionManager::startDrag( const DragGestureEvent& trigger, sal_Int8 sourceActions, sal_Int32, sal_Int32, const Reference< XTransferable >& transferable, const Reference< XDragSourceListener >& listener ) throw() { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "startDrag( sourceActions = %x )\n", (int)sourceActions ); #endif DragSourceDropEvent aDragFailedEvent; aDragFailedEvent.Source = static_cast< OWeakObject* >(this); aDragFailedEvent.DragSource = static_cast< XDragSource* >(this); aDragFailedEvent.DragSourceContext = new DragSourceContext( None, CurrentTime, *this ); aDragFailedEvent.DropAction = DNDConstants::ACTION_NONE; aDragFailedEvent.DropSuccess = sal_False; if( m_aDragRunning.check() ) { if( listener.is() ) listener->dragDropEnd( aDragFailedEvent ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "*** ERROR *** second drag and drop started.\n" ); if( m_xDragSourceListener.is() ) fprintf( stderr, "*** ERROR *** drag source listener already set.\n" ); else fprintf( stderr, "*** ERROR *** drag thread already running.\n" ); #endif return; } { ClearableMutexGuard aGuard(m_aMutex); // first get the current pointer position and the window that // the pointer is located in. since said window should be one // of our DropTargets at the time of executeDrag we can use // them for a start Window aRoot, aParent, aChild; int root_x, root_y, win_x, win_y; unsigned int mask; ::std::hash_map< Window, DropTargetEntry >::const_iterator it; it = m_aDropTargets.begin(); while( it != m_aDropTargets.end() ) { if( XQueryPointer( m_pDisplay, it->second.m_aRootWindow, &aRoot, &aParent, &root_x, &root_y, &win_x, &win_y, &mask ) ) { aParent = it->second.m_aRootWindow; break; } ++it; } // don't start DnD if there is none of our windows on the same screen as // the pointer or if no mouse button is pressed if( it == m_aDropTargets.end() || (mask & (Button1Mask|Button2Mask|Button3Mask)) == 0 ) { aGuard.clear(); if( listener.is() ) listener->dragDropEnd( aDragFailedEvent ); return; } // try to find which of our drop targets is the drag source // if that drop target is deregistered we should stop executing // the drag (actually this is a poor substitute for an "endDrag" // method ). m_aDragSourceWindow = None; aParent = aRoot = it->second.m_aRootWindow; do { XTranslateCoordinates( m_pDisplay, aRoot, aParent, root_x, root_y, &win_x, &win_y, &aChild ); if( aChild != None && m_aDropTargets.find( aChild ) != m_aDropTargets.end() ) { m_aDragSourceWindow = aChild; #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found drag source window 0x%lx\n", m_aDragSourceWindow ); #endif break; } aParent = aChild; } while( aChild != None ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "try to grab pointer ... " ); #endif int nPointerGrabSuccess = XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True, DRAG_EVENT_MASK, GrabModeAsync, GrabModeAsync, None, None, CurrentTime ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%d\n", nPointerGrabSuccess ); #endif #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "try to grab keyboard ... " ); #endif int nKeyboardGrabSuccess = XGrabKeyboard( m_pDisplay, it->second.m_aRootWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%d\n", nKeyboardGrabSuccess ); #endif if( nPointerGrabSuccess != GrabSuccess || nKeyboardGrabSuccess != GrabSuccess ) { if( nPointerGrabSuccess == GrabSuccess ) XUngrabPointer( m_pDisplay, CurrentTime ); if( nKeyboardGrabSuccess == GrabSuccess ) XUngrabKeyboard( m_pDisplay, CurrentTime ); XFlush( m_pDisplay ); aGuard.clear(); if( listener.is() ) listener->dragDropEnd( aDragFailedEvent ); return; } m_xDragSourceTransferable = transferable; m_xDragSourceListener = listener; m_aDragFlavors = transferable->getTransferDataFlavors(); m_aCurrentCursor = None; requestOwnership( m_nXdndSelection ); ::std::list< Atom > aConversions; ::std::list< Atom >::const_iterator type_it; getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); int nTypes = aConversions.size(); Atom* pTypes = (Atom*)alloca( sizeof(Atom)*nTypes ); type_it = aConversions.begin(); for( int n = 0; n < nTypes; n++, ++type_it ) pTypes[n] = *type_it; XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes ); m_nSourceActions = sourceActions | DNDConstants::ACTION_DEFAULT; m_nUserDragAction = DNDConstants::ACTION_MOVE & m_nSourceActions; if( ! m_nUserDragAction ) m_nUserDragAction = DNDConstants::ACTION_COPY & m_nSourceActions; if( ! m_nUserDragAction ) m_nUserDragAction = DNDConstants::ACTION_LINK & m_nSourceActions; m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; m_bDropSent = false; m_bDropSuccess = false; m_bWaitingForPrimaryConversion = false; m_nDragButton = Button1; // default to left button if( trigger.Event.getValueTypeName().equalsAsciiL( "com.sun.star.awt.MouseEvent", 27 ) ) { MouseEvent aEvent; trigger.Event >>= aEvent; if( aEvent.Buttons & MouseButton::LEFT ) m_nDragButton = Button1; else if( aEvent.Buttons & MouseButton::RIGHT ) m_nDragButton = Button3; else if( aEvent.Buttons & MouseButton::MIDDLE ) m_nDragButton = Button2; } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "m_nUserDragAction = %x\n", (int)m_nUserDragAction ); #endif updateDragWindow( root_x, root_y, aRoot ); m_nUserDragAction = ~0; updateDragAction( mask ); } m_aDragRunning.set(); m_aDragExecuteThread = osl_createSuspendedThread( call_SelectionManager_runDragExecute, this ); if( m_aDragExecuteThread ) osl_resumeThread( m_aDragExecuteThread ); else { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "osl_createSuspendedThread failed for drag execute\n" ); #endif m_xDragSourceListener.clear(); m_xDragSourceTransferable.clear(); m_bDropSent = false; m_bDropSuccess = false; m_bWaitingForPrimaryConversion = false; m_aDropWindow = None; m_aDropProxy = None; m_nCurrentProtocolVersion = nXdndProtocolRevision; m_nNoPosX = 0; m_nNoPosY = 0; m_nNoPosWidth = 0; m_nNoPosHeight = 0; m_aCurrentCursor = None; XUngrabPointer( m_pDisplay, CurrentTime ); XUngrabKeyboard( m_pDisplay, CurrentTime ); XFlush( m_pDisplay ); m_aDragRunning.reset(); if( listener.is() ) listener->dragDropEnd( aDragFailedEvent ); } } void SelectionManager::runDragExecute( void* pThis ) { SelectionManager* This = (SelectionManager*)pThis; This->dragDoDispatch(); } void SelectionManager::dragDoDispatch() { // do drag // m_xDragSourceListener will be cleared on finished drop #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "begin executeDrag dispatching\n" ); #endif TimeValue aTVal; aTVal.Seconds = 0; aTVal.Nanosec = 200000000; oslThread aThread = m_aDragExecuteThread; while( m_xDragSourceListener.is() && ( ! m_bDropSent || time(NULL)-m_nDropTimeout < 5 ) && osl_scheduleThread( aThread ) ) { // let the thread in the run method do the dispatching // just look occasionally here whether drop timed out or is completed osl_waitThread( &aTVal ); } #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "end executeDrag dispatching\n" ); #endif { ClearableMutexGuard aGuard(m_aMutex); Reference< XDragSourceListener > xListener( m_xDragSourceListener ); Reference< XTransferable > xTransferable( m_xDragSourceTransferable ); m_xDragSourceListener.clear(); m_xDragSourceTransferable.clear(); DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; // cleanup after drag if( m_bWaitingForPrimaryConversion ) getAdaptor( XA_PRIMARY )->clearTransferable(); m_bDropSent = false; m_bDropSuccess = false; m_bWaitingForPrimaryConversion = false; m_aDropWindow = None; m_aDropProxy = None; m_nCurrentProtocolVersion = nXdndProtocolRevision; m_nNoPosX = 0; m_nNoPosY = 0; m_nNoPosWidth = 0; m_nNoPosHeight = 0; m_aCurrentCursor = None; XUngrabPointer( m_pDisplay, CurrentTime ); XUngrabKeyboard( m_pDisplay, CurrentTime ); XFlush( m_pDisplay ); m_aDragExecuteThread = NULL; m_aDragRunning.reset(); aGuard.clear(); if( xListener.is() ) { xTransferable.clear(); xListener->dragDropEnd( dsde ); } } osl_destroyThread( aThread ); } /* * XDragSourceContext */ sal_Int32 SelectionManager::getCurrentCursor() { return m_aCurrentCursor; } // ------------------------------------------------------------------------ void SelectionManager::setCursor( sal_Int32 cursor, Window aDropWindow, Time ) { MutexGuard aGuard( m_aMutex ); if( aDropWindow == m_aDropWindow && Cursor(cursor) != m_aCurrentCursor ) { if( m_xDragSourceListener.is() && ! m_bDropSent ) { m_aCurrentCursor = cursor; XChangeActivePointerGrab( m_pDisplay, DRAG_EVENT_MASK, cursor, CurrentTime ); XFlush( m_pDisplay ); } } } // ------------------------------------------------------------------------ void SelectionManager::setImage( sal_Int32, Window, Time ) { } // ------------------------------------------------------------------------ void SelectionManager::transferablesFlavorsChanged() { MutexGuard aGuard(m_aMutex); m_aDragFlavors = m_xDragSourceTransferable->getTransferDataFlavors(); int i; std::list< Atom > aConversions; std::list< Atom >::const_iterator type_it; getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); int nTypes = aConversions.size(); Atom* pTypes = (Atom*)alloca( sizeof(Atom)*aConversions.size() ); for( i = 0, type_it = aConversions.begin(); type_it != aConversions.end(); ++type_it, i++ ) pTypes[i] = *type_it; XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes ); if( m_aCurrentDropWindow != None && m_nCurrentProtocolVersion >= 0 ) { // send synthetic leave and enter events XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; aEvent.xclient.message_type = m_nXdndLeave; aEvent.xclient.data.l[1] = 0; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); aEvent.xclient.message_type = m_nXdndEnter; aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24; memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 ); // fill in data types if( nTypes > 3 ) aEvent.xclient.data.l[1] |= 1; for( int j = 0; j < nTypes && j < 3; j++ ) aEvent.xclient.data.l[j+2] = pTypes[j]; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); } } /* * dispatch loop */ // ------------------------------------------------------------------------ bool SelectionManager::handleXEvent( XEvent& rEvent ) { /* * since we are XConnectionListener to a second X display * to get client messages it is essential not to dispatch * events twice that we get on both connections * * #95201# between dispatching ButtonPress and startDrag * the user can already have released the mouse. The ButtonRelease * will then be dispatched in VCLs queue and never turn up here. * Which is not so good, since startDrag will XGrabPointer and * XGrabKeyboard -> solid lock. */ if( rEvent.xany.display != m_pDisplay && rEvent.type != ClientMessage && rEvent.type != ButtonPress && rEvent.type != ButtonRelease ) return false; bool bHandled = false; switch (rEvent.type) { case SelectionClear: { ClearableMutexGuard aGuard(m_aMutex); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "SelectionClear for selection %s\n", OUStringToOString( getString( rEvent.xselectionclear.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); #endif SelectionAdaptor* pAdaptor = getAdaptor( rEvent.xselectionclear.selection ); std::hash_map< Atom, Selection* >::iterator it( m_aSelections.find( rEvent.xselectionclear.selection ) ); if( it != m_aSelections.end() ) it->second->m_bOwner = false; aGuard.clear(); if ( pAdaptor ) pAdaptor->clearTransferable(); } break; case SelectionRequest: bHandled = handleSelectionRequest( rEvent.xselectionrequest ); break; case PropertyNotify: if( rEvent.xproperty.window == m_aWindow || rEvent.xproperty.window == m_aCurrentDropWindow ) bHandled = handleReceivePropertyNotify( rEvent.xproperty ); else bHandled = handleSendPropertyNotify( rEvent.xproperty ); break; case SelectionNotify: bHandled = handleSelectionNotify( rEvent.xselection ); break; case ClientMessage: // messages from drag target if( rEvent.xclient.message_type == m_nXdndStatus || rEvent.xclient.message_type == m_nXdndFinished ) bHandled = handleDragEvent( rEvent ); // messages from drag source else if( rEvent.xclient.message_type == m_nXdndEnter || rEvent.xclient.message_type == m_nXdndLeave || rEvent.xclient.message_type == m_nXdndPosition || rEvent.xclient.message_type == m_nXdndDrop ) bHandled = handleDropEvent( rEvent.xclient ); break; case EnterNotify: case LeaveNotify: case MotionNotify: case ButtonPress: case ButtonRelease: case KeyPress: case KeyRelease: bHandled = handleDragEvent( rEvent ); break; default: ; } return bHandled; } // ------------------------------------------------------------------------ void SelectionManager::dispatchEvent( int millisec ) { pollfd aPollFD; XEvent event; // query socket handle to poll on aPollFD.fd = ConnectionNumber( m_pDisplay ); aPollFD.events = POLLIN; aPollFD.revents = 0; // wait for activity (outside the xlib) if( poll( &aPollFD, 1, millisec ) > 0 ) { // now acquire the mutex to prevent other threads // from using the same X connection ResettableMutexGuard aGuard(m_aMutex); // prevent that another thread already ate the input // this can happen if e.g. another thread does // an X request getting a response. the response // would be removed from the queue and we would end up // with an empty socket here if( poll( &aPollFD, 1, 0 ) > 0 ) { int nPending = 1; while( nPending ) { nPending = XPending( m_pDisplay ); if( nPending ) { XNextEvent( m_pDisplay, &event ); aGuard.clear(); handleXEvent( event ); aGuard.reset(); } } } } } // ------------------------------------------------------------------------ void SelectionManager::run( void* pThis ) { #if OSL_DEBUG_LEVEL > 1 fprintf(stderr, "SelectionManager::run\n" ); #endif // dispatch until the cows come home SelectionManager* This = (SelectionManager*)pThis; timeval aLast; gettimeofday( &aLast, 0 ); while( osl_scheduleThread(This->m_aThread) ) { This->dispatchEvent( 1000 ); timeval aNow; gettimeofday( &aNow, 0 ); if( (aNow.tv_sec - aLast.tv_sec) > 0 ) { ClearableMutexGuard aGuard(This->m_aMutex); std::list< std::pair< SelectionAdaptor*, Reference< XInterface > > > aChangeList; for( std::hash_map< Atom, Selection* >::iterator it = This->m_aSelections.begin(); it != This->m_aSelections.end(); ++it ) { if( it->first != This->m_nXdndSelection && ! it->second->m_bOwner ) { Window aOwner = XGetSelectionOwner( This->m_pDisplay, it->first ); if( aOwner != it->second->m_aLastOwner ) { it->second->m_aLastOwner = aOwner; std::pair< SelectionAdaptor*, Reference< XInterface > > aKeep( it->second->m_pAdaptor, it->second->m_pAdaptor->getReference() ); aChangeList.push_back( aKeep ); } } } aGuard.clear(); while( aChangeList.begin() != aChangeList.end() ) { aChangeList.front().first->fireContentsChanged(); aChangeList.pop_front(); } aLast = aNow; } } #if OSL_DEBUG_LEVEL > 1 fprintf(stderr, "SelectionManager::run end\n" ); #endif } // ------------------------------------------------------------------------ sal_Bool SelectionManager::handleEvent( const Any& event ) throw() { Sequence< sal_Int8 > aSeq; if( (event >>= aSeq) ) { XEvent* pEvent = (XEvent*)aSeq.getArray(); Time nTimestamp = CurrentTime; if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease ) nTimestamp = pEvent->xbutton.time; else if( pEvent->type == KeyPress || pEvent->type == KeyRelease ) nTimestamp = pEvent->xkey.time; else if( pEvent->type == MotionNotify ) nTimestamp = pEvent->xmotion.time; else if( pEvent->type == PropertyNotify ) nTimestamp = pEvent->xproperty.time; if( nTimestamp != CurrentTime ) { MutexGuard aGuard(m_aMutex); m_nSelectionTimestamp = nTimestamp; } return sal_Bool( handleXEvent( *pEvent ) ); } else { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "SelectionManager got downing event\n" ); #endif MutexGuard aGuard(m_aMutex); // stop dispatching if( m_aThread ) osl_terminateThread( m_aThread ); m_xDisplayConnection->removeEventHandler( Any(), this ); m_xDisplayConnection.clear(); } return sal_True; } // ------------------------------------------------------------------------ void SelectionManager::registerHandler( Atom selection, SelectionAdaptor& rAdaptor ) { MutexGuard aGuard(m_aMutex); Selection* pNewSelection = new Selection(); pNewSelection->m_pAdaptor = &rAdaptor; pNewSelection->m_aAtom = selection; m_aSelections[ selection ] = pNewSelection; } // ------------------------------------------------------------------------ void SelectionManager::deregisterHandler( Atom selection ) { MutexGuard aGuard(m_aMutex); ::std::hash_map< Atom, Selection* >::iterator it = m_aSelections.find( selection ); if( it != m_aSelections.end() ) { delete it->second->m_pPixmap; delete it->second; m_aSelections.erase( it ); } } // ------------------------------------------------------------------------ void SelectionManager::registerDropTarget( Window aWindow, DropTarget* pTarget ) { MutexGuard aGuard(m_aMutex); // sanity check ::std::hash_map< Window, DropTargetEntry >::const_iterator it = m_aDropTargets.find( aWindow ); if( it != m_aDropTargets.end() ) OSL_ASSERT( "attempt to register window as drop target twice" ); else if( aWindow && m_pDisplay ) { XSelectInput( m_pDisplay, aWindow, PropertyChangeMask ); // set XdndAware XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&nXdndProtocolRevision, 1 ); DropTargetEntry aEntry( pTarget ); // get root window of window (in 99.999% of all cases this will be // DefaultRootWindow( m_pDisplay ) int x, y; unsigned int w, h, bw, d; XGetGeometry( m_pDisplay, aWindow, &aEntry.m_aRootWindow, &x, &y, &w, &h, &bw, &d ); m_aDropTargets[ aWindow ] = aEntry; } else OSL_ASSERT( "attempt to register None as drop target" ); } // ------------------------------------------------------------------------ void SelectionManager::deregisterDropTarget( Window aWindow ) { ClearableMutexGuard aGuard(m_aMutex); m_aDropTargets.erase( aWindow ); if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() ) { // abort drag std::hash_map< Window, DropTargetEntry >::const_iterator it = m_aDropTargets.find( m_aDropWindow ); if( it != m_aDropTargets.end() ) { DropTargetEvent dte; dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); aGuard.clear(); it->second.m_pTarget->dragExit( dte ); } else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) { // send XdndLeave XEvent aEvent; aEvent.type = ClientMessage; aEvent.xclient.display = m_pDisplay; aEvent.xclient.format = 32; aEvent.xclient.message_type = m_nXdndLeave; aEvent.xclient.window = m_aDropWindow; aEvent.xclient.data.l[0] = m_aWindow; memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4); m_aDropWindow = m_aDropProxy = None; XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); } // notify the listener DragSourceDropEvent dsde; dsde.Source = static_cast< OWeakObject* >(this); dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); dsde.DragSource = static_cast< XDragSource* >(this); dsde.DropAction = DNDConstants::ACTION_NONE; dsde.DropSuccess = sal_False; Reference< XDragSourceListener > xListener( m_xDragSourceListener ); m_xDragSourceListener.clear(); aGuard.clear(); xListener->dragDropEnd( dsde ); } } /* * SelectionAdaptor */ Reference< XTransferable > SelectionManager::getTransferable() throw() { return m_xDragSourceTransferable; } // ------------------------------------------------------------------------ void SelectionManager::clearTransferable() throw() { m_xDragSourceTransferable.clear(); } // ------------------------------------------------------------------------ void SelectionManager::fireContentsChanged() throw() { } // ------------------------------------------------------------------------ Reference< XInterface > SelectionManager::getReference() throw() { return Reference< XInterface >( static_cast(this) ); } // ------------------------------------------------------------------------ /* * SelectionManagerHolder */ SelectionManagerHolder::SelectionManagerHolder() : ::cppu::WeakComponentImplHelper3< XDragSource, XInitialization, XServiceInfo > (m_aMutex) { } // ------------------------------------------------------------------------ SelectionManagerHolder::~SelectionManagerHolder() { } // ------------------------------------------------------------------------ void SelectionManagerHolder::initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ) { OUString aDisplayName; if( arguments.getLength() > 0 ) { Reference< XDisplayConnection > xConn; arguments.getConstArray()[0] >>= xConn; if( xConn.is() ) { Any aIdentifier; aIdentifier >>= aDisplayName; } } SelectionManager& rManager = SelectionManager::get( aDisplayName ); rManager.initialize( arguments ); m_xRealDragSource = static_cast< XDragSource* >(&rManager); } /* * XDragSource */ sal_Bool SelectionManagerHolder::isDragImageSupported() throw() { return m_xRealDragSource.is() ? m_xRealDragSource->isDragImageSupported() : sal_False; } // ------------------------------------------------------------------------ sal_Int32 SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction ) throw() { return m_xRealDragSource.is() ? m_xRealDragSource->getDefaultCursor( dragAction ) : 0; } // ------------------------------------------------------------------------ void SelectionManagerHolder::startDrag( const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger, sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image, const Reference< ::com::sun::star::datatransfer::XTransferable >& transferable, const Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener ) throw() { if( m_xRealDragSource.is() ) m_xRealDragSource->startDrag( trigger, sourceActions, cursor, image, transferable, listener ); } // ------------------------------------------------------------------------ /* * XServiceInfo */ // ------------------------------------------------------------------------ OUString SelectionManagerHolder::getImplementationName() throw() { return OUString::createFromAscii(XDND_IMPLEMENTATION_NAME); } // ------------------------------------------------------------------------ sal_Bool SelectionManagerHolder::supportsService( const OUString& ServiceName ) throw() { Sequence < OUString > SupportedServicesNames = Xdnd_getSupportedServiceNames(); for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; ) if (SupportedServicesNames[n].compareTo(ServiceName) == 0) return sal_True; return sal_False; } // ------------------------------------------------------------------------ Sequence< OUString > SelectionManagerHolder::getSupportedServiceNames() throw() { return Xdnd_getSupportedServiceNames(); } // ------------------------------------------------------------------------