view j2se/src/windows/native/sun/windows/awt_DnDDS.cpp @ 2:16f2b6c91171 trunk

[svn] Load openjdk/jdk7/b14 into jdk/trunk.
author xiomara
date Fri, 22 Jun 2007 00:46:43 +0000
parents a4ed3fb96592
children
line wrap: on
line source

/*
 * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

#include "jlong.h"
#include "awt_DataTransferer.h"
#include "awt_DnDDS.h"
#include "awt_DnDDT.h"
#include "awt_Cursor.h"
#include "awt_Toolkit.h"
#include "awt_Component.h"

#include "java_awt_event_InputEvent.h"
#include "java_awt_dnd_DnDConstants.h"
#include "sun_awt_dnd_SunDragSourceContextPeer.h"
#include "sun_awt_windows_WDragSourceContextPeer.h"

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <shlobj.h>

#define GALLOCFLG (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT)
#define JAVA_BUTTON_MASK (java_awt_event_InputEvent_BUTTON1_DOWN_MASK | \
                          java_awt_event_InputEvent_BUTTON2_DOWN_MASK | \
                          java_awt_event_InputEvent_BUTTON3_DOWN_MASK)

extern "C" {
DWORD __cdecl convertActionsToDROPEFFECT(jint actions);
jint  __cdecl convertDROPEFFECTToActions(DWORD effects);
}

typedef struct {
    AwtDragSource* dragSource;
    jobject        cursor;
} StartDragRec;

/**
 * StartDrag
 */

void AwtDragSource::StartDrag(AwtDragSource* self, jobject cursor) {
    StartDragRec* sdrp = new StartDragRec;
    sdrp->dragSource = self;
    sdrp->cursor = cursor;

    AwtToolkit::GetInstance().WaitForSingleObject(self->m_mutex);

    AwtToolkit::GetInstance().InvokeFunctionLater((void (*)(void *))&AwtDragSource::_DoDragDrop, (void *)sdrp);

    self->WaitUntilSignalled(FALSE);
}

/**
 * DoDragDrop - called from message pump thread
 */

void AwtDragSource::_DoDragDrop(void* param) {
    StartDragRec*  sdrp         = (StartDragRec*)param;
    AwtDragSource* dragSource   = sdrp->dragSource;
    DWORD          effects      = DROPEFFECT_NONE;
    JNIEnv*        env          = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jobject        peer         = env->NewLocalRef(dragSource->GetPeer());

    dragSource->SetCursor(sdrp->cursor);
    env->DeleteGlobalRef(sdrp->cursor);
    delete sdrp;

    HRESULT        res;

    // StartDrag has caused dragSource->m_mutex to be held by our thread now

    AwtDropTarget::SetCurrentDnDDataObject(dragSource);

    ::GetCursorPos(&dragSource->m_dragPoint);

    dragSource->Signal();

    res = ::DoDragDrop(dragSource,
		       dragSource,
		       convertActionsToDROPEFFECT(dragSource->m_actions),
		       &effects
	  );

    if (effects == DROPEFFECT_NONE && dragSource->m_dwPerformedDropEffect != DROPEFFECT_NONE) {
        effects = dragSource->m_dwPerformedDropEffect;
    }
    dragSource->m_dwPerformedDropEffect = DROPEFFECT_NONE;

    call_dSCddfinished(env, peer, res == DRAGDROP_S_DROP && effects != DROPEFFECT_NONE,
                       convertDROPEFFECTToActions(effects), 
                       dragSource->m_dragPoint.x, dragSource->m_dragPoint.y);

    env->DeleteLocalRef(peer);

    DASSERT(AwtDropTarget::IsCurrentDnDDataObject(dragSource));
    AwtDropTarget::SetCurrentDnDDataObject(NULL);

    dragSource->Release();
}

/**
 * constructor
 */

AwtDragSource::AwtDragSource(JNIEnv* env, jobject peer, jobject component, 
			     jobject transferable, jobject trigger,
			     jint actions, jlongArray formats,
			     jobject formatMap) {
    m_peer	= env->NewGlobalRef(peer);

    m_refs	= 1;

    m_actions   = actions;

    m_ntypes    = 0;

    m_initmods  = 0;
    m_lastmods  = 0;
  
    m_droptarget   = NULL;
    m_enterpending = TRUE;

    m_cursor     = NULL;

    m_mutex      = ::CreateMutex(NULL, FALSE, NULL);

    m_component     = env->NewGlobalRef(component);
    m_transferable  = env->NewGlobalRef(transferable);
    m_formatMap     = env->NewGlobalRef(formatMap);
  
    m_dragPoint.x = 0;
    m_dragPoint.y = 0;

    m_fNC         = TRUE;
    m_dropPoint.x = 0;
    m_dropPoint.y = 0;

    m_dwPerformedDropEffect = DROPEFFECT_NONE;
    m_bRestoreNodropCustomCursor = FALSE;

    LoadCache(formats);
}

/**
 * destructor
 */

AwtDragSource::~AwtDragSource() {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    // fix for 6212440: on application shutdown, this object's 
    // destruction might be suppressed due to dangling COM references.
    // On destruction, VM might be shut down already, so we should make
    // a null check on env.
    if (env) {
        env->DeleteGlobalRef(m_peer);
        env->DeleteGlobalRef(m_component);
        env->DeleteGlobalRef(m_transferable);
        env->DeleteGlobalRef(m_formatMap);
    }

    ::CloseHandle(m_mutex);

    UnloadCache();
}

