/*
 * Copyright (c) 1997, 2009, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "awt.h"
#include <shlwapi.h>
#include <shellapi.h>
#include <memory.h>

#include "awt_DataTransferer.h"
#include "awt_Toolkit.h"
#include "java_awt_dnd_DnDConstants.h"
#include "sun_awt_windows_WDropTargetContextPeer.h"
#include "awt_Container.h"
#include "alloc.h"
#include "awt_ole.h"
#include "awt_DnDDT.h"
#include "awt_DnDDS.h"


// forwards

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


IDataObject* AwtDropTarget::sm_pCurrentDnDDataObject = (IDataObject*)NULL;

/**
 * constructor
 */

AwtDropTarget::AwtDropTarget(JNIEnv* env, AwtComponent* component) {

    m_component     = component;
    m_window        = component->GetHWnd();
    m_refs          = 1U;
    m_target        = env->NewGlobalRef(component->GetTarget(env));
    m_registered    = 0;
    m_dataObject    = NULL;
    m_formats       = NULL;
    m_nformats      = 0;
    m_dtcp          = NULL;
    m_cfFormats     = NULL;
    m_mutex         = ::CreateMutex(NULL, FALSE, NULL);
    m_pIDropTargetHelper = NULL;
}

/**
 * destructor
 */

AwtDropTarget::~AwtDropTarget() {
    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_target);
        env->DeleteGlobalRef(m_dtcp);
    }

    ::CloseHandle(m_mutex);

    UnloadCache();
}

/**
 * QueryInterface
 */

HRESULT __stdcall AwtDropTarget::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) {
    if ( IID_IUnknown == riid ||
         IID_IDropTarget == riid )
    {
        *ppvObject = static_cast<IDropTarget*>(this);
        AddRef();
        return S_OK;
    }
    *ppvObject = NULL;
    return E_NOINTERFACE;
}

/**
 * AddRef
 */

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

/**
 * Release
 */

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

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

    return (ULONG)refs;
}

/**
 * DragEnter
 */

HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) {
    TRY;
    if (NULL != m_pIDropTargetHelper) {
        m_pIDropTargetHelper->DragEnter(
            m_window,
            pDataObj,
            (LPPOINT)&pt,
            *pdwEffect);
    }

    AwtInterfaceLocker _lk(this);

    JNIEnv*    env       = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    HRESULT    ret       = S_OK;
    DWORD      retEffect = DROPEFFECT_NONE;
    jobject    dtcp = NULL;

    if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(NULL)) ||
        (IsLocalDnD()  && !IsLocalDataObject(pDataObj)))
    {
        *pdwEffect = retEffect;
        return ret;
    }

    dtcp = call_dTCcreate(env);
    if (dtcp) {
        env->DeleteGlobalRef(m_dtcp);
        m_dtcp = env->NewGlobalRef(dtcp);
        env->DeleteLocalRef(dtcp);
    }

    if (JNU_IsNull(env, m_dtcp) || !JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        return ret;
    }

    LoadCache(pDataObj);

    {
        POINT cp;
        RECT  wr;

        ::GetWindowRect(m_window, &wr);

        cp.x = pt.x - wr.left;
        cp.y = pt.y - wr.top;

        jint actions = call_dTCenter(env, m_dtcp, m_target,
                                     (jint)cp.x, (jint)cp.y,
                                     ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)),
                                     ::convertDROPEFFECTToActions(*pdwEffect),
                                     m_cfFormats, (jlong)this);

        try {
            if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
                env->ExceptionDescribe();
                env->ExceptionClear();
                actions = java_awt_dnd_DnDConstants_ACTION_NONE;
            }
        } catch (std::bad_alloc&) {
            retEffect = ::convertActionsToDROPEFFECT(actions);
            *pdwEffect = retEffect;
            throw;
        }

        retEffect = ::convertActionsToDROPEFFECT(actions);
    }

    *pdwEffect = retEffect;

    return ret;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * DragOver
 */

HRESULT __stdcall AwtDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) {
    TRY;
    if (NULL != m_pIDropTargetHelper) {
        m_pIDropTargetHelper->DragOver(
            (LPPOINT)&pt,
            *pdwEffect
        );
    }

    AwtInterfaceLocker _lk(this);

    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    HRESULT ret = S_OK;
    POINT   cp;
    RECT    wr;
    jint    actions;

    if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(m_dataObject)) ||
        (IsLocalDnD()  && !IsLocalDataObject(m_dataObject)))
    {
        *pdwEffect = DROPEFFECT_NONE;
        return ret;
    }

    ::GetWindowRect(m_window, &wr);

    cp.x = pt.x - wr.left;
    cp.y = pt.y - wr.top;

    actions = call_dTCmotion(env, m_dtcp, m_target,(jint)cp.x, (jint)cp.y,
                             ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)),
                             ::convertDROPEFFECTToActions(*pdwEffect),
                             m_cfFormats, (jlong)this);

    try {
        if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            actions = java_awt_dnd_DnDConstants_ACTION_NONE;
        }
    } catch (std::bad_alloc&) {
        *pdwEffect = ::convertActionsToDROPEFFECT(actions);
        throw;
    }

    *pdwEffect = ::convertActionsToDROPEFFECT(actions);

    return ret;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * DragLeave
 */

HRESULT __stdcall AwtDropTarget::DragLeave() {
    TRY_NO_VERIFY;
    if (NULL != m_pIDropTargetHelper) {
        m_pIDropTargetHelper->DragLeave();
    }

    AwtInterfaceLocker _lk(this);

    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    HRESULT ret = S_OK;

    if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(m_dataObject)) ||
        (IsLocalDnD()  && !IsLocalDataObject(m_dataObject)))
    {
        DragCleanup();
        return ret;
    }

    call_dTCexit(env, m_dtcp, m_target, (jlong)this);

    try {
        if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
    } catch (std::bad_alloc&) {
        DragCleanup();
        throw;
    }

    DragCleanup();

    return ret;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * Drop
 */

