blob: 12f47a2e732cd334ac7078a0cbd2638314273bb5 [file] [log] [blame]
/*
* Copyright (c) 1996, 2011, 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_Clipboard.h"
#include "awt_DataTransferer.h"
#include "awt_Toolkit.h"
#include <shlobj.h>
#include <sun_awt_windows_WClipboard.h>
/************************************************************************
* AwtClipboard fields
*/
jmethodID AwtClipboard::lostSelectionOwnershipMID;
jobject AwtClipboard::theCurrentClipboard;
/* This flag is set while we call EmptyClipboard to indicate to
WM_DESTROYCLIPBOARD handler that we are not losing ownership */
BOOL AwtClipboard::isGettingOwnership = FALSE;
volatile jmethodID AwtClipboard::handleContentsChangedMID;
volatile BOOL AwtClipboard::skipInitialWmDrawClipboardMsg = TRUE;
volatile BOOL AwtClipboard::isClipboardViewerRegistered = FALSE;
volatile HWND AwtClipboard::hwndNextViewer = NULL;
#define GALLOCFLG (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT)
/************************************************************************
* AwtClipboard methods
*/
void AwtClipboard::LostOwnership(JNIEnv *env) {
if (theCurrentClipboard != NULL) {
env->CallVoidMethod(theCurrentClipboard, lostSelectionOwnershipMID);
DASSERT(!safe_ExceptionOccurred(env));
}
}
void AwtClipboard::WmChangeCbChain(WPARAM wParam, LPARAM lParam) {
if ((HWND)wParam == hwndNextViewer) {
hwndNextViewer = (HWND)lParam;
} else if (hwndNextViewer != NULL) {
::SendMessage(hwndNextViewer, WM_CHANGECBCHAIN, wParam, lParam);
}
}
void AwtClipboard::WmDrawClipboard(JNIEnv *env, WPARAM wParam, LPARAM lParam) {
if (skipInitialWmDrawClipboardMsg) {
// skipping the first contents change notification as it comes
// immediately after registering the clipboard viewer window
// and it is not caused by an actual contents change.
skipInitialWmDrawClipboardMsg = FALSE;
return;
}
if (theCurrentClipboard != NULL) {
env->CallVoidMethod(theCurrentClipboard, handleContentsChangedMID);
DASSERT(!safe_ExceptionOccurred(env));
}
::SendMessage(hwndNextViewer, WM_DRAWCLIPBOARD, wParam, lParam);
}
void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) {
if (isClipboardViewerRegistered) {
return;
}
if (theCurrentClipboard == NULL) {
theCurrentClipboard = env->NewGlobalRef(jclipboard);
}
jclass cls = env->GetObjectClass(jclipboard);
AwtClipboard::handleContentsChangedMID =
env->GetMethodID(cls, "handleContentsChanged", "()V");
DASSERT(AwtClipboard::handleContentsChangedMID != NULL);
hwndNextViewer = ::SetClipboardViewer(AwtToolkit::GetInstance().GetHWnd());
isClipboardViewerRegistered = TRUE;
}
void AwtClipboard::UnregisterClipboardViewer(JNIEnv *env) {
TRY;
if (isClipboardViewerRegistered) {
::ChangeClipboardChain(AwtToolkit::GetInstance().GetHWnd(), AwtClipboard::hwndNextViewer);
AwtClipboard::hwndNextViewer = NULL;
isClipboardViewerRegistered = FALSE;
skipInitialWmDrawClipboardMsg = TRUE;
}
CATCH_BAD_ALLOC;
}
extern "C" {
void awt_clipboard_uninitialize(JNIEnv *env) {
AwtClipboard::UnregisterClipboardViewer(env);
env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard);
AwtClipboard::theCurrentClipboard = NULL;
}
/************************************************************************
* WClipboard native methods
*/
/*
* Class: sun_awt_windows_WClipboard
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_init(JNIEnv *env, jclass cls)
{
TRY;
AwtClipboard::lostSelectionOwnershipMID =
env->GetMethodID(cls, "lostSelectionOwnershipImpl", "()V");
DASSERT(AwtClipboard::lostSelectionOwnershipMID != NULL);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WClipboard
* Method: openClipboard
* Signature: (Lsun/awt/windows/WClipboard;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self,
jobject newOwner)
{
TRY;
DASSERT(::GetOpenClipboardWindow() != AwtToolkit::GetInstance().GetHWnd());
if (!::OpenClipboard(AwtToolkit::GetInstance().GetHWnd())) {
JNU_ThrowByName(env, "java/lang/IllegalStateException",
"cannot open system clipboard");
return;
}
if (newOwner != NULL) {
AwtClipboard::GetOwnership();
if (AwtClipboard::theCurrentClipboard != NULL) {
env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard);
}
AwtClipboard::theCurrentClipboard = env->NewGlobalRef(newOwner);
}
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WClipboard
* Method: closeClipboard
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_closeClipboard(JNIEnv *env, jobject self)
{
TRY;
if (::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd()) {
VERIFY(::CloseClipboard());
}
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WClipboard
* Method: registerClipboardViewer
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_registerClipboardViewer(JNIEnv *env, jobject self)
{
TRY;
AwtClipboard::RegisterClipboardViewer(env, self);
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WClipboard
* Method: publishClipboardData
* Signature: (J[B)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WClipboard_publishClipboardData(JNIEnv *env,
jobject self,
jlong format,
jbyteArray bytes)
{
TRY;
DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
if (bytes == NULL) {
return;
}
jint nBytes = env->GetArrayLength(bytes);
if (format == CF_ENHMETAFILE) {
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) {
VERIFY(::SetClipboardData((UINT)format, hemf));
}
return;
} else if (format == CF_METAFILEPICT) {
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;
}
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);
VERIFY(::SetClipboardData((UINT)format, hmfp));
return;
}
// We have to prepend the DROPFILES structure here because WDataTransferer
// doesn't.
HGLOBAL hglobal = ::GlobalAlloc(GALLOCFLG, nBytes + ((format == CF_HDROP)
? sizeof(DROPFILES)
: 0));
if (hglobal == NULL) {
throw std::bad_alloc();
}
char *dataout = (char *)::GlobalLock(hglobal);
if (format == CF_HDROP) {
DROPFILES *dropfiles = (DROPFILES *)dataout;
dropfiles->pFiles = sizeof(DROPFILES);
dropfiles->fWide = TRUE; // we publish only Unicode
dataout += sizeof(DROPFILES);
}
env->GetByteArrayRegion(bytes, 0, nBytes, (jbyte *)dataout);
::GlobalUnlock(hglobal);
VERIFY(::SetClipboardData((UINT)format, hglobal));
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WClipboard
* Method: getClipboardFormats
* Signature: ()[J
*/
JNIEXPORT jlongArray JNICALL
Java_sun_awt_windows_WClipboard_getClipboardFormats
(JNIEnv *env, jobject self)
{
TRY;
DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
jsize nFormats = ::CountClipboardFormats();
jlongArray formats = env->NewLongArray(nFormats);
if (formats == NULL) {
throw std::bad_alloc();
}
if (nFormats == 0) {
return formats;
}
jboolean isCopy;
jlong *lFormats = env->GetLongArrayElements(formats, &isCopy),
*saveFormats = lFormats;
UINT num = 0;
for (jsize i = 0; i < nFormats; i++, lFormats++) {
*lFormats = num = ::EnumClipboardFormats(num);
}
env->ReleaseLongArrayElements(formats, saveFormats, 0);
return formats;
CATCH_BAD_ALLOC_RET(NULL);
}
/*
* Class: sun_awt_windows_WClipboard
* Method: getClipboardData
* Signature: (J)[B
*/
JNIEXPORT jbyteArray JNICALL
Java_sun_awt_windows_WClipboard_getClipboardData
(JNIEnv *env, jobject self, jlong format)
{
TRY;
DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
HANDLE handle = ::GetClipboardData((UINT)format);
if (handle == NULL) {
JNU_ThrowIOException(env, "system clipboard data unavailable");
return NULL;
}
jbyteArray bytes = NULL;
jbyteArray paletteData = NULL;
switch (format) {
case CF_ENHMETAFILE:
case CF_METAFILEPICT: {
HENHMETAFILE hemf = NULL;
if (format == CF_METAFILEPICT) {
HMETAFILEPICT hMetaFilePict = (HMETAFILEPICT)handle;
LPMETAFILEPICT lpMetaFilePict =
(LPMETAFILEPICT)::GlobalLock(hMetaFilePict);
UINT uSize = ::GetMetaFileBitsEx(lpMetaFilePict->hMF, 0, NULL);
DASSERT(uSize != 0);
try {
LPBYTE lpMfBits = (LPBYTE)safe_Malloc(uSize);
VERIFY(::GetMetaFileBitsEx(lpMetaFilePict->hMF, uSize,
lpMfBits) == uSize);
hemf = ::SetWinMetaFileBits(uSize, lpMfBits, NULL,
lpMetaFilePict);
free(lpMfBits);
if (hemf == NULL) {
::GlobalUnlock(hMetaFilePict);
JNU_ThrowIOException(env, "failed to get system clipboard data");
return NULL;
}
} catch (...) {
::GlobalUnlock(hMetaFilePict);
throw;
}
::GlobalUnlock(hMetaFilePict);
} else {
hemf = (HENHMETAFILE)handle;
}
UINT uEmfSize = ::GetEnhMetaFileBits(hemf, 0, NULL);
if (uEmfSize == 0) {
JNU_ThrowIOException(env, "cannot retrieve metafile bits");
return NULL;
}
bytes = env->NewByteArray(uEmfSize);
if (bytes == NULL) {
throw std::bad_alloc();
}
LPBYTE lpbEmfBuffer =
(LPBYTE)env->GetPrimitiveArrayCritical(bytes, NULL);
if (lpbEmfBuffer == NULL) {
env->DeleteLocalRef(bytes);
throw std::bad_alloc();
}
VERIFY(::GetEnhMetaFileBits(hemf, uEmfSize, lpbEmfBuffer) == uEmfSize);
env->ReleasePrimitiveArrayCritical(bytes, lpbEmfBuffer, 0);
paletteData =
AwtDataTransferer::GetPaletteBytes(hemf, OBJ_ENHMETAFILE, FALSE);
break;
}
case CF_LOCALE: {
LCID *lcid = (LCID *)::GlobalLock(handle);
if (lcid == NULL) {
JNU_ThrowIOException(env, "invalid LCID");
return NULL;
}
try {
bytes = AwtDataTransferer::LCIDToTextEncoding(env, *lcid);
} catch (...) {
::GlobalUnlock(handle);
throw;
}
::GlobalUnlock(handle);
break;
}
default: {
::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(handle);
jsize size = (globalSize <= INT_MAX) ? (jsize)globalSize : INT_MAX;
if (::GetLastError() != 0) {
JNU_ThrowIOException(env, "invalid global memory block handle");
return NULL;
}
bytes = env->NewByteArray(size);
if (bytes == NULL) {
throw std::bad_alloc();
}
if (size != 0) {
LPVOID data = ::GlobalLock(handle);
env->SetByteArrayRegion(bytes, 0, size, (jbyte *)data);
::GlobalUnlock(handle);
}
break;
}
}
switch (format) {
case CF_ENHMETAFILE:
case CF_METAFILEPICT:
case CF_DIB: {
if (JNU_IsNull(env, paletteData)) {
HPALETTE hPalette = (HPALETTE)::GetClipboardData(CF_PALETTE);
paletteData =
AwtDataTransferer::GetPaletteBytes(hPalette, OBJ_PAL, TRUE);
}
DASSERT(!JNU_IsNull(env, paletteData) &&
!JNU_IsNull(env, bytes));
jbyteArray concat =
(jbyteArray)AwtDataTransferer::ConcatData(env, paletteData, bytes);
if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(bytes);
env->DeleteLocalRef(paletteData);
return NULL;
}
env->DeleteLocalRef(bytes);
env->DeleteLocalRef(paletteData);
bytes = concat;
break;
}
}
return bytes;
CATCH_BAD_ALLOC_RET(NULL);
}
} /* extern "C" */