| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| * @author Pavel Dolgov |
| */ |
| #include <windows.h> |
| |
| #include <atlbase.h> |
| #include <atlcom.h> |
| #include <atlcoll.h> |
| |
| #include <oleidl.h> |
| #include <shlobj.h> |
| #include <MLang.h> |
| |
| #include <stdio.h> |
| |
| #pragma comment(lib, "gdi32.lib") |
| |
| class AtlModule : public CAtlModuleT<AtlModule> { |
| } _AtlModule; |
| |
| #include "WinDataTransfer.h" |
| |
| // Data format names |
| static const char * FORMAT_TEXT = "text/plain"; |
| static const char * FORMAT_FILE_LIST = "application/x-java-file-list"; |
| static const char * FORMAT_URL = "application/x-java-url"; |
| static const char * FORMAT_HTML = "text/html"; |
| static const char * FORMAT_IMAGE = "image/x-java-image"; |
| static const wchar_t * FORMAT_SERIALIZED = |
| L"application/x-java-serialized-object"; |
| |
| // Windows clipboard formats |
| UINT WinDataObject::cfShellUrlA = 0; |
| UINT WinDataObject::cfShellUrlW = 0; |
| UINT WinDataObject::cfHTML = 0; |
| FORMATETC WinDataObject::textFormats[] = { |
| { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, |
| { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } }; |
| FORMATETC WinDataObject::fileListFormat = |
| { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| FORMATETC WinDataObject::urlFormats[] = { |
| { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, |
| { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } }; |
| FORMATETC WinDataObject::htmlFormat = |
| { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| FORMATETC WinDataObject::imageFormats[] = { |
| { CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, |
| { CF_ENHMETAFILE, 0, DVASPECT_CONTENT, -1, TYMED_ENHMF } }; |
| |
| // Cached JNI data |
| static JavaVM * jvm = NULL; |
| static jclass classWinDataTransfer = NULL; |
| static jclass classString = NULL; |
| |
| static jclass classDataSnapshot = NULL; |
| static jmethodID methodDataSnapshotGetNativeFormats = NULL; |
| static jmethodID methodDataSnapshotGetText = NULL; |
| static jmethodID methodDataSnapshotGetFileList = NULL; |
| static jmethodID methodDataSnapshotGetURL = NULL; |
| static jmethodID methodDataSnapshotGetHTML = NULL; |
| static jmethodID methodDataSnapshotGetRawBitmapHeader = NULL; |
| static jmethodID methodDataSnapshotGetRawBitmapBuffer8 = NULL; |
| static jmethodID methodDataSnapshotGetRawBitmapBuffer16 = NULL; |
| static jmethodID methodDataSnapshotGetRawBitmapBuffer32 = NULL; |
| static jmethodID methodDataSnapshotGetSerializedObject = NULL; |
| |
| static jclass classWinDropTarget = NULL; |
| static jmethodID methodWinDropTargetDragEnter = NULL; |
| static jmethodID methodWinDropTargetDragLeave = NULL; |
| static jmethodID methodWinDropTargetDragOver = NULL; |
| static jmethodID methodWinDropTargetDrop = NULL; |
| |
| static jclass classWinDragSource = NULL; |
| static jmethodID methodWinDragSourceContinueDrag = NULL; |
| static jmethodID methodWinDragSourceGiveFeedback = NULL; |
| static jmethodID methodWinDragSourceEndDrag = NULL; |
| |
| static const int RAW_BITMAP_HEADER_LENGTH = 7; |
| |
| inline static JNIEnv * getEnv() { |
| JNIEnv* env; |
| jvm->GetEnv((void**)&env, JNI_VERSION_1_2); |
| return env; |
| } |
| |
| |
| |
| JNIEXPORT void JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_init( |
| JNIEnv * env, jclass clazz) { |
| |
| HRESULT hr = OleInitialize(NULL); |
| |
| if (jvm != NULL) { |
| return; |
| } |
| |
| WinDataObject::registerFormats(); |
| |
| env->GetJavaVM(&jvm); |
| classWinDataTransfer = (jclass)env->NewGlobalRef(clazz); |
| |
| classString = env->FindClass("java/lang/String"); |
| classString = (jclass)env->NewGlobalRef(classString); |
| |
| classDataSnapshot = env->FindClass("org/apache/harmony/awt/datatransfer/DataSnapshot"); |
| classDataSnapshot = (jclass)env->NewGlobalRef(classDataSnapshot); |
| |
| methodDataSnapshotGetNativeFormats = env->GetMethodID( |
| classDataSnapshot, "getNativeFormats", "()[Ljava/lang/String;"); |
| methodDataSnapshotGetText = env->GetMethodID( |
| classDataSnapshot, "getText", "()Ljava/lang/String;"); |
| methodDataSnapshotGetFileList = env->GetMethodID( |
| classDataSnapshot, "getFileList", "()[Ljava/lang/String;"); |
| methodDataSnapshotGetURL = env->GetMethodID( |
| classDataSnapshot, "getURL", "()Ljava/lang/String;"); |
| methodDataSnapshotGetHTML = env->GetMethodID( |
| classDataSnapshot, "getHTML", "()Ljava/lang/String;"); |
| methodDataSnapshotGetRawBitmapHeader = env->GetMethodID( |
| classDataSnapshot, "getRawBitmapHeader", "()[I"); |
| methodDataSnapshotGetRawBitmapBuffer8 = env->GetMethodID( |
| classDataSnapshot, "getRawBitmapBuffer8", "()[B"); |
| methodDataSnapshotGetRawBitmapBuffer16 = env->GetMethodID( |
| classDataSnapshot, "getRawBitmapBuffer16", "()[S"); |
| methodDataSnapshotGetRawBitmapBuffer32 = env->GetMethodID( |
| classDataSnapshot, "getRawBitmapBuffer32", "()[I"); |
| methodDataSnapshotGetSerializedObject = env->GetMethodID( |
| classDataSnapshot, "getSerializedObject", "(Ljava/lang/String;)[B"); |
| |
| classWinDropTarget = env->FindClass("org/apache/harmony/awt/datatransfer/windows/WinDropTarget"); |
| classWinDropTarget = (jclass)env->NewGlobalRef(classWinDropTarget); |
| |
| methodWinDropTargetDragEnter = env->GetMethodID( |
| classWinDropTarget, "dragEnter", "(JIIII)I"); |
| methodWinDropTargetDragLeave = env->GetMethodID( |
| classWinDropTarget, "dragLeave", "()V"); |
| methodWinDropTargetDragOver = env->GetMethodID( |
| classWinDropTarget, "dragOver", "(IIII)I"); |
| methodWinDropTargetDrop = env->GetMethodID( |
| classWinDropTarget, "drop", "(JIIII)I"); |
| |
| classWinDragSource = env->FindClass("org/apache/harmony/awt/datatransfer/windows/WinDragSource");; |
| classWinDragSource = (jclass)env->NewGlobalRef(classWinDragSource); |
| methodWinDragSourceContinueDrag = env->GetMethodID( |
| classWinDragSource, "continueDrag", "()V"); |
| methodWinDragSourceGiveFeedback = env->GetMethodID( |
| classWinDragSource, "giveFeedback", "(IZ)V"); |
| methodWinDragSourceEndDrag = env->GetMethodID( |
| classWinDragSource, "endDrag", "(IZ)V"); |
| } |
| |
| JNIEXPORT jlong JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getOleClipboardDataObject( |
| JNIEnv *, jclass) { |
| IDataObject * dataObject = NULL; |
| HRESULT hr = OleGetClipboard(&dataObject); |
| if (SUCCEEDED(hr)) { |
| return (jlong)dataObject; |
| } |
| return 0; |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_releaseDataObject( |
| JNIEnv *, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return; |
| } |
| IDataObject * dataObject = (IDataObject *)pointer; |
| dataObject->Release(); |
| } |
| |
| JNIEXPORT jstring JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectText( |
| JNIEnv * env, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getText(env, (IDataObject *)pointer); |
| } |
| |
| JNIEXPORT jobjectArray JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectFileList( |
| JNIEnv * env, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getFileList(env, (IDataObject *)pointer); |
| } |
| |
| JNIEXPORT jstring JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectURL( |
| JNIEnv * env, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getURL(env, (IDataObject *)pointer); |
| } |
| |
| JNIEXPORT jstring JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectHTML( |
| JNIEnv * env, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getHTML(env, (IDataObject *)pointer); |
| } |
| |
| JNIEXPORT jobject JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectImage( |
| JNIEnv * env, jclass, jlong pointer, jintArray header) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getBitmap(env, (IDataObject *)pointer, header); |
| } |
| |
| JNIEXPORT jbyteArray JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectSerialized( |
| JNIEnv * env, jclass, jlong pointer, jstring nativeFormat) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::getSerialized(env, (IDataObject *)pointer, nativeFormat); |
| } |
| |
| JNIEXPORT jstring JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getSystemDefaultCharset( |
| JNIEnv * env, jclass) { |
| |
| CHARSETINFO charset; |
| UINT_PTR lcid = GetSystemDefaultLCID(); |
| BOOL ok = TranslateCharsetInfo((DWORD*)lcid, &charset, TCI_SRCLOCALE); |
| if (!ok) { |
| return NULL; |
| } |
| |
| CoInitialize(NULL); |
| CComPtr<IMultiLanguage> multiLanguage; |
| HRESULT hr = multiLanguage.CoCreateInstance(CLSID_CMultiLanguage); |
| if (FAILED(hr)) { |
| return NULL; |
| } |
| |
| MIMECPINFO info; |
| memset(&info, 0, sizeof(MIMECPINFO)); |
| hr = multiLanguage->GetCodePageInfo(charset.ciACP, &info); |
| if (FAILED(hr)) { |
| return NULL; |
| } |
| |
| return env->NewString((const jchar *)info.wszWebCharset, (jsize)wcslen(info.wszWebCharset)); |
| } |
| |
| JNIEXPORT jobjectArray JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_getDataObjectFormats( |
| JNIEnv * env, jclass, jlong pointer) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::enumFormats(env, (IDataObject *)pointer); |
| } |
| |
| JNIEXPORT jboolean JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_isDataObjectFormatAvailable( |
| JNIEnv * env, jclass, jlong pointer, jstring nativeFormat) { |
| if (pointer == 0) { |
| return NULL; |
| } |
| return WinDataObject::queryFormat(env, (IDataObject *)pointer, nativeFormat); |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_setClipboardContents( |
| JNIEnv * env, jclass, jobject dataSnapshot) { |
| if (dataSnapshot == NULL) { |
| OleSetClipboard(NULL); |
| return; |
| } |
| |
| WinDataObject * winDataObject = WinDataObject::CreateInstance(); |
| winDataObject->init(env, dataSnapshot); |
| OleSetClipboard(winDataObject); |
| OleFlushClipboard(); |
| winDataObject->Release(); |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_startDrag( |
| JNIEnv * env, jclass, jobject dataSnapshot, jobject dragSource, jint actions) { |
| if (dataSnapshot == NULL || dragSource == NULL || actions == 0) { |
| return; |
| } |
| |
| WinDataObject * winDataObject = WinDataObject::CreateInstance(); |
| winDataObject->init(env, dataSnapshot); |
| |
| WinDragSource * winDragSource = WinDragSource::CreateInstance(); |
| winDragSource->init(env, dragSource); |
| |
| DWORD dwEffect = 0; |
| HRESULT hr = DoDragDrop(winDataObject, winDragSource, |
| (DWORD)actions, &dwEffect); |
| |
| winDataObject->Release(); |
| winDragSource->Release(); |
| |
| getEnv()->CallVoidMethod(dragSource, |
| methodWinDragSourceEndDrag, (jint)dwEffect, (hr == DRAGDROP_S_DROP)); |
| } |
| |
| JNIEXPORT jlong JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_registerDropTarget( |
| JNIEnv * env, jclass, jlong hWnd, jobject targetObj) { |
| |
| WinDropTarget * target = WinDropTarget::CreateInstance(); |
| target->init((HWND)hWnd, targetObj); |
| |
| HRESULT hr = CoLockObjectExternal(target, TRUE, FALSE); |
| if (SUCCEEDED(hr)) { |
| hr = RegisterDragDrop((HWND)hWnd, target); |
| } |
| if (FAILED(hr)) { |
| target->Release(); |
| return 0; |
| } |
| |
| IDropTarget * pdt = static_cast<IDropTarget *>(target); |
| return (jlong)pdt; |
| } |
| |
| JNIEXPORT void JNICALL |
| Java_org_apache_harmony_awt_nativebridge_windows_WinDataTransfer_revokeDropTarget( |
| JNIEnv * env, jclass, jlong hWnd, jlong target) { |
| |
| RevokeDragDrop((HWND)hWnd); |
| if (target != 0) { |
| IDropTarget * pdt = (IDropTarget *)target; |
| CoLockObjectExternal(pdt, FALSE, TRUE); |
| pdt->Release(); |
| } |
| } |
| |
| |
| |
| void WinDataObject::registerFormats() { |
| cfShellUrlW = RegisterClipboardFormat(CFSTR_INETURLW); |
| cfShellUrlA = RegisterClipboardFormat(CFSTR_INETURLA); |
| cfHTML = RegisterClipboardFormat(TEXT("HTML Format")); |
| htmlFormat.cfFormat = cfHTML; |
| urlFormats[0].cfFormat = cfShellUrlW; |
| urlFormats[1].cfFormat = cfShellUrlA; |
| } |
| |
| jstring WinDataObject::getStringA(JNIEnv * env, const char * cstr) { |
| if (cstr == NULL) { |
| return env->NewString((const jchar *)L"", 0); |
| } |
| jstring jstr = NULL; |
| int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0); |
| if (len > 0) { |
| wchar_t * wstr = new wchar_t[len]; |
| if (len == MultiByteToWideChar(CP_ACP, 0, cstr, -1, wstr, len)) { |
| jstr = env->NewString((const jchar *)wstr, (jsize)wcslen(wstr)); |
| } |
| delete [] wstr; |
| } |
| return jstr; |
| } |
| |
| jstring WinDataObject::getStringA(JNIEnv * env, HGLOBAL hGlobal) { |
| char * cstr = (char *)GlobalLock(hGlobal); |
| jstring jstr = getStringA(env, cstr); |
| GlobalUnlock(hGlobal); |
| return jstr; |
| } |
| |
| jstring WinDataObject::getStringW(JNIEnv * env, HGLOBAL hGlobal) { |
| wchar_t * wstr = (wchar_t *)GlobalLock(hGlobal); |
| if (wstr == NULL) { |
| return env->NewString((const jchar *)L"", 0); |
| } |
| jstring jstr = env->NewString((const jchar *)wstr, (jsize)wcslen(wstr)); |
| GlobalUnlock(hGlobal); |
| return jstr; |
| } |
| |
| jstring WinDataObject::getStringA(JNIEnv * env, IDataObject * dataObject, |
| UINT format) { |
| FORMATETC fmt = { format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| STGMEDIUM stgmed; |
| jstring jstr = NULL; |
| if(SUCCEEDED(dataObject->GetData(&fmt, &stgmed))) |
| { |
| jstr = getStringA(env, stgmed.hGlobal); |
| ReleaseStgMedium(&stgmed); |
| } |
| return jstr; |
| } |
| |
| jstring WinDataObject::getStringW(JNIEnv * env, IDataObject * dataObject, |
| UINT format) { |
| FORMATETC fmt = { format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| STGMEDIUM stgmed; |
| jstring jstr = NULL; |
| if(SUCCEEDED(dataObject->GetData(&fmt, &stgmed))) |
| { |
| jstr = getStringW(env, stgmed.hGlobal); |
| ReleaseStgMedium(&stgmed); |
| } |
| return jstr; |
| } |
| |
| jobjectArray WinDataObject::getFileListA(JNIEnv * env, const char * files) { |
| const char * cstr = files; |
| jsize count = 0; |
| while (*cstr) { |
| cstr += strlen(cstr) + 1; |
| count ++; |
| } |
| jobjectArray result = env->NewObjectArray(count, classString, NULL); |
| cstr = files; |
| for (jsize i=0; *cstr; i++) { |
| jstring jstr = getStringA(env, cstr); |
| cstr += strlen(cstr) + 1; |
| env->SetObjectArrayElement(result, i, jstr); |
| } |
| return result; |
| } |
| |
| jobjectArray WinDataObject::getFileListW(JNIEnv * env, const wchar_t * files) { |
| const wchar_t * wstr = files; |
| jsize count = 0; |
| while (*wstr) { |
| wstr += wcslen(wstr) + 1; |
| count ++; |
| } |
| jobjectArray result = env->NewObjectArray(count, classString, NULL); |
| wstr = files; |
| for (jsize i=0; *wstr; i++) { |
| size_t len = wcslen(wstr); |
| jstring jstr = env->NewString((const jchar *)wstr, (jsize)len); |
| wstr += len + 1; |
| env->SetObjectArrayElement(result, i, jstr); |
| } |
| return result; |
| } |
| |
| jstring WinDataObject::getText(JNIEnv * env, IDataObject * dataObject) { |
| jstring jstr = getStringW(env, dataObject, CF_UNICODETEXT); |
| if (jstr != NULL) { |
| return jstr; |
| } |
| return getStringA(env, dataObject, CF_TEXT); |
| } |
| |
| jobjectArray WinDataObject::getFileList(JNIEnv * env, IDataObject * dataObject) { |
| STGMEDIUM stgmed; |
| if(FAILED(dataObject->GetData(&fileListFormat, &stgmed))) { |
| return NULL; |
| } |
| |
| jobjectArray fileList = NULL; |
| DROPFILES * drop = (DROPFILES *)GlobalLock(stgmed.hGlobal); |
| BYTE * fileNames = (BYTE *)drop + drop->pFiles; |
| if (drop->fWide) { |
| fileList = getFileListW(env, (wchar_t *)fileNames); |
| } else { |
| fileList = getFileListA(env, (char *)fileNames); |
| } |
| GlobalUnlock(stgmed.hGlobal); |
| ReleaseStgMedium(&stgmed); |
| |
| return fileList; |
| } |
| |
| jstring WinDataObject::getURL(JNIEnv * env, IDataObject * dataObject) { |
| jstring jstr = getStringW(env, dataObject, cfShellUrlW); |
| if (jstr != NULL) { |
| return jstr; |
| } |
| return getStringA(env, dataObject, cfShellUrlA); |
| } |
| |
| int WinDataObject::parseCfHtmlTag(const char * cstr, const char * tag) { |
| const char * pos = strstr(cstr, tag); |
| if (pos == NULL) { |
| return -1; |
| } |
| return atoi(pos + strlen(tag)); |
| } |
| |
| jstring WinDataObject::getHTML(JNIEnv * env, IDataObject * dataObject) { |
| STGMEDIUM stgmed; |
| HRESULT hr = dataObject->GetData(&htmlFormat, &stgmed); |
| if(FAILED(hr)) { |
| return NULL; |
| } |
| |
| jstring html = NULL; |
| |
| int length = (int)GlobalSize(stgmed.hGlobal); |
| const char * cstr = (const char *)GlobalLock(stgmed.hGlobal); |
| |
| int start = parseCfHtmlTag(cstr, "StartHTML:"); |
| int end = parseCfHtmlTag(cstr, "EndHTML:"); |
| if (start < 0 || end < 0) { |
| start = parseCfHtmlTag(cstr, "StartFragment:"); |
| end = parseCfHtmlTag(cstr, "EndFragment:"); |
| } |
| |
| if (start > 0 && end > 0 && start < end && end <= length) { |
| char * buffer = new char[end - start + 2]; |
| memcpy(buffer, cstr + start, end - start + 1); |
| buffer[end - start + 1] = 0; |
| html = env->NewStringUTF(buffer); |
| delete [] buffer; |
| } |
| |
| GlobalUnlock(stgmed.hGlobal); |
| ReleaseStgMedium(&stgmed); |
| |
| return html; |
| } |
| |
| jobject WinDataObject::getBitmap(JNIEnv * env, IDataObject * dataObject, jintArray header) { |
| STGMEDIUM stgmed; |
| |
| for (int i = 0; i < sizeof(imageFormats)/sizeof(FORMATETC); i++) { |
| if(SUCCEEDED(dataObject->GetData(&imageFormats[i], &stgmed))) { |
| jobject result = NULL; |
| |
| switch (imageFormats[i].cfFormat) { |
| case CF_DIB: |
| { |
| LPVOID data = (LPVOID)GlobalLock(stgmed.hGlobal); |
| result = getDIB(env, (BITMAPINFO *)data, header); |
| GlobalUnlock(stgmed.hGlobal); |
| break; |
| } |
| case CF_ENHMETAFILE: |
| { |
| result = getEnhMetaFile(env, stgmed.hEnhMetaFile, header); |
| break; |
| } |
| } |
| |
| ReleaseStgMedium(&stgmed); |
| |
| if (result != NULL) { |
| return result; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| jobject WinDataObject::getDIB(JNIEnv * env, BITMAPINFO * info, jintArray header) { |
| BITMAPINFOHEADER & hdr = info->bmiHeader; |
| BYTE * colors = (BYTE *)info + hdr.biSize; |
| BYTE * buffer = colors + hdr.biClrUsed * sizeof(RGBQUAD); |
| jint rMask = 0, gMask = 0, bMask = 0; |
| jint bitCount = hdr.biBitCount; |
| jint stride = hdr.biWidth; |
| jsize height = labs(hdr.biHeight); |
| jobject result = NULL; |
| |
| switch(hdr.biCompression) { |
| case BI_RGB: |
| if (bitCount == 32 || bitCount == 24) { |
| rMask = 0xFF; |
| gMask = 0xFF00; |
| bMask = 0xFF0000; |
| } else if (bitCount == 16) { |
| rMask = 0x7C00; |
| gMask = 0x3E0; |
| bMask = 0x1F; |
| } |
| break; |
| case BI_BITFIELDS: |
| rMask = ((jint*)colors)[0]; |
| gMask = ((jint*)colors)[1]; |
| bMask = ((jint*)colors)[2]; |
| buffer += 3 * sizeof(RGBQUAD); |
| break; |
| default: |
| // TODO: convert to 24 or 32 bit RGB bitmap |
| return NULL; |
| } |
| |
| switch (hdr.biBitCount) { |
| case 32: |
| { |
| jsize length = stride * height; |
| jintArray array = env->NewIntArray(length); |
| jint * pixels = (jint *)buffer; |
| if (hdr.biHeight > 0) { |
| pixels += stride * (hdr.biHeight - 1); |
| for (jsize i = 0; i < hdr.biHeight; i++, pixels -= stride) { |
| env->SetIntArrayRegion(array, i * stride, hdr.biWidth, pixels); |
| } |
| } else { |
| env->SetIntArrayRegion(array, 0, length, pixels); |
| } |
| result = array; |
| break; |
| } |
| case 24: |
| { |
| stride = hdr.biWidth * 3; |
| if (stride % 4 > 0) { |
| stride += 4 - stride % 4; |
| } |
| jsize length = stride * height; |
| jbyteArray array = env->NewByteArray(length); |
| jbyte * pixels = (jbyte *)buffer; |
| if (hdr.biHeight > 0) { |
| pixels += stride * (hdr.biHeight - 1); |
| for (jsize i = 0; i < hdr.biHeight; i++, pixels -= stride) { |
| env->SetByteArrayRegion(array, i * stride, hdr.biWidth * 3, pixels); |
| } |
| } else { |
| env->SetByteArrayRegion(array, 0, length, pixels); |
| } |
| result = array; |
| break; |
| } |
| case 16: |
| case 15: |
| { |
| stride = hdr.biWidth + hdr.biWidth % 2; |
| jsize length = stride * height; |
| jshortArray array = env->NewShortArray(length); |
| jshort * pixels = (jshort *)buffer; |
| if (hdr.biHeight > 0) { |
| pixels += stride * (hdr.biHeight - 1); |
| for (jsize i = 0; i < hdr.biHeight; i++, pixels -= stride) { |
| env->SetShortArrayRegion(array, i * stride, hdr.biWidth, pixels); |
| } |
| } else { |
| env->SetShortArrayRegion(array, 0, length, pixels); |
| } |
| result = array; |
| break; |
| } |
| case 8: |
| { |
| bitCount = 32; |
| const jint opaque = 0xFF000000; |
| rMask = 0xFF0000; |
| gMask = 0xFF00; |
| bMask = 0xFF; |
| if (stride % 4 > 0) { |
| stride += 4 - stride % 4; |
| } |
| |
| jsize length = hdr.biWidth * height; |
| jintArray array = env->NewIntArray(length); |
| BYTE * pixels = buffer; |
| jint * palette = (jint *)colors; |
| jint * line = new jint[hdr.biWidth]; |
| jint step = stride; |
| if (hdr.biHeight > 0) { |
| pixels += stride * (hdr.biHeight - 1); |
| step = -stride; |
| } |
| for (jsize i = 0; i < height; i++, pixels += step) { |
| for (int j = 0; j < hdr.biWidth; j++) { |
| BYTE c = pixels[j]; |
| line[j] = (c < hdr.biClrUsed) ? (palette[c] | opaque) : opaque; |
| } |
| env->SetIntArrayRegion(array, i * hdr.biWidth, hdr.biWidth, line); |
| } |
| delete [] line; |
| stride = hdr.biWidth; |
| result = array; |
| break; |
| } |
| default: |
| // TODO: convert to 24 or 32 bit RGB bitmap |
| return NULL; |
| } |
| jint rawHeader[] = { hdr.biWidth, height, stride, |
| bitCount, rMask, gMask, bMask }; |
| env->SetIntArrayRegion(header, 0, |
| RAW_BITMAP_HEADER_LENGTH, rawHeader); |
| |
| return result; |
| } |
| |
| jobject WinDataObject::getEnhMetaFile(JNIEnv * env, HENHMETAFILE hEMF, jintArray header) { |
| const float screenResulutionUnit = 0.01f; // in millimetres |
| ENHMETAHEADER hemf; |
| memset(&hemf, 0, sizeof(ENHMETAHEADER)); |
| hemf.iType = EMR_HEADER; |
| hemf.nSize = sizeof(ENHMETAHEADER); |
| hemf.dSignature = ENHMETA_SIGNATURE; |
| |
| if (GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &hemf) == 0) { |
| return NULL; |
| } |
| |
| HDC screenDC = GetDC(NULL); |
| |
| int width = hemf.rclFrame.right - hemf.rclFrame.left; |
| int height = hemf.rclFrame.bottom - hemf.rclFrame.top; |
| |
| float hRes = (float)GetDeviceCaps(screenDC, HORZRES) / |
| (float)GetDeviceCaps(screenDC, HORZSIZE); |
| float vRes = (float)GetDeviceCaps(screenDC, VERTRES) / |
| (float)GetDeviceCaps(screenDC, VERTSIZE); |
| width = (int)(width * screenResulutionUnit * hRes); |
| height = (int)(height * screenResulutionUnit * vRes); |
| int screenBits = GetDeviceCaps(screenDC, BITSPIXEL); |
| |
| HDC memoryDC = CreateCompatibleDC(screenDC); |
| HBITMAP hBitmap = CreateCompatibleBitmap(memoryDC, width, height); |
| SelectObject(memoryDC, hBitmap); |
| ReleaseDC(NULL, screenDC); |
| |
| jobject result = NULL; |
| RECT rc = {0, 0, width, height }; |
| HBRUSH hbr = CreateSolidBrush(0xFFFFFFFF); |
| FillRect(memoryDC, &rc, hbr); |
| DeleteObject(hbr); |
| |
| BOOL ok = PlayEnhMetaFile(memoryDC, hEMF, &rc); |
| BITMAPINFO info; |
| memset(&info, 0, sizeof(BITMAPINFO)); |
| info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
| info.bmiHeader.biPlanes = 1; |
| info.bmiHeader.biWidth = width; |
| info.bmiHeader.biHeight = -height; |
| info.bmiHeader.biBitCount = (screenBits > 16) ? 32 : 16; |
| info.bmiHeader.biCompression = BI_RGB; |
| if (ok) { |
| ok = GetDIBits(memoryDC, hBitmap, 0, 0, NULL, &info, DIB_RGB_COLORS); |
| } |
| int stride = 0; |
| if (ok) { |
| switch (info.bmiHeader.biBitCount) { |
| case 32: |
| stride = info.bmiHeader.biWidth * 4; |
| break; |
| case 16: |
| stride = (info.bmiHeader.biWidth + info.bmiHeader.biWidth % 2) * 2; |
| break; |
| default: |
| ok = FALSE; |
| } |
| } |
| BYTE * bitmap = NULL; |
| if (ok) { |
| bitmap = new BYTE[sizeof(BITMAPINFOHEADER) + stride * height]; |
| BYTE * buffer = bitmap + sizeof(BITMAPINFOHEADER); |
| memcpy(bitmap, &info.bmiHeader, sizeof(BITMAPINFOHEADER)); |
| ok = GetDIBits(memoryDC, hBitmap, 0, height, buffer, |
| (BITMAPINFO *)bitmap, DIB_RGB_COLORS); |
| } |
| if (ok) { |
| result = getDIB(env, (BITMAPINFO *)bitmap, header); |
| } |
| if (bitmap != NULL) { |
| delete [] bitmap; |
| } |
| DeleteObject(hBitmap); |
| DeleteDC(memoryDC); |
| |
| return result; |
| } |
| |
| jobjectArray WinDataObject::enumFormats(JNIEnv * env, IDataObject * dataObject) { |
| CComPtr<IEnumFORMATETC> enumFormats; |
| HRESULT hr = dataObject->EnumFormatEtc(DATADIR_GET, &enumFormats); |
| if (FAILED(hr)) { |
| return NULL; |
| } |
| |
| enum { maskText = 1, maskFileList = 2, maskUrl = 4, maskHtml = 8, maskImage = 16 }; |
| DWORD formatsMask = 0; |
| |
| CSimpleArray<jstring> formatList; |
| |
| FORMATETC format; |
| DWORD count = 0; |
| |
| while (enumFormats->Next(1, &format, &count) == S_OK) { |
| if (format.ptd != NULL) { |
| CoTaskMemFree(format.ptd); |
| } |
| if (format.dwAspect != DVASPECT_CONTENT) { |
| continue; |
| } |
| if ((format.tymed & TYMED_ENHMF) != 0 |
| && format.cfFormat == CF_ENHMETAFILE) { |
| jstring jstr = env->NewStringUTF(FORMAT_IMAGE); |
| formatList.Add(jstr); |
| continue; |
| } |
| if ((format.tymed & TYMED_HGLOBAL) == 0) { |
| continue; |
| } |
| if (format.cfFormat == CF_UNICODETEXT || format.cfFormat == CF_TEXT) { |
| jstring jstr = env->NewStringUTF(FORMAT_TEXT); |
| formatList.Add(jstr); |
| continue; |
| } |
| if (format.cfFormat == CF_HDROP) { |
| jstring jstr = env->NewStringUTF(FORMAT_FILE_LIST); |
| formatList.Add(jstr); |
| continue; |
| } |
| if (format.cfFormat == CF_DIB) { |
| jstring jstr = env->NewStringUTF(FORMAT_IMAGE); |
| formatList.Add(jstr); |
| continue; |
| } |
| if (format.cfFormat == cfShellUrlA || format.cfFormat == cfShellUrlW) { |
| jstring jstr = env->NewStringUTF(FORMAT_URL); |
| formatList.Add(jstr); |
| continue; |
| } |
| if (format.cfFormat == cfHTML) { |
| jstring jstr = env->NewStringUTF(FORMAT_HTML); |
| formatList.Add(jstr); |
| continue; |
| } |
| jstring jstr = getSerializedFormatName(env, format.cfFormat); |
| if (jstr != NULL) { |
| formatList.Add(jstr); |
| } |
| } |
| enumFormats.Release(); |
| |
| jobjectArray result = env->NewObjectArray((jsize)formatList.GetSize(), classString, NULL); |
| for (jsize i = 0; i < (jsize)formatList.GetSize(); i++) { |
| env->SetObjectArrayElement(result, i, formatList[i]); |
| } |
| return result; |
| } |
| |
| jbyteArray WinDataObject::getSerialized(JNIEnv * env, IDataObject * dataObject, |
| jstring nativeFormat) { |
| FORMATETC format = { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| jboolean isCopy; |
| const jchar * name = env->GetStringChars(nativeFormat, &isCopy); |
| format.cfFormat = RegisterClipboardFormatW((LPCWSTR)name); |
| env->ReleaseStringChars(nativeFormat, name); |
| |
| STGMEDIUM stgmed; |
| if(FAILED(dataObject->GetData(&format, &stgmed))) { |
| return NULL; |
| } |
| |
| jsize length = (jsize)GlobalSize(stgmed.hGlobal); |
| jbyte * bytes = (jbyte *)GlobalLock(stgmed.hGlobal); |
| jbyteArray array = env->NewByteArray(length); |
| env->SetByteArrayRegion(array, 0, length, bytes); |
| |
| GlobalUnlock(stgmed.hGlobal); |
| ReleaseStgMedium(&stgmed); |
| return array; |
| } |
| |
| jboolean WinDataObject::queryFormat(IDataObject * dataObject, UINT format, |
| DWORD tymed) { |
| FORMATETC fmt = { format, 0, DVASPECT_CONTENT, -1, tymed }; |
| return (SUCCEEDED(dataObject->QueryGetData(&fmt))) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| jboolean WinDataObject::queryFormat(JNIEnv * env, |
| IDataObject * dataObject, |
| jstring nativeFormat) { |
| const char * formatNameA = |
| (const char *)env->GetStringUTFChars(nativeFormat, NULL); |
| BOOL found = FALSE; |
| jboolean result = JNI_FALSE; |
| if (strcmp(formatNameA, FORMAT_TEXT) == 0) { |
| found = TRUE; |
| result = queryFormat(dataObject, CF_TEXT) |
| || queryFormat(dataObject, CF_UNICODETEXT); |
| |
| } else if (strcmp(formatNameA, FORMAT_FILE_LIST) == 0) { |
| found = TRUE; |
| result = queryFormat(dataObject, CF_HDROP); |
| |
| } else if (strcmp(formatNameA, FORMAT_URL) == 0) { |
| found = TRUE; |
| result = queryFormat(dataObject, cfShellUrlW) |
| || queryFormat(dataObject, cfShellUrlA); |
| |
| } else if (strcmp(formatNameA, FORMAT_HTML) == 0) { |
| found = TRUE; |
| result = queryFormat(dataObject, cfHTML); |
| |
| } else if (strcmp(formatNameA, FORMAT_IMAGE) == 0) { |
| found = TRUE; |
| result = queryFormat(dataObject, CF_DIB) |
| || queryFormat(dataObject, CF_ENHMETAFILE, TYMED_ENHMF); |
| } |
| env->ReleaseStringUTFChars(nativeFormat, formatNameA); |
| if (found) { |
| return result; |
| } |
| const jchar * formatNameW = env->GetStringChars(nativeFormat, NULL); |
| if (wcsstr((const wchar_t *)formatNameW, FORMAT_SERIALIZED) != NULL) { |
| UINT format = RegisterClipboardFormatW((LPCWSTR)formatNameW); |
| if (format != 0) { |
| result = queryFormat(dataObject, format); |
| } |
| } |
| env->ReleaseStringChars(nativeFormat, formatNameW); |
| return result; |
| } |
| |
| jstring WinDataObject::getSerializedFormatName(JNIEnv * env, UINT format) { |
| const int formatNameLength = 512; |
| wchar_t formatName[formatNameLength]; |
| if (0 != GetClipboardFormatNameW(format, formatName, formatNameLength) ) { |
| if (wcsstr(formatName, FORMAT_SERIALIZED) != NULL) { |
| return env->NewString((const jchar *)formatName, (jsize)wcslen(formatName)); |
| } |
| } |
| return NULL; |
| } |
| |
| |
| WinDataObject::WinDataObject() { |
| dataSnapshotGlobalRef = NULL; |
| } |
| |
| WinDataObject::~WinDataObject() { |
| if (dataSnapshotGlobalRef != NULL) { |
| getEnv()->DeleteGlobalRef(dataSnapshotGlobalRef); |
| } |
| } |
| |
| int WinDataObject::getFormatsForName(const char * formatName, |
| FORMATETC ** formats) { |
| |
| if (strcmp(formatName, FORMAT_TEXT) == 0) { |
| *formats = textFormats; |
| return sizeof(textFormats)/sizeof(FORMATETC); |
| } |
| if (strcmp(formatName, FORMAT_FILE_LIST) == 0) { |
| *formats = &fileListFormat; |
| return 1; |
| } |
| if (strcmp(formatName, FORMAT_URL) == 0) { |
| *formats = urlFormats; |
| return sizeof(urlFormats)/sizeof(FORMATETC); |
| } |
| if (strcmp(formatName, FORMAT_HTML) == 0) { |
| *formats = &htmlFormat; |
| return 1; |
| } |
| if (strcmp(formatName, FORMAT_IMAGE) == 0) { |
| *formats = imageFormats; |
| return sizeof(imageFormats)/sizeof(FORMATETC); |
| } |
| return 0; |
| } |
| |
| int WinDataObject::getFormatsForName(JNIEnv * env, jstring formatName, |
| FORMATETC ** formats) { |
| const char * formatNameA = |
| (const char *)env->GetStringUTFChars(formatName, NULL); |
| int cnt = getFormatsForName(formatNameA, formats); |
| env->ReleaseStringUTFChars(formatName, formatNameA); |
| return cnt; |
| } |
| |
| UINT WinDataObject::getSerializedFormat(JNIEnv * env, jstring formatName) { |
| const jchar * formatNameW = env->GetStringChars(formatName, NULL); |
| UINT result = 0; |
| if (wcsstr((const wchar_t *)formatNameW, FORMAT_SERIALIZED) != NULL) { |
| result = RegisterClipboardFormatW((LPCWSTR)formatNameW); |
| } |
| env->ReleaseStringChars(formatName, formatNameW); |
| return result; |
| } |
| |
| void WinDataObject::init(JNIEnv * env, jobject dataSnapshot) { |
| dataSnapshotGlobalRef = env->NewGlobalRef(dataSnapshot); |
| |
| jobjectArray nativeFormats = (jobjectArray)env->CallObjectMethod( |
| dataSnapshot, methodDataSnapshotGetNativeFormats); |
| jsize formatCount = env->GetArrayLength(nativeFormats); |
| |
| for (jsize i = 0; i < formatCount; i++) { |
| jstring formatName = |
| (jstring)(env->GetObjectArrayElement(nativeFormats, i)); |
| FORMATETC * list; |
| int listLen = getFormatsForName(env, formatName, &list); |
| for (int j = 0; j < listLen; j++) { |
| formatArray.Add(list[j]); |
| } |
| if (listLen == 0) { |
| UINT serializedFormat = getSerializedFormat(env, formatName); |
| if (serializedFormat != 0) { |
| FORMATETC fmt = { serializedFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| formatArray.Add(fmt); |
| } |
| } |
| } |
| } |
| |
| STDMETHODIMP WinDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium) { |
| if (pFormatEtc == NULL || pMedium == NULL) { |
| return E_INVALIDARG; |
| } |
| if (pFormatEtc->tymed != TYMED_HGLOBAL) { |
| return DV_E_FORMATETC; |
| } |
| |
| int idx = getFormatIndex(pFormatEtc); |
| if(idx < 0) { |
| return DV_E_FORMATETC; |
| } |
| |
| pMedium->tymed = TYMED_HGLOBAL; |
| pMedium->pUnkForRelease = NULL; |
| |
| if (pFormatEtc->cfFormat == CF_UNICODETEXT || |
| pFormatEtc->cfFormat == CF_TEXT) { |
| return getText(pFormatEtc->cfFormat == CF_UNICODETEXT, pMedium); |
| } |
| |
| if (pFormatEtc->cfFormat == CF_HDROP) { |
| return getFileList(pMedium); |
| } |
| |
| if (pFormatEtc->cfFormat == cfHTML) { |
| return getHTML(pMedium); |
| } |
| |
| if (pFormatEtc->cfFormat == cfShellUrlW || |
| pFormatEtc->cfFormat == cfShellUrlA) { |
| return getURL(pFormatEtc->cfFormat == cfShellUrlA, pMedium); |
| } |
| |
| for (int i = 0; i < sizeof(imageFormats)/sizeof(FORMATETC); i++) { |
| if (pFormatEtc->cfFormat == imageFormats[i].cfFormat) { |
| return getImage(pFormatEtc->cfFormat, pMedium); |
| } |
| } |
| |
| return getSerializedObject(pFormatEtc->cfFormat, pMedium); |
| } |
| |
| STDMETHODIMP WinDataObject::GetDataHere(FORMATETC *,STGMEDIUM *) { |
| return DATA_E_FORMATETC; |
| } |
| |
| STDMETHODIMP WinDataObject::QueryGetData(FORMATETC * pFormatEtc) { |
| HRESULT hr = (getFormatIndex(pFormatEtc) >= 0) ? S_OK: DV_E_FORMATETC; |
| return hr; |
| } |
| |
| STDMETHODIMP WinDataObject::GetCanonicalFormatEtc(FORMATETC *, |
| FORMATETC * pFormatEtcOut) { |
| pFormatEtcOut->ptd = NULL; |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP WinDataObject::SetData(FORMATETC * pFormatetc, |
| STGMEDIUM * pmedium, BOOL fRelease) { |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP WinDataObject::EnumFormatEtc(DWORD dwDirection, |
| IEnumFORMATETC ** ppEnumFormatEtc) { |
| if(dwDirection == DATADIR_GET) { |
| return SHCreateStdEnumFmtEtc((UINT)formatArray.GetSize(), |
| formatArray.GetData(), ppEnumFormatEtc); |
| } |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP WinDataObject::DAdvise(FORMATETC *,DWORD,IAdviseSink *,DWORD *) { |
| return OLE_E_ADVISENOTSUPPORTED; |
| } |
| |
| STDMETHODIMP WinDataObject::DUnadvise(DWORD) { |
| return OLE_E_ADVISENOTSUPPORTED; |
| } |
| |
| STDMETHODIMP WinDataObject::EnumDAdvise(IEnumSTATDATA **) { |
| return OLE_E_ADVISENOTSUPPORTED; |
| } |
| |
| int WinDataObject::getFormatIndex(FORMATETC * fmt) { |
| for (int i=0; i<formatArray.GetSize(); i++) { |
| const FORMATETC & f = formatArray[i]; |
| if (f.cfFormat == fmt->cfFormat |
| && f.ptd == fmt->ptd |
| && f.dwAspect == fmt->dwAspect |
| && f.lindex == fmt->lindex |
| && f.tymed == fmt->tymed) { |
| return (int)i; |
| } |
| } |
| return -1; |
| } |
| |
| HGLOBAL WinDataObject::getTextGlobal(JNIEnv * env, jstring text, BOOL unicode) { |
| jsize len = env->GetStringLength(text); |
| |
| if (unicode) { |
| wchar_t * wstr = (wchar_t *)GlobalAlloc(GMEM_FIXED, |
| (len + 1) * sizeof(wchar_t)); |
| if (wstr == NULL) { |
| return NULL; |
| } |
| env->GetStringRegion(text, 0, len, (jchar *)wstr); |
| wstr[len] = 0; |
| return (HGLOBAL)wstr; |
| } |
| |
| // convert to ASCII text |
| wchar_t * wstr = new wchar_t[len + 1]; |
| if (wstr == NULL) { |
| return NULL; |
| } |
| env->GetStringRegion(text, 0, len, (jchar *)wstr); |
| wstr[len] = 0; |
| |
| int cLen = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); |
| char * cstr = (char *)GlobalAlloc(GMEM_FIXED, cLen); |
| if (cstr == NULL) { |
| delete [] wstr; |
| return NULL; |
| } |
| WideCharToMultiByte(CP_ACP, 0, wstr, -1, cstr, cLen, NULL, NULL); |
| delete [] wstr; |
| |
| return (HGLOBAL)cstr; |
| } |
| |
| HRESULT WinDataObject::getText(BOOL unicode, STGMEDIUM * pMedium) { |
| JNIEnv * env = getEnv(); |
| jstring text = (jstring)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetText); |
| if (text == NULL) { |
| return DV_E_FORMATETC; |
| } |
| pMedium->hGlobal = getTextGlobal(env, text, unicode); |
| return (pMedium->hGlobal != NULL) ? S_OK : E_OUTOFMEMORY; |
| } |
| |
| HRESULT WinDataObject::getHTML(STGMEDIUM * pMedium) { |
| JNIEnv * env = getEnv(); |
| jstring text = (jstring)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetHTML); |
| if (text == NULL) { |
| return DV_E_FORMATETC; |
| } |
| |
| const char startFragment[] = "<!--StartFragment-->"; |
| const char endFragment[] = "<!--EndFragment-->"; |
| const char headerFormat[] = |
| "Version:1.0\r\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n"; |
| char header[sizeof(headerFormat) + 40]; |
| wsprintfA(header, headerFormat, 0, 0, 0, 0); |
| size_t headerLen = strlen(header); |
| |
| jboolean isCopy; |
| const char * utfStr = env->GetStringUTFChars(text, &isCopy); |
| |
| const char * pStart = strstr(utfStr, startFragment); |
| const char * pEnd = strstr(utfStr, endFragment); |
| size_t iLen = strlen(utfStr); |
| size_t iStart = (pStart != NULL) ? |
| (pStart - utfStr) + strlen(startFragment) : 0; |
| size_t iEnd = (pEnd != NULL) ? (pEnd - utfStr) : iLen; |
| |
| char * cstr = (char *)GlobalAlloc(GMEM_FIXED, headerLen + iLen + 1); |
| wsprintfA(cstr, headerFormat, headerLen, headerLen + iLen + 1, |
| headerLen + iStart, headerLen + iEnd); |
| memcpy(cstr + headerLen, utfStr, iLen); |
| cstr[headerLen + iLen] = 0; |
| |
| env->ReleaseStringUTFChars(text, utfStr); |
| |
| pMedium->hGlobal = cstr; |
| return S_OK; |
| } |
| |
| HRESULT WinDataObject::getFileList(STGMEDIUM * pMedium) { |
| JNIEnv * env = getEnv(); |
| jobjectArray fileList = |
| (jobjectArray)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetFileList); |
| if (fileList == NULL) { |
| return DV_E_FORMATETC; |
| } |
| |
| jsize listLength = env->GetArrayLength(fileList); |
| jsize charCount = 0, i; |
| for (i=0; i<listLength; i++) { |
| jstring jstr = |
| (jstring)env->GetObjectArrayElement(fileList, i); |
| jsize len = (jstr != NULL) ? env->GetStringLength(jstr) : 0; |
| if (len != 0) { |
| charCount += len + 1; |
| } |
| } |
| if (charCount == 0) { |
| return DV_E_FORMATETC; |
| } |
| |
| BYTE * buffer = (BYTE *)GlobalAlloc(GMEM_FIXED, |
| sizeof(DROPFILES) + (charCount + 1) * sizeof(wchar_t)); |
| DROPFILES * dropFiles = (DROPFILES *)buffer; |
| memset(dropFiles, 0, sizeof(DROPFILES)); |
| dropFiles->fWide = TRUE; |
| dropFiles->pFiles = sizeof(DROPFILES); |
| wchar_t * wstr = (wchar_t *)(buffer + sizeof(DROPFILES)); |
| |
| charCount = 0; |
| for (i=0; i<listLength; i++) { |
| jstring jstr = |
| (jstring)env->GetObjectArrayElement(fileList, i); |
| jsize len = (jstr != NULL) ? env->GetStringLength(jstr) : 0; |
| if (len != 0) { |
| env->GetStringRegion(jstr, 0, len, (jchar *)(wstr + charCount)); |
| charCount += len + 1; |
| wstr[charCount - 1] = 0; |
| } |
| } |
| wstr[charCount] = 0; |
| |
| pMedium->hGlobal = buffer; |
| return S_OK; |
| } |
| |
| HRESULT WinDataObject::getURL(BOOL unicode, STGMEDIUM * pMedium) { |
| JNIEnv * env = getEnv(); |
| jstring url = (jstring)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetURL); |
| if (url == NULL) { |
| return DV_E_FORMATETC; |
| } |
| pMedium->hGlobal = getTextGlobal(env, url, unicode); |
| return (pMedium->hGlobal != NULL) ? S_OK : E_OUTOFMEMORY; |
| } |
| |
| HRESULT WinDataObject::getImage(UINT format, STGMEDIUM * pMedium) { |
| if (format != CF_DIB) { |
| return DV_E_FORMATETC; |
| } |
| JNIEnv * env = getEnv(); |
| jintArray bitmapBuffer = |
| (jintArray)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetRawBitmapBuffer32); |
| jintArray bitmapHeader = |
| (jintArray)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetRawBitmapHeader); |
| if (bitmapBuffer == NULL || bitmapHeader == NULL) { |
| return DV_E_FORMATETC; |
| } |
| |
| jsize headerLength = env->GetArrayLength(bitmapHeader); |
| if (headerLength != RAW_BITMAP_HEADER_LENGTH) { |
| return DV_E_FORMATETC; |
| } |
| |
| jint rawHeader[RAW_BITMAP_HEADER_LENGTH]; |
| env->GetIntArrayRegion(bitmapHeader, 0, |
| RAW_BITMAP_HEADER_LENGTH, rawHeader); |
| |
| int width = rawHeader[0]; |
| int height = rawHeader[1]; |
| // Accept only 32-bit RGB bitmap |
| if (rawHeader[2] != width || rawHeader[3] != 32 |
| || rawHeader[4] != 0xFF0000 |
| || rawHeader[5] != 0xFF00 |
| || rawHeader[6] != 0xFF) { |
| return DV_E_FORMATETC; |
| } |
| |
| pMedium->hGlobal = GlobalAlloc(GMEM_FIXED, |
| sizeof(BITMAPINFOHEADER) + width * height * 4); |
| if (pMedium->hGlobal == NULL) { |
| return E_OUTOFMEMORY; |
| } |
| BITMAPINFOHEADER * hdr = (BITMAPINFOHEADER *)(pMedium->hGlobal); |
| memset(hdr, 0, sizeof(BITMAPINFOHEADER)); |
| hdr->biSize = sizeof(BITMAPINFOHEADER); |
| hdr->biWidth = width; |
| hdr->biHeight = -height; |
| hdr->biPlanes = 1; |
| hdr->biBitCount = 32; |
| hdr->biCompression = BI_RGB; |
| |
| jint * rawBuffer = (jint *)(hdr + 1); |
| env->GetIntArrayRegion(bitmapBuffer, 0, |
| width * height, rawBuffer); |
| return S_OK; |
| } |
| |
| HRESULT WinDataObject::getSerializedObject( |
| UINT format, STGMEDIUM * pMedium) { |
| JNIEnv * env = getEnv(); |
| jstring jstr = getSerializedFormatName(env, format); |
| if (jstr == NULL) { |
| return DV_E_FORMATETC; |
| } |
| jbyteArray bytes = (jbyteArray)env->CallObjectMethod(dataSnapshotGlobalRef, |
| methodDataSnapshotGetSerializedObject, jstr); |
| if (bytes == NULL) { |
| return DV_E_FORMATETC; |
| } |
| |
| jsize length = env->GetArrayLength(bytes); |
| pMedium->hGlobal = GlobalAlloc(GMEM_FIXED, length); |
| if (pMedium->hGlobal == NULL) { |
| return E_OUTOFMEMORY; |
| } |
| env->GetByteArrayRegion(bytes, 0, length, (jbyte *)pMedium->hGlobal); |
| return S_OK; |
| } |
| |
| |
| WinDropTarget::WinDropTarget() { |
| hWnd = NULL; |
| dropTargetGlobalRef = NULL; |
| } |
| |
| WinDropTarget::~WinDropTarget(void) { |
| if (dropTargetGlobalRef != NULL) { |
| getEnv()->DeleteGlobalRef(dropTargetGlobalRef); |
| } |
| } |
| |
| void WinDropTarget::init(HWND hwnd, jobject targetObj) { |
| hWnd = hwnd; |
| dropTargetGlobalRef = getEnv()->NewGlobalRef(targetObj); |
| } |
| |
| HRESULT WinDropTarget::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) { |
| currentData = NULL; |
| if (pdwEffect == NULL) { |
| return E_POINTER; |
| } |
| |
| *pdwEffect &= fireDragEnter(pDataObject, grfKeyState, pt, *pdwEffect); |
| |
| if (*pdwEffect != DROPEFFECT_NONE) { |
| currentData = pDataObject; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT WinDropTarget::DragLeave() { |
| fireDragLeave(); |
| currentData = NULL; |
| return S_OK; |
| } |
| |
| HRESULT WinDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { |
| if (pdwEffect == NULL) { |
| return E_POINTER; |
| } |
| |
| *pdwEffect &= fireDragOver(grfKeyState, pt, *pdwEffect); |
| |
| return S_OK; |
| } |
| |
| HRESULT WinDropTarget::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { |
| if (pdwEffect == NULL) { |
| currentData = NULL; |
| return E_POINTER; |
| } |
| |
| *pdwEffect &= fireDrop(pDataObject, grfKeyState, pt, *pdwEffect); |
| currentData = NULL; |
| return S_OK; |
| } |
| |
| int WinDropTarget::fireDrop(IDataObject * pDataObject, DWORD keyState, POINTL pt, DWORD allowedActions) { |
| JNIEnv * env = getEnv(); |
| jint dropAction = (jint)getDropAction(keyState, allowedActions); |
| return env->CallIntMethod(dropTargetGlobalRef, methodWinDropTargetDrop, |
| (jlong)pDataObject, (jint)pt.x, (jint)pt.y, dropAction, (jint)allowedActions); |
| } |
| |
| int WinDropTarget::fireDragEnter(IDataObject * pDataObject, DWORD keyState, POINTL pt, DWORD allowedActions) { |
| JNIEnv * env = getEnv(); |
| jint dropAction = (jint)getDropAction(keyState, allowedActions); |
| return env->CallIntMethod(dropTargetGlobalRef, methodWinDropTargetDragEnter, |
| (jlong)pDataObject, (jint)pt.x, (jint)pt.y, dropAction, (jint)allowedActions); |
| } |
| |
| int WinDropTarget::fireDragOver(DWORD keyState, POINTL pt, DWORD allowedActions) { |
| JNIEnv * env = getEnv(); |
| jint dropAction = (jint)getDropAction(keyState, allowedActions); |
| return env->CallIntMethod(dropTargetGlobalRef, methodWinDropTargetDragOver, |
| (jint)pt.x, (jint)pt.y, dropAction, (jint)allowedActions); |
| } |
| |
| void WinDropTarget::fireDragLeave() { |
| JNIEnv * env = getEnv(); |
| env->CallVoidMethod(dropTargetGlobalRef, methodWinDropTargetDragLeave); |
| } |
| |
| DWORD WinDropTarget::getDropAction(DWORD keyState, DWORD allowedActions) { |
| if(keyState & MK_CONTROL) { |
| if(keyState & MK_SHIFT) { |
| return DROPEFFECT_LINK; |
| } else { |
| return DROPEFFECT_COPY; |
| } |
| } else if(keyState & MK_SHIFT) { |
| return DROPEFFECT_MOVE; |
| } |
| if (allowedActions & DROPEFFECT_MOVE) { |
| return DROPEFFECT_MOVE; |
| } |
| if (allowedActions & DROPEFFECT_COPY) { |
| return DROPEFFECT_COPY; |
| } |
| if (allowedActions & DROPEFFECT_LINK) { |
| return DROPEFFECT_LINK; |
| } |
| return DROPEFFECT_NONE; |
| } |
| |
| |
| WinDragSource::WinDragSource() { |
| dragSourceGlobalRef = NULL; |
| } |
| |
| WinDragSource::~WinDragSource() { |
| if (dragSourceGlobalRef != NULL) { |
| getEnv()->DeleteGlobalRef(dragSourceGlobalRef); |
| } |
| } |
| |
| STDMETHODIMP WinDragSource::QueryContinueDrag(BOOL fEscapePressed, |
| DWORD grfKeyState) { |
| if(fEscapePressed == TRUE) |
| return DRAGDROP_S_CANCEL; |
| |
| if((grfKeyState & MK_LBUTTON) == 0) |
| return DRAGDROP_S_DROP; |
| |
| getEnv()->CallVoidMethod(dragSourceGlobalRef, |
| methodWinDragSourceContinueDrag); |
| return S_OK; |
| } |
| |
| STDMETHODIMP WinDragSource::GiveFeedback(DWORD dwEffect) { |
| jint actions = (jint)(dwEffect & ~DROPEFFECT_SCROLL); |
| jboolean scroll = (dwEffect & DROPEFFECT_SCROLL) != 0; |
| getEnv()->CallVoidMethod(dragSourceGlobalRef, |
| methodWinDragSourceGiveFeedback, actions, scroll); |
| |
| return DRAGDROP_S_USEDEFAULTCURSORS; |
| } |
| |
| void WinDragSource::init(JNIEnv * env, jobject winDragSource) { |
| dragSourceGlobalRef = env->NewGlobalRef(winDragSource); |
| } |