blob: b0438eae061231dc0cd1c4d39628c097aaeff7d5 [file] [log] [blame]
/*
* Copyright 1997-2008 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 "awt.h"
#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 <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 = TRUE; // 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 = TRUE; // 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" */