/**
 * _compar
 *
 * compare format's then tymed's .... only one tymed bit may be set
 * at any time in a FORMATETC in the cache.
 */

int AwtDragSource::_compar(const void* first, const void* second) {
    FORMATETC *fp = (FORMATETC *)first;
    FORMATETC *sp = (FORMATETC *)second;
    int      r  = fp->cfFormat - sp->cfFormat;

    return r != 0 ? r : fp->tymed - sp->tymed;
}

/**
 * LoadCache
 */

void AwtDragSource::LoadCache(jlongArray formats) {
    JNIEnv*      env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    unsigned int items = 0;
    unsigned int i = 0;
    unsigned int idx = 0;

    if (m_types != (FORMATETC *)NULL) {
        UnloadCache();
    }

    items = env->GetArrayLength(formats);

    if (items == 0) {
        return;
    }

    jboolean isCopy;
    jlong *lFormats = env->GetLongArrayElements(formats, &isCopy),
        *saveFormats = lFormats;

    for (i = 0, m_ntypes = 0; i < items; i++, lFormats++) {
	// Warning C4244.
	// Cast from jlong to CLIPFORMAT (WORD).
	CLIPFORMAT fmt = (CLIPFORMAT)*lFormats;
	switch (fmt) {
	case CF_ENHMETAFILE: 
	    m_ntypes++;    // Only TYMED_ENHMF
	    break;
        case CF_METAFILEPICT: 
            m_ntypes++;    // Only TYMED_MFPICT
            break;
	case CF_HDROP:
	    m_ntypes++;    // Only TYMED_HGLOBAL
	    break;
	default:
	    m_ntypes += 2; // TYMED_HGLOBAL and TYMED_ISTREAM
	    break;
	}
    }

    try {
        m_types = (FORMATETC *)safe_Calloc(sizeof(FORMATETC), m_ntypes);
    } catch (std::bad_alloc&) {
	m_ntypes = 0;
	throw;
    }

    lFormats = saveFormats;

    for (i = 0, idx = 0; i < items; i++, lFormats++) {
	// Warning C4244.
	// Cast from jlong to CLIPFORMAT (WORD).
	CLIPFORMAT fmt = (CLIPFORMAT)*lFormats;

	m_types[idx].cfFormat = fmt;
	m_types[idx].dwAspect = DVASPECT_CONTENT;
	m_types[idx].lindex   = -1;

	switch (fmt) {
	default:
	    m_types[idx].tymed = TYMED_ISTREAM;
	    idx++;

	    // now make a copy, but with a TYMED of HGLOBAL
	    memcpy(m_types + idx, m_types + idx - 1, sizeof(FORMATETC));
	case CF_HDROP:
	    m_types[idx].tymed = TYMED_HGLOBAL;
	    idx++;
	    break;
	case CF_ENHMETAFILE:
	    m_types[idx].tymed = TYMED_ENHMF;
	    idx++;
	    break;
        case CF_METAFILEPICT:
            m_types[idx].tymed = TYMED_MFPICT;
            idx++;
            break;
	}
    }
    DASSERT(idx == m_ntypes);

    env->ReleaseLongArrayElements(formats, saveFormats, 0);

    // sort them in ascending order of format
    qsort((void *)m_types, (size_t)m_ntypes, (size_t)sizeof(FORMATETC),
	  _compar);
}

/**
 * UnloadCache
 */

void AwtDragSource::UnloadCache() {
    if (m_ntypes == 0) {
        return;
    }

    free((void*)m_types);
    m_ntypes = 0;
    m_types  = (FORMATETC *)NULL;
}

/**
 * ChangeCursor
 */
HRESULT AwtDragSource::ChangeCursor()
{
    if (m_cursor != NULL) {
        ::SetCursor(m_cursor->GetHCursor());
        return S_OK; 
    } 
    return DRAGDROP_S_USEDEFAULTCURSORS;
}

/**
 * SetCursor
 */
void AwtDragSource::SetCursor(jobject cursor) {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    if (JNU_IsNull(env, cursor)) {
        m_cursor = NULL;
        return;
    }

    jlong pData = env->GetLongField(cursor, AwtCursor::pDataID);
    // Warning C4312. 
    // Cast jlong (__int64) to pointer.
    m_cursor = (AwtCursor*)pData;

    if (m_cursor == NULL) {
        m_cursor = AwtCursor::CreateSystemCursor(cursor);
    }
}

/**
 * MatchFormatEtc
 */

