/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include "saldisp.hxx"
#include "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) || defined(OPENBSD)
#include <sys/poll.h>
#else
#include <poll.h>
#endif
#include <sal/alloca.h>
#include <sal/macros.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 <osl/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 x11;

using ::rtl::OUString;
using ::rtl::OUStringHash;
using ::rtl::OStringToOUString;

// 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;
}

// ------------------------------------------------------------------------

::boost::unordered_map< OUString, SelectionManager*, OUStringHash >& SelectionManager::getInstances()
{
    static ::boost::unordered_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 unsigned char* pPointerData, const unsigned 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,
                               reinterpret_cast<const char*>(pPointerData),
                               width,
                               height );
    aMask
        = XCreateBitmapFromData( m_pDisplay,
                                 m_aWindow,
                                 reinterpret_cast<const char*>(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)
{
    osl::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() )
        {
        }
        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(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) );

            // special targets
            m_nTARGETSAtom		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TARGETS")) );
            m_nTIMESTAMPAtom    = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TIMESTAMP")) );
            m_nTEXTAtom			= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TEXT")) );
            m_nINCRAtom			= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("INCR")) );
            m_nCOMPOUNDAtom		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("COMPOUND_TEXT")) );
            m_nMULTIPLEAtom		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("MULTIPLE")) );
            m_nUTF16Atom		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("ISO10646-1")) );
            m_nImageBmpAtom     = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("image/bmp")) );

            // Atoms for Xdnd protocol
            m_nXdndAware		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndAware")) );
            m_nXdndEnter		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndEnter")) );
            m_nXdndLeave		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndLeave")) );
            m_nXdndPosition		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndPosition")) );
            m_nXdndStatus		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndStatus")) );
            m_nXdndDrop			= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndDrop")) );
            m_nXdndFinished		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndFinished")) );
            m_nXdndSelection	= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndSelection")) );
            m_nXdndTypeList		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndTypeList")) );
            m_nXdndProxy		= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndProxy")) );
            m_nXdndActionCopy	= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionCopy")) );
            m_nXdndActionMove	= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionMove")) );
            m_nXdndActionLink	= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionLink")) );
            m_nXdndActionAsk	= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionAsk")) );
            m_nXdndActionPrivate= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionPrivate")) );

            // initialize map with member none
            m_aAtomToString[ 0 ]= OUString(RTL_CONSTASCII_USTRINGPARAM("None"));
            m_aAtomToString[ XA_PRIMARY ] = OUString(RTL_CONSTASCII_USTRINGPARAM("PRIMARY"));

            // create a (invisible) message window
            m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ),
                                             10, 10, 10, 10, 0, 0, 1 );

            // initialize threshold for incremetal transfers
            // ICCCM says it should be smaller that the max request size
            // which in turn is guaranteed to be at least 16k bytes
            m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024;

            if( m_aWindow )
            {
                // initialize default cursors
                m_aMoveCursor = createCursor( movedata_curs_bits,
                                              movedata_mask_bits,
                                              movedata_curs_width,
                                              movedata_curs_height,
                                              movedata_curs_x_hot,
                                              movedata_curs_y_hot );
                m_aCopyCursor = createCursor( copydata_curs_bits,
                                              copydata_mask_bits,
                                              copydata_curs_width,
                                              copydata_curs_height,
                                              copydata_curs_x_hot,
                                              copydata_curs_y_hot );
                m_aLinkCursor = createCursor( linkdata_curs_bits,
                                              linkdata_mask_bits,
                                              linkdata_curs_width,
                                              linkdata_curs_height,
                                              linkdata_curs_x_hot,
                                              linkdata_curs_y_hot );
                m_aNoneCursor = createCursor( nodrop_curs_bits,
                                              nodrop_mask_bits,
                                              nodrop_curs_width,
                                              nodrop_curs_height,
                                              nodrop_curs_x_hot,
                                              nodrop_curs_y_hot );




                // just interested in SelectionClear/Notify/Request and PropertyChange
                XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask );
                // create the transferable for Drag operations
                m_xDropTransferable = new X11Transferable( *this, static_cast< OWeakObject* >(this), m_nXdndSelection );
                registerHandler( m_nXdndSelection, *this );

                m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this );
                if( m_aThread )
                    osl_resumeThread( m_aThread );
