diff --git a/Engine/lib/nativeFileDialogs/SConstruct b/Engine/lib/nativeFileDialogs/SConstruct index 342fa1a3a..b1a9e4881 100644 --- a/Engine/lib/nativeFileDialogs/SConstruct +++ b/Engine/lib/nativeFileDialogs/SConstruct @@ -3,7 +3,8 @@ # # Scons build script -- GCC, Clang, Visual Studio # Does not build test - +# +# SCons builds are deprecated -- see README.md for details. import os @@ -97,3 +98,5 @@ set_warnings(nfd_env) nfd_env.Append( CPPPATH=['.','./include'] ) nfd_env.StaticLibrary( get_lib_name('nfd', debug), nfd_files ) + +print "*** Scons builds are deprecated! See README.md for details." diff --git a/Engine/lib/nativeFileDialogs/common.h b/Engine/lib/nativeFileDialogs/common.h index 688b0b1fd..7745d323b 100644 --- a/Engine/lib/nativeFileDialogs/common.h +++ b/Engine/lib/nativeFileDialogs/common.h @@ -11,7 +11,7 @@ #define _NFD_COMMON_H #define NFD_MAX_STRLEN 256 -#define _NFD_UNUSED(x) ((void)x) +#define _NFD_UNUSED(x) ((void)x) void *NFDi_Malloc( size_t bytes ); void NFDi_Free( void *ptr ); diff --git a/Engine/lib/nativeFileDialogs/include/nfd.h b/Engine/lib/nativeFileDialogs/include/nfd.h index 03fe53206..74c92743f 100644 --- a/Engine/lib/nativeFileDialogs/include/nfd.h +++ b/Engine/lib/nativeFileDialogs/include/nfd.h @@ -50,6 +50,11 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, const nfdchar_t *defaultPath, nfdchar_t **outPath ); + +/* select folder dialog */ +nfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath, + nfdchar_t **outPath); + /* nfd_common.c */ /* get last error -- set when nfdresult_t returns NFD_ERROR */ diff --git a/Engine/lib/nativeFileDialogs/nfd_cocoa.m b/Engine/lib/nativeFileDialogs/nfd_cocoa.m index a73e15714..d3fb48347 100644 --- a/Engine/lib/nativeFileDialogs/nfd_cocoa.m +++ b/Engine/lib/nativeFileDialogs/nfd_cocoa.m @@ -122,7 +122,8 @@ nfdresult_t NFD_OpenDialog( const char *filterList, nfdchar_t **outPath ) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; NSOpenPanel *dialog = [NSOpenPanel openPanel]; [dialog setAllowsMultipleSelection:NO]; @@ -152,6 +153,7 @@ nfdresult_t NFD_OpenDialog( const char *filterList, } [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; return nfdResult; } @@ -233,3 +235,42 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, return nfdResult; } + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:NO]; + [dialog setCanChooseDirectories:YES]; + [dialog setCanCreateDirectories:YES]; + [dialog setCanChooseFiles:NO]; + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + // byte count, not char count + size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); + + *outPath = NFDi_Malloc( len+1 ); + if ( !*outPath ) + { + [pool release]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ + nfdResult = NFD_OKAY; + } + [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} diff --git a/Engine/lib/nativeFileDialogs/nfd_gtk.c b/Engine/lib/nativeFileDialogs/nfd_gtk.c index fd15a86dd..65bc41dad 100644 --- a/Engine/lib/nativeFileDialogs/nfd_gtk.c +++ b/Engine/lib/nativeFileDialogs/nfd_gtk.c @@ -47,14 +47,10 @@ static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList ) if ( NFDi_IsFilterSegmentChar(*p_filterList) ) { char typebufWildcard[NFD_MAX_STRLEN]; - /* add another type to the filter */ - if (strlen(typebuf) <= 0 || strlen(typebuf) > NFD_MAX_STRLEN-1) - { - p_filterList++; - continue; - } - + assert( strlen(typebuf) > 0 ); + assert( strlen(typebuf) < NFD_MAX_STRLEN-1 ); + snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf ); AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN ); @@ -297,6 +293,59 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, /* Build the filter list */ AddFiltersToDialog(dialog, filterList); + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free(filename); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + GtkWidget *dialog; + nfdresult_t result; + + if (!gtk_init_check(NULL, NULL)) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Select folder", + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Select", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + /* Set the default path */ SetDefaultPath(dialog, defaultPath); diff --git a/Engine/lib/nativeFileDialogs/nfd_win.cpp b/Engine/lib/nativeFileDialogs/nfd_win.cpp index 45878824a..a73fd8a46 100644 --- a/Engine/lib/nativeFileDialogs/nfd_win.cpp +++ b/Engine/lib/nativeFileDialogs/nfd_win.cpp @@ -9,13 +9,17 @@ #define UNICODE #endif +#ifdef __MINGW32__ +// Explicitly setting NTDDI version, this is necessary for the MinGW compiler +#define NTDDI_VERSION NTDDI_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif #include #include #include #include #include - #include "nfd_common.h" @@ -359,14 +363,15 @@ nfdresult_t NFD_OpenDialog( const char *filterList, HRESULT result = ::CoInitializeEx(NULL, ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE ); + + ::IFileOpenDialog *fileOpenDialog(NULL); + if ( !SUCCEEDED(result)) { NFDi_SetError("Could not initialize COM."); goto end; } - ::IFileOpenDialog *fileOpenDialog(NULL); - // Create dialog result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, CLSCTX_ALL, ::IID_IFileOpenDialog, @@ -616,3 +621,133 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, return nfdResult; } + +class AutoCoInit +{ +public: + AutoCoInit() + { + mResult = ::CoInitializeEx(NULL, + ::COINIT_APARTMENTTHREADED | + ::COINIT_DISABLE_OLE1DDE); + } + + ~AutoCoInit() + { + if (SUCCEEDED(mResult)) + { + ::CoUninitialize(); + } + } + + HRESULT Result() const { return mResult; } +private: + HRESULT mResult; +}; + +// VS2010 hasn't got a copy of CComPtr - this was first added in the 2003 SDK, so we make our own small CComPtr instead +template +class ComPtr +{ +public: + ComPtr() : mPtr(NULL) { } + ~ComPtr() + { + if (mPtr) + { + mPtr->Release(); + } + } + + T* Ptr() const { return mPtr; } + T** operator&() { return &mPtr; } + T* operator->() const { return mPtr; } +private: + // Don't allow copy or assignment + ComPtr(const ComPtr&); + ComPtr& operator = (const ComPtr&) const; + T* mPtr; +}; + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + // Init COM + AutoCoInit autoCoInit; + if (!SUCCEEDED(autoCoInit.Result())) + { + NFDi_SetError("CoInitializeEx failed."); + return NFD_ERROR; + } + + // Create the file dialog COM object + ComPtr pFileDialog; + if (!SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, + NULL, + CLSCTX_ALL, + IID_PPV_ARGS(&pFileDialog)))) + { + NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed."); + return NFD_ERROR; + } + + // Set the default path + if (SetDefaultPath(pFileDialog.Ptr(), defaultPath) != NFD_OKAY) + { + NFDi_SetError("SetDefaultPath failed."); + return NFD_ERROR; + } + + // Get the dialogs options + DWORD dwOptions = 0; + if (!SUCCEEDED(pFileDialog->GetOptions(&dwOptions))) + { + NFDi_SetError("GetOptions for IFileDialog failed."); + return NFD_ERROR; + } + + // Add in FOS_PICKFOLDERS which hides files and only allows selection of folders + if (!SUCCEEDED(pFileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS))) + { + NFDi_SetError("SetOptions for IFileDialog failed."); + return NFD_ERROR; + } + + // Show the dialog to the user + const HRESULT result = pFileDialog->Show(NULL); + if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) + { + return NFD_CANCEL; + } + else if (!SUCCEEDED(result)) + { + NFDi_SetError("Show for IFileDialog failed."); + return NFD_ERROR; + } + + // Get the shell item result + ComPtr pShellItem; + if (!SUCCEEDED(pFileDialog->GetResult(&pShellItem))) + { + return NFD_OKAY; + } + + // Finally get the path + wchar_t *path = NULL; + if (!SUCCEEDED(pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path))) + { + NFDi_SetError("GetDisplayName for IShellItem failed."); + return NFD_ERROR; + } + + // Convert string + CopyWCharToNFDChar(path, outPath); + CoTaskMemFree(path); + if (!*outPath) + { + // error is malloc-based, error message would be redundant + return NFD_ERROR; + } + + return NFD_OKAY; +} diff --git a/Engine/source/platform/nativeDialogs/fileDialog.cpp b/Engine/source/platform/nativeDialogs/fileDialog.cpp index 09ed1c63e..34e57e3e6 100644 --- a/Engine/source/platform/nativeDialogs/fileDialog.cpp +++ b/Engine/source/platform/nativeDialogs/fileDialog.cpp @@ -252,12 +252,14 @@ bool FileDialog::Execute() rootDir.replace("/", "\\"); #endif - if (mData.mStyle & FileDialogData::FDS_OPEN) + if (mData.mStyle & FileDialogData::FDS_OPEN && !(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)) result = NFD_OpenDialog(strippedFilters.c_str(), defaultPath.c_str(), &outPath); - else if (mData.mStyle & FileDialogData::FDS_SAVE) + else if (mData.mStyle & FileDialogData::FDS_SAVE && !(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)) result = NFD_SaveDialog(strippedFilters.c_str(), defaultPath.c_str(), &outPath); else if (mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) result = NFD_OpenDialogMultiple(strippedFilters.c_str(), defaultPath.c_str(), &pathSet); + else if (mData.mStyle & FileDialogData::FDS_BROWSEFOLDER) + result = NFD_PickFolder(defaultPath.c_str(), &outPath); if (result == NFD_CANCEL) {