HRESULT __stdcall 
AwtDragSource::MatchFormatEtc(FORMATETC __RPC_FAR *pFormatEtcIn,
			      FORMATETC *cacheEnt) {
    TRY;

    if ((pFormatEtcIn->tymed & (TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ENHMF |
                                TYMED_MFPICT)) == 0) {
	return DV_E_TYMED;
    } else if (pFormatEtcIn->lindex != -1) {
        return DV_E_LINDEX;
    } else if (pFormatEtcIn->dwAspect != DVASPECT_CONTENT) {
        return DV_E_DVASPECT;
    }

    FORMATETC tmp;
    memcpy(&tmp, pFormatEtcIn, sizeof(FORMATETC));

    static const DWORD supportedTymeds[] = 
        { TYMED_ISTREAM, TYMED_HGLOBAL, TYMED_ENHMF, TYMED_MFPICT };
    static const int nSupportedTymeds = 4;

    for (int i = 0; i < nSupportedTymeds; i++) {
        /*
         * Fix for BugTraq Id 4426805.
         * Match only if the tymed is supported by the requestor.
         */
        if ((pFormatEtcIn->tymed & supportedTymeds[i]) == 0) {
            continue;
        }

	tmp.tymed = supportedTymeds[i];
    	FORMATETC *cp = (FORMATETC *)bsearch((const void *)&tmp,
					     (const void *)m_types,
					     (size_t)      m_ntypes,
					     (size_t)      sizeof(FORMATETC),
					                   _compar
					     );
	if (cp != (FORMATETC *)NULL) {
	    if (cacheEnt != (FORMATETC *)NULL) {
	        memcpy(cacheEnt, cp, sizeof(FORMATETC));
	    }
	    return S_OK;
	}
    }

    return DV_E_FORMATETC;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * QueryInterface
 */

HRESULT __stdcall AwtDragSource::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) {
    TRY;

    if (riid == IID_IUnknown) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IUnknown*)(IDropSource*)this;
	AddRef();
	return S_OK;
    } else if (riid == IID_IDropSource) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IDropSource*)this;
	AddRef();
	return S_OK;
    } else if (riid == IID_IDataObject) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IDataObject*)this;
	AddRef();
	return S_OK;
    } else {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)NULL;
	return E_NOINTERFACE;
    }

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * AddRef
 */

ULONG __stdcall AwtDragSource::AddRef() {
    return (ULONG)++m_refs;
}

/**
 * Release
 */

ULONG __stdcall AwtDragSource::Release() {
    int refs;

    if ((refs = --m_refs) == 0) delete this;

    return (ULONG)refs;
}

/**
 * QueryContinueDrag
 */

