| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |   Native File Dialog | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   http://www.frogtoss.com/labs
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-28 16:07:01 -05:00
										 |  |  | #ifdef __MINGW32__
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | // Explicitly setting NTDDI version, this is necessary for the MinGW compiler
 | 
					
						
							| 
									
										
										
										
											2023-09-20 16:39:52 -04:00
										 |  |  | #ifdef NTDDI_VERSION
 | 
					
						
							|  |  |  | #undef NTDDI_VERSION
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef _WIN32_WINNT
 | 
					
						
							|  |  |  | #undef _WIN32_WINNT
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-12-28 16:07:01 -05:00
										 |  |  | #define NTDDI_VERSION NTDDI_VISTA
 | 
					
						
							|  |  |  | #define _WIN32_WINNT _WIN32_WINNT_VISTA
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define _CRTDBG_MAP_ALLOC
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <crtdbg.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* only locally define UNICODE in this compilation unit */ | 
					
						
							|  |  |  | #ifndef UNICODE
 | 
					
						
							|  |  |  | #define UNICODE
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <wchar.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <windows.h>
 | 
					
						
							|  |  |  | #include <shobjidl.h>
 | 
					
						
							|  |  |  | #include "nfd_common.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  | // hack I know
 | 
					
						
							|  |  |  | #include "../../../src/utfutils.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  | // hack 2...
 | 
					
						
							|  |  |  | #include "../../../src/ta-log.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  | class NFDWinEvents: public IFileDialogEvents { | 
					
						
							|  |  |  |   nfdselcallback_t selCallback; | 
					
						
							|  |  |  |   size_t refCount; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   virtual ~NFDWinEvents() { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   public: | 
					
						
							|  |  |  |     IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: QueryInterface called DAMN IT",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       *ppv=NULL; | 
					
						
							|  |  |  |       return E_NOTIMPL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IFACEMETHODIMP_(ULONG) AddRef() { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: AddRef() called",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       return InterlockedIncrement(&refCount); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IFACEMETHODIMP_(ULONG) Release() { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: Release() called",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       LONG ret=InterlockedDecrement(&refCount); | 
					
						
							|  |  |  |       if (ret==0) { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |         logV("%p: Destroying the final object.",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |         delete this; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-24 16:34:16 -04:00
										 |  |  |     IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; } | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     IFACEMETHODIMP OnFolderChange(IFileDialog*) { return E_NOTIMPL; } | 
					
						
							|  |  |  |     IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return E_NOTIMPL; } | 
					
						
							|  |  |  |     IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return E_NOTIMPL; } | 
					
						
							|  |  |  |     IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return E_NOTIMPL; } | 
					
						
							|  |  |  |     IFACEMETHODIMP OnTypeChange(IFileDialog*) { return E_NOTIMPL; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) { | 
					
						
							|  |  |  |       // Get the file name
 | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: OnSelectionChange() called",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       ::IShellItem *shellItem(NULL); | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: GetCurrentSelection",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       HRESULT result = dialog->GetCurrentSelection(&shellItem); | 
					
						
							|  |  |  |       if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |         logV("%p: failure!",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |         return S_OK; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       wchar_t *filePath(NULL); | 
					
						
							|  |  |  |       result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); | 
					
						
							|  |  |  |       if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |           logV("%p: GDN failure!",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |           shellItem->Release(); | 
					
						
							|  |  |  |           return S_OK; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       std::string utf8FilePath=utf16To8(filePath); | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       if (selCallback!=NULL) { | 
					
						
							|  |  |  |         logV("%p: calling back.",(const void*)this); | 
					
						
							|  |  |  |         selCallback(utf8FilePath.c_str()); | 
					
						
							|  |  |  |         logV("%p: end of callback",(const void*)this); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         logV("%p: no callback.",(const void*)this); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       logV("%p: I got you for a value of %s",(const void*)this,utf8FilePath.c_str()); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       shellItem->Release(); | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |       logV("%p: shellItem->Release()",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |       return S_OK; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     NFDWinEvents(nfdselcallback_t callback): | 
					
						
							|  |  |  |       selCallback(callback), | 
					
						
							|  |  |  |       refCount(1) { | 
					
						
							| 
									
										
										
										
											2023-01-16 22:38:46 -05:00
										 |  |  |         logV("%p: CONSTRUCT!",(const void*)this); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define COM_INITFLAGS ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static BOOL COMIsInitialized(HRESULT coResult) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (coResult == RPC_E_CHANGED_MODE) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // If COM was previously initialized with different init flags,
 | 
					
						
							|  |  |  |         // NFD still needs to operate. Eat this warning.
 | 
					
						
							|  |  |  |         return TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return SUCCEEDED(coResult); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static HRESULT COMInit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return ::CoInitializeEx(NULL, COM_INITFLAGS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void COMUninit(HRESULT coResult) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // do not uninitialize if RPC_E_CHANGED_MODE occurred -- this
 | 
					
						
							|  |  |  |     // case does not refcount COM.
 | 
					
						
							|  |  |  |     if (SUCCEEDED(coResult)) | 
					
						
							|  |  |  |         ::CoUninitialize(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // allocs the space in outPath -- call free()
 | 
					
						
							|  |  |  | static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int inStrCharacterCount = static_cast<int>(wcslen(inStr));  | 
					
						
							|  |  |  |     int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, | 
					
						
							|  |  |  |                                            inStr, inStrCharacterCount, | 
					
						
							|  |  |  |                                            NULL, 0, NULL, NULL );     | 
					
						
							|  |  |  |     assert( bytesNeeded ); | 
					
						
							|  |  |  |     bytesNeeded += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded ); | 
					
						
							|  |  |  |     if ( !*outStr ) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, | 
					
						
							|  |  |  |                                             inStr, -1, | 
					
						
							|  |  |  |                                             *outStr, bytesNeeded, | 
					
						
							|  |  |  |                                             NULL, NULL ); | 
					
						
							|  |  |  |     assert( bytesWritten ); _NFD_UNUSED( bytesWritten ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* includes NULL terminator byte in return */ | 
					
						
							|  |  |  | static size_t GetUTF8ByteCountForWChar( const wchar_t *str ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, | 
					
						
							|  |  |  |                                               str, -1, | 
					
						
							|  |  |  |                                               NULL, 0, NULL, NULL ); | 
					
						
							|  |  |  |     assert( bytesNeeded ); | 
					
						
							|  |  |  |     return bytesNeeded+1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // write to outPtr -- no free() necessary.
 | 
					
						
							|  |  |  | static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr )); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* invocation copies null term */ | 
					
						
							|  |  |  |     int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, | 
					
						
							|  |  |  |                                             inStr, -1, | 
					
						
							|  |  |  |                                             outPtr, bytesNeeded, | 
					
						
							|  |  |  |                                             NULL, 0 ); | 
					
						
							|  |  |  |     assert( bytesWritten ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return bytesWritten; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // allocs the space in outStr -- call free()
 | 
					
						
							|  |  |  | static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int inStrByteCount = static_cast<int>(strlen(inStr)); | 
					
						
							|  |  |  |     int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, | 
					
						
							|  |  |  |                                           inStr, inStrByteCount, | 
					
						
							|  |  |  |                                           NULL, 0 );     | 
					
						
							|  |  |  |     assert( charsNeeded ); | 
					
						
							|  |  |  |     assert( !*outStr ); | 
					
						
							|  |  |  |     charsNeeded += 1; // terminator
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     *outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) );     | 
					
						
							|  |  |  |     if ( !*outStr ) | 
					
						
							|  |  |  |         return;         | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int ret = MultiByteToWideChar(CP_UTF8, 0, | 
					
						
							|  |  |  |                                   inStr, inStrByteCount, | 
					
						
							|  |  |  |                                   *outStr, charsNeeded); | 
					
						
							|  |  |  |     (*outStr)[charsNeeded-1] = '\0'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef _DEBUG
 | 
					
						
							|  |  |  |     int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr)); | 
					
						
							| 
									
										
										
										
											2023-04-10 14:12:01 -04:00
										 |  |  |     if (ret!=inStrCharacterCount) { | 
					
						
							|  |  |  |       logW("length does not match! %d != %d",ret,inStrCharacterCount); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | #else
 | 
					
						
							|  |  |  |     _NFD_UNUSED(ret); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  | static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const std::vector<std::string>& filterList ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     const wchar_t WILDCARD[] = L"*.*"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |     if (filterList.empty()) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |         return NFD_OKAY; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |     // list size has to be an even number (name/filter)
 | 
					
						
							|  |  |  |     if (filterList.size()&1) | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     // Count rows to alloc
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |     UINT filterCount = filterList.size()>>1; /* guaranteed to have one filter on a correct, non-empty parse */ | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |     if (filterCount==0) filterCount=1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     if ( !filterCount ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Error parsing filters."); | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |     COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount) ); | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     if ( !specList ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |     for (UINT i = 0; i < filterCount; ++i ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     { | 
					
						
							|  |  |  |         specList[i].pszName = NULL; | 
					
						
							|  |  |  |         specList[i].pszSpec = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     size_t specIdx = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |     for (size_t i=0; i<filterList.size(); i+=2) { | 
					
						
							|  |  |  |       String name=filterList[i]; | 
					
						
							|  |  |  |       String spec=filterList[i+1]; | 
					
						
							|  |  |  |       for (char& i: spec) { | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |         if (i==' ') i=';'; | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |       if (spec==".*") spec="*.*"; | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  |       CopyNFDCharToWChar( name.c_str(), (wchar_t**)&specList[specIdx].pszName ); | 
					
						
							|  |  |  |       CopyNFDCharToWChar( spec.c_str(), (wchar_t**)&specList[specIdx].pszSpec ); | 
					
						
							|  |  |  |       ++specIdx; | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |     /* Add wildcard if specIdx is 0 */ | 
					
						
							|  |  |  |     if (specIdx==0) { | 
					
						
							|  |  |  |       specList[specIdx].pszSpec = WILDCARD; | 
					
						
							|  |  |  |       specList[specIdx].pszName = WILDCARD; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2022-06-27 04:23:13 -04:00
										 |  |  |     fileOpenDialog->SetFileTypes( filterCount, specList ); | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* free speclist */ | 
					
						
							|  |  |  |     for ( size_t i = 0; i < filterCount; ++i ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_Free( (void*)specList[i].pszSpec ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     NFDi_Free( specList );     | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return NFD_OKAY; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const char ERRORMSG[] = "Error allocating pathset."; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(shellItems); | 
					
						
							|  |  |  |     assert(pathSet); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // How many items in shellItems?
 | 
					
						
							|  |  |  |     DWORD numShellItems; | 
					
						
							|  |  |  |     HRESULT result = shellItems->GetCount(&numShellItems); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError(ERRORMSG); | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pathSet->count = static_cast<size_t>(numShellItems); | 
					
						
							|  |  |  |     assert( pathSet->count > 0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count ); | 
					
						
							|  |  |  |     if ( !pathSet->indices ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* count the total bytes needed for buf */ | 
					
						
							|  |  |  |     size_t bufSize = 0; | 
					
						
							|  |  |  |     for ( DWORD i = 0; i < numShellItems; ++i ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ::IShellItem *shellItem; | 
					
						
							|  |  |  |         result = shellItems->GetItemAt(i, &shellItem); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError(ERRORMSG); | 
					
						
							|  |  |  |             return NFD_ERROR; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
 | 
					
						
							|  |  |  |         SFGAOF attribs; | 
					
						
							|  |  |  |         result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError(ERRORMSG); | 
					
						
							|  |  |  |             return NFD_ERROR; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ( !(attribs & SFGAO_FILESYSTEM) ) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         LPWSTR name; | 
					
						
							|  |  |  |         shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Calculate length of name with UTF-8 encoding
 | 
					
						
							|  |  |  |         bufSize += GetUTF8ByteCountForWChar( name ); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         CoTaskMemFree(name); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(bufSize); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); | 
					
						
							|  |  |  |     memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* fill buf */ | 
					
						
							|  |  |  |     nfdchar_t *p_buf = pathSet->buf; | 
					
						
							|  |  |  |     for (DWORD i = 0; i < numShellItems; ++i ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ::IShellItem *shellItem; | 
					
						
							|  |  |  |         result = shellItems->GetItemAt(i, &shellItem); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError(ERRORMSG); | 
					
						
							|  |  |  |             return NFD_ERROR; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
 | 
					
						
							|  |  |  |         SFGAOF attribs; | 
					
						
							|  |  |  |         result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError(ERRORMSG); | 
					
						
							|  |  |  |             return NFD_ERROR; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ( !(attribs & SFGAO_FILESYSTEM) ) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         LPWSTR name; | 
					
						
							|  |  |  |         shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf); | 
					
						
							|  |  |  |         CoTaskMemFree(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ptrdiff_t index = p_buf - pathSet->buf; | 
					
						
							|  |  |  |         assert( index >= 0 ); | 
					
						
							|  |  |  |         pathSet->indices[i] = static_cast<size_t>(index); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         p_buf += bytesWritten;  | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |       | 
					
						
							|  |  |  |     return NFD_OKAY; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-14 01:14:33 -04:00
										 |  |  |     if ( !defaultPath || strlen(defaultPath) == 0 || strcmp(defaultPath,"\\")==0 ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |         return NFD_OKAY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     wchar_t *defaultPathW = {0}; | 
					
						
							|  |  |  |     CopyNFDCharToWChar( defaultPath, &defaultPathW ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IShellItem *folder; | 
					
						
							|  |  |  |     HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Valid non results.
 | 
					
						
							|  |  |  |     if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_Free( defaultPathW ); | 
					
						
							|  |  |  |         return NFD_OKAY; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Error creating ShellItem"); | 
					
						
							|  |  |  |         NFDi_Free( defaultPathW ); | 
					
						
							|  |  |  |         return NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API.
 | 
					
						
							|  |  |  |     dialog->SetFolder( folder ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NFDi_Free( defaultPathW ); | 
					
						
							|  |  |  |     folder->Release(); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return NFD_OKAY; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* public */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  | nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList, | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |                             const nfdchar_t *defaultPath, | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |                             nfdchar_t **outPath, | 
					
						
							| 
									
										
										
										
											2024-09-25 20:03:40 -04:00
										 |  |  |                             nfdselcallback_t selCallback, | 
					
						
							|  |  |  |                             const nfdchar_t *defaultFileName ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     nfdresult_t nfdResult = NFD_ERROR; | 
					
						
							| 
									
										
										
										
											2023-01-18 14:22:36 -05:00
										 |  |  |     NFDWinEvents* winEvents; | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     bool hasEvents=true; | 
					
						
							|  |  |  |     DWORD eventID=0; | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |      | 
					
						
							|  |  |  |     HRESULT coResult = COMInit(); | 
					
						
							|  |  |  |     if (!COMIsInitialized(coResult)) | 
					
						
							|  |  |  |     {         | 
					
						
							|  |  |  |         NFDi_SetError("Could not initialize COM."); | 
					
						
							|  |  |  |         return nfdResult; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create dialog
 | 
					
						
							|  |  |  |     ::IFileOpenDialog *fileOpenDialog(NULL);     | 
					
						
							|  |  |  |     HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, | 
					
						
							|  |  |  |                                         CLSCTX_ALL, ::IID_IFileOpenDialog, | 
					
						
							|  |  |  |                                         reinterpret_cast<void**>(&fileOpenDialog) ); | 
					
						
							|  |  |  |                                  | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not create dialog."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Build the filter list
 | 
					
						
							|  |  |  |     if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set the default path
 | 
					
						
							|  |  |  |     if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 01:30:16 -04:00
										 |  |  |     // Set the file name
 | 
					
						
							|  |  |  |     if (defaultFileName!=NULL) { | 
					
						
							|  |  |  |       std::wstring defFileName=utf8To16(defaultFileName); | 
					
						
							|  |  |  |       result = fileOpenDialog->SetFileName(defFileName.c_str()); | 
					
						
							|  |  |  |       if ( !SUCCEEDED(result) ) { | 
					
						
							|  |  |  |         // ignore
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     // Pass the callback
 | 
					
						
							| 
									
										
										
										
											2023-01-18 14:22:36 -05:00
										 |  |  |     winEvents=new NFDWinEvents(selCallback); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     if ( !SUCCEEDED(fileOpenDialog->Advise(winEvents,&eventID)) ) { | 
					
						
							|  |  |  |       // error... ignore
 | 
					
						
							|  |  |  |       hasEvents=false; | 
					
						
							|  |  |  |       winEvents->Release(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       winEvents->Release(); | 
					
						
							| 
									
										
										
										
											2023-01-18 14:22:36 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Show the dialog.
 | 
					
						
							| 
									
										
										
										
											2022-06-19 01:11:18 -04:00
										 |  |  |     // TODO: pass the Furnace window here
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     result = fileOpenDialog->Show(NULL); | 
					
						
							|  |  |  |     if ( SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Get the file name
 | 
					
						
							|  |  |  |         ::IShellItem *shellItem(NULL); | 
					
						
							|  |  |  |         result = fileOpenDialog->GetResult(&shellItem); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("Could not get shell item from dialog."); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         wchar_t *filePath(NULL); | 
					
						
							|  |  |  |         result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("Could not get file path for selected."); | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         CopyWCharToNFDChar( filePath, outPath ); | 
					
						
							|  |  |  |         CoTaskMemFree(filePath); | 
					
						
							|  |  |  |         if ( !*outPath ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             /* error is malloc-based, error message would be redundant */ | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         nfdResult = NFD_OKAY; | 
					
						
							|  |  |  |         shellItem->Release(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         nfdResult = NFD_CANCEL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("File dialog box show failed."); | 
					
						
							|  |  |  |         nfdResult = NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end: | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     if (fileOpenDialog) { | 
					
						
							|  |  |  |         if (hasEvents) { | 
					
						
							|  |  |  |           fileOpenDialog->Unadvise(eventID); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |         fileOpenDialog->Release(); | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     COMUninit(coResult); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return nfdResult; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  | nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList, | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |                                     const nfdchar_t *defaultPath, | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |                                     nfdpathset_t *outPaths, | 
					
						
							| 
									
										
										
										
											2024-09-25 20:03:40 -04:00
										 |  |  |                                     nfdselcallback_t selCallback, | 
					
						
							|  |  |  |                             const nfdchar_t *defaultFileName ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     nfdresult_t nfdResult = NFD_ERROR; | 
					
						
							| 
									
										
										
										
											2023-08-07 20:12:24 -04:00
										 |  |  |     NFDWinEvents* winEvents; | 
					
						
							|  |  |  |     bool hasEvents=true; | 
					
						
							|  |  |  |     DWORD eventID=0; | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HRESULT coResult = COMInit(); | 
					
						
							|  |  |  |     if (!COMIsInitialized(coResult)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not initialize COM.");         | 
					
						
							|  |  |  |         return nfdResult; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create dialog
 | 
					
						
							|  |  |  |     ::IFileOpenDialog *fileOpenDialog(NULL);     | 
					
						
							|  |  |  |     HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, | 
					
						
							|  |  |  |                                         CLSCTX_ALL, ::IID_IFileOpenDialog, | 
					
						
							|  |  |  |                                         reinterpret_cast<void**>(&fileOpenDialog) ); | 
					
						
							|  |  |  |                                  | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         fileOpenDialog = NULL; | 
					
						
							|  |  |  |         NFDi_SetError("Could not create dialog."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Build the filter list
 | 
					
						
							|  |  |  |     if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set the default path
 | 
					
						
							|  |  |  |     if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 01:30:16 -04:00
										 |  |  |     // Set the file name
 | 
					
						
							|  |  |  |     if (defaultFileName!=NULL) { | 
					
						
							|  |  |  |       std::wstring defFileName=utf8To16(defaultFileName); | 
					
						
							|  |  |  |       result = fileOpenDialog->SetFileName(defFileName.c_str()); | 
					
						
							|  |  |  |       if ( !SUCCEEDED(result) ) { | 
					
						
							|  |  |  |         // ignore
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 20:12:24 -04:00
										 |  |  |     // Pass the callback
 | 
					
						
							|  |  |  |     winEvents=new NFDWinEvents(selCallback); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(fileOpenDialog->Advise(winEvents,&eventID)) ) { | 
					
						
							|  |  |  |       // error... ignore
 | 
					
						
							|  |  |  |       hasEvents=false; | 
					
						
							|  |  |  |       winEvents->Release(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       winEvents->Release(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     // Set a flag for multiple options
 | 
					
						
							|  |  |  |     DWORD dwFlags; | 
					
						
							|  |  |  |     result = fileOpenDialog->GetOptions(&dwFlags); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not get options."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not set options."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |     // Show the dialog.
 | 
					
						
							|  |  |  |     result = fileOpenDialog->Show(NULL); | 
					
						
							|  |  |  |     if ( SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         IShellItemArray *shellItems; | 
					
						
							|  |  |  |         result = fileOpenDialog->GetResults( &shellItems ); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("Could not get shell items."); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             shellItems->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         shellItems->Release(); | 
					
						
							|  |  |  |         nfdResult = NFD_OKAY; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         nfdResult = NFD_CANCEL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("File dialog box show failed."); | 
					
						
							|  |  |  |         nfdResult = NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end: | 
					
						
							| 
									
										
										
										
											2023-08-07 20:12:24 -04:00
										 |  |  |     if (fileOpenDialog) { | 
					
						
							|  |  |  |         if (hasEvents) { | 
					
						
							|  |  |  |           fileOpenDialog->Unadvise(eventID); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |         fileOpenDialog->Release(); | 
					
						
							| 
									
										
										
										
											2023-08-07 20:12:24 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     COMUninit(coResult); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return nfdResult; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-24 04:11:16 -04:00
										 |  |  | nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList, | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |                             const nfdchar_t *defaultPath, | 
					
						
							| 
									
										
										
										
											2022-06-19 02:36:36 -04:00
										 |  |  |                             nfdchar_t **outPath, | 
					
						
							| 
									
										
										
										
											2024-09-25 20:03:40 -04:00
										 |  |  |                             nfdselcallback_t selCallback, | 
					
						
							|  |  |  |                             const nfdchar_t *defaultFileName ) | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     nfdresult_t nfdResult = NFD_ERROR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HRESULT coResult = COMInit(); | 
					
						
							|  |  |  |     if (!COMIsInitialized(coResult)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not initialize COM."); | 
					
						
							|  |  |  |         return nfdResult;         | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Create dialog
 | 
					
						
							|  |  |  |     ::IFileSaveDialog *fileSaveDialog(NULL);     | 
					
						
							|  |  |  |     HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL, | 
					
						
							|  |  |  |                                         CLSCTX_ALL, ::IID_IFileSaveDialog, | 
					
						
							|  |  |  |                                         reinterpret_cast<void**>(&fileSaveDialog) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         fileSaveDialog = NULL; | 
					
						
							|  |  |  |         NFDi_SetError("Could not create dialog."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Build the filter list
 | 
					
						
							|  |  |  |     if ( !AddFiltersToDialog( fileSaveDialog, filterList ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set the default path
 | 
					
						
							|  |  |  |     if ( !SetDefaultPath( fileSaveDialog, defaultPath ) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 01:30:16 -04:00
										 |  |  |     // Set the file name
 | 
					
						
							|  |  |  |     if (defaultFileName!=NULL) { | 
					
						
							|  |  |  |       std::wstring defFileName=utf8To16(defaultFileName); | 
					
						
							|  |  |  |       result = fileSaveDialog->SetFileName(defFileName.c_str()); | 
					
						
							|  |  |  |       if ( !SUCCEEDED(result) ) { | 
					
						
							|  |  |  |         // ignore
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 19:13:00 -04:00
										 |  |  |     // Set a flag for no history
 | 
					
						
							|  |  |  |     DWORD dwFlags; | 
					
						
							|  |  |  |     result = fileSaveDialog->GetOptions(&dwFlags); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not get options."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     result = fileSaveDialog->SetOptions(dwFlags | FOS_DONTADDTORECENT); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Could not set options."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 01:02:29 -04:00
										 |  |  |     // Show the dialog.
 | 
					
						
							|  |  |  |     result = fileSaveDialog->Show(NULL); | 
					
						
							|  |  |  |     if ( SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Get the file name
 | 
					
						
							|  |  |  |         ::IShellItem *shellItem; | 
					
						
							|  |  |  |         result = fileSaveDialog->GetResult(&shellItem); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("Could not get shell item from dialog."); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         wchar_t *filePath(NULL); | 
					
						
							|  |  |  |         result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             NFDi_SetError("Could not get file path for selected."); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         CopyWCharToNFDChar( filePath, outPath ); | 
					
						
							|  |  |  |         CoTaskMemFree(filePath); | 
					
						
							|  |  |  |         if ( !*outPath ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             /* error is malloc-based, error message would be redundant */ | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         nfdResult = NFD_OKAY; | 
					
						
							|  |  |  |         shellItem->Release(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         nfdResult = NFD_CANCEL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("File dialog box show failed."); | 
					
						
							|  |  |  |         nfdResult = NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     if ( fileSaveDialog ) | 
					
						
							|  |  |  |         fileSaveDialog->Release(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMUninit(coResult); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return nfdResult; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, | 
					
						
							|  |  |  |     nfdchar_t **outPath) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nfdresult_t nfdResult = NFD_ERROR; | 
					
						
							|  |  |  |     DWORD dwOptions = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HRESULT coResult = COMInit(); | 
					
						
							|  |  |  |     if (!COMIsInitialized(coResult)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("CoInitializeEx failed."); | 
					
						
							|  |  |  |         return nfdResult; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create dialog
 | 
					
						
							|  |  |  |     ::IFileOpenDialog *fileDialog(NULL); | 
					
						
							|  |  |  |     HRESULT result = CoCreateInstance(CLSID_FileOpenDialog, | 
					
						
							|  |  |  |                                       NULL, | 
					
						
							|  |  |  |                                       CLSCTX_ALL, | 
					
						
							|  |  |  |                                       IID_PPV_ARGS(&fileDialog)); | 
					
						
							|  |  |  |     if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |     {         | 
					
						
							|  |  |  |         NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set the default path
 | 
					
						
							|  |  |  |     if (SetDefaultPath(fileDialog, defaultPath) != NFD_OKAY) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("SetDefaultPath failed."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Get the dialogs options
 | 
					
						
							|  |  |  |     if (!SUCCEEDED(fileDialog->GetOptions(&dwOptions))) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("GetOptions for IFileDialog failed."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Add in FOS_PICKFOLDERS which hides files and only allows selection of folders
 | 
					
						
							|  |  |  |     if (!SUCCEEDED(fileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS))) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("SetOptions for IFileDialog failed."); | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Show the dialog to the user
 | 
					
						
							|  |  |  |     result = fileDialog->Show(NULL); | 
					
						
							|  |  |  |     if ( SUCCEEDED(result) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Get the folder name
 | 
					
						
							|  |  |  |         ::IShellItem *shellItem(NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result = fileDialog->GetResult(&shellItem); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("Could not get file path for selected."); | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         wchar_t *path = NULL; | 
					
						
							|  |  |  |         result = shellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); | 
					
						
							|  |  |  |         if ( !SUCCEEDED(result) ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             NFDi_SetError("GetDisplayName for IShellItem failed.");             | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         CopyWCharToNFDChar(path, outPath); | 
					
						
							|  |  |  |         CoTaskMemFree(path); | 
					
						
							|  |  |  |         if ( !*outPath ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             shellItem->Release(); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         nfdResult = NFD_OKAY; | 
					
						
							|  |  |  |         shellItem->Release(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         nfdResult = NFD_CANCEL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         NFDi_SetError("Show for IFileDialog failed."); | 
					
						
							|  |  |  |         nfdResult = NFD_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  end: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (fileDialog) | 
					
						
							|  |  |  |         fileDialog->Release(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMUninit(coResult); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return nfdResult; | 
					
						
							|  |  |  | } |