HRESULT __stdcall AwtDropTarget::Drop(IDataObject __RPC_FAR *pDataObj, DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) {
    TRY;
    if (NULL != m_pIDropTargetHelper) {
        m_pIDropTargetHelper->Drop(
            pDataObj,
            (LPPOINT)&pt,
            *pdwEffect
        );
    }
    AwtInterfaceLocker _lk(this);

    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    HRESULT ret = S_OK;
    POINT   cp;
    RECT    wr;

    if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(pDataObj)) ||
        (IsLocalDnD()  && !IsLocalDataObject(pDataObj)))
    {
        *pdwEffect = DROPEFFECT_NONE;
        DragCleanup();
        return ret;
    }

    LoadCache(pDataObj);

    ::GetWindowRect(m_window, &wr);

    cp.x = pt.x - wr.left;
    cp.y = pt.y - wr.top;

    m_dropActions = java_awt_dnd_DnDConstants_ACTION_NONE;

    call_dTCdrop(env, m_dtcp, m_target, (jint)cp.x, (jint)cp.y,
                 ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)),
                 ::convertDROPEFFECTToActions(*pdwEffect),
                 m_cfFormats, (jlong)this);

    try {
        if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            ret = E_FAIL;
        }
    } catch (std::bad_alloc&) {
        AwtToolkit::GetInstance().MessageLoop(AwtToolkit::SecondaryIdleFunc,
                                              AwtToolkit::CommonPeekMessageFunc);
        *pdwEffect = ::convertActionsToDROPEFFECT(m_dropActions);
        DragCleanup();
        throw;
    }

    /*
     * Fix for 4623377.
     * Dispatch all messages in the nested message loop running while the drop is
     * processed. This ensures that the modal dialog shown during drop receives
     * all events and so it is able to close. This way the app won't deadlock.
     */
    AwtToolkit::GetInstance().MessageLoop(AwtToolkit::SecondaryIdleFunc,
                                          AwtToolkit::CommonPeekMessageFunc);

    ret = (m_dropSuccess == JNI_TRUE) ? S_OK : E_FAIL;
    *pdwEffect = ::convertActionsToDROPEFFECT(m_dropActions);

    DragCleanup();

    return ret;

    CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY);
}

/**
 * DoDropDone
 */

void AwtDropTarget::DoDropDone(jboolean success, jint action) {
    DropDoneRec ddr = { this, success, action };

    AwtToolkit::GetInstance().InvokeFunction(_DropDone, &ddr);
}

/**
 * _DropDone
 */

void AwtDropTarget::_DropDone(void* param) {
    DropDonePtr ddrp = (DropDonePtr)param;

    (ddrp->dropTarget)->DropDone(ddrp->success, ddrp->action);
}

/**
 * DropDone
 */

void AwtDropTarget::DropDone(jboolean success, jint action) {
    m_dropSuccess = success;
    m_dropActions = action;
    AwtToolkit::GetInstance().QuitMessageLoop(AwtToolkit::EXIT_ENCLOSING_LOOP);
}

/**
 * DoRegisterTarget
 */

void AwtDropTarget::_RegisterTarget(void* param) {
    RegisterTargetPtr rtrp = (RegisterTargetPtr)param;

    rtrp->dropTarget->RegisterTarget(rtrp->show);
}

/**
 * RegisterTarget
 */

void AwtDropTarget::RegisterTarget(WORD show) {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    HRESULT res;

    if (!AwtToolkit::IsMainThread()) {
        RegisterTargetRec rtr = { this, show };

        AwtToolkit::GetInstance().InvokeFunction(_RegisterTarget, &rtr);

        return;
    }

    // if we are'nt yet visible, defer until the parent is!

    if (show) {
        OLE_TRY
        OLE_HRT(CoCreateInstance(
            CLSID_DragDropHelper,
            NULL,
            CLSCTX_ALL,
            IID_IDropTargetHelper,
            (LPVOID*)&m_pIDropTargetHelper
        ))
        OLE_HRT(::RegisterDragDrop(m_window, (IDropTarget*)this))
        OLE_CATCH
        res = OLE_HR;
    } else {
        res = ::RevokeDragDrop(m_window);
        if (NULL != m_pIDropTargetHelper) {
            m_pIDropTargetHelper->Release();
        }
    }

    if (res == S_OK) m_registered = show;
}

/**
 * DoGetData
 */

jobject AwtDropTarget::DoGetData(jlong format) {
    jobject    ret = (jobject)NULL;
    GetDataRec gdr = { this, format, &ret };

    AwtToolkit::GetInstance().WaitForSingleObject(m_mutex);

    AwtToolkit::GetInstance().InvokeFunctionLater(_GetData, &gdr);

    WaitUntilSignalled(FALSE);

    return ret;
}

/**
 * _GetData
 */

void AwtDropTarget::_GetData(void* param) {
    GetDataPtr gdrp = (GetDataPtr)param;

    *(gdrp->ret) = gdrp->dropTarget->GetData(gdrp->format);

    gdrp->dropTarget->Signal();
}


/**
 * GetData
 *
 * Returns the data object being transferred.
 */

HRESULT AwtDropTarget::ExtractNativeData(
    jlong fmt,
    LONG lIndex,
    STGMEDIUM *pmedium)
{
    FORMATETC format = { (unsigned short)fmt };
    HRESULT hr = E_INVALIDARG;

    static const DWORD supportedTymeds[] = {
        TYMED_ISTREAM,
        TYMED_ENHMF,
        TYMED_GDI,
        TYMED_MFPICT,
        TYMED_FILE,
        TYMED_HGLOBAL
    };

    for (int i = 0; i < sizeof(supportedTymeds)/sizeof(supportedTymeds[0]); ++i) {
        // Only TYMED_HGLOBAL is supported for CF_LOCALE.
        if (fmt == CF_LOCALE && supportedTymeds[i] != TYMED_HGLOBAL) {
            continue;
        }

        format.tymed = supportedTymeds[i];
        FORMATETC *cpp = (FORMATETC *)bsearch(
            (const void *)&format,
            (const void *)m_formats,
            (size_t)m_nformats,
            (size_t)sizeof(FORMATETC),
            _compar);

        if (NULL == cpp) {
            continue;
        }

        format = *cpp;
        format.lindex = lIndex;

        hr = m_dataObject->GetData(&format, pmedium);
        if (SUCCEEDED(hr)) {
            return hr;
        }
    }
    return hr;
}