HRESULT __stdcall  AwtDragSource::QueryContinueDrag(BOOL fEscapeKeyPressed, DWORD grfKeyState) {
    TRY;

    JNIEnv* env       = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    if (fEscapeKeyPressed)
	return DRAGDROP_S_CANCEL;

    jint modifiers = AwtComponent::GetJavaModifiers();

    POINT dragPoint;

    ::GetCursorPos(&dragPoint);

    if ( (dragPoint.x != m_dragPoint.x || dragPoint.y != m_dragPoint.y) && 
         m_lastmods == modifiers) {//cannot move before cursor change
        call_dSCmouseMoved(env, m_peer,
                           m_actions, modifiers, dragPoint.x, dragPoint.y); 
        m_dragPoint = dragPoint;
    }

    if ((modifiers & JAVA_BUTTON_MASK) == 0) {
        return DRAGDROP_S_DROP;
    } else if (m_initmods == 0) {
        m_initmods = modifiers;
    } else if ((modifiers & JAVA_BUTTON_MASK) != (m_initmods & JAVA_BUTTON_MASK)) {
        return DRAGDROP_S_CANCEL;
    } else if (m_lastmods != modifiers) {
        call_dSCchanged(env, m_peer,
                        m_actions, modifiers, dragPoint.x, dragPoint.y); 
        m_bRestoreNodropCustomCursor = TRUE;
    }

    m_lastmods = modifiers;

    //CR 6480706 - MS Bug on hold
    HCURSOR hNeedCursor; 
    if( 
        m_bRestoreNodropCustomCursor && 
        m_cursor != NULL && 
        (hNeedCursor = m_cursor->GetHCursor()) != ::GetCursor() )
    {        
        ChangeCursor();
        m_bRestoreNodropCustomCursor = FALSE;    
    }
    return S_OK;

   CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * GiveFeedback
 */

HRESULT __stdcall  AwtDragSource::GiveFeedback(DWORD dwEffect) {
    TRY;

    JNIEnv* env       = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jint    modifiers = 0;
    SHORT   mods = 0;

    m_actions = convertDROPEFFECTToActions(dwEffect);

    if (::GetKeyState(VK_LBUTTON) & 0xff00) {
	mods |= MK_LBUTTON;
    } else if (::GetKeyState(VK_MBUTTON) & 0xff00) {
	mods |= MK_MBUTTON;
    } else if (::GetKeyState(VK_RBUTTON) & 0xff00) {
	mods |= MK_RBUTTON;
    }

    if (::GetKeyState(VK_SHIFT)   & 0xff00)
	mods |= MK_SHIFT;
    if (::GetKeyState(VK_CONTROL) & 0xff00)
	mods |= MK_CONTROL;
    if (::GetKeyState(VK_MENU) & 0xff00)
	mods |= MK_ALT;
	
    modifiers = AwtComponent::GetJavaModifiers();

    POINT curs;

    ::GetCursorPos(&curs);

    m_droptarget = ::WindowFromPoint(curs);

    int invalid = (dwEffect == DROPEFFECT_NONE);

    if (invalid) {
	// Don't call dragExit if dragEnter and dragOver haven't been called.
	if (!m_enterpending) {
	    call_dSCexit(env, m_peer, curs.x, curs.y);
	}
        m_droptarget = (HWND)NULL;
        m_enterpending = TRUE;
    } else if (m_droptarget != NULL) {
        (*(m_enterpending ? call_dSCenter : call_dSCmotion))
            (env, m_peer, m_actions, modifiers, curs.x, curs.y);

        m_enterpending = FALSE;
    }
 
    if (m_droptarget != NULL) {
	RECT  rect;
	POINT client = curs;
	VERIFY(::ScreenToClient(m_droptarget, &client));
	VERIFY(::GetClientRect(m_droptarget, &rect));
	if (::PtInRect(&rect, client)) {
	    m_fNC = FALSE;
	    m_dropPoint = client;
	} else {
	    m_fNC = TRUE;
	    m_dropPoint = curs;
	}
    } else {
	m_fNC = TRUE;
	m_dropPoint.x = 0;
	m_dropPoint.y = 0;
    }

    m_bRestoreNodropCustomCursor = (dwEffect == DROPEFFECT_NONE);

    return ChangeCursor();

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}


/**
 * GetData
 */

HRESULT __stdcall AwtDragSource::GetData(FORMATETC __RPC_FAR *pFormatEtc,
					 STGMEDIUM __RPC_FAR *pmedium) {
    TRY;

    HRESULT res = GetProcessId(pFormatEtc, pmedium);
    if (res == S_OK) {
        return res;
    }

    FORMATETC matchedFormatEtc;
    res = MatchFormatEtc(pFormatEtc, &matchedFormatEtc);
    if (res != S_OK) {
        return res;
    }

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    if (env->PushLocalFrame(2) < 0) {
	return E_OUTOFMEMORY;
    }

    jbyteArray bytes = 
        AwtDataTransferer::ConvertData(env, m_component, m_transferable,
                                       (jlong)matchedFormatEtc.cfFormat,
                                       m_formatMap);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
	env->ExceptionClear();
	env->PopLocalFrame(NULL);
	return E_UNEXPECTED;
    }
    if (bytes == NULL) {
	env->PopLocalFrame(NULL);
        return E_UNEXPECTED;
    }

    jint nBytes = env->GetArrayLength(bytes);

    if ((matchedFormatEtc.tymed & TYMED_ISTREAM) != 0) {
        ADSIStreamProxy *istream = new ADSIStreamProxy(this, bytes, nBytes);

	if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
	    env->ExceptionDescribe();
	    env->ExceptionClear();
	    env->PopLocalFrame(NULL);
	    return E_UNEXPECTED;
	}

	pmedium->tymed = TYMED_ISTREAM;
	pmedium->pstm = istream;
	pmedium->pUnkForRelease = (IUnknown *)NULL;

	env->PopLocalFrame(NULL);
	return S_OK;
    } else if ((matchedFormatEtc.tymed & TYMED_HGLOBAL) != 0) {
        HGLOBAL copy = ::GlobalAlloc(GALLOCFLG, nBytes +
                                     ((matchedFormatEtc.cfFormat == CF_HDROP)
				          ? sizeof(DROPFILES)
				          : 0));
	if (copy == NULL) {
	    env->PopLocalFrame(NULL);
	    throw std::bad_alloc();
	}

	char *dataout = (char *)::GlobalLock(copy);

        if (matchedFormatEtc.cfFormat == CF_HDROP) {
	    DROPFILES *dropfiles = (DROPFILES *)dataout;
	    dropfiles->pFiles = sizeof(DROPFILES);
	    dropfiles->pt.x = m_dropPoint.x;
	    dropfiles->pt.y = m_dropPoint.y;
	    dropfiles->fNC = m_fNC;
	    dropfiles->fWide = FALSE; // good guess!
	    dataout += sizeof(DROPFILES);
	}

	env->GetByteArrayRegion(bytes, 0, nBytes, (jbyte *)dataout);
	::GlobalUnlock(copy);

	pmedium->tymed = TYMED_HGLOBAL;
	pmedium->hGlobal = copy;
	pmedium->pUnkForRelease = (IUnknown *)NULL;

	env->PopLocalFrame(NULL);
	return S_OK;
    } else if ((matchedFormatEtc.tymed & TYMED_ENHMF) != 0) {
	LPBYTE lpbEmfBuffer = 
	    (LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
	if (lpbEmfBuffer == NULL) {
	    env->PopLocalFrame(NULL);
	    throw std::bad_alloc();
	}

	HENHMETAFILE hemf = ::SetEnhMetaFileBits(nBytes, lpbEmfBuffer);

	env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbEmfBuffer, JNI_ABORT);

	if (hemf == NULL) {
	    env->PopLocalFrame(NULL);
	    return E_UNEXPECTED;
	}

	pmedium->tymed = TYMED_ENHMF;
	pmedium->hEnhMetaFile = hemf;
	pmedium->pUnkForRelease = (IUnknown *)NULL;

	env->PopLocalFrame(NULL);
	return S_OK;
    } else if ((matchedFormatEtc.tymed & TYMED_MFPICT) != 0) {
        LPBYTE lpbMfpBuffer =
            (LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
        if (lpbMfpBuffer == NULL) {
            env->PopLocalFrame(NULL);
            throw std::bad_alloc();
        }

        HMETAFILE hmf = ::SetMetaFileBitsEx(nBytes - sizeof(METAFILEPICT),
                                         lpbMfpBuffer + sizeof(METAFILEPICT));
        if (hmf == NULL) {
            env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);
            env->PopLocalFrame(NULL);
            return E_UNEXPECTED;
        }

        LPMETAFILEPICT lpMfpOld = (LPMETAFILEPICT)lpbMfpBuffer;

        HMETAFILEPICT hmfp = ::GlobalAlloc(GALLOCFLG, sizeof(METAFILEPICT));
        if (hmfp == NULL) {
            VERIFY(::DeleteMetaFile(hmf));
            env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);
            env->PopLocalFrame(NULL);
            throw std::bad_alloc();
        }

        LPMETAFILEPICT lpMfp = (LPMETAFILEPICT)::GlobalLock(hmfp);
        lpMfp->mm = lpMfpOld->mm;
        lpMfp->xExt = lpMfpOld->xExt;
        lpMfp->yExt = lpMfpOld->yExt;
        lpMfp->hMF = hmf;
        ::GlobalUnlock(hmfp);

        env->ReleasePrimitiveArrayCritical(bytes, (LPVOID)lpbMfpBuffer, JNI_ABORT);

        pmedium->tymed = TYMED_MFPICT;
        pmedium->hMetaFilePict = hmfp;
        pmedium->pUnkForRelease = (IUnknown *)NULL;

        env->PopLocalFrame(NULL);
        return S_OK;
    }
    
    env->PopLocalFrame(NULL);
    return DV_E_TYMED;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * GetDataHere
 */

