Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory picker support #22 #24

Merged
merged 1 commit into from
Aug 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}