HRESULT CheckRetValue(
    JNIEnv* env,
    jobject ret)
{
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        return E_UNEXPECTED;
    } else if (JNU_IsNull(env, ret)) {
        return E_INVALIDARG;
    }
    return S_OK;
}

jobject AwtDropTarget::ConvertNativeData(JNIEnv* env, jlong fmt, STGMEDIUM *pmedium) /*throw std::bad_alloc */
{
    jobject ret = NULL;
    jbyteArray paletteDataLocal = NULL;
    HRESULT hr = S_OK;
    switch (pmedium->tymed) {
        case TYMED_HGLOBAL: {
            if (fmt == CF_LOCALE) {
                LCID *lcid = (LCID *)::GlobalLock(pmedium->hGlobal);
                if (NULL == lcid) {
                    hr = E_INVALIDARG;
                } else {
                    try{
                        ret = AwtDataTransferer::LCIDToTextEncoding(env, *lcid);
                        hr = CheckRetValue(env, ret);
                    } catch (std::bad_alloc&) {
                        hr = E_OUTOFMEMORY;
                    }
                    ::GlobalUnlock(pmedium->hGlobal);
                }
            } else {
                ::SetLastError(0); // clear error
                // Warning C4244.
                // Cast SIZE_T (__int64 on 64-bit/unsigned int on 32-bit)
                // to jsize (long).
                SIZE_T globalSize = ::GlobalSize(pmedium->hGlobal);
                jsize size = (globalSize <= INT_MAX) ? (jsize)globalSize : INT_MAX;
                if (size == 0 && ::GetLastError() != 0) {
                    hr = E_INVALIDARG;
                } else {
                    jbyteArray bytes = env->NewByteArray(size);
                    if (NULL == bytes) {
                        hr = E_OUTOFMEMORY;
                    } else {
                        LPVOID data = ::GlobalLock(pmedium->hGlobal);
                        if (NULL == data) {
                            hr = E_INVALIDARG;
                        } else {
                            env->SetByteArrayRegion(bytes, 0, size, (jbyte *)data);
                            ret = bytes;
                            //bytes is not null here => no CheckRetValue call
                            ::GlobalUnlock(pmedium->hGlobal);
                        }
                    }
                }
            }
            break;
        }
        case TYMED_FILE: {
            jobject local = JNU_NewStringPlatform(
                env,
                pmedium->lpszFileName);
            jstring fileName = (jstring)env->NewGlobalRef(local);
            env->DeleteLocalRef(local);

            STGMEDIUM *stgm = NULL;
            try {
                //on success stgm would be deallocated by JAVA call freeStgMedium
                stgm = (STGMEDIUM *)safe_Malloc(sizeof(STGMEDIUM));
                memcpy(stgm, pmedium, sizeof(STGMEDIUM));
                // Warning C4311.
                // Cast pointer to jlong (__int64).
                ret = call_dTCgetfs(env, fileName, (jlong)stgm);
                hr = CheckRetValue(env, ret);
            } catch (std::bad_alloc&) {
                hr = E_OUTOFMEMORY;
            }
            if (FAILED(hr)) {
                //free just on error
                env->DeleteGlobalRef(fileName);
                free(stgm);
            }
            break;
        }
        case TYMED_ISTREAM: {
            WDTCPIStreamWrapper* istream = NULL;
            try {
                istream = new WDTCPIStreamWrapper(pmedium);
                // Warning C4311.
                // Cast pointer to jlong (__int64).
                ret = call_dTCgetis(env, (jlong)istream);
                hr = CheckRetValue(env, ret);
            } catch (std::bad_alloc&) {
                hr = E_OUTOFMEMORY;
            }
            if (FAILED(hr) && NULL!=istream) {
                //free just on error
                istream->Close();
            }
            break;
        }
        case TYMED_GDI:
            // Currently support only CF_PALETTE for TYMED_GDI.
            if (CF_PALETTE == fmt) {
                ret = AwtDataTransferer::GetPaletteBytes(
                    pmedium->hBitmap,
                    0,
                    TRUE);
                hr = CheckRetValue(env, ret);
            }
            break;
        case TYMED_MFPICT:
        case TYMED_ENHMF: {
            HENHMETAFILE hEnhMetaFile = NULL;
            if (pmedium->tymed == TYMED_MFPICT ) {
                //let's create ENHMF from MFPICT to simplify treatment
                LPMETAFILEPICT lpMetaFilePict =
                    (LPMETAFILEPICT)::GlobalLock(pmedium->hMetaFilePict);
                if (NULL == lpMetaFilePict) {
                    hr = E_INVALIDARG;
                } else {
                    UINT uSize = ::GetMetaFileBitsEx(lpMetaFilePict->hMF, 0, NULL);
                    if (0 == uSize) {
                        hr = E_INVALIDARG;
                    } else {
                        try{
                            LPBYTE lpMfBits = (LPBYTE)safe_Malloc(uSize);
                            VERIFY(::GetMetaFileBitsEx(
                                lpMetaFilePict->hMF,
                                uSize,
                                lpMfBits) == uSize);
                            hEnhMetaFile = ::SetWinMetaFileBits(
                                uSize,
                                lpMfBits,
                                NULL,
                                lpMetaFilePict);
                            free(lpMfBits);
                        } catch (std::bad_alloc&) {
                            hr = E_OUTOFMEMORY;
                        }
                    }
                    ::GlobalUnlock(pmedium->hMetaFilePict);
                }
            } else {
                hEnhMetaFile = pmedium->hEnhMetaFile;
            }

            if (NULL == hEnhMetaFile) {
                hr = E_INVALIDARG;
            } else {
                try {
                    paletteDataLocal = AwtDataTransferer::GetPaletteBytes(
                        hEnhMetaFile,
                        OBJ_ENHMETAFILE,
                        FALSE);
                    //paletteDataLocal can be NULL here - it is not a error!

                    UINT uEmfSize = ::GetEnhMetaFileBits(hEnhMetaFile, 0, NULL);
                    DASSERT(uEmfSize != 0);

                    LPBYTE lpEmfBits = (LPBYTE)safe_Malloc(uEmfSize);
                    //no chance to throw exception before catch => no more try-blocks
                    //and no leaks on lpEmfBits

                    VERIFY(::GetEnhMetaFileBits(
                        hEnhMetaFile,
                        uEmfSize,
                        lpEmfBits) == uEmfSize);

                    jbyteArray bytes = env->NewByteArray(uEmfSize);
                    if (NULL == bytes) {
                        hr = E_OUTOFMEMORY;
                    } else {
                        env->SetByteArrayRegion(bytes, 0, uEmfSize, (jbyte*)lpEmfBits);
                        ret = bytes;
                        //bytes is not null here => no CheckRetValue call
                    }
                    free(lpEmfBits);
                } catch (std::bad_alloc&) {
                    hr = E_OUTOFMEMORY;
                }
                if (pmedium->tymed == TYMED_MFPICT) {
                    //because we create it manually
                    ::DeleteEnhMetaFile(hEnhMetaFile);
                }
            }
            break;
        }
        case TYMED_ISTORAGE:
        default:
            hr = E_NOTIMPL;
            break;
    }

    if (FAILED(hr)) {
        //clear exception garbage for hr = E_UNEXPECTED
        ret  = NULL;
    } else {
        switch (fmt) {
        case CF_METAFILEPICT:
        case CF_ENHMETAFILE:
            // If we failed to retrieve palette entries from metafile,
            // fall through and try CF_PALETTE format.
        case CF_DIB: {
            if (JNU_IsNull(env, paletteDataLocal)) {
                jobject paletteData = GetData(CF_PALETTE);

                if (JNU_IsNull(env, paletteData)) {
                    paletteDataLocal =
                        AwtDataTransferer::GetPaletteBytes(NULL, 0, TRUE);
                } else {
                    // GetData() returns a global ref.
                    // We want to deal with local ref.
                    paletteDataLocal = (jbyteArray)env->NewLocalRef(paletteData);
                    env->DeleteGlobalRef(paletteData);
                }
            }
            DASSERT(!JNU_IsNull(env, paletteDataLocal) &&
                    !JNU_IsNull(env, ret));

            jobject concat = AwtDataTransferer::ConcatData(env, paletteDataLocal, ret);
            env->DeleteLocalRef(ret);
            ret = concat;
            hr = CheckRetValue(env, ret);
            break;
        }
        }
    }

    if (!JNU_IsNull(env, paletteDataLocal) ) {
        env->DeleteLocalRef(paletteDataLocal);
    }
    jobject global = NULL;
    if (SUCCEEDED(hr)) {
        global = env->NewGlobalRef(ret);
        env->DeleteLocalRef(ret);
    } else if (E_UNEXPECTED == hr) {
        //internal Java non-GPF exception
        env->ExceptionDescribe();
        env->ExceptionClear();
    } else if (E_OUTOFMEMORY == hr) {
        throw std::bad_alloc();
    } //NULL returns for all other cases
    return global;
}