HRESULT __stdcall AwtDragSource::GetDataHere(FORMATETC __RPC_FAR *pFormatEtc,
					     STGMEDIUM __RPC_FAR *pmedium) {
    TRY;

    if (pmedium->pUnkForRelease != (IUnknown *)NULL) {
        return E_INVALIDARG;
    }

    HRESULT res = GetProcessId(pFormatEtc, pmedium);
    if (res == S_OK) {
        return res;
    }

    FORMATETC matchedFormatEtc;
    res = MatchFormatEtc(pFormatEtc, &matchedFormatEtc);
    if (res != S_OK) {
        return res;
    }

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    if (env->PushLocalFrame(2) < 0) {
	return E_OUTOFMEMORY;
    }

    jbyteArray bytes = 
        AwtDataTransferer::ConvertData(env, m_component, m_transferable,
                                       (jlong)matchedFormatEtc.cfFormat,
                                       m_formatMap);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
	env->ExceptionClear();
	env->PopLocalFrame(NULL);
	return E_UNEXPECTED;
    }
    if (bytes == NULL) {
	env->PopLocalFrame(NULL);
        return E_UNEXPECTED;
    }

    jint nBytes = env->GetArrayLength(bytes);

    // NOTE: TYMED_ENHMF and TYMED_MFPICT are not valid for GetDataHere().
    if ((matchedFormatEtc.tymed & TYMED_ISTREAM) != 0) {
        jboolean isCopy;
        jbyte *bBytes = env->GetByteArrayElements(bytes, &isCopy);

        ULONG act;
        HRESULT res = pmedium->pstm->Write((const void *)bBytes, (ULONG)nBytes,
                                           &act);

        env->ReleaseByteArrayElements(bytes, bBytes, JNI_ABORT);

        env->PopLocalFrame(NULL);
        return S_OK;
    } else if ((matchedFormatEtc.tymed & TYMED_HGLOBAL) != 0) {
        ::SetLastError(0); // clear error
        // Warning C4244. 
        SIZE_T mBytes = ::GlobalSize(pmedium->hGlobal);
        if (::GetLastError() != 0) {
            env->PopLocalFrame(NULL);
            return E_UNEXPECTED;
        }

        if (nBytes + ((matchedFormatEtc.cfFormat == CF_HDROP)
		        ? sizeof(DROPFILES) : 0) > mBytes) {
	    env->PopLocalFrame(NULL);
	    return STG_E_MEDIUMFULL;
	}

	char *dataout = (char *)::GlobalLock(pmedium->hGlobal);

        if (matchedFormatEtc.cfFormat == CF_HDROP) {
	    DROPFILES *dropfiles = (DROPFILES *)dataout;
	    dropfiles->pFiles = sizeof(DROPFILES);
	    dropfiles->pt.x = m_dropPoint.x;
	    dropfiles->pt.y = m_dropPoint.y;
	    dropfiles->fNC = m_fNC;
	    dropfiles->fWide = FALSE; // good guess!
	    dataout += sizeof(DROPFILES);
	}

	env->GetByteArrayRegion(bytes, 0, nBytes, (jbyte *)dataout);
	::GlobalUnlock(pmedium->hGlobal);

	env->PopLocalFrame(NULL);
	return S_OK;
    }
    
    env->PopLocalFrame(NULL);
    return DV_E_TYMED;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * QueryGetData
 */

HRESULT __stdcall  AwtDragSource::QueryGetData(FORMATETC __RPC_FAR *pFormatEtc) {
    TRY;

    return MatchFormatEtc(pFormatEtc, (FORMATETC *)NULL);

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}


/**
 * GetCanonicalFormatEtc
 */