#if OSL_DEBUG_LEVEL > 1
                else
                    fprintf( stderr, "SelectionManager::initialize: creation of dispatch thread failed !\n" );
#endif
            }
        }
    }
}

// ------------------------------------------------------------------------

SelectionManager::~SelectionManager()
{
#if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "SelectionManager::~SelectionManager (%s)\n", m_pDisplay ? DisplayString(m_pDisplay) : "no display" );
#endif
    {
        osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );

        ::boost::unordered_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()
    }

    osl::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 )
{
    ::boost::unordered_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 )
{
    osl::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 )
{
    osl::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
        /*
         *  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(RTL_CONSTASCII_USTRINGPARAM("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 )
{
    osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );

    OUString aDisplayName( rDisplayName );
    if( ! aDisplayName.getLength() )
        aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 );
    SelectionManager* pInstance = NULL;

    ::boost::unordered_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 )
{
    osl::MutexGuard aGuard(m_aMutex);

    ::boost::unordered_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 )
{
    osl::MutexGuard aGuard(m_aMutex);

    ::boost::unordered_map< OUString, Atom, OUStringHash >::const_iterator it;
    if( ( it = m_aStringToAtom.find( rString ) ) == m_aStringToAtom.end() )
    {
        static Atom nNoDisplayAtoms = 1;
        Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ), False ) : nNoDisplayAtoms++;
        m_aStringToAtom[ rString ] = aAtom;
        m_aAtomToString[ aAtom ] = rString;
    }
    return m_aStringToAtom[ rString ];
}

// ------------------------------------------------------------------------

bool SelectionManager::requestOwnership( Atom selection )
{
    bool bSuccess = false;
    if( m_pDisplay && m_aWindow )
    {
        osl::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 ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);

    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(RTL_CONSTASCII_USTRINGPARAM("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) ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);

    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 )
{
    osl::ResettableMutexGuard aGuard(m_aMutex);
    ::boost::unordered_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 )
{
    bool bSuccess = false;

    ::boost::unordered_map< Atom, Selection* >::iterator it;
    {
        osl::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 };
            {
                osl::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 )
                {
                    osl::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 )
            {
                osl::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 )
    {
        int nFormat;
        ::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 %" SAL_PRIdINT32 "\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 )
{
    ::boost::unordered_map< Atom, Selection* >::iterator it;
    {
        osl::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
                osl::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(RTL_CONSTASCII_USTRINGPARAM("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;
        }
    }

    {
        osl::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
    {
        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 )
{
    boost::unordered_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 )
{
    osl::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(RTL_CONSTASCII_USTRINGPARAM("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" );
            boost::unordered_map< XLIB_Window, boost::unordered_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor );
            if( win_it != m_aIncrementals.end() )
            {
                boost::unordered_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 )
{
    osl::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 )
{
    osl::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;

    ::boost::unordered_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 )
{
    osl::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 )
    {
        boost::unordered_map< XLIB_Window, boost::unordered_map< Atom, IncrementalTransfer > >::iterator it;
        it = m_aIncrementals.find( rNotify.window );
        if( it != m_aIncrementals.end() )
        {
            bHandled = true;
            int nCurrentTime = time( NULL );
            boost::unordered_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 )
{
    osl::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(RTL_CONSTASCII_USTRINGPARAM("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
    ::boost::unordered_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 )
{
    osl::ResettableMutexGuard aGuard(m_aMutex);

    // handle drop related events
    XLIB_Window aSource = rMessage.data.l[0];
    XLIB_Window aTarget = rMessage.window;

    bool bHandled = false;

    ::boost::unordered_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_FAIL( "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 )
{
    osl::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_FAIL( "dropComplete from invalid DropTargetDropContext" );
}

/*
 *	methods for XDropTargetDragContext
 */

// ------------------------------------------------------------------------

void SelectionManager::sendDragStatus( Atom nDropAction )
{
    osl::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 )
{
    osl::ClearableMutexGuard aGuard(m_aMutex);

    if( m_bDropSent )
        return;

    ::boost::unordered_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;

    osl::ResettableMutexGuard aGuard(m_aMutex);

    bool bHandled = false;

    // for shortcut
    ::boost::unordered_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:
            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;
        KeySym aKey = XKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0 );
        if( aKey == XK_Escape )
        {
            // abort drag
            if( it != m_aDropTargets.end() )
            {
                DropTargetEvent dte;
                dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
                aGuard.clear();
                it->second.m_pTarget->dragExit( dte );
            }
            else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
            {
                // send XdndLeave
                XEvent aEvent;
                aEvent.type = ClientMessage;
                aEvent.xclient.display		= m_pDisplay;
                aEvent.xclient.format		= 32;
                aEvent.xclient.message_type	= m_nXdndLeave;
                aEvent.xclient.window		= m_aDropWindow;
                aEvent.xclient.data.l[0]	= m_aWindow;
                memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
                m_aDropWindow = m_aDropProxy = None;
                XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
            }
            // notify the listener
            DragSourceDropEvent dsde;
            dsde.Source				= static_cast< OWeakObject* >(this);
            dsde.DragSourceContext	= new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this );
            dsde.DragSource			= static_cast< XDragSource* >(this);
            dsde.DropAction			= DNDConstants::ACTION_NONE;
            dsde.DropSuccess		= sal_False;
            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 )
{
    osl::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;

    ::boost::unordered_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;

    {
        osl::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;

        ::boost::unordered_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 )
        {
            osl::SolarMutex& 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 )
            {
                osl::SolarMutex& rSolarMutex( Application::GetSolarMutex() );
                if( rSolarMutex.tryToAcquire() )
                    GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame );
#if OSL_DEBUG_LEVEL > 0
                else
                    OSL_FAIL( "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 )
        {
            osl::SolarMutex& rSolarMutex( Application::GetSolarMutex() );
            if( rSolarMutex.tryToAcquire() )
                GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame );
#if OSL_DEBUG_LEVEL > 0
            else
                OSL_FAIL( "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
    {
        osl::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 )
{
    osl::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()
{
    osl::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
     *
     *  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:
        {
            osl::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 );
            boost::unordered_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 )
{
    // acquire the mutex to prevent other threads
    // from using the same X connection
    osl::ResettableMutexGuard aGuard(m_aMutex);

    if( !XPending( m_pDisplay ))
    { // wait for any events if none are already queued
        pollfd aPollFD;
        aPollFD.fd      = XConnectionNumber( m_pDisplay );
        aPollFD.events  = POLLIN;
        aPollFD.revents = 0;
        // release mutex for the time of waiting for possible data
        aGuard.clear();
        if( poll( &aPollFD, 1, millisec ) <= 0 )
            return;
        aGuard.reset();
    }
    while( XPending( m_pDisplay ))
    {
        XEvent event;
        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(RTL_CONSTASCII_USTRINGPARAM("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 )
        {
            osl::ClearableMutexGuard aGuard(This->m_aMutex);
            std::list< std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > > > aChangeList;

            for( boost::unordered_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()
{
    osl::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))
        {
            SolarMutexGuard guard2;
            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 )
        {
            osl::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 )
{
    osl::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 )
{
    osl::MutexGuard aGuard(m_aMutex);

    ::boost::unordered_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 )
{
    osl::MutexGuard aGuard(m_aMutex);

    // sanity check
    ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it =
          m_aDropTargets.find( aWindow );
    if( it != m_aDropTargets.end() )
        OSL_FAIL( "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_FAIL( "attempt to register None as drop target" );
}

// ------------------------------------------------------------------------

void SelectionManager::deregisterDropTarget( XLIB_Window aWindow )
{
    osl::ClearableMutexGuard aGuard(m_aMutex);

    m_aDropTargets.erase( aWindow );
    if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() )
    {
        // abort drag
        boost::unordered_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(RTL_CONSTASCII_USTRINGPARAM(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();
}


// ------------------------------------------------------------------------

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