HRESULT AwtDropTarget::SaveIndexToFile(LPCTSTR pFileName, UINT lIndex)
{
    OLE_TRY
    STGMEDIUM stgmedium;
    OLE_HRT( ExtractNativeData(CF_FILECONTENTS, lIndex, &stgmedium) );
    OLE_NEXT_TRY
        IStreamPtr spSrc;
        if (TYMED_HGLOBAL == stgmedium.tymed) {
            OLE_HRT( CreateStreamOnHGlobal(
                stgmedium.hGlobal,
                FALSE,
                &spSrc
            ));
        } else if(TYMED_ISTREAM == stgmedium.tymed) {
            spSrc = stgmedium.pstm;
        }
        if (NULL == spSrc) {
            OLE_HRT(E_INVALIDARG);
        }
        IStreamPtr spDst;
        OLE_HRT(SHCreateStreamOnFile(
            pFileName,
            STGM_WRITE | STGM_CREATE,
            &spDst
        ));
        STATSTG si = {0};
        OLE_HRT( spSrc->Stat(&si, STATFLAG_NONAME ) );
        OLE_HRT( spSrc->CopyTo(spDst, si.cbSize, NULL, NULL) );
    OLE_CATCH
    ::ReleaseStgMedium(&stgmedium);
    OLE_CATCH
    OLE_RETURN_HR;
}


HRESULT GetTempPathWithSlash(JNIEnv *env, _bstr_t &bsTempPath) /*throws _com_error*/
{
    static _bstr_t _bsPath;

    OLE_TRY
    if (0 == _bsPath.length()) {
        BOOL bSafeEmergency = TRUE;
        TCHAR szPath[MAX_PATH*2];
        JLClass systemCls(env, env->FindClass("java/lang/System"));
        if (systemCls) {
            jmethodID idGetProperty = env->GetStaticMethodID(
                    systemCls,
                    "getProperty",
                    "(Ljava/lang/String;)Ljava/lang/String;");
            if (0 != idGetProperty) {
                static TCHAR param[] = _T("java.io.tmpdir");
                JLString tempdir(env, JNU_NewStringPlatform(env, param));
                if (tempdir) {
                    JLString jsTempPath(env, (jstring)env->CallStaticObjectMethod(
                        systemCls,
                        idGetProperty,
                        (jstring)tempdir
                    ));
                    if (jsTempPath) {
                        _bsPath = (LPCWSTR)JavaStringBuffer(env, jsTempPath);
                        OLE_HRT(SHGetFolderPath(
                            NULL,
                            CSIDL_WINDOWS,
                            NULL,
                            0,
                            szPath));
                        _tcscat(szPath, _T("\\"));
                        //Dead environment block leads to fact that windows folder becomes temporary path.
                        //For example while jtreg execution %TEMP%, %TMP% and etc. aren't defined.
                        bSafeEmergency = ( 0 == _tcsicmp(_bsPath, szPath) );
                    }
                }
            }
        }
        if (bSafeEmergency) {
            OLE_HRT(SHGetFolderPath(
                NULL,
                CSIDL_INTERNET_CACHE|CSIDL_FLAG_CREATE,
                NULL,
                0,
                szPath));
            _tcscat(szPath, _T("\\"));
            _bsPath = szPath;
        }
    }
    OLE_CATCH
    bsTempPath = _bsPath;
    OLE_RETURN_HR
}