HRESULT __stdcall  AwtDragSource::GetCanonicalFormatEtc(FORMATETC __RPC_FAR *pFormatEtcIn, FORMATETC __RPC_FAR *pFormatEtcOut) {
    TRY;

    HRESULT   res = MatchFormatEtc(pFormatEtcIn, (FORMATETC *)NULL);

    if (res != S_OK) return res;

    *pFormatEtcOut = *pFormatEtcIn;

    pFormatEtcOut->ptd = NULL;

    return DATA_S_SAMEFORMATETC;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * SetData
 */

HRESULT __stdcall AwtDragSource::SetData(FORMATETC __RPC_FAR *pFormatEtc, STGMEDIUM __RPC_FAR *pmedium, BOOL fRelease) {
    static CLIPFORMAT CF_PERFORMEDDROPEFFECT = ::RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);

    if (pFormatEtc->cfFormat == CF_PERFORMEDDROPEFFECT && pmedium->tymed == TYMED_HGLOBAL) {
        m_dwPerformedDropEffect = *(DWORD*)::GlobalLock(pmedium->hGlobal);
        ::GlobalUnlock(pmedium->hGlobal);
        if (fRelease) {
            ::ReleaseStgMedium(pmedium);
        }
        return S_OK;
    }
    return E_UNEXPECTED;
}

/**
 * EnumFormatEtc
 */

HRESULT __stdcall  AwtDragSource::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC *__RPC_FAR *ppenumFormatEtc) {
    TRY;

    *ppenumFormatEtc = new ADSIEnumFormatEtc(this);
    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * DAdvise
 */

HRESULT __stdcall  AwtDragSource::DAdvise(FORMATETC __RPC_FAR *pFormatEtc, DWORD advf, IAdviseSink __RPC_FAR *pAdvSink, DWORD __RPC_FAR *pdwConnection) {
    return E_NOTIMPL;
}

/**
 * DUnadvise
 */

HRESULT __stdcall  AwtDragSource::DUnadvise(DWORD dwConnection) {
    return OLE_E_ADVISENOTSUPPORTED;
}

/**
 * EnumAdvise
 */

HRESULT __stdcall  AwtDragSource::EnumDAdvise(IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise) {
    return OLE_E_ADVISENOTSUPPORTED;
}

const UINT AwtDragSource::PROCESS_ID_FORMAT = 
    ::RegisterClipboardFormat(TEXT("_SUNW_JAVA_AWT_PROCESS_ID"));

HRESULT __stdcall AwtDragSource::GetProcessId(FORMATETC __RPC_FAR *pFormatEtc, STGMEDIUM __RPC_FAR *pmedium) {

    if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0) {
	return DV_E_TYMED;
    } else if (pFormatEtc->lindex != -1) {
        return DV_E_LINDEX;
    } else if (pFormatEtc->dwAspect != DVASPECT_CONTENT) {
        return DV_E_DVASPECT;
    } else if (pFormatEtc->cfFormat != PROCESS_ID_FORMAT) {
	return DV_E_FORMATETC;
    }

    DWORD id = ::CoGetCurrentProcess();

    HGLOBAL copy = ::GlobalAlloc(GALLOCFLG, sizeof(id));

    if (copy == NULL) {
	throw std::bad_alloc();
    }

    char *dataout = (char *)::GlobalLock(copy);

    memcpy(dataout, &id, sizeof(id));
    ::GlobalUnlock(copy);
    
    pmedium->tymed = TYMED_HGLOBAL;
    pmedium->hGlobal = copy;
    pmedium->pUnkForRelease = (IUnknown *)NULL;
    
    return S_OK;
}

DECLARE_JAVA_CLASS(dSCClazz, "sun/awt/windows/WDragSourceContextPeer")

void 
AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions, 
			     jint modifiers, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCenter, dSCClazz, "dragEnter", "(IIII)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCenter, targetActions, modifiers, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

void 
AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions, 
			      jint modifiers, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCmotion, dSCClazz, "dragMotion", "(IIII)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCmotion, targetActions, modifiers, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

void 
AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions, 
			       jint modifiers, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCchanged, dSCClazz, "operationChanged",
                             "(IIII)V"); 
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCchanged, targetActions, modifiers, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

void 
AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCexit, dSCClazz, "dragExit", "(II)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCexit, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

void 
AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success, 
                                  jint operations, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCddfinished, dSCClazz, "dragDropFinished", "(ZIII)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCddfinished, success, operations, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}
  
void 
AwtDragSource::call_dSCmouseMoved(JNIEnv* env, jobject self, jint targetActions, 
				  jint modifiers, jint x, jint y) {
    DECLARE_VOID_JAVA_METHOD(dSCmouseMoved, dSCClazz, "dragMouseMoved",
                             "(IIII)V"); 
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dSCmouseMoved, targetActions, modifiers, x, y);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

DECLARE_JAVA_CLASS(awtIEClazz, "java/awt/event/InputEvent")

/**
 * Constructor
 */

AwtDragSource::ADSIEnumFormatEtc::ADSIEnumFormatEtc(AwtDragSource* parent) {
    m_parent = parent;
    m_idx    = 0;

    m_refs   = 0;

    m_parent->AddRef();

    AddRef();
}

/**
 * Destructor
 */

AwtDragSource::ADSIEnumFormatEtc::~ADSIEnumFormatEtc() {
    m_parent->Release();
}

/**
 * QueryInterface
 */

HRESULT __stdcall  AwtDragSource::ADSIEnumFormatEtc::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) {
    TRY;

    if (riid == IID_IUnknown) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IUnknown*)this;
	AddRef();
	return S_OK;
    } else if (riid == IID_IEnumFORMATETC) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IEnumFORMATETC*)this;
	AddRef();
	return S_OK;
    } else {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)NULL;
	return E_NOINTERFACE;
    }

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * AddRef
 */

