| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #include "unx/saldisp.hxx" |
| #include "unx/saldata.hxx" |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/time.h> |
| |
| #include "tools/prex.h" |
| #include <X11/Xatom.h> |
| #include <X11/keysym.h> |
| #include <X11/Xutil.h> |
| #include "tools/postx.h" |
| #if defined(LINUX) || defined(NETBSD) || defined (FREEBSD) |
| #include <sys/poll.h> |
| #else |
| #include <poll.h> |
| #endif |
| #include <sal/alloca.h> |
| |
| #include <X11_selection.hxx> |
| #include <X11_clipboard.hxx> |
| #include <X11_transferable.hxx> |
| #include <X11_dndcontext.hxx> |
| #include <bmp.hxx> |
| |
| #include "vcl/svapp.hxx" |
| |
| // pointer bitmaps |
| #include <copydata_curs.h> |
| #include <copydata_mask.h> |
| #include <movedata_curs.h> |
| #include <movedata_mask.h> |
| #include <linkdata_curs.h> |
| #include <linkdata_mask.h> |
| #include <nodrop_curs.h> |
| #include <nodrop_mask.h> |
| #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> |
| #include <com/sun/star/awt/MouseEvent.hpp> |
| #include <com/sun/star/awt/MouseButton.hpp> |
| #include <rtl/tencinfo.h> |
| #include <osl/process.h> |
| |
| #include <comphelper/processfactory.hxx> |
| #include <vos/mutex.hxx> |
| |
| #define DRAG_EVENT_MASK ButtonPressMask |\ |
| ButtonReleaseMask |\ |
| PointerMotionMask |\ |
| EnterWindowMask |\ |
| LeaveWindowMask |
| |
| namespace { |
| |
| namespace css = com::sun::star; |
| |
| } |
| |
| 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 com::sun::star::frame; |
| 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_bDropEnterSent( true ), |
| m_aCurrentDropWindow( None ), |
| m_nDropTime( None ), |
| m_nLastDropAction( 0 ), |
| m_nLastX( 0 ), |
| m_nLastY( 0 ), |
| m_nDropTimestamp( 0 ), |
| m_bDropWaitingForCompletion( false ), |
| m_aDropWindow( None ), |
| m_aDropProxy( None ), |
| m_aDragSourceWindow( None ), |
| m_nLastDragX( 0 ), |
| m_nLastDragY( 0 ), |
| m_nNoPosX( 0 ), |
| m_nNoPosY( 0 ), |
| m_nNoPosWidth( 0 ), |
| m_nNoPosHeight( 0 ), |
| m_nDragButton( 0 ), |
| m_nUserDragAction( 0 ), |
| m_nTargetAcceptAction( 0 ), |
| m_nSourceActions( 0 ), |
| m_bLastDropAccepted( false ), |
| m_bDropSuccess( false ), |
| m_bDropSent( false ), |
| m_bWaitingForPrimaryConversion( false ), |
| m_nDragTimestamp( None ), |
| m_aMoveCursor( None ), |
| m_aCopyCursor( None ), |
| m_aLinkCursor( None ), |
| m_aNoneCursor( None ), |
| m_aCurrentCursor( None ), |
| m_nCurrentProtocolVersion( nXdndProtocolRevision ), |
| m_nCLIPBOARDAtom( None ), |
| m_nTARGETSAtom( None ), |
| m_nTIMESTAMPAtom( None ), |
| m_nTEXTAtom( None ), |
| m_nINCRAtom( None ), |
| m_nCOMPOUNDAtom( None ), |
| m_nMULTIPLEAtom( None ), |
| m_nUTF16Atom( None ), |
| m_nImageBmpAtom( None ), |
| m_nXdndAware( None ), |
| m_nXdndEnter( None ), |
| m_nXdndLeave( None ), |
| m_nXdndPosition( None ), |
| m_nXdndStatus( None ), |
| m_nXdndDrop( None ), |
| m_nXdndFinished( None ), |
| m_nXdndSelection( None ), |
| m_nXdndTypeList( None ), |
| m_nXdndProxy( None ), |
| m_nXdndActionCopy( None ), |
| m_nXdndActionMove( None ), |
| m_nXdndActionLink( None ), |
| m_nXdndActionAsk( None ), |
| m_nXdndActionPrivate( None ), |
| m_bShutDown( false ) |
| { |
| m_aDropEnterEvent.data.l[0] = None; |
| m_aDragRunning.reset(); |
| } |
| |
| XLIB_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 ); |
| XLIB_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: |
| * FIXME: |
| * finally the clipboard and XDND service is back in the module it belongs |
| * now cleanup and sharing of resources with the normal vcl event loop |
| * needs to be added. The display used whould be that of the normal event loop |
| * and synchronization should be done via the SolarMutex. |
| */ |
| 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; |
| } |
| |
| OUString aParam; |
| if( ! m_pDisplay ) |
| { |
| 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 ) |
| { |
| #define createCursorFromXPM(name) createCursor((const char*)name##curs##_bits, (const char*)name##mask##_bits, name##curs_width, name##curs_height, name##curs_x_hot, name##curs_y_hot ); |
| // initialize default cursors |
| m_aMoveCursor = createCursorFromXPM( movedata_); |
| m_aCopyCursor = createCursorFromXPM( copydata_); |
| m_aLinkCursor = createCursorFromXPM( linkdata_); |
| m_aNoneCursor = createCursorFromXPM( nodrop_); |
| |
| // 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<char*>(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 css::uno::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).getStr(), 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; |
| |
| XLIB_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, |
| XLIB_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 |
| css::uno::Reference<XBitmap> xBM( new BmpTransporter( aData ) ); |
| Sequence<Any> aArgs(2), aOutArgs; |
| Sequence<sal_Int16> 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< XLIB_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 ) |
| { |
| css::uno::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] ) : "<None>" ); |
| #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; |
| } |
| css::uno::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< XLIB_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 |
| XLIB_Window aSource = rMessage.data.l[0]; |
| XLIB_Window aTarget = rMessage.window; |
| |
| bool bHandled = false; |
| |
| ::std::hash_map< XLIB_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 && (XLIB_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 || XLIB_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 == XLIB_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; |
| |
| XLIB_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 == XLIB_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 == XLIB_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, XLIB_Window aDropWindow, XLIB_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; |
| css::uno::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(); |
| |
| css::uno::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, XLIB_Time eventTime ) |
| { |
| ClearableMutexGuard aGuard(m_aMutex); |
| |
| if( m_bDropSent ) |
| return; |
| |
| ::std::hash_map< XLIB_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; |
| XLIB_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< XLIB_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 XLIB_KeyPress: |
| fprintf( stderr, "handleDragEvent: KeyPress\n" ); |
| break; |
| case KeyRelease: |
| fprintf( stderr, "handleDragEvent: KeyRelease\n" ); |
| break; |
| default: |
| fprintf( stderr, "handleDragEvent: <unknown type %d>\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; |
| css::uno::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; |
| XLIB_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 == XLIB_KeyPress || rMessage.type == KeyRelease ) |
| { |
| bHandled = true; |
| const KeySym aKey = XkbKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0, 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; |
| css::uno::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 == XLIB_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; |
| XLIB_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; |
| |
| XLIB_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, css::uno::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; |
| css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); |
| m_xDragSourceListener.clear(); |
| aGuard.clear(); |
| xListener->dragDropEnd( dsde ); |
| bHandled = true; |
| } |
| } |
| return bHandled; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void SelectionManager::accept( sal_Int8 dragOperation, XLIB_Window aDropWindow, XLIB_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( XLIB_Window aDropWindow, XLIB_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() |
| { |
| XLIB_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( XLIB_Window aWindow, XLIB_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 = *(XLIB_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 && *(XLIB_Window*)pBytes != rProxy ) |
| rProxy = None; |
| XFree( pBytes ); |
| pBytes = NULL; |
| } |
| else |
| rProxy = None; |
| } |
| } |
| break; |
| } |
| } |
| XLIB_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, XLIB_Window aRoot ) |
| { |
| ResettableMutexGuard aGuard( m_aMutex ); |
| |
| css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); |
| |
| m_nLastDragX = nX; |
| m_nLastDragY = nY; |
| |
| XLIB_Window aParent = aRoot; |
| XLIB_Window aChild; |
| XLIB_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< XLIB_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 css::uno::Reference< XTransferable >& transferable, |
| const css::uno::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; |
| } |
| |
| SalFrame* pCaptureFrame = NULL; |
| |
| { |
| 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 |
| XLIB_Window aRoot, aParent, aChild; |
| int root_x, root_y, win_x, win_y; |
| unsigned int mask; |
| |
| ::std::hash_map< XLIB_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 we could not grab the pointer here, there is a chance |
| that the pointer is grabbed by the other vcl display (the main loop) |
| so let's break that grab an reset it later |
| |
| remark: this whole code should really be molten into normal vcl so only |
| one display is used .... |
| */ |
| if( nPointerGrabSuccess != GrabSuccess ) |
| { |
| vos::IMutex& rSolarMutex( Application::GetSolarMutex() ); |
| if( rSolarMutex.tryToAcquire() ) |
| { |
| pCaptureFrame = GetX11SalData()->GetDisplay()->GetCaptureFrame(); |
| if( pCaptureFrame ) |
| { |
| GetX11SalData()->GetDisplay()->CaptureMouse( NULL ); |
| 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 ); |
| if( pCaptureFrame ) |
| { |
| vos::IMutex& rSolarMutex( Application::GetSolarMutex() ); |
| if( rSolarMutex.tryToAcquire() ) |
| GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame ); |
| #if OSL_DEBUG_LEVEL > 0 |
| else |
| OSL_ENSURE( 0, "failed to acquire SolarMutex to reset capture frame" ); |
| #endif |
| } |
| 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 |
| com::sun::star::awt::MouseEvent aEvent; |
| if( 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 ); |
| |
| if( pCaptureFrame ) |
| { |
| vos::IMutex& rSolarMutex( Application::GetSolarMutex() ); |
| if( rSolarMutex.tryToAcquire() ) |
| GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame ); |
| #if OSL_DEBUG_LEVEL > 0 |
| else |
| OSL_ENSURE( 0, "failed to acquire SolarMutex to reset capture frame" ); |
| #endif |
| } |
| |
| 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); |
| |
| css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); |
| css::uno::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, XLIB_Window aDropWindow, XLIB_Time ) |
| { |
| MutexGuard aGuard( m_aMutex ); |
| if( aDropWindow == m_aDropWindow && XLIB_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, XLIB_Window, XLIB_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 XLIB_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 ); |
| |
| css::uno::Reference< XMultiServiceFactory > xFact( ::comphelper::getProcessServiceFactory() ); |
| if( xFact.is() ) |
| { |
| css::uno::Reference< XDesktop > xDesktop( xFact->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.frame.Desktop" ) ), UNO_QUERY ); |
| if( xDesktop.is() ) |
| xDesktop->addTerminateListener(This); |
| } |
| |
| 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*, css::uno::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 ) |
| { |
| XLIB_Window aOwner = XGetSelectionOwner( This->m_pDisplay, it->first ); |
| if( aOwner != it->second->m_aLastOwner ) |
| { |
| it->second->m_aLastOwner = aOwner; |
| std::pair< SelectionAdaptor*, css::uno::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 |
| } |
| |
| void SelectionManager::shutdown() throw() |
| { |
| ResettableMutexGuard aGuard(m_aMutex); |
| if( m_bShutDown ) |
| { |
| return; |
| } |
| m_bShutDown = true; |
| // stop dispatching |
| if( m_aThread ) |
| { |
| osl_terminateThread( m_aThread ); |
| /* |
| * Allow thread to finish before app exits to avoid pulling the carpet |
| * out from under it if pasting is occuring during shutdown |
| * |
| * a) allow it to have the Mutex and |
| * b) reschedule to allow it to complete callbacks to any |
| * Application::GetSolarMutex protected regions, etc. e.g. |
| * TransferableHelper::getTransferDataFlavors (via |
| * SelectionManager::handleSelectionRequest) which it might |
| * currently be trying to enter. |
| * |
| * Otherwise the thread may be left still waiting on a GlobalMutex |
| * when that gets destroyed, letting the thread blow up and die |
| * when enters the section in a now dead OOo instance. |
| */ |
| aGuard.clear(); |
| while (osl_isThreadRunning(m_aThread)) |
| { |
| vos::OGuard guard2(Application::GetSolarMutex()); |
| Application::Reschedule(); |
| } |
| osl_joinWithThread( m_aThread ); |
| osl_destroyThread( m_aThread ); |
| m_aThread = NULL; |
| aGuard.reset(); |
| } |
| m_xDisplayConnection->removeEventHandler( Any(), this ); |
| m_xDisplayConnection.clear(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| sal_Bool SelectionManager::handleEvent( const Any& event ) throw() |
| { |
| Sequence< sal_Int8 > aSeq; |
| if( (event >>= aSeq) ) |
| { |
| XEvent* pEvent = (XEvent*)aSeq.getArray(); |
| XLIB_Time nTimestamp = CurrentTime; |
| if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease ) |
| nTimestamp = pEvent->xbutton.time; |
| else if( pEvent->type == XLIB_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 |
| shutdown(); |
| } |
| return sal_True; |
| } |
| |
| void SAL_CALL SelectionManager::disposing( const ::com::sun::star::lang::EventObject& ) |
| throw( ::com::sun::star::uno::RuntimeException ) |
| { |
| } |
| |
| void SAL_CALL SelectionManager::queryTermination( const ::com::sun::star::lang::EventObject& ) |
| throw( ::com::sun::star::frame::TerminationVetoException, ::com::sun::star::uno::RuntimeException ) |
| { |
| } |
| |
| /* |
| * To be safe, shutdown needs to be called before the ~SfxApplication is called, waiting until |
| * the downing event can be too late if paste are requested during shutdown and ~SfxApplication |
| * has been called before vcl is shutdown |
| */ |
| void SAL_CALL SelectionManager::notifyTermination( const ::com::sun::star::lang::EventObject& rEvent ) |
| throw( ::com::sun::star::uno::RuntimeException ) |
| { |
| css::uno::Reference< XDesktop > xDesktop( rEvent.Source, UNO_QUERY ); |
| if( xDesktop.is() == sal_True ) |
| xDesktop->removeTerminateListener( this ); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "SelectionManager got app termination event\n" ); |
| #endif |
| shutdown(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| 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 ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| static bool bWasError = false; |
| |
| extern "C" |
| { |
| int local_xerror_handler(Display* , XErrorEvent*) |
| { |
| bWasError = true; |
| return 0; |
| } |
| typedef int(*xerror_hdl_t)(Display*,XErrorEvent*); |
| } |
| |
| void SelectionManager::registerDropTarget( XLIB_Window aWindow, DropTarget* pTarget ) |
| { |
| MutexGuard aGuard(m_aMutex); |
| |
| // sanity check |
| ::std::hash_map< XLIB_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 ) |
| { |
| DropTargetEntry aEntry( pTarget ); |
| bWasError=false; |
| /* #i100000# ugly workaround: gtk sets its own XErrorHandler which is not suitable for us |
| unfortunately XErrorHandler is not per display, so this is just and ugly hack |
| Need to remove separate display and integrate clipboard/dnd into vcl's unx code ASAP |
| */ |
| xerror_hdl_t pOldHandler = XSetErrorHandler( local_xerror_handler ); |
| XSelectInput( m_pDisplay, aWindow, PropertyChangeMask ); |
| if( ! bWasError ) |
| { |
| // set XdndAware |
| XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&nXdndProtocolRevision, 1 ); |
| if( ! bWasError ) |
| { |
| // 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 ); |
| } |
| } |
| XSetErrorHandler( pOldHandler ); |
| if(bWasError) |
| return; |
| m_aDropTargets[ aWindow ] = aEntry; |
| } |
| else |
| OSL_ASSERT( "attempt to register None as drop target" ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void SelectionManager::deregisterDropTarget( XLIB_Window aWindow ) |
| { |
| ClearableMutexGuard aGuard(m_aMutex); |
| |
| m_aDropTargets.erase( aWindow ); |
| if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() ) |
| { |
| // abort drag |
| std::hash_map< XLIB_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; |
| css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); |
| m_xDragSourceListener.clear(); |
| aGuard.clear(); |
| xListener->dragDropEnd( dsde ); |
| } |
| } |
| |
| /* |
| * SelectionAdaptor |
| */ |
| |
| css::uno::Reference< XTransferable > SelectionManager::getTransferable() throw() |
| { |
| return m_xDragSourceTransferable; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void SelectionManager::clearTransferable() throw() |
| { |
| m_xDragSourceTransferable.clear(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void SelectionManager::fireContentsChanged() throw() |
| { |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| css::uno::Reference< XInterface > SelectionManager::getReference() throw() |
| { |
| return css::uno::Reference< XInterface >( static_cast<OWeakObject*>(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 ) |
| { |
| css::uno::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 css::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& transferable, |
| const css::uno::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(); |
| } |
| |
| |
| // ------------------------------------------------------------------------ |
| |