jobject AwtDropTarget::ConvertMemoryMappedData(JNIEnv* env, jlong fmt, STGMEDIUM *pmedium) /*throw std::bad_alloc */
{
    jobject retObj = NULL;
    OLE_TRY
    if (TYMED_HGLOBAL != pmedium->tymed) {
        OLE_HRT(E_INVALIDARG);
    }
    FILEGROUPDESCRIPTORA *pfgdHead = (FILEGROUPDESCRIPTORA *)::GlobalLock(pmedium->hGlobal);
    if (NULL == pfgdHead) {
        OLE_HRT(E_INVALIDARG);
    }
    OLE_NEXT_TRY
        if (0 == pfgdHead->cItems) {
            OLE_HRT(E_INVALIDARG);
        }
        IStreamPtr spFileNames;
        OLE_HRT( CreateStreamOnHGlobal(
            NULL,
            TRUE,
            &spFileNames
        ));

        _bstr_t sbTempDir;
        OLE_HRT( GetTempPathWithSlash(env, sbTempDir) );
        FILEDESCRIPTORA *pfgdA = pfgdHead->fgd;
        FILEDESCRIPTORW *pfgdW = (FILEDESCRIPTORW *)pfgdA;
        for (UINT i = 0; i < pfgdHead->cItems; ++i) {
            _bstr_t stFullName(sbTempDir);
            if(CF_FILEGROUPDESCRIPTORA == fmt) {
                stFullName += pfgdA->cFileName; //as CHAR
                ++pfgdA;
            } else {
                stFullName += pfgdW->cFileName; //as WCHAR
                ++pfgdW;
            }
            OLE_HRT(SaveIndexToFile(
                stFullName,
                i));
            //write to stream with zero terminator
            OLE_HRT( spFileNames->Write((LPCTSTR)stFullName, (stFullName.length() + 1)*sizeof(TCHAR), NULL) );
        }
        OLE_HRT( spFileNames->Write(_T(""), sizeof(TCHAR), NULL) );
        STATSTG st;
        OLE_HRT( spFileNames->Stat(&st, STATFLAG_NONAME) );

        //empty lists was forbidden: pfgdHead->cItems > 0
        jbyteArray bytes = env->NewByteArray(st.cbSize.LowPart);
        if (NULL == bytes) {
            OLE_HRT(E_OUTOFMEMORY);
        } else {
            HGLOBAL glob;
            OLE_HRT(GetHGlobalFromStream(spFileNames, &glob));
            jbyte *pFileListWithDoubleZeroTerminator = (jbyte *)::GlobalLock(glob);
            env->SetByteArrayRegion(bytes, 0, st.cbSize.LowPart, pFileListWithDoubleZeroTerminator);
            ::GlobalUnlock(pFileListWithDoubleZeroTerminator);
            retObj = bytes;
        }
        //std::bad_alloc could happen in JStringBuffer
        //no leaks due to wrapper
    OLE_CATCH_BAD_ALLOC
    ::GlobalUnlock(pmedium->hGlobal);
    OLE_CATCH
    jobject global = NULL;
    if (SUCCEEDED(OLE_HR)) {
        global = env->NewGlobalRef(retObj);
        env->DeleteLocalRef(retObj);
    } else if (E_OUTOFMEMORY == OLE_HR) {
        throw std::bad_alloc();
    }
    return global;
}

jobject AwtDropTarget::GetData(jlong fmt)
{
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    if (env->EnsureLocalCapacity(1) < 0) {
        return (jobject)NULL;
    }
    jobject ret = NULL;
    OLE_TRY
    STGMEDIUM stgmedium;
    OLE_HRT( ExtractNativeData(fmt, -1, &stgmedium) );
    OLE_NEXT_TRY
        if (CF_FILEGROUPDESCRIPTORA == fmt ||
            CF_FILEGROUPDESCRIPTORW == fmt)
        {
            ret = ConvertMemoryMappedData(env, fmt, &stgmedium);
        } else {
            ret = ConvertNativeData(env, fmt, &stgmedium);
        }
    OLE_CATCH_BAD_ALLOC
    ::ReleaseStgMedium(&stgmedium);
    OLE_CATCH
    if (E_OUTOFMEMORY == OLE_HR) {
        throw std::bad_alloc();
    }
    return ret;
}

/**
 *
 */

int __cdecl AwtDropTarget::_compar(const void* first, const void* second) {
    FORMATETC *fp = (FORMATETC *)first;
    FORMATETC *sp = (FORMATETC *)second;

    if (fp->cfFormat == sp->cfFormat) {
        return fp->tymed - sp->tymed;
    }

    return fp->cfFormat - sp->cfFormat;
}

const unsigned int AwtDropTarget::CACHE_INCR = 16;

void AwtDropTarget::LoadCache(IDataObject* pDataObj) {
    JNIEnv*      env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    unsigned int cnt = 0;
    HRESULT      res;
    IEnumFORMATETC* pEnumFormatEtc = NULL;

    if (m_dataObject != (IDataObject*)NULL) UnloadCache();

    if (!IsLocalDnD()) {
        SetCurrentDnDDataObject(pDataObj);
    }

    (m_dataObject = pDataObj)->AddRef();

    res = m_dataObject->EnumFormatEtc(DATADIR_GET, &pEnumFormatEtc);

    if (res == S_OK) {
    for (;;) {

        FORMATETC tmp;
        ULONG     actual = 1;

            res = pEnumFormatEtc->Next((ULONG)1, &tmp, &actual);
            if (res == S_FALSE)
                break;

        if (!(tmp.cfFormat  >= 1                &&
              tmp.ptd       == NULL             &&
                (tmp.lindex == -1 || CF_FILECONTENTS==tmp.cfFormat) &&
              tmp.dwAspect  == DVASPECT_CONTENT &&
                ( tmp.tymed == TYMED_HGLOBAL ||
               tmp.tymed    == TYMED_FILE       ||
               tmp.tymed    == TYMED_ISTREAM    ||
               tmp.tymed    == TYMED_GDI        ||
               tmp.tymed    == TYMED_MFPICT     ||
               tmp.tymed    == TYMED_ENHMF
              ) // but not ISTORAGE
             )
            )
                continue;

        if (m_dataObject->QueryGetData(&tmp) != S_OK) continue;

        if (m_nformats % CACHE_INCR == 0) {
            m_formats = (FORMATETC *)safe_Realloc(m_formats,
                                                  (CACHE_INCR + m_nformats) *
                                                  sizeof(FORMATETC));
        }

        memcpy(m_formats + m_nformats, &tmp, sizeof(FORMATETC));

        m_nformats++;
    }

        // We are responsible for releasing the enumerator.
        pEnumFormatEtc->Release();
    }

    if (m_nformats > 0) {
        qsort((void*)m_formats, m_nformats, sizeof(FORMATETC),
              AwtDropTarget::_compar);
    }

    if (m_cfFormats != NULL) {
        env->DeleteGlobalRef(m_cfFormats);
    }
    jlongArray l_cfFormats = env->NewLongArray(m_nformats);
    if (l_cfFormats == NULL) {
        throw std::bad_alloc();
    }
    m_cfFormats = (jlongArray)env->NewGlobalRef(l_cfFormats);
    env->DeleteLocalRef(l_cfFormats);

    jboolean isCopy;
    jlong *lcfFormats = env->GetLongArrayElements(m_cfFormats, &isCopy),
        *saveFormats = lcfFormats;

    for (unsigned int i = 0; i < m_nformats; i++, lcfFormats++) {
        *lcfFormats = m_formats[i].cfFormat;
    }

    env->ReleaseLongArrayElements(m_cfFormats, saveFormats, 0);
}

/**
 * UnloadCache
 */

void AwtDropTarget::UnloadCache() {
    if (m_dataObject == (IDataObject*)NULL) return;

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

    free((void*)m_formats);
    m_formats  = (FORMATETC *)NULL;
    m_nformats = 0;

    // fix for 6212440: on application shutdown, this object's
    // destruction might be suppressed due to dangling COM references.
    // This method is called from the destructor.
    // On destruction, VM might be shut down already, so we should make
    // a null check on env.
    if (env) {
        env->DeleteGlobalRef(m_cfFormats);
    }
    m_cfFormats = NULL;

    if (!IsLocalDnD()) {
        DASSERT(IsCurrentDnDDataObject(m_dataObject));
        SetCurrentDnDDataObject(NULL);
    }

    m_dataObject->Release();
    m_dataObject = (IDataObject*)NULL;
}

/**
 * DragCleanup
 */

void AwtDropTarget::DragCleanup(void) {
    UnloadCache();
}

BOOL AwtDropTarget::IsLocalDataObject(IDataObject __RPC_FAR *pDataObject) {
    BOOL local = FALSE;

    if (pDataObject != NULL) {
        FORMATETC format;
        STGMEDIUM stgmedium;

        format.cfFormat = AwtDragSource::PROCESS_ID_FORMAT;
        format.ptd      = NULL;
        format.dwAspect = DVASPECT_CONTENT;
        format.lindex   = -1;
        format.tymed    = TYMED_HGLOBAL;

        if (pDataObject->GetData(&format, &stgmedium) == S_OK) {
            ::SetLastError(0); // clear error
            // Warning C4244.
            SIZE_T size = ::GlobalSize(stgmedium.hGlobal);
            if (size < sizeof(DWORD) || ::GetLastError() != 0) {
                ::SetLastError(0); // clear error
            } else {

                DWORD id = ::CoGetCurrentProcess();

                LPVOID data = ::GlobalLock(stgmedium.hGlobal);
                if (memcmp(data, &id, sizeof(id)) == 0) {
                    local = TRUE;
                }
                ::GlobalUnlock(stgmedium.hGlobal);
            }
            ::ReleaseStgMedium(&stgmedium);
        }
    }

    return local;
}

DECLARE_JAVA_CLASS(dTCClazz, "sun/awt/windows/WDropTargetContextPeer")

jobject
AwtDropTarget::call_dTCcreate(JNIEnv* env) {
    DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCcreate, dTCClazz,
                                      "getWDropTargetContextPeer",
                                      "()Lsun/awt/windows/WDropTargetContextPeer;");
    return env->CallStaticObjectMethod(clazz, dTCcreate);
}

jint
AwtDropTarget::call_dTCenter(JNIEnv* env, jobject self, jobject component, jint x, jint y,
              jint dropAction, jint actions, jlongArray formats,
              jlong nativeCtxt) {
    DECLARE_JINT_JAVA_METHOD(dTCenter, dTCClazz, "handleEnterMessage",
                            "(Ljava/awt/Component;IIII[JJ)I");
    DASSERT(!JNU_IsNull(env, self));
    return env->CallIntMethod(self, dTCenter, component, x, y, dropAction,
                              actions, formats, nativeCtxt);
}

void
AwtDropTarget::call_dTCexit(JNIEnv* env, jobject self, jobject component, jlong nativeCtxt) {
    DECLARE_VOID_JAVA_METHOD(dTCexit, dTCClazz, "handleExitMessage",
                            "(Ljava/awt/Component;J)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dTCexit, component, nativeCtxt);
}

jint
AwtDropTarget::call_dTCmotion(JNIEnv* env, jobject self, jobject component, jint x, jint y,
               jint dropAction, jint actions, jlongArray formats,
               jlong nativeCtxt) {
    DECLARE_JINT_JAVA_METHOD(dTCmotion, dTCClazz, "handleMotionMessage",
                            "(Ljava/awt/Component;IIII[JJ)I");
    DASSERT(!JNU_IsNull(env, self));
    return env->CallIntMethod(self, dTCmotion, component, x, y,
                                 dropAction, actions, formats, nativeCtxt);
}

void
AwtDropTarget::call_dTCdrop(JNIEnv* env, jobject self, jobject component, jint x, jint y,
             jint dropAction, jint actions, jlongArray formats,
             jlong nativeCtxt) {
    DECLARE_VOID_JAVA_METHOD(dTCdrop, dTCClazz, "handleDropMessage",
                            "(Ljava/awt/Component;IIII[JJ)V");
    DASSERT(!JNU_IsNull(env, self));
    env->CallVoidMethod(self, dTCdrop, component, x, y,
                           dropAction, actions, formats, nativeCtxt);
}

jobject
AwtDropTarget::call_dTCgetfs(JNIEnv* env, jstring fileName, jlong stgmedium) {
    DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCgetfs, dTCClazz, "getFileStream",
                                      "(Ljava/lang/String;J)Ljava/io/FileInputStream;");
    return env->CallStaticObjectMethod(clazz, dTCgetfs, fileName, stgmedium);
}