ULONG __stdcall  AwtDragSource::ADSIEnumFormatEtc::AddRef(void) {
    return (ULONG)++m_refs;
}

/**
 * Release
 */

ULONG __stdcall  AwtDragSource::ADSIEnumFormatEtc::Release(void) {
    int refs;

    if ((refs = --m_refs) == 0) delete this;

    return (ULONG)refs;
}

/**
 * Next
 */

HRESULT _stdcall AwtDragSource::ADSIEnumFormatEtc::Next(ULONG celt, FORMATETC __RPC_FAR *rgelt, ULONG __RPC_FAR *pceltFetched) {
    TRY;

    unsigned int len = m_parent->getNTypes();
    unsigned int i;

    for (i = 0; i < celt && m_idx < len; i++, m_idx++) {
	FORMATETC fetc = m_parent->getType(m_idx);
	rgelt[i] = fetc;
    }

    if (pceltFetched != NULL) *pceltFetched = i;

    return i == celt ? S_OK : S_FALSE;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Skip
 */

HRESULT __stdcall  AwtDragSource::ADSIEnumFormatEtc::Skip(ULONG celt) {
    TRY;

    unsigned int len = m_parent->getNTypes();
    unsigned int tmp = m_idx + celt;

    if (tmp < len) {
	m_idx = tmp;

        return S_OK;
    } else {
	m_idx = len;

        return S_FALSE;
    }

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Reset
 */

HRESULT __stdcall  AwtDragSource::ADSIEnumFormatEtc::Reset(void) {
    m_idx = 0;

    return S_OK;
}

/**
 * Clone
 */

HRESULT __stdcall  AwtDragSource::ADSIEnumFormatEtc::Clone(IEnumFORMATETC  __RPC_FAR *__RPC_FAR *ppenum) {
    TRY;

    *ppenum = new ADSIEnumFormatEtc(m_parent);
    (*ppenum)->Skip(m_idx);
    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * constructor
 */

AwtDragSource::ADSIStreamProxy::ADSIStreamProxy(AwtDragSource* parent, jbyteArray buffer, jint blen) {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    m_parent = parent;

    m_buffer = (signed char *)safe_Calloc(sizeof(signed char), m_blen = blen);

    env->GetByteArrayRegion(buffer, 0, blen, m_buffer);

    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) return;

    m_off     = 0;

    m_cloneof = (ADSIStreamProxy*)NULL;
    
    m_refs    = 0;

    FILETIME now;

    ::CoFileTimeNow(&now);

    m_statstg.pwcsName		= (LPWSTR)NULL;
    m_statstg.type		= STGTY_STREAM;
    m_statstg.cbSize.HighPart 	= 0;
    m_statstg.cbSize.LowPart  	= m_blen;
    m_statstg.mtime		= now;
    m_statstg.ctime 		= now;
    m_statstg.atime 		= now;
    m_statstg.grfMode 		= STGM_READ;
    m_statstg.grfLocksSupported = FALSE;
    m_statstg.clsid 	 	= CLSID_NULL;
    m_statstg.grfStateBits 	= 0;
    m_statstg.reserved 	 	= 0;

    m_parent->AddRef();

    AddRef();
}

/**
 * constructor (clone)
 */

AwtDragSource::ADSIStreamProxy::ADSIStreamProxy(ADSIStreamProxy* cloneof) {
    m_cloneof = cloneof;

    m_parent  = cloneof->m_parent;

    m_buffer  = cloneof->m_buffer;
    m_blen    = cloneof->m_blen;
    m_off     = cloneof->m_off;

    m_statstg = cloneof->m_statstg;

    m_refs    = 0;

    m_parent->AddRef();
    m_cloneof->AddRef();
}

/**
 * destructor
 */

AwtDragSource::ADSIStreamProxy::~ADSIStreamProxy() {
    if (m_cloneof == (ADSIStreamProxy*)NULL)
	free((void *)m_buffer);
    else {
	m_cloneof->Release();
    }

    m_parent->Release();
}

/**
 * QueryInterface
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) {
    TRY;

    if (riid == IID_IUnknown) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IUnknown*)this;
	AddRef();
	return S_OK;
    } else if (riid == IID_IStream) {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)(IStream*)this;
	AddRef();
	return S_OK;
    } else {
	*ppvObject = (void __RPC_FAR *__RPC_FAR)NULL;
	return E_NOINTERFACE;
    }

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * AddRef
 */

ULONG __stdcall  AwtDragSource::ADSIStreamProxy::AddRef(void) {
    return (ULONG)++m_refs;
}

/**
 * Release
 */

ULONG __stdcall  AwtDragSource::ADSIStreamProxy::Release(void) {
    int refs;

    if ((refs = --m_refs) == 0) delete this;

    return (ULONG)refs;
}

/**
 * Read
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead) {
    TRY;

    unsigned int rem  = m_blen - m_off;
    int          read = cb > rem ? rem : cb;
    
    if (read > 0) memmove(pv, (void *)(m_buffer + m_off), read);

    m_off += read;

    if (pcbRead != (ULONG __RPC_FAR *)NULL) {
	*pcbRead = read;
    }

    FILETIME now; ::CoFileTimeNow(&now); m_statstg.atime = now;

    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Write
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Write(const void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbWritten) {
    TRY;

    if (pcbWritten != (ULONG __RPC_FAR *)NULL) {
	*pcbWritten = 0;
    }

    FILETIME now; ::CoFileTimeNow(&now); m_statstg.atime = now;

    return STG_E_CANTSAVE; // dont support writing

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Seek
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER __RPC_FAR *plibNewPosition) {
    TRY;
  
    if (dlibMove.HighPart != 0) return STG_E_INVALIDPOINTER;

    if (plibNewPosition != (ULARGE_INTEGER __RPC_FAR *)NULL) {
	plibNewPosition->HighPart = 0;
	plibNewPosition->LowPart  = 0;
    }

    switch (dwOrigin) {
	case STREAM_SEEK_SET: {
	    if (dlibMove.HighPart != 0 || dlibMove.LowPart >= m_blen) return STG_E_INVALIDPOINTER;

	    m_off = dlibMove.LowPart;
	}
	break;

	case STREAM_SEEK_CUR:
	case STREAM_SEEK_END: {
	    if (dlibMove.HighPart > 0) return STG_E_INVALIDPOINTER;

	    long newoff = (dwOrigin == STREAM_SEEK_END ? m_blen : m_off) + dlibMove.LowPart;

	    if (newoff < 0 || newoff >= (long)m_blen)
		return STG_E_INVALIDPOINTER;
	    else
		m_off = newoff;
	}
	break;

	default: return STG_E_INVALIDFUNCTION;
    }

    if (plibNewPosition != (ULARGE_INTEGER __RPC_FAR *)NULL)
	plibNewPosition->LowPart = m_off;

    FILETIME now; ::CoFileTimeNow(&now); m_statstg.atime = now;

    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * SetSize
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::SetSize(ULARGE_INTEGER libNewSize) {
    return STG_E_INVALIDFUNCTION;
}

/**
 * CopyTo
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::CopyTo(IStream __RPC_FAR *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER __RPC_FAR *pcbRead, ULARGE_INTEGER __RPC_FAR *pcbWritten) {
    TRY;

    ULONG written = 0;

    pcbWritten->HighPart = (ULONG)0;
    pcbWritten->LowPart  = (ULONG)0;

    pcbRead->HighPart     = (ULONG)0;

    unsigned int rem     = m_blen - m_off;
    int          ovrflow = cb.LowPart >= rem;


    if (cb.HighPart != 0) return STG_E_INVALIDPOINTER;

    ULONG nbytes = pcbRead->LowPart = (ULONG)(ovrflow ? rem : cb.LowPart);

    HRESULT res = pstm->Write((const void *)(m_buffer + m_off), nbytes, &written);

    pcbWritten->LowPart = written;

    FILETIME now; ::CoFileTimeNow(&now); m_statstg.atime = now;

    return res;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Commit
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Commit(DWORD grfCommitFlags) {
    return S_OK;
}

/**
 * Revert
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Revert() {
    return S_OK;
}

/**
 * LockRegion
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
    return STG_E_INVALIDFUNCTION;
}

/**
 * UnlockRegion
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {
    return STG_E_INVALIDFUNCTION;
}

/**
 * Stat
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Stat(STATSTG __RPC_FAR *pstatstg, DWORD grfStatFlag) {
    TRY;

    *pstatstg = m_statstg;

    FILETIME now; ::CoFileTimeNow(&now); m_statstg.atime = now;

    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Clone
 */

HRESULT __stdcall  AwtDragSource::ADSIStreamProxy::Clone(IStream __RPC_FAR *__RPC_FAR *ppstm) {
    TRY;

    *ppstm = new ADSIStreamProxy(this);
    return S_OK;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/*****************************************************************************/

extern "C" {

/**
 * setNativeCursor
 */

JNIEXPORT void JNICALL 
Java_sun_awt_windows_WDragSourceContextPeer_setNativeCursor(JNIEnv* env, 
							    jobject self, 
							    jlong nativeCtxt, 
							    jobject cursor, 
							    jint type) {
    TRY;

    AwtDragSource* ds = (AwtDragSource*)nativeCtxt;
    if (ds != NULL) { 
        ds->SetCursor(cursor);
    }

    CATCH_BAD_ALLOC;
}

/**
 * createDragSource
 */

JNIEXPORT jlong JNICALL
Java_sun_awt_windows_WDragSourceContextPeer_createDragSource(
    JNIEnv* env, jobject self, jobject component, jobject transferable, 
    jobject trigger, jint actions,
    jlongArray formats, jobject formatMap)
{
    TRY;

    if (!AwtDropTarget::IsCurrentDnDDataObject(NULL)) {
        JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException",
                        "Drag and drop is in progress");
	return (jlong)NULL;
    }

    AwtDragSource* ds = new AwtDragSource(env, self, component,
					  transferable, trigger, actions,
					  formats, formatMap);

    DASSERT(AwtDropTarget::IsLocalDataObject(ds));

    return (jlong)ds;

    CATCH_BAD_ALLOC_RET(0);
}

/**
 * doDragDrop
 */

JNIEXPORT void JNICALL Java_sun_awt_windows_WDragSourceContextPeer_doDragDrop(JNIEnv* env, jobject self, jlong nativeCtxt, jobject cursor) {
    TRY;

    cursor = env->NewGlobalRef(cursor);

    AwtDragSource::StartDrag((AwtDragSource*)nativeCtxt, cursor);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */