blob: cadb09c3507ab07df4bec2f5306e6e9970c61a73 [file] [log] [blame]
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "awt.h"
#include "awt_FileDialog.h"
#include "awt_Dialog.h"
#include "awt_Toolkit.h"
#include "awt_ole.h"
#include "ComCtl32Util.h"
#include <commdlg.h>
#include <cderr.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shobjidl.h>
/************************************************************************
* AwtFileDialog fields
*/
/* WFileDialogPeer ids */
jfieldID AwtFileDialog::parentID;
jfieldID AwtFileDialog::fileFilterID;
jmethodID AwtFileDialog::setHWndMID;
jmethodID AwtFileDialog::handleSelectedMID;
jmethodID AwtFileDialog::handleCancelMID;
jmethodID AwtFileDialog::checkFilenameFilterMID;
jmethodID AwtFileDialog::isMultipleModeMID;
/* FileDialog ids */
jfieldID AwtFileDialog::modeID;
jfieldID AwtFileDialog::dirID;
jfieldID AwtFileDialog::fileID;
jfieldID AwtFileDialog::filterID;
class CoTaskStringHolder {
public:
CoTaskStringHolder() : m_str(NULL) {}
CoTaskStringHolder& operator=(CoTaskStringHolder& other) {
Clean();
m_str = other.m_str;
other.m_str = NULL;
return *this;
}
LPTSTR* operator&() {
return &m_str;
}
operator bool() {
return m_str != NULL;
}
operator LPTSTR() {
return m_str;
}
~CoTaskStringHolder() {
Clean();
}
private:
LPTSTR m_str;
void Clean() {
if (m_str)
::CoTaskMemFree(m_str);
}
};
template <typename T>
class SmartHolderBase {
public:
SmartHolderBase() : m_pointer(NULL) {}
void Attach(T* other) {
Clean();
m_pointer = other;
}
operator bool() {
return m_pointer != NULL;
}
operator T*() {
return m_pointer;
}
~SmartHolderBase() {
Clean();
}
protected:
T* m_pointer;
virtual void Clean() {
if (m_pointer)
delete m_pointer;
}
};
template<typename T>
class SmartHolder : public SmartHolderBase<T> {
};
template <typename T>
class SmartHolder<T[]> : public SmartHolderBase<T> {
virtual void Clean() {
if (m_pointer)
delete [] m_pointer;
}
};
/* Localized filter string */
#define MAX_FILTER_STRING 128
static TCHAR s_fileFilterString[MAX_FILTER_STRING];
/* Non-localized suffix of the filter string */
static const TCHAR s_additionalString[] = TEXT(" (*.*)\0*.*\0");
static SmartHolder<COMDLG_FILTERSPEC> s_fileFilterSpec;
static UINT s_fileFilterCount;
// Default limit of the output buffer.
#define SINGLE_MODE_BUFFER_LIMIT MAX_PATH+1
#define MULTIPLE_MODE_BUFFER_LIMIT 32768
// The name of the property holding the pointer to the OPENFILENAME structure.
static LPCTSTR OpenFileNameProp = TEXT("AWT_OFN");
_COM_SMARTPTR_TYPEDEF(IFileDialog, __uuidof(IFileDialog));
_COM_SMARTPTR_TYPEDEF(IFileDialogEvents, __uuidof(IFileDialogEvents));
_COM_SMARTPTR_TYPEDEF(IShellItem, __uuidof(IShellItem));
_COM_SMARTPTR_TYPEDEF(IFileOpenDialog, __uuidof(IFileOpenDialog));
_COM_SMARTPTR_TYPEDEF(IShellItemArray, __uuidof(IShellItemArray));
_COM_SMARTPTR_TYPEDEF(IOleWindowPtr, __uuidof(IOleWindowPtr));
/***********************************************************************/
COMDLG_FILTERSPEC *CreateFilterSpec(UINT *count) {
UINT filterCount = 0;
for (UINT index = 0; index < MAX_FILTER_STRING - 1; index++) {
if (s_fileFilterString[index] == _T('\0')) {
filterCount++;
if (s_fileFilterString[index + 1] == _T('\0'))
break;
}
}
filterCount /= 2;
COMDLG_FILTERSPEC *filterSpec = new COMDLG_FILTERSPEC[filterCount];
UINT currentIndex = 0;
TCHAR *currentStart = s_fileFilterString;
for (UINT index = 0; index < MAX_FILTER_STRING - 1; index++) {
if (s_fileFilterString[index] == _T('\0')) {
if (currentIndex & 1) {
filterSpec[currentIndex / 2].pszSpec = currentStart;
} else {
filterSpec[currentIndex / 2].pszName = currentStart;
}
currentStart = s_fileFilterString + index + 1;
currentIndex++;
if (s_fileFilterString[index + 1] == _T('\0'))
break;
}
}
*count = filterCount;
return filterSpec;
}
void
AwtFileDialog::Initialize(JNIEnv *env, jstring filterDescription)
{
int length = env->GetStringLength(filterDescription);
DASSERT(length + 1 < MAX_FILTER_STRING);
LPCTSTR tmp = JNU_GetStringPlatformChars(env, filterDescription, NULL);
_tcscpy_s(s_fileFilterString, MAX_FILTER_STRING, tmp);
JNU_ReleaseStringPlatformChars(env, filterDescription, tmp);
//AdditionalString should be terminated by two NULL characters (Windows
//requirement), so we have to organize the following cycle and use memcpy
//unstead of, for example, strcat.
LPTSTR s = s_fileFilterString;
while (*s) {
++s;
DASSERT(s < s_fileFilterString + MAX_FILTER_STRING);
}
DASSERT(s + sizeof(s_additionalString) < s_fileFilterString + MAX_FILTER_STRING);
memcpy(s, s_additionalString, sizeof(s_additionalString));
s_fileFilterSpec.Attach(CreateFilterSpec(&s_fileFilterCount));
}
LRESULT CALLBACK FileDialogWndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
switch (message) {
case WM_COMMAND: {
if (LOWORD(wParam) == IDCANCEL)
{
// Unlike Print/Page dialogs, we only handle IDCANCEL here and
// don't handle IDOK. This is because user can press OK button
// when no file is selected, and the dialog is not closed. So
// OK button is handled in the CDN_FILEOK notification handler
// (see FileDialogHookProc below)
jobject peer = (jobject)(::GetProp(hWnd, ModalDialogPeerProp));
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID, (jlong)0);
}
break;
}
}
WNDPROC lpfnWndProc = (WNDPROC)(::GetProp(hWnd, NativeDialogWndProcProp));
return ComCtl32Util::GetInstance().DefWindowProc(lpfnWndProc, hWnd, message, wParam, lParam);
}
static UINT_PTR CALLBACK
FileDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
TRY;
HWND parent = ::GetParent(hdlg);
switch(uiMsg) {
case WM_INITDIALOG: {
OPENFILENAME *ofn = (OPENFILENAME *)lParam;
jobject peer = (jobject)(ofn->lCustData);
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID,
(jlong)parent);
::SetProp(parent, ModalDialogPeerProp, reinterpret_cast<HANDLE>(peer));
// fix for 4508670 - disable CS_SAVEBITS
DWORD style = ::GetClassLong(hdlg,GCL_STYLE);
::SetClassLong(hdlg,GCL_STYLE,style & ~CS_SAVEBITS);
// set appropriate icon for parentless dialogs
jobject awtParent = env->GetObjectField(peer, AwtFileDialog::parentID);
if (awtParent == NULL) {
::SendMessage(parent, WM_SETICON, (WPARAM)ICON_BIG,
(LPARAM)AwtToolkit::GetInstance().GetAwtIcon());
} else {
env->DeleteLocalRef(awtParent);
}
// subclass dialog's parent to receive additional messages
WNDPROC lpfnWndProc = ComCtl32Util::GetInstance().SubclassHWND(parent,
FileDialogWndProc);
::SetProp(parent, NativeDialogWndProcProp, reinterpret_cast<HANDLE>(lpfnWndProc));
::SetProp(parent, OpenFileNameProp, (void *)lParam);
break;
}
case WM_DESTROY: {
HIMC hIMC = ::ImmGetContext(hdlg);
if (hIMC != NULL) {
::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
::ImmReleaseContext(hdlg, hIMC);
}
WNDPROC lpfnWndProc = (WNDPROC)(::GetProp(parent, NativeDialogWndProcProp));
ComCtl32Util::GetInstance().UnsubclassHWND(parent,
FileDialogWndProc,
lpfnWndProc);
::RemoveProp(parent, ModalDialogPeerProp);
::RemoveProp(parent, NativeDialogWndProcProp);
::RemoveProp(parent, OpenFileNameProp);
break;
}
case WM_NOTIFY: {
OFNOTIFYEX *notifyEx = (OFNOTIFYEX *)lParam;
if (notifyEx) {
jobject peer = (jobject)(::GetProp(parent, ModalDialogPeerProp));
if (notifyEx->hdr.code == CDN_INCLUDEITEM) {
LPITEMIDLIST pidl = (LPITEMIDLIST)notifyEx->pidl;
// Get the filename and directory
TCHAR szPath[MAX_PATH];
if (!::SHGetPathFromIDList(pidl, szPath)) {
return TRUE;
}
jstring strPath = JNU_NewStringPlatform(env, szPath);
if (strPath == NULL) {
throw std::bad_alloc();
}
// Call FilenameFilter.accept with path and filename
UINT uRes = (env->CallBooleanMethod(peer,
AwtFileDialog::checkFilenameFilterMID, strPath) == JNI_TRUE);
env->DeleteLocalRef(strPath);
return uRes;
} else if (notifyEx->hdr.code == CDN_FILEOK) {
// This notification is sent when user selects some file and presses
// OK button; it is not sent when no file is selected. So it's time
// to unblock all the windows blocked by this dialog as it will
// be closed soon
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID, (jlong)0);
} else if (notifyEx->hdr.code == CDN_SELCHANGE) {
// reallocate the buffer if the buffer is too small
LPOPENFILENAME lpofn = (LPOPENFILENAME)GetProp(parent, OpenFileNameProp);
UINT nLength = CommDlg_OpenSave_GetSpec(parent, NULL, 0) +
CommDlg_OpenSave_GetFolderPath(parent, NULL, 0);
if (lpofn->nMaxFile < nLength)
{
// allocate new buffer
LPTSTR newBuffer = new TCHAR[nLength];
if (newBuffer) {
memset(newBuffer, 0, nLength * sizeof(TCHAR));
LPTSTR oldBuffer = lpofn->lpstrFile;
lpofn->lpstrFile = newBuffer;
lpofn->nMaxFile = nLength;
// free the previously allocated buffer
if (oldBuffer) {
delete[] oldBuffer;
}
}
}
}
}
break;
}
}
return FALSE;
CATCH_BAD_ALLOC_RET(TRUE);
}
struct FileDialogData {
IFileDialogPtr fileDialog;
SmartHolder<TCHAR[]> result;
UINT resultSize;
jobject peer;
};
HRESULT GetSelectedResults(FileDialogData *data) {
OLE_TRY
IFileOpenDialogPtr fileOpenDialog;
UINT currentOffset = 0;
IShellItemArrayPtr psia;
DWORD itemsCount;
OLE_HRT(data->fileDialog->QueryInterface(IID_PPV_ARGS(&fileOpenDialog)))
OLE_HRT(fileOpenDialog->GetSelectedItems(&psia));
OLE_HRT(psia->GetCount(&itemsCount));
UINT maxBufferSize = (MAX_PATH + 1) * itemsCount + 1;
data->result.Attach(new TCHAR[maxBufferSize]);
data->resultSize = maxBufferSize;
LPTSTR resultBuffer = data->result;
for (DWORD i = 0; i < itemsCount; i++) {
IShellItemPtr psi;
OLE_HRT(psia->GetItemAt(i, &psi));
if (i == 0 && itemsCount > 1) {
IShellItemPtr psiParent;
CoTaskStringHolder filePath;
OLE_HRT(psi->GetParent(&psiParent));
OLE_HRT(psiParent->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
size_t filePathLength = _tcslen(filePath);
_tcsncpy(resultBuffer + currentOffset, filePath, filePathLength);
resultBuffer[currentOffset + filePathLength] = _T('\0');
currentOffset += filePathLength + 1;
}
CoTaskStringHolder filePath;
SIGDN displayForm = itemsCount > 1 ? SIGDN_PARENTRELATIVE : SIGDN_FILESYSPATH;
OLE_HRT(psi->GetDisplayName(displayForm, &filePath));
size_t filePathLength = _tcslen(filePath);
_tcsncpy(resultBuffer + currentOffset, filePath, filePathLength);
resultBuffer[currentOffset + filePathLength] = _T('\0');
currentOffset += filePathLength + 1;
}
resultBuffer[currentOffset] = _T('\0');
resultBuffer[currentOffset + 1] = _T('\0');
data->fileDialog->Close(S_OK);
OLE_CATCH
OLE_RETURN_HR
}
LRESULT CALLBACK
FileDialogSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
TRY;
HWND parent = ::GetParent(hWnd);
switch (uMsg) {
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) {
OLE_TRY
OLE_HRT(GetSelectedResults((FileDialogData*) dwRefData));
OLE_CATCH
}
if (LOWORD(wParam) == IDCANCEL) {
jobject peer = (jobject) (::GetProp(hWnd, ModalDialogPeerProp));
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID, (jlong) 0);
}
break;
}
case WM_SETICON: {
return 0;
}
case WM_DESTROY: {
HIMC hIMC = ::ImmGetContext(hWnd);
if (hIMC != NULL) {
::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
::ImmReleaseContext(hWnd, hIMC);
}
RemoveWindowSubclass(hWnd, &FileDialogSubclassProc, uIdSubclass);
::RemoveProp(parent, ModalDialogPeerProp);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
CATCH_BAD_ALLOC_RET(TRUE);
}
class CDialogEventHandler : public IFileDialogEvents
{
public:
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_refCount);
}
IFACEMETHODIMP_(ULONG) Release()
{
long retVal = InterlockedDecrement(&m_refCount);
if (!retVal)
delete this;
return retVal;
}
IFACEMETHODIMP OnFolderChange(IFileDialog *fileDialog) {
if (!m_activated) {
InitDialog(fileDialog);
m_activated = true;
}
return S_OK;
};
IFACEMETHODIMP OnFileOk(IFileDialog *) {
if (!data->result) {
OLE_TRY
OLE_HRT(GetSelectedResults(data));
OLE_CATCH
}
return S_OK;
};
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog *pfd) { return S_OK; };
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; };
CDialogEventHandler(FileDialogData *data) : data(data), m_refCount(1), m_activated(false) { };
private:
~CDialogEventHandler() { };
FileDialogData *data;
bool m_activated;
long m_refCount;
void InitDialog(IFileDialog *fileDialog) {
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
TRY;
OLE_TRY
IOleWindowPtr pWindow;
OLE_HR = fileDialog->QueryInterface(IID_PPV_ARGS(&pWindow));
if (!SUCCEEDED(OLE_HR))
return;
HWND hdlg;
OLE_HRT(pWindow->GetWindow(&hdlg));
HWND parent = ::GetParent(hdlg);
jobject peer = data->peer;
env->CallVoidMethod(peer, AwtFileDialog::setHWndMID, (jlong)parent);
::SetProp(parent, ModalDialogPeerProp, reinterpret_cast<HANDLE>(peer));
// fix for 4508670 - disable CS_SAVEBITS
DWORD style = ::GetClassLong(hdlg, GCL_STYLE);
::SetClassLong(hdlg, GCL_STYLE, style & ~CS_SAVEBITS);
// set appropriate icon for parentless dialogs
jobject awtParent = env->GetObjectField(peer, AwtFileDialog::parentID);
if (awtParent == NULL) {
::SendMessage(parent, WM_SETICON, (WPARAM)ICON_BIG,
(LPARAM)AwtToolkit::GetInstance().GetAwtIcon());
} else {
AwtWindow *awtWindow = (AwtWindow *)JNI_GET_PDATA(awtParent);
::SendMessage(parent, WM_SETICON, (WPARAM)ICON_BIG,
(LPARAM)(awtWindow->GetHIcon()));
::SendMessage(parent, WM_SETICON, (WPARAM)ICON_SMALL,
(LPARAM)(awtWindow->GetHIconSm()));
env->DeleteLocalRef(awtParent);
}
SetWindowSubclass(hdlg, &FileDialogSubclassProc, 0, (DWORD_PTR) data);
OLE_CATCH
CATCH_BAD_ALLOC;
}
};
HRESULT CDialogEventHandler_CreateInstance(FileDialogData *data, REFIID riid, void **ppv)
{
OLE_TRY
IFileDialogEventsPtr dlg(new CDialogEventHandler(data), false);
OLE_HRT(dlg->QueryInterface(riid, ppv));
OLE_CATCH
OLE_RETURN_HR
}
HRESULT CreateShellItem(LPTSTR path, IShellItemPtr& shellItem) {
size_t pathLength = _tcslen(path);
for (size_t index = 0; index < pathLength; index++) {
if (path[index] == _T('/'))
path[index] = _T('\\');
}
return ::SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, path, IID_PPV_ARGS(&shellItem));
}
CoTaskStringHolder GetShortName(LPTSTR path) {
CoTaskStringHolder shortName;
OLE_TRY
IShellItemPtr shellItem;
OLE_HRT(CreateShellItem(path, shellItem));
OLE_HRT(shellItem->GetDisplayName(SIGDN_PARENTRELATIVE, &shortName));
OLE_CATCH
return SUCCEEDED(OLE_HR) ? shortName : CoTaskStringHolder();
}
void
AwtFileDialog::Show(void *p)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject peer;
LPTSTR currentDirectory = NULL;
jint mode = 0;
BOOL result = FALSE;
DWORD dlgerr;
jstring directory = NULL;
jstring title = NULL;
jstring file = NULL;
jobject fileFilter = NULL;
jobject target = NULL;
jobject parent = NULL;
AwtComponent* awtParent = NULL;
jboolean multipleMode = JNI_FALSE;
OLE_DECL
OLEHolder _ole_;
IFileDialogPtr pfd;
IFileDialogEventsPtr pfde;
IShellItemPtr psiResult;
FileDialogData data;
DWORD dwCookie = OLE_BAD_COOKIE;
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
peer = (jobject)p;
static BOOL useCommonItemDialog = JNU_CallStaticMethodByName(env, NULL,
"sun/awt/windows/WFileDialogPeer", "useCommonItemDialog", "()Z").z == JNI_TRUE;
try {
DASSERT(peer);
target = env->GetObjectField(peer, AwtObject::targetID);
parent = env->GetObjectField(peer, AwtFileDialog::parentID);
if (parent != NULL) {
awtParent = (AwtComponent *)JNI_GET_PDATA(parent);
}
// DASSERT(awtParent);
title = (jstring)(env)->GetObjectField(target, AwtDialog::titleID);
/*
Fix for 6488834.
To disable Win32 native parent modality we have to set
hwndOwner field to either NULL or some hidden window. For
parentless dialogs we use NULL to show them in the taskbar,
and for all other dialogs AwtToolkit's HWND is used.
*/
HWND hwndOwner = awtParent ? AwtToolkit::GetInstance().GetHWnd() : NULL;
if (title == NULL || env->GetStringLength(title)==0) {
title = JNU_NewStringPlatform(env, L" ");
if (title == NULL) {
throw std::bad_alloc();
}
}
JavaStringBuffer titleBuffer(env, title);
directory =
(jstring)env->GetObjectField(target, AwtFileDialog::dirID);
JavaStringBuffer directoryBuffer(env, directory);
multipleMode = env->CallBooleanMethod(peer, AwtFileDialog::isMultipleModeMID);
UINT bufferLimit;
if (multipleMode == JNI_TRUE) {
bufferLimit = MULTIPLE_MODE_BUFFER_LIMIT;
} else {
bufferLimit = SINGLE_MODE_BUFFER_LIMIT;
}
LPTSTR fileBuffer = new TCHAR[bufferLimit];
memset(fileBuffer, 0, bufferLimit * sizeof(TCHAR));
file = (jstring)env->GetObjectField(target, AwtFileDialog::fileID);
if (file != NULL) {
LPCTSTR tmp = JNU_GetStringPlatformChars(env, file, NULL);
_tcsncpy(fileBuffer, tmp, bufferLimit - 2); // the fileBuffer is double null terminated string
JNU_ReleaseStringPlatformChars(env, file, tmp);
} else {
fileBuffer[0] = _T('\0');
}
fileFilter = env->GetObjectField(peer, AwtFileDialog::fileFilterID);
if (!useCommonItemDialog) {
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = s_fileFilterString;
ofn.nFilterIndex = 1;
ofn.hwndOwner = hwndOwner;
ofn.lpstrFile = fileBuffer;
ofn.nMaxFile = bufferLimit;
ofn.lpstrTitle = titleBuffer;
ofn.lpstrInitialDir = directoryBuffer;
ofn.Flags = OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY |
OFN_ENABLEHOOK | OFN_EXPLORER | OFN_ENABLESIZING;
if (!JNU_IsNull(env,fileFilter)) {
ofn.Flags |= OFN_ENABLEINCLUDENOTIFY;
}
ofn.lCustData = (LPARAM)peer;
ofn.lpfnHook = (LPOFNHOOKPROC)FileDialogHookProc;
if (multipleMode == JNI_TRUE) {
ofn.Flags |= OFN_ALLOWMULTISELECT;
}
// Save current directory, so we can reset if it changes.
currentDirectory = new TCHAR[MAX_PATH+1];
VERIFY(::GetCurrentDirectory(MAX_PATH, currentDirectory) > 0);
}
mode = env->GetIntField(target, AwtFileDialog::modeID);
AwtDialog::CheckInstallModalHook();
if (useCommonItemDialog) {
OLE_NEXT_TRY
GUID fileDialogMode = mode == java_awt_FileDialog_LOAD ? CLSID_FileOpenDialog : CLSID_FileSaveDialog;
OLE_HRT(pfd.CreateInstance(fileDialogMode));
data.fileDialog = pfd;
data.peer = peer;
OLE_HRT(CDialogEventHandler_CreateInstance(&data, IID_PPV_ARGS(&pfde)));
OLE_HRT(pfd->Advise(pfde, &dwCookie));
DWORD dwFlags;
OLE_HRT(pfd->GetOptions(&dwFlags));
dwFlags |= FOS_FORCEFILESYSTEM;
if (multipleMode == JNI_TRUE) {
dwFlags |= FOS_ALLOWMULTISELECT;
}
OLE_HRT(pfd->SetOptions(dwFlags));
OLE_HRT(pfd->SetTitle(titleBuffer));
OLE_HRT(pfd->SetFileTypes(s_fileFilterCount, s_fileFilterSpec));
OLE_HRT(pfd->SetFileTypeIndex(1));
IShellItemPtr directoryItem;
OLE_NEXT_TRY
OLE_HRT(CreateShellItem(directoryBuffer, directoryItem));
OLE_HRT(pfd->SetFolder(directoryItem));
OLE_CATCH
CoTaskStringHolder shortName = GetShortName(fileBuffer);
if (shortName) {
OLE_HRT(pfd->SetFileName(shortName));
}
OLE_CATCH
}
if (useCommonItemDialog && SUCCEEDED(OLE_HR)) {
if (mode == java_awt_FileDialog_LOAD) {
result = SUCCEEDED(pfd->Show(hwndOwner)) && data.result;
if (!result) {
OLE_NEXT_TRY
OLE_HRT(pfd->GetResult(&psiResult));
CoTaskStringHolder filePath;
OLE_HRT(psiResult->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
size_t filePathLength = _tcslen(filePath);
data.result.Attach(new TCHAR[filePathLength + 1]);
_tcscpy_s(data.result, filePathLength + 1, filePath);
OLE_CATCH
result = SUCCEEDED(OLE_HR);
}
} else {
result = SUCCEEDED(pfd->Show(hwndOwner));
if (result) {
OLE_NEXT_TRY
OLE_HRT(pfd->GetResult(&psiResult));
CoTaskStringHolder filePath;
OLE_HRT(psiResult->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
size_t filePathLength = _tcslen(filePath);
data.result.Attach(new TCHAR[filePathLength + 1]);
_tcscpy_s(data.result, filePathLength + 1, filePath);
OLE_CATCH
result = SUCCEEDED(OLE_HR);
}
}
} else {
// show the Win32 file dialog
if (mode == java_awt_FileDialog_LOAD) {
result = ::GetOpenFileName(&ofn);
} else {
result = ::GetSaveFileName(&ofn);
}
// Fix for 4181310: FileDialog does not show up.
// If the dialog is not shown because of invalid file name
// replace the file name by empty string.
if (!result) {
dlgerr = ::CommDlgExtendedError();
if (dlgerr == FNERR_INVALIDFILENAME) {
_tcscpy_s(fileBuffer, bufferLimit, TEXT(""));
if (mode == java_awt_FileDialog_LOAD) {
result = ::GetOpenFileName(&ofn);
} else {
result = ::GetSaveFileName(&ofn);
}
}
}
}
AwtDialog::CheckUninstallModalHook();
DASSERT(env->GetLongField(peer, AwtComponent::hwndID) == 0L);
AwtDialog::ModalActivateNextWindow(NULL, target, peer);
if (useCommonItemDialog) {
VERIFY(::SetCurrentDirectory(currentDirectory));
}
// Report result to peer.
if (result) {
jint length;
if (useCommonItemDialog) {
length = (jint) GetBufferLength(data.result, data.resultSize);
} else {
length = multipleMode
? (jint) GetBufferLength(ofn.lpstrFile, ofn.nMaxFile)
: (jint) _tcslen(ofn.lpstrFile);
}
jcharArray jnames = env->NewCharArray(length);
if (jnames == NULL) {
throw std::bad_alloc();
}
if (useCommonItemDialog) {
env->SetCharArrayRegion(jnames, 0, length, (jchar *) (LPTSTR) data.result);
} else {
env->SetCharArrayRegion(jnames, 0, length, (jchar *) ofn.lpstrFile);
}
env->CallVoidMethod(peer, AwtFileDialog::handleSelectedMID, jnames);
env->DeleteLocalRef(jnames);
} else {
env->CallVoidMethod(peer, AwtFileDialog::handleCancelMID);
}
DASSERT(!safe_ExceptionOccurred(env));
} catch (...) {
if (useCommonItemDialog) {
if (pfd && dwCookie != OLE_BAD_COOKIE) {
pfd->Unadvise(dwCookie);
}
}
env->DeleteLocalRef(target);
env->DeleteLocalRef(parent);
env->DeleteLocalRef(title);
env->DeleteLocalRef(directory);
env->DeleteLocalRef(file);
env->DeleteLocalRef(fileFilter);
env->DeleteGlobalRef(peer);
delete[] currentDirectory;
if (ofn.lpstrFile)
delete[] ofn.lpstrFile;
throw;
}
if (useCommonItemDialog) {
if (pfd && dwCookie != OLE_BAD_COOKIE) {
pfd->Unadvise(dwCookie);
}
}
env->DeleteLocalRef(target);
env->DeleteLocalRef(parent);
env->DeleteLocalRef(title);
env->DeleteLocalRef(directory);
env->DeleteLocalRef(file);
env->DeleteLocalRef(fileFilter);
env->DeleteGlobalRef(peer);
delete[] currentDirectory;
if (ofn.lpstrFile)
delete[] ofn.lpstrFile;
}
BOOL AwtFileDialog::InheritsNativeMouseWheelBehavior() {return true;}
void AwtFileDialog::_DisposeOrHide(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
HWND hdlg = (HWND)(env->GetLongField(self, AwtComponent::hwndID));
if (::IsWindow(hdlg))
{
::SendMessage(hdlg, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0),
(LPARAM)hdlg);
}
env->DeleteGlobalRef(self);
}
void AwtFileDialog::_ToFront(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
HWND hdlg = (HWND)(env->GetLongField(self, AwtComponent::hwndID));
if (::IsWindow(hdlg))
{
::SetWindowPos(hdlg, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
env->DeleteGlobalRef(self);
}
void AwtFileDialog::_ToBack(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
HWND hdlg = (HWND)(env->GetLongField(self, AwtComponent::hwndID));
if (::IsWindow(hdlg))
{
::SetWindowPos(hdlg, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
env->DeleteGlobalRef(self);
}
// Returns the length of the double null terminated output buffer
UINT AwtFileDialog::GetBufferLength(LPTSTR buffer, UINT limit)
{
UINT index = 0;
while ((index < limit) &&
(buffer[index] != NULL || buffer[index+1] != NULL))
{
index++;
}
return index;
}
/************************************************************************
* WFileDialogPeer native methods
*/
extern "C" {
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer_initIDs(JNIEnv *env, jclass cls)
{
TRY;
AwtFileDialog::parentID =
env->GetFieldID(cls, "parent", "Lsun/awt/windows/WComponentPeer;");
DASSERT(AwtFileDialog::parentID != NULL);
CHECK_NULL(AwtFileDialog::parentID);
AwtFileDialog::fileFilterID =
env->GetFieldID(cls, "fileFilter", "Ljava/io/FilenameFilter;");
DASSERT(AwtFileDialog::fileFilterID != NULL);
CHECK_NULL(AwtFileDialog::fileFilterID);
AwtFileDialog::setHWndMID = env->GetMethodID(cls, "setHWnd", "(J)V");
DASSERT(AwtFileDialog::setHWndMID != NULL);
CHECK_NULL(AwtFileDialog::setHWndMID);
AwtFileDialog::handleSelectedMID =
env->GetMethodID(cls, "handleSelected", "([C)V");
DASSERT(AwtFileDialog::handleSelectedMID != NULL);
CHECK_NULL(AwtFileDialog::handleSelectedMID);
AwtFileDialog::handleCancelMID =
env->GetMethodID(cls, "handleCancel", "()V");
DASSERT(AwtFileDialog::handleCancelMID != NULL);
CHECK_NULL(AwtFileDialog::handleCancelMID);
AwtFileDialog::checkFilenameFilterMID =
env->GetMethodID(cls, "checkFilenameFilter", "(Ljava/lang/String;)Z");
DASSERT(AwtFileDialog::checkFilenameFilterMID != NULL);
CHECK_NULL(AwtFileDialog::checkFilenameFilterMID);
AwtFileDialog::isMultipleModeMID = env->GetMethodID(cls, "isMultipleMode", "()Z");
DASSERT(AwtFileDialog::isMultipleModeMID != NULL);
CHECK_NULL(AwtFileDialog::isMultipleModeMID);
/* java.awt.FileDialog fields */
cls = env->FindClass("java/awt/FileDialog");
CHECK_NULL(cls);
AwtFileDialog::modeID = env->GetFieldID(cls, "mode", "I");
DASSERT(AwtFileDialog::modeID != NULL);
CHECK_NULL(AwtFileDialog::modeID);
AwtFileDialog::dirID = env->GetFieldID(cls, "dir", "Ljava/lang/String;");
DASSERT(AwtFileDialog::dirID != NULL);
CHECK_NULL(AwtFileDialog::dirID);
AwtFileDialog::fileID = env->GetFieldID(cls, "file", "Ljava/lang/String;");
DASSERT(AwtFileDialog::fileID != NULL);
CHECK_NULL(AwtFileDialog::fileID);
AwtFileDialog::filterID =
env->GetFieldID(cls, "filter", "Ljava/io/FilenameFilter;");
DASSERT(AwtFileDialog::filterID != NULL);
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer_setFilterString(JNIEnv *env, jclass cls,
jstring filterDescription)
{
TRY;
AwtFileDialog::Initialize(env, filterDescription);
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer__1show(JNIEnv *env, jobject peer)
{
TRY;
/*
* Fix for 4906972.
* 'peer' reference has to be global as it's used further in another thread.
*/
jobject peerGlobal = env->NewGlobalRef(peer);
if (!AwtToolkit::GetInstance().PostMessage(WM_AWT_INVOKE_METHOD,
(WPARAM)AwtFileDialog::Show, (LPARAM)peerGlobal)) {
env->DeleteGlobalRef(peerGlobal);
}
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer__1dispose(JNIEnv *env, jobject peer)
{
TRY_NO_VERIFY;
jobject peerGlobal = env->NewGlobalRef(peer);
AwtToolkit::GetInstance().SyncCall(AwtFileDialog::_DisposeOrHide,
(void *)peerGlobal);
// peerGlobal ref is deleted in _DisposeOrHide
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer__1hide(JNIEnv *env, jobject peer)
{
TRY;
jobject peerGlobal = env->NewGlobalRef(peer);
AwtToolkit::GetInstance().SyncCall(AwtFileDialog::_DisposeOrHide,
(void *)peerGlobal);
// peerGlobal ref is deleted in _DisposeOrHide
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer_toFront(JNIEnv *env, jobject peer)
{
TRY;
AwtToolkit::GetInstance().SyncCall(AwtFileDialog::_ToFront,
(void *)(env->NewGlobalRef(peer)));
// global ref is deleted in _ToFront
CATCH_BAD_ALLOC;
}
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFileDialogPeer_toBack(JNIEnv *env, jobject peer)
{
TRY;
AwtToolkit::GetInstance().SyncCall(AwtFileDialog::_ToBack,
(void *)(env->NewGlobalRef(peer)));
// global ref is deleted in _ToBack
CATCH_BAD_ALLOC;
}
} /* extern "C" */