jobject
AwtDropTarget::call_dTCgetis(JNIEnv* env, jlong istream) {
    DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCgetis, dTCClazz, "getIStream",
                                      "(J)Ljava/lang/Object;");
    return env->CallStaticObjectMethod(clazz, dTCgetis, istream);
}

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

jclass WDTCPIStreamWrapper::javaIOExceptionClazz = (jclass)NULL;

/**
 * construct a wrapper
 */

WDTCPIStreamWrapper::WDTCPIStreamWrapper(STGMEDIUM* stgmedium) {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    m_stgmedium = *stgmedium;
    m_istream   = stgmedium->pstm;
    m_istream->AddRef();
    m_mutex     = ::CreateMutex(NULL, FALSE, NULL);

    if (javaIOExceptionClazz == (jclass)NULL) {
        javaIOExceptionClazz = env->FindClass("java/io/IOException");

        if (JNU_IsNull(env, javaIOExceptionClazz)) {
            env->ThrowNew(env->FindClass("java/lang/ClassNotFoundException"),
                          "Cant find java/io/IOException"
            );
        }
    }
}

/**
 * destroy a wrapper
 */

WDTCPIStreamWrapper::~WDTCPIStreamWrapper() {
    ::CloseHandle(m_mutex);
    m_istream->Release();
    ::ReleaseStgMedium(&m_stgmedium);
}

/**
 * return available data
 */

jint WDTCPIStreamWrapper::DoAvailable(WDTCPIStreamWrapper* istream) {
    WDTCPIStreamWrapperRec iswr = { istream, 0 };

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

    AwtToolkit::GetInstance().InvokeFunctionLater( _Available, &iswr);

    istream->WaitUntilSignalled(FALSE);

    return iswr.ret;
}

/**
 * return available data
 */

void WDTCPIStreamWrapper::_Available(void *param) {
    WDTCPIStreamWrapperPtr iswrp = (WDTCPIStreamWrapperPtr)param;

    iswrp->ret = (iswrp->istream)->Available();

    iswrp->istream->Signal();
}

/**
 * return available data
 */

jint WDTCPIStreamWrapper::Available() {
    JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    if (m_istream->Stat(&m_statstg, STATFLAG_NONAME) != S_OK) {
        env->ThrowNew(javaIOExceptionClazz, "IStream::Stat() failed");
        return 0;
    }

    if (m_statstg.cbSize.QuadPart > 0x7ffffffL) {
        env->ThrowNew(javaIOExceptionClazz, "IStream::Stat() cbSize > 0x7ffffff");
        return 0;
    }

    return (jint)m_statstg.cbSize.LowPart;
}

/**
 * read 1 byte
 */

jint WDTCPIStreamWrapper::DoRead(WDTCPIStreamWrapper* istream) {
    WDTCPIStreamWrapperRec iswr = { istream, 0 };

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

    AwtToolkit::GetInstance().InvokeFunctionLater(_Read, &iswr);

    istream->WaitUntilSignalled(FALSE);

    return iswr.ret;
}

/**
 * read 1 byte
 */

void WDTCPIStreamWrapper::_Read(void* param) {
    WDTCPIStreamWrapperPtr iswrp = (WDTCPIStreamWrapperPtr)param;

    iswrp->ret = (iswrp->istream)->Read();

    iswrp->istream->Signal();
}

/**
 * read 1 byte
 */

jint WDTCPIStreamWrapper::Read() {
    JNIEnv* env    = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jint    b      = 0;
    ULONG   actual = 0;
    HRESULT res;

    switch (res = m_istream->Read((void *)&b, (ULONG)1, &actual)) {
        case S_FALSE:
            return (jint)-1;

        case S_OK:
            return (jint)(actual == 0 ? -1 : b);

        default:
            env->ThrowNew(javaIOExceptionClazz, "IStream::Read failed");
    }
    return (jint)-1;
}

/**
 * read Buffer
 */

jint WDTCPIStreamWrapper::DoReadBytes(WDTCPIStreamWrapper* istream, jbyteArray array, jint off, jint len) {
    WDTCPIStreamWrapperReadBytesRec iswrbr = { istream, 0, array, off, len };

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

    AwtToolkit::GetInstance().InvokeFunctionLater(_ReadBytes, &iswrbr);

    istream->WaitUntilSignalled(FALSE);

    return iswrbr.ret;
}

/**
 * read buffer
 */

void WDTCPIStreamWrapper::_ReadBytes(void*  param) {
    WDTCPIStreamWrapperReadBytesPtr iswrbrp =
        (WDTCPIStreamWrapperReadBytesPtr)param;

    iswrbrp->ret = (iswrbrp->istream)->ReadBytes(iswrbrp->array,
                                                 iswrbrp->off,
                                                 iswrbrp->len);
    iswrbrp->istream->Signal();
}

/**
 * read buffer
 */

jint WDTCPIStreamWrapper::ReadBytes(jbyteArray buf, jint off, jint len) {
    JNIEnv*  env     = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jboolean isCopy  = JNI_FALSE;
    ULONG    actual  = 0;
    jbyte*   local   = env->GetByteArrayElements(buf, &isCopy);
    HRESULT  res;

    switch (res = m_istream->Read((void *)(local + off), (ULONG)len, &actual)) {
        case S_FALSE:
        case S_OK: {
            int eof = (actual == 0);

            env->ReleaseByteArrayElements(buf, local, !eof ? 0 : JNI_ABORT);
            return (jint)(!eof ? actual : -1);
        }

        default:
            env->ReleaseByteArrayElements(buf, local, JNI_ABORT);
            env->ThrowNew(javaIOExceptionClazz, "IStream::Read failed");
    }

    return (jint)-1;
}

