1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "common.h"
  27 
  28 #include <ShObjIdl.h>
  29 
  30 #include "CommonDialogs_COM.h"
  31 
  32 #include "com_sun_glass_ui_CommonDialogs_Type.h"
  33 
  34 /*****************************
  35  * IFileDialog implementation
  36  *****************************/
  37 
  38 _COM_SMARTPTR_TYPEDEF(IFileDialog, __uuidof(IFileDialog));
  39 _COM_SMARTPTR_TYPEDEF(IFileOpenDialog, __uuidof(IFileOpenDialog));
  40 _COM_SMARTPTR_TYPEDEF(IShellItem, __uuidof(IShellItem));
  41 _COM_SMARTPTR_TYPEDEF(IShellItemArray, __uuidof(IShellItemArray));
  42 
  43 #if (_WIN32_IE < _WIN32_IE_IE70)
  44 SHSTDAPI SHCreateItemFromParsingName(__in PCWSTR pszPath, __in_opt IBindCtx *pbc, __in REFIID riid, __deref_out void **ppv);
  45 #endif  // (_WIN32_IE < _WIN32_IE_IE70)
  46 
  47 const HRESULT CANCEL_HRT = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  48 
  49 jstring CreateJString(JNIEnv *env, IShellItemPtr pFile)
  50 {
  51     LPWSTR path = NULL;
  52     jstring ret = NULL;
  53 
  54     OLE_TRY
  55     OLE_HRT( pFile->GetDisplayName(SIGDN_FILESYSPATH, &path) );
  56     OLE_CATCH
  57 
  58     ret = CreateJString(env, path);
  59     CoTaskMemFree(path);
  60     return ret;
  61 }
  62 
  63 wchar_t *GetDescription(JNIEnv *env, jobject jFilter)
  64 {
  65     JLString jDesc(env, (jstring)env->CallObjectMethod(jFilter,
  66                         javaIDs.CommonDialogs.ExtensionFilter.getDescription));
  67     CheckAndClearException(env);
  68     JString desc(env, jDesc, false);
  69     return desc;
  70 }
  71 
  72 wchar_t *GetExtensions(JNIEnv *env, jobject jFilter)
  73 {
  74     JLObjectArray jExts(env, (jobjectArray)env->CallObjectMethod(jFilter,
  75                                javaIDs.CommonDialogs.ExtensionFilter.extensionsToArray));
  76     CheckAndClearException(env);
  77 
  78     jsize size = env->GetArrayLength(jExts);
  79     BOOL isHeading = TRUE; // extension without semicolon
  80 
  81     JLString jExt(env, CreateJString(env, _T("")));
  82     JLString semicolon(env, CreateJString(env, _T(";")));
  83 
  84     for (int j = 0; j < size; j++) {
  85         if (!isHeading) {
  86             jExt.Attach( ConcatJStrings(env, jExt, semicolon) );
  87         }
  88         isHeading = FALSE;
  89 
  90         JLString jExtension(env, (jstring)env->GetObjectArrayElement(jExts, j));
  91         jExt.Attach( ConcatJStrings(env, jExt, jExtension) );
  92     }
  93 
  94     JString ext(env, jExt, false);
  95     return ext;
  96 }
  97 
  98 void SetFilters(IFileDialogPtr pDialog, jobjectArray jFilters, jint defaultFilterIndex)
  99 {
 100     JNIEnv *env = GetEnv();
 101 
 102     jsize size = env->GetArrayLength(jFilters);
 103     COMDLG_FILTERSPEC *filterSpec = new COMDLG_FILTERSPEC[size];
 104 
 105     for (int i = 0; i < size; i++) {
 106         JLObject jFilter(env, env->GetObjectArrayElement(jFilters, i));
 107         COMDLG_FILTERSPEC c = {GetDescription(env, jFilter),
 108                                GetExtensions(env, jFilter)};
 109         filterSpec[i] = c;
 110     }
 111 
 112     OLE_TRY
 113     OLE_HRT( pDialog->SetDefaultExtension(L"") );
 114     OLE_HRT( pDialog->SetFileTypes(size, filterSpec) );
 115     if (size > 0) {
 116         OLE_HRT( pDialog->SetFileTypeIndex(defaultFilterIndex + 1) ); // 1-based index required
 117     }
 118     OLE_CATCH
 119 
 120     for (int i = 0; i < size; i++) {
 121         if (filterSpec[i].pszName) {
 122             delete[] filterSpec[i].pszName;
 123         }
 124         if (filterSpec[i].pszSpec) {
 125             delete[] filterSpec[i].pszSpec;
 126         }
 127     }
 128     delete[] filterSpec;
 129 }
 130 
 131 jobjectArray GetFiles(IFileDialogPtr pDialog, BOOL isCancelled, jint type)
 132 {
 133     JNIEnv* env = GetEnv();
 134     jclass jc = env->FindClass("java/lang/String");
 135     if (CheckAndClearException(env)) return NULL;
 136     JLClass cls(env, jc);
 137 
 138     jobjectArray ret = NULL;
 139 
 140     if (isCancelled) {
 141         ret = env->NewObjectArray(0, cls, NULL);
 142         if (CheckAndClearException(env)) return NULL;
 143         return ret;
 144     }
 145 
 146     OLE_TRY
 147     if (type == com_sun_glass_ui_CommonDialogs_Type_SAVE) {
 148         ret = env->NewObjectArray(1, cls, NULL);
 149         if (CheckAndClearException(env)) return NULL;
 150 
 151         IShellItemPtr pFile;
 152         OLE_HRT( pDialog->GetResult(&pFile) );
 153         OLE_CHECK_NOTNULLSP(pFile)
 154 
 155         env->SetObjectArrayElement(ret, 0,
 156                     jstring(JLString(env, CreateJString(env, pFile))));
 157         CheckAndClearException(env);
 158         return ret;
 159     }
 160 
 161     IFileOpenDialogPtr pOpenDialog(pDialog);
 162     OLE_CHECK_NOTNULLSP(pOpenDialog)
 163 
 164     IShellItemArrayPtr pFiles;
 165     OLE_HRT( pOpenDialog->GetResults(&pFiles) );
 166     OLE_CHECK_NOTNULLSP(pFiles)
 167 
 168     DWORD count = 0;
 169     OLE_HRT( pFiles->GetCount(&count) );
 170 
 171     ret = env->NewObjectArray(count, cls, NULL);
 172     if (CheckAndClearException(env)) return NULL;
 173 
 174     for (DWORD i = 0; i < count; i++) {
 175         IShellItemPtr pFile;
 176         OLE_HRT( pFiles->GetItemAt(i, &pFile) );
 177         OLE_CHECK_NOTNULLSP(pFile)
 178 
 179         env->SetObjectArrayElement(ret, i,
 180                     jstring(JLString(env, CreateJString(env, pFile))));
 181         CheckAndClearException(env);
 182     }
 183     OLE_CATCH
 184 
 185     return ret;
 186 }
 187 
 188 jobject COMFileChooser_Show(HWND owner, LPCTSTR folder, LPCTSTR filename, LPCTSTR title, jint type,
 189                                  jboolean multipleMode, jobjectArray jFilters, jint defaultFilterIndex)
 190 {
 191     OLEHolder _ole_;
 192     IFileDialogPtr pDialog;
 193 
 194     OLE_TRY
 195 
 196     switch(type) {
 197         case com_sun_glass_ui_CommonDialogs_Type_OPEN:
 198             OLE_HRT( ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
 199                                         IID_IFileOpenDialog, (void**)&pDialog) );
 200 
 201             if (multipleMode == TRUE) {
 202                 DWORD dwOptions = 0;
 203                 OLE_HRT( pDialog->GetOptions(&dwOptions) );
 204                 dwOptions |= FOS_ALLOWMULTISELECT;
 205                 OLE_HRT( pDialog->SetOptions(dwOptions) );
 206             }
 207 
 208             break;
 209         case com_sun_glass_ui_CommonDialogs_Type_SAVE:
 210             OLE_HRT( ::CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL,
 211                                         IID_IFileSaveDialog, (void**)&pDialog) );
 212             break;
 213     }
 214 
 215     if (folder) {
 216         IShellItemPtr pItem;
 217         OLE_HRT( SHCreateItemFromParsingName((PCWSTR)folder, NULL,
 218                                              IID_IShellItem, (void **)&pItem) );
 219         if (pItem) {
 220             OLE_HRT( pDialog->SetFolder( pItem ) );
 221         }
 222     }
 223 
 224     if (type == com_sun_glass_ui_CommonDialogs_Type_SAVE && filename && *filename) {
 225         OLE_HRT( pDialog->SetFileName(filename); );
 226     }
 227 
 228     if (title) {
 229         OLE_HRT( pDialog->SetTitle(title) );
 230     }
 231 
 232     if (jFilters != NULL) {
 233         SetFilters(pDialog, jFilters, defaultFilterIndex);
 234     }
 235 
 236     OLE_HR = pDialog->Show(owner);
 237     if (OLE_HR != CANCEL_HRT && FAILED(OLE_HR)) {
 238         OLE_THROW_LASTERROR(_T("pDialog->Show(NULL)"))
 239     }
 240     OLE_CATCH
 241 
 242     jobjectArray ret = GetFiles(pDialog, OLE_HR == CANCEL_HRT, type);
 243 
 244     UINT index = 0;
 245     pDialog->GetFileTypeIndex(&index);
 246 
 247     JNIEnv* env = GetEnv();
 248     jclass jc = env->FindClass("com/sun/glass/ui/CommonDialogs");
 249     if (CheckAndClearException(env)) return NULL;
 250     JLClass cls(env, jc);
 251     jobject jobj = env->CallStaticObjectMethod(cls, javaIDs.CommonDialogs.createFileChooserResult,
 252             ret, jFilters, (jint)(index - 1));
 253     if (CheckAndClearException(env)) return NULL;
 254     return jobj;
 255 }
 256 
 257 /*****************************
 258  * IFileDialog implementation
 259  *****************************/
 260 
 261 jstring GetFolder(IFileDialogPtr pDialog, BOOL isCancelled)
 262 {
 263     if (isCancelled) {
 264         return NULL;
 265     }
 266 
 267     JNIEnv* env = GetEnv();
 268     IShellItemPtr pFile;
 269 
 270     OLE_TRY
 271     OLE_HRT( pDialog->GetResult(&pFile) );
 272     OLE_CATCH
 273 
 274     return CreateJString(env, pFile);
 275 }
 276 
 277 jstring COMFolderChooser_Show(HWND owner, LPCTSTR folder, LPCTSTR title)
 278 {
 279     OLEHolder _ole_;
 280     IFileDialogPtr pDialog;
 281 
 282     OLE_TRY
 283     OLE_HRT( ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
 284                                 IID_IFileOpenDialog, (void**)&pDialog) );
 285 
 286     DWORD dwOptions = 0;
 287     OLE_HRT( pDialog->GetOptions(&dwOptions) );
 288     dwOptions |= FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM;
 289     OLE_HRT( pDialog->SetOptions(dwOptions) );
 290 
 291     if (folder) {
 292         IShellItemPtr pItem;
 293         OLE_HRT( SHCreateItemFromParsingName((PCWSTR)folder, NULL,
 294                                              IID_IShellItem, (void **)&pItem) );
 295         if (pItem) {
 296             OLE_HRT( pDialog->SetFolder( pItem ) );
 297         }
 298     }
 299 
 300     if (title) {
 301         OLE_HRT( pDialog->SetTitle(title) );
 302     }
 303 
 304     OLE_HR = pDialog->Show(owner);
 305     if (OLE_HR != CANCEL_HRT && FAILED(OLE_HR)) {
 306         OLE_THROW_LASTERROR(_T("pDialog->Show(NULL)"))
 307     }
 308     OLE_CATCH
 309 
 310     return GetFolder(pDialog, OLE_HR == CANCEL_HRT);
 311 }