Skip to content

Commit

Permalink
Merge pull request #24 from paulsapps/NFD_PickFolder
Browse files Browse the repository at this point in the history
Directory picker support #22
  • Loading branch information
mlabbe authored Aug 24, 2016
2 parents 9784f06 + 3f96999 commit b35ff97
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 2 deletions.
3 changes: 2 additions & 1 deletion build/premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ local make_test = function(name)
links {"ole32", "uuid"}

end


make_test("test_pickfolder")
make_test("test_opendialog")
make_test("test_opendialogmultiple")
make_test("test_savedialog")
Expand Down
5 changes: 5 additions & 0 deletions src/include/nfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
39 changes: 39 additions & 0 deletions src/nfd_cocoa.m
Original file line number Diff line number Diff line change
Expand Up @@ -235,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;
}
53 changes: 53 additions & 0 deletions src/nfd_gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,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);

Expand Down
131 changes: 130 additions & 1 deletion src/nfd_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <assert.h>
#include <windows.h>
#include <ShObjIdl.h>

#include "nfd_common.h"


Expand Down Expand Up @@ -622,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;
}
29 changes: 29 additions & 0 deletions test/test_pickfolder.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "nfd.h"

#include <stdio.h>
#include <stdlib.h>


/* this test should compile on all supported platforms */

int main( void )
{
nfdchar_t *outPath = NULL;
nfdresult_t result = NFD_PickFolder( NULL, &outPath );
if ( result == NFD_OKAY )
{
puts("Success!");
puts(outPath);
free(outPath);
}
else if ( result == NFD_CANCEL )
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError() );
}

return 0;
}

0 comments on commit b35ff97

Please sign in to comment.