Updates Native File Dialogs lib to enable browsing and selecting of folders, instead of just files.

This commit is contained in:
Areloch 2017-12-06 14:09:27 -06:00
parent 4f78143dc8
commit f657f774ce
7 changed files with 250 additions and 15 deletions

View file

@ -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."

View file

@ -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 );

View file

@ -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 */

View file

@ -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;
}

View file

@ -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);

View file

@ -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 <wchar.h>
#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <ShObjIdl.h>
#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 T>
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<IFileDialog> 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<IShellItem> 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;
}