/**
 * close
 */

void WDTCPIStreamWrapper::DoClose(WDTCPIStreamWrapper* istream) {
    AwtToolkit::GetInstance().InvokeFunctionLater(_Close, istream);
}

/**
 * close
 */

void WDTCPIStreamWrapper::_Close(void* param) {
    ((WDTCPIStreamWrapper*)param)->Close();
}

/**
 * close
 */

void WDTCPIStreamWrapper::Close() {
    delete this;
}

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

extern "C" {

/**
 * awt_dnd_initialize: initial DnD system
 */

void awt_dnd_initialize() {
    ::OleInitialize((LPVOID)NULL);
}

/**
 * awt_dnd_uninitialize: deactivate DnD system
 */

void awt_dnd_uninitialize() {
    ::OleUninitialize();
}

/**
 * convertActionsToDROPEFFECT
 */

DWORD convertActionsToDROPEFFECT(jint actions) {
    DWORD effects = DROPEFFECT_NONE;

    if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) effects |= DROPEFFECT_LINK;
    if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) effects |= DROPEFFECT_MOVE;
    if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) effects |= DROPEFFECT_COPY;
    return effects;
}

/**
 * convertDROPEFFECTToAction
 */

jint convertDROPEFFECTToActions(DWORD effects) {
    jint actions = java_awt_dnd_DnDConstants_ACTION_NONE;

    if (effects & DROPEFFECT_LINK) actions |= java_awt_dnd_DnDConstants_ACTION_LINK;
    if (effects & DROPEFFECT_MOVE) actions |= java_awt_dnd_DnDConstants_ACTION_MOVE;
    if (effects & DROPEFFECT_COPY) actions |= java_awt_dnd_DnDConstants_ACTION_COPY;

    return actions;
}

/**
 * map keyboard modifiers to a DROPEFFECT
 */

DWORD mapModsToDROPEFFECT(DWORD effects, DWORD mods) {
    DWORD ret = DROPEFFECT_NONE;

    /*
     * Fix for 4285634.
     * Calculate the drop action to match Motif DnD behavior.
     * If the user selects an operation (by pressing a modifier key),
     * return the selected operation or DROPEFFECT_NONE if the selected
     * operation is not supported by the drag source.
     * If the user doesn't select an operation search the set of operations
     * supported by the drag source for DROPEFFECT_MOVE, then for
     * DROPEFFECT_COPY, then for DROPEFFECT_LINK and return the first operation
     * found.
     */
    switch (mods & (MK_CONTROL | MK_SHIFT)) {
        case MK_CONTROL:
            ret = DROPEFFECT_COPY;
        break;

        case MK_CONTROL | MK_SHIFT:
            ret = DROPEFFECT_LINK;
        break;

        case MK_SHIFT:
            ret = DROPEFFECT_MOVE;
        break;

        default:
            if (effects & DROPEFFECT_MOVE) {
                ret = DROPEFFECT_MOVE;
            } else if (effects & DROPEFFECT_COPY) {
                ret = DROPEFFECT_COPY;
            } else if (effects & DROPEFFECT_LINK) {
                ret = DROPEFFECT_LINK;
            }
            break;
    }

    return ret & effects;
}

/**
 * downcall to fetch data ... gets scheduled on message thread
 */

JNIEXPORT jobject JNICALL Java_sun_awt_windows_WDropTargetContextPeer_getData(JNIEnv* env, jobject self, jlong dropTarget, jlong format) {
    TRY;

    AwtDropTarget* pDropTarget = (AwtDropTarget*)dropTarget;

    DASSERT(!::IsBadReadPtr(pDropTarget, sizeof(AwtDropTarget)));
    return pDropTarget->DoGetData(format);

    CATCH_BAD_ALLOC_RET(NULL);
}

/**
 * downcall to signal drop done ... gets scheduled on message thread
 */

JNIEXPORT void JNICALL
Java_sun_awt_windows_WDropTargetContextPeer_dropDone(JNIEnv* env, jobject self,
                             jlong dropTarget, jboolean success, jint actions) {
    TRY_NO_HANG;

    AwtDropTarget* pDropTarget = (AwtDropTarget*)dropTarget;

    DASSERT(!::IsBadReadPtr(pDropTarget, sizeof(AwtDropTarget)));
    pDropTarget->DoDropDone(success, actions);

    CATCH_BAD_ALLOC;
}

/**
 * downcall to free up storage medium for FileStream
 */

JNIEXPORT void JNICALL Java_sun_awt_windows_WDropTargetContextPeerFileStream_freeStgMedium(JNIEnv* env, jobject self, jlong stgmedium) {
    TRY;

    ::ReleaseStgMedium((STGMEDIUM*)stgmedium);

    free((void*)stgmedium);

    CATCH_BAD_ALLOC;
}

/**
 *
 */

JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Available(JNIEnv* env, jobject self, jlong istream) {
    TRY;

    return WDTCPIStreamWrapper::DoAvailable((WDTCPIStreamWrapper*)istream);

    CATCH_BAD_ALLOC_RET(0);
}

/**
 *
 */

JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Read(JNIEnv* env, jobject self, jlong istream) {
    TRY;

    return WDTCPIStreamWrapper::DoRead((WDTCPIStreamWrapper*)istream);

    CATCH_BAD_ALLOC_RET(0);
}

/**
 *
 */

JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_ReadBytes(JNIEnv* env, jobject self, jlong istream, jbyteArray buf, jint off, jint len) {
    TRY;

    return WDTCPIStreamWrapper::DoReadBytes((WDTCPIStreamWrapper*)istream, buf, off, len);

    CATCH_BAD_ALLOC_RET(0);
}

/**
 *
 */

JNIEXPORT void JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Close(JNIEnv* env, jobject self, jlong istream) {
    TRY_NO_VERIFY;

    WDTCPIStreamWrapper::DoClose((WDTCPIStreamWrapper*)istream);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */
