add modified version of Native File Dialog
it will replace portable-file-dialogs on Windows, and perhaps in the rest of operating systems (maybe not Linux) as well.
This commit is contained in:
parent
ad5072dad6
commit
d5d381328b
19 changed files with 2796 additions and 0 deletions
21
extern/nfd-modified/src/common.h
vendored
Normal file
21
extern/nfd-modified/src/common.h
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
Internal, common across platforms
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NFD_COMMON_H
|
||||
#define _NFD_COMMON_H
|
||||
|
||||
#define NFD_MAX_STRLEN 256
|
||||
#define _NFD_UNUSED(x) ((void)x)
|
||||
|
||||
void *NFDi_Malloc( size_t bytes );
|
||||
void NFDi_Free( void *ptr );
|
||||
void NFDi_SetError( const char *msg );
|
||||
void NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
|
||||
|
||||
#endif
|
||||
74
extern/nfd-modified/src/include/nfd.h
vendored
Normal file
74
extern/nfd-modified/src/include/nfd.h
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
User API
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NFD_H
|
||||
#define _NFD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* denotes UTF-8 char */
|
||||
typedef char nfdchar_t;
|
||||
|
||||
/* opaque data structure -- see NFD_PathSet_* */
|
||||
typedef struct {
|
||||
nfdchar_t *buf;
|
||||
size_t *indices; /* byte offsets into buf */
|
||||
size_t count; /* number of indices into buf */
|
||||
}nfdpathset_t;
|
||||
|
||||
typedef enum {
|
||||
NFD_ERROR, /* programmatic error */
|
||||
NFD_OKAY, /* user pressed okay, or successful return */
|
||||
NFD_CANCEL /* user pressed cancel */
|
||||
}nfdresult_t;
|
||||
|
||||
|
||||
/* nfd_<targetplatform>.c */
|
||||
|
||||
/* single file open dialog */
|
||||
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath );
|
||||
|
||||
/* multiple file open dialog */
|
||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdpathset_t *outPaths );
|
||||
|
||||
/* save dialog */
|
||||
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 */
|
||||
const char *NFD_GetError( void );
|
||||
/* get the number of entries stored in pathSet */
|
||||
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathSet );
|
||||
/* Get the UTF-8 path at offset index */
|
||||
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index );
|
||||
/* Free the pathSet */
|
||||
void NFD_PathSet_Free( nfdpathset_t *pathSet );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
286
extern/nfd-modified/src/nfd_cocoa.m
vendored
Normal file
286
extern/nfd-modified/src/nfd_cocoa.m
vendored
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
#include "nfd.h"
|
||||
#include "nfd_common.h"
|
||||
|
||||
static NSArray *BuildAllowedFileTypes( const char *filterList )
|
||||
{
|
||||
// Commas and semicolons are the same thing on this platform
|
||||
|
||||
NSMutableArray *buildFilterList = [[NSMutableArray alloc] init];
|
||||
|
||||
char typebuf[NFD_MAX_STRLEN] = {0};
|
||||
|
||||
size_t filterListLen = strlen(filterList);
|
||||
char *p_typebuf = typebuf;
|
||||
for ( size_t i = 0; i < filterListLen+1; ++i )
|
||||
{
|
||||
if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' )
|
||||
{
|
||||
if (filterList[i] != '\0')
|
||||
++p_typebuf;
|
||||
*p_typebuf = '\0';
|
||||
|
||||
NSString *thisType = [NSString stringWithUTF8String: typebuf];
|
||||
[buildFilterList addObject:thisType];
|
||||
p_typebuf = typebuf;
|
||||
*p_typebuf = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
*p_typebuf = filterList[i];
|
||||
++p_typebuf;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *returnArray = [NSArray arrayWithArray:buildFilterList];
|
||||
|
||||
[buildFilterList release];
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList )
|
||||
{
|
||||
if ( !filterList || strlen(filterList) == 0 )
|
||||
return;
|
||||
|
||||
NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList );
|
||||
if ( [allowedFileTypes count] != 0 )
|
||||
{
|
||||
[dialog setAllowedFileTypes:allowedFileTypes];
|
||||
}
|
||||
}
|
||||
|
||||
static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath )
|
||||
{
|
||||
if ( !defaultPath || strlen(defaultPath) == 0 )
|
||||
return;
|
||||
|
||||
NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath];
|
||||
NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
|
||||
/* fixme: pathset should be pathSet */
|
||||
static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset )
|
||||
{
|
||||
assert(pathset);
|
||||
assert([urls count]);
|
||||
|
||||
pathset->count = (size_t)[urls count];
|
||||
pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count );
|
||||
if ( !pathset->indices )
|
||||
{
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
// count the total space needed for buf
|
||||
size_t bufsize = 0;
|
||||
for ( NSURL *url in urls )
|
||||
{
|
||||
NSString *path = [url path];
|
||||
bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
}
|
||||
|
||||
pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize );
|
||||
if ( !pathset->buf )
|
||||
{
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
// fill buf
|
||||
nfdchar_t *p_buf = pathset->buf;
|
||||
size_t count = 0;
|
||||
for ( NSURL *url in urls )
|
||||
{
|
||||
NSString *path = [url path];
|
||||
const nfdchar_t *utf8Path = [path UTF8String];
|
||||
size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
memcpy( p_buf, utf8Path, byteLen );
|
||||
|
||||
ptrdiff_t index = p_buf - pathset->buf;
|
||||
assert( index >= 0 );
|
||||
pathset->indices[count] = (size_t)index;
|
||||
|
||||
p_buf += byteLen;
|
||||
++count;
|
||||
}
|
||||
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
/* public */
|
||||
|
||||
|
||||
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
[dialog setAllowsMultipleSelection:NO];
|
||||
|
||||
// Build the filter list
|
||||
AddFilterListToDialog(dialog, filterList);
|
||||
|
||||
// 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];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return NFD_ERROR;
|
||||
}
|
||||
memcpy( *outPath, utf8Path, len+1 ); /* copy null term */
|
||||
nfdResult = NFD_OKAY;
|
||||
}
|
||||
[pool release];
|
||||
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return nfdResult;
|
||||
}
|
||||
|
||||
|
||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdpathset_t *outPaths )
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
|
||||
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
[dialog setAllowsMultipleSelection:YES];
|
||||
|
||||
// Build the fiter list.
|
||||
AddFilterListToDialog(dialog, filterList);
|
||||
|
||||
// Set the starting directory
|
||||
SetDefaultPath(dialog, defaultPath);
|
||||
|
||||
nfdresult_t nfdResult = NFD_CANCEL;
|
||||
if ( [dialog runModal] == NSModalResponseOK )
|
||||
{
|
||||
NSArray *urls = [dialog URLs];
|
||||
|
||||
if ( [urls count] == 0 )
|
||||
{
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return NFD_CANCEL;
|
||||
}
|
||||
|
||||
if ( AllocPathSet( urls, outPaths ) == NFD_ERROR )
|
||||
{
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
nfdResult = NFD_OKAY;
|
||||
}
|
||||
[pool release];
|
||||
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return nfdResult;
|
||||
}
|
||||
|
||||
|
||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
|
||||
|
||||
NSSavePanel *dialog = [NSSavePanel savePanel];
|
||||
[dialog setExtensionHidden:NO];
|
||||
|
||||
// Build the filter list.
|
||||
AddFilterListToDialog(dialog, filterList);
|
||||
|
||||
// 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];
|
||||
|
||||
size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
|
||||
*outPath = NFDi_Malloc( byteLen );
|
||||
if ( !*outPath )
|
||||
{
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return NFD_ERROR;
|
||||
}
|
||||
memcpy( *outPath, utf8Path, byteLen );
|
||||
nfdResult = NFD_OKAY;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
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];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return NFD_ERROR;
|
||||
}
|
||||
memcpy( *outPath, utf8Path, len+1 ); /* copy null term */
|
||||
nfdResult = NFD_OKAY;
|
||||
}
|
||||
[pool release];
|
||||
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
return nfdResult;
|
||||
}
|
||||
142
extern/nfd-modified/src/nfd_common.c
vendored
Normal file
142
extern/nfd-modified/src/nfd_common.c
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "nfd_common.h"
|
||||
|
||||
static char g_errorstr[NFD_MAX_STRLEN] = {0};
|
||||
|
||||
/* public routines */
|
||||
|
||||
const char *NFD_GetError( void )
|
||||
{
|
||||
return g_errorstr;
|
||||
}
|
||||
|
||||
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathset )
|
||||
{
|
||||
assert(pathset);
|
||||
return pathset->count;
|
||||
}
|
||||
|
||||
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathset, size_t num )
|
||||
{
|
||||
assert(pathset);
|
||||
assert(num < pathset->count);
|
||||
|
||||
return pathset->buf + pathset->indices[num];
|
||||
}
|
||||
|
||||
void NFD_PathSet_Free( nfdpathset_t *pathset )
|
||||
{
|
||||
assert(pathset);
|
||||
NFDi_Free( pathset->indices );
|
||||
NFDi_Free( pathset->buf );
|
||||
}
|
||||
|
||||
/* internal routines */
|
||||
|
||||
void *NFDi_Malloc( size_t bytes )
|
||||
{
|
||||
void *ptr = malloc(bytes);
|
||||
if ( !ptr )
|
||||
NFDi_SetError("NFDi_Malloc failed.");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void NFDi_Free( void *ptr )
|
||||
{
|
||||
assert(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void NFDi_SetError( const char *msg )
|
||||
{
|
||||
int bTruncate = NFDi_SafeStrncpy( g_errorstr, msg, NFD_MAX_STRLEN );
|
||||
assert( !bTruncate ); _NFD_UNUSED(bTruncate);
|
||||
}
|
||||
|
||||
|
||||
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy )
|
||||
{
|
||||
size_t n = maxCopy;
|
||||
char *d = dst;
|
||||
|
||||
assert( src );
|
||||
assert( dst );
|
||||
|
||||
while ( n > 0 && *src != '\0' )
|
||||
{
|
||||
*d++ = *src++;
|
||||
--n;
|
||||
}
|
||||
|
||||
/* Truncation case -
|
||||
terminate string and return true */
|
||||
if ( n == 0 )
|
||||
{
|
||||
dst[maxCopy-1] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No truncation. Append a single NULL and return. */
|
||||
*d = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* adapted from microutf8 */
|
||||
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str )
|
||||
{
|
||||
/* This function doesn't properly check validity of UTF-8 character
|
||||
sequence, it is supposed to use only with valid UTF-8 strings. */
|
||||
|
||||
int32_t character_count = 0;
|
||||
int32_t i = 0; /* Counter used to iterate over string. */
|
||||
nfdchar_t maybe_bom[4];
|
||||
|
||||
/* If there is UTF-8 BOM ignore it. */
|
||||
if (strlen(str) > 2)
|
||||
{
|
||||
strncpy(maybe_bom, str, 3);
|
||||
maybe_bom[3] = 0;
|
||||
if (strcmp(maybe_bom, (nfdchar_t*)NFD_UTF8_BOM) == 0)
|
||||
i += 3;
|
||||
}
|
||||
|
||||
while(str[i])
|
||||
{
|
||||
if (str[i] >> 7 == 0)
|
||||
{
|
||||
/* If bit pattern begins with 0 we have ascii character. */
|
||||
++character_count;
|
||||
}
|
||||
else if (str[i] >> 6 == 3)
|
||||
{
|
||||
/* If bit pattern begins with 11 it is beginning of UTF-8 byte sequence. */
|
||||
++character_count;
|
||||
}
|
||||
else if (str[i] >> 6 == 2)
|
||||
; /* If bit pattern begins with 10 it is middle of utf-8 byte sequence. */
|
||||
else
|
||||
{
|
||||
/* In any other case this is not valid UTF-8. */
|
||||
return -1;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return character_count;
|
||||
}
|
||||
|
||||
int NFDi_IsFilterSegmentChar( char ch )
|
||||
{
|
||||
return (ch==','||ch==';'||ch=='\0');
|
||||
}
|
||||
|
||||
39
extern/nfd-modified/src/nfd_common.h
vendored
Normal file
39
extern/nfd-modified/src/nfd_common.h
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
Internal, common across platforms
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NFD_COMMON_H
|
||||
#define _NFD_COMMON_H
|
||||
|
||||
#include "nfd.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NFD_MAX_STRLEN 256
|
||||
#define _NFD_UNUSED(x) ((void)x)
|
||||
|
||||
#define NFD_UTF8_BOM "\xEF\xBB\xBF"
|
||||
|
||||
|
||||
void *NFDi_Malloc( size_t bytes );
|
||||
void NFDi_Free( void *ptr );
|
||||
void NFDi_SetError( const char *msg );
|
||||
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
|
||||
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str );
|
||||
int NFDi_IsFilterSegmentChar( char ch );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
379
extern/nfd-modified/src/nfd_gtk.c
vendored
Normal file
379
extern/nfd-modified/src/nfd_gtk.c
vendored
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "nfd.h"
|
||||
#include "nfd_common.h"
|
||||
|
||||
|
||||
const char INIT_FAIL_MSG[] = "gtk_init_check failed to initilaize GTK+";
|
||||
|
||||
|
||||
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize )
|
||||
{
|
||||
const char SEP[] = ", ";
|
||||
|
||||
size_t len = strlen(filterName);
|
||||
if ( len != 0 )
|
||||
{
|
||||
strncat( filterName, SEP, bufsize - len - 1 );
|
||||
len += strlen(SEP);
|
||||
}
|
||||
|
||||
strncat( filterName, typebuf, bufsize - len - 1 );
|
||||
}
|
||||
|
||||
static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList )
|
||||
{
|
||||
GtkFileFilter *filter;
|
||||
char typebuf[NFD_MAX_STRLEN] = {0};
|
||||
const char *p_filterList = filterList;
|
||||
char *p_typebuf = typebuf;
|
||||
char filterName[NFD_MAX_STRLEN] = {0};
|
||||
|
||||
if ( !filterList || strlen(filterList) == 0 )
|
||||
return;
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
while ( 1 )
|
||||
{
|
||||
|
||||
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
|
||||
{
|
||||
char typebufWildcard[NFD_MAX_STRLEN];
|
||||
/* add another type to the filter */
|
||||
assert( strlen(typebuf) > 0 );
|
||||
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 );
|
||||
|
||||
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf );
|
||||
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN );
|
||||
|
||||
gtk_file_filter_add_pattern( filter, typebufWildcard );
|
||||
|
||||
p_typebuf = typebuf;
|
||||
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN );
|
||||
}
|
||||
|
||||
if ( *p_filterList == ';' || *p_filterList == '\0' )
|
||||
{
|
||||
/* end of filter -- add it to the dialog */
|
||||
|
||||
gtk_file_filter_set_name( filter, filterName );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
|
||||
|
||||
filterName[0] = '\0';
|
||||
|
||||
if ( *p_filterList == '\0' )
|
||||
break;
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
}
|
||||
|
||||
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) )
|
||||
{
|
||||
*p_typebuf = *p_filterList;
|
||||
p_typebuf++;
|
||||
}
|
||||
|
||||
p_filterList++;
|
||||
}
|
||||
|
||||
/* always append a wildcard option to the end*/
|
||||
|
||||
filter = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name( filter, "*.*" );
|
||||
gtk_file_filter_add_pattern( filter, "*" );
|
||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
|
||||
}
|
||||
|
||||
static void SetDefaultPath( GtkWidget *dialog, const char *defaultPath )
|
||||
{
|
||||
if ( !defaultPath || strlen(defaultPath) == 0 )
|
||||
return;
|
||||
|
||||
/* GTK+ manual recommends not specifically setting the default path.
|
||||
We do it anyway in order to be consistent across platforms.
|
||||
|
||||
If consistency with the native OS is preferred, this is the line
|
||||
to comment out. -ml */
|
||||
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), defaultPath );
|
||||
}
|
||||
|
||||
static nfdresult_t AllocPathSet( GSList *fileList, nfdpathset_t *pathSet )
|
||||
{
|
||||
size_t bufSize = 0;
|
||||
GSList *node;
|
||||
nfdchar_t *p_buf;
|
||||
size_t count = 0;
|
||||
|
||||
assert(fileList);
|
||||
assert(pathSet);
|
||||
|
||||
pathSet->count = (size_t)g_slist_length( fileList );
|
||||
assert( pathSet->count > 0 );
|
||||
|
||||
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count );
|
||||
if ( !pathSet->indices )
|
||||
{
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
/* count the total space needed for buf */
|
||||
for ( node = fileList; node; node = node->next )
|
||||
{
|
||||
assert(node->data);
|
||||
bufSize += strlen( (const gchar*)node->data ) + 1;
|
||||
}
|
||||
|
||||
pathSet->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufSize );
|
||||
|
||||
/* fill buf */
|
||||
p_buf = pathSet->buf;
|
||||
for ( node = fileList; node; node = node->next )
|
||||
{
|
||||
nfdchar_t *path = (nfdchar_t*)(node->data);
|
||||
size_t byteLen = strlen(path)+1;
|
||||
ptrdiff_t index;
|
||||
|
||||
memcpy( p_buf, path, byteLen );
|
||||
g_free(node->data);
|
||||
|
||||
index = p_buf - pathSet->buf;
|
||||
assert( index >= 0 );
|
||||
pathSet->indices[count] = (size_t)index;
|
||||
|
||||
p_buf += byteLen;
|
||||
++count;
|
||||
}
|
||||
|
||||
g_slist_free( fileList );
|
||||
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
static void WaitForCleanup(void)
|
||||
{
|
||||
while (gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
/* public */
|
||||
|
||||
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
|
||||
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( "Open File",
|
||||
NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT,
|
||||
NULL );
|
||||
|
||||
/* 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_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdpathset_t *outPaths )
|
||||
{
|
||||
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( "Open Files",
|
||||
NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT,
|
||||
NULL );
|
||||
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE );
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
|
||||
if ( AllocPathSet( fileList, outPaths ) == NFD_ERROR )
|
||||
{
|
||||
gtk_widget_destroy(dialog);
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
result = NFD_OKAY;
|
||||
}
|
||||
|
||||
WaitForCleanup();
|
||||
gtk_widget_destroy(dialog);
|
||||
WaitForCleanup();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
||||
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( "Save File",
|
||||
NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Save", GTK_RESPONSE_ACCEPT,
|
||||
NULL );
|
||||
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
762
extern/nfd-modified/src/nfd_win.cpp
vendored
Normal file
762
extern/nfd-modified/src/nfd_win.cpp
vendored
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
|
||||
#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"
|
||||
|
||||
|
||||
#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));
|
||||
assert( ret == inStrCharacterCount );
|
||||
#else
|
||||
_NFD_UNUSED(ret);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* ext is in format "jpg", no wildcards or separators */
|
||||
static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen )
|
||||
{
|
||||
const char SEP[] = ";";
|
||||
assert( specBufLen > strlen(ext)+3 );
|
||||
|
||||
if ( strlen(specBuf) > 0 )
|
||||
{
|
||||
strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 );
|
||||
specBufLen += strlen(SEP);
|
||||
}
|
||||
|
||||
char extWildcard[NFD_MAX_STRLEN];
|
||||
int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
|
||||
assert( bytesWritten == (int)(strlen(ext)+2) );
|
||||
_NFD_UNUSED(bytesWritten);
|
||||
|
||||
strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
|
||||
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
|
||||
{
|
||||
const wchar_t WILDCARD[] = L"*.*";
|
||||
|
||||
if ( !filterList || strlen(filterList) == 0 )
|
||||
return NFD_OKAY;
|
||||
|
||||
// Count rows to alloc
|
||||
UINT filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */
|
||||
const char *p_filterList;
|
||||
for ( p_filterList = filterList; *p_filterList; ++p_filterList )
|
||||
{
|
||||
if ( *p_filterList == ';' )
|
||||
++filterCount;
|
||||
}
|
||||
|
||||
assert(filterCount);
|
||||
if ( !filterCount )
|
||||
{
|
||||
NFDi_SetError("Error parsing filters.");
|
||||
return NFD_ERROR;
|
||||
}
|
||||
|
||||
/* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */
|
||||
COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount + 1) );
|
||||
if ( !specList )
|
||||
{
|
||||
return NFD_ERROR;
|
||||
}
|
||||
for (UINT i = 0; i < filterCount+1; ++i )
|
||||
{
|
||||
specList[i].pszName = NULL;
|
||||
specList[i].pszSpec = NULL;
|
||||
}
|
||||
|
||||
size_t specIdx = 0;
|
||||
p_filterList = filterList;
|
||||
char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */
|
||||
char *p_typebuf = typebuf;
|
||||
|
||||
char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
|
||||
{
|
||||
/* append a type to the specbuf (pending filter) */
|
||||
AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN );
|
||||
|
||||
p_typebuf = typebuf;
|
||||
memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN );
|
||||
}
|
||||
|
||||
if ( *p_filterList == ';' || *p_filterList == '\0' )
|
||||
{
|
||||
/* end of filter -- add it to specList */
|
||||
|
||||
CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszName );
|
||||
CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec );
|
||||
|
||||
memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN );
|
||||
++specIdx;
|
||||
if ( specIdx == filterCount )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !NFDi_IsFilterSegmentChar( *p_filterList ))
|
||||
{
|
||||
*p_typebuf = *p_filterList;
|
||||
++p_typebuf;
|
||||
}
|
||||
|
||||
++p_filterList;
|
||||
}
|
||||
|
||||
/* Add wildcard */
|
||||
specList[specIdx].pszSpec = WILDCARD;
|
||||
specList[specIdx].pszName = WILDCARD;
|
||||
|
||||
fileOpenDialog->SetFileTypes( filterCount+1, specList );
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
if ( !defaultPath || strlen(defaultPath) == 0 )
|
||||
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 */
|
||||
|
||||
|
||||
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
nfdresult_t nfdResult = NFD_ERROR;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Show the dialog.
|
||||
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:
|
||||
if (fileOpenDialog)
|
||||
fileOpenDialog->Release();
|
||||
|
||||
COMUninit(coResult);
|
||||
|
||||
return nfdResult;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdpathset_t *outPaths )
|
||||
{
|
||||
nfdresult_t nfdResult = NFD_ERROR;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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:
|
||||
if ( fileOpenDialog )
|
||||
fileOpenDialog->Release();
|
||||
|
||||
COMUninit(coResult);
|
||||
|
||||
return nfdResult;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
307
extern/nfd-modified/src/nfd_zenity.c
vendored
Normal file
307
extern/nfd-modified/src/nfd_zenity.c
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
Native File Dialog
|
||||
|
||||
http://www.frogtoss.com/labs
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "nfd.h"
|
||||
#include "nfd_common.h"
|
||||
|
||||
#define SIMPLE_EXEC_IMPLEMENTATION
|
||||
#include "simple_exec.h"
|
||||
|
||||
|
||||
const char NO_ZENITY_MSG[] = "zenity not installed";
|
||||
|
||||
|
||||
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize )
|
||||
{
|
||||
size_t len = strlen(filterName);
|
||||
if( len > 0 )
|
||||
strncat( filterName, " *.", bufsize - len - 1 );
|
||||
else
|
||||
strncat( filterName, "--file-filter=*.", bufsize - len - 1 );
|
||||
|
||||
len = strlen(filterName);
|
||||
strncat( filterName, typebuf, bufsize - len - 1 );
|
||||
}
|
||||
|
||||
static void AddFiltersToCommandArgs(char** commandArgs, int commandArgsLen, const char *filterList )
|
||||
{
|
||||
char typebuf[NFD_MAX_STRLEN] = {0};
|
||||
const char *p_filterList = filterList;
|
||||
char *p_typebuf = typebuf;
|
||||
char filterName[NFD_MAX_STRLEN] = {0};
|
||||
int i;
|
||||
|
||||
if ( !filterList || strlen(filterList) == 0 )
|
||||
return;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
|
||||
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
|
||||
{
|
||||
char typebufWildcard[NFD_MAX_STRLEN];
|
||||
/* add another type to the filter */
|
||||
assert( strlen(typebuf) > 0 );
|
||||
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 );
|
||||
|
||||
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf );
|
||||
|
||||
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN );
|
||||
|
||||
p_typebuf = typebuf;
|
||||
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN );
|
||||
}
|
||||
|
||||
if ( *p_filterList == ';' || *p_filterList == '\0' )
|
||||
{
|
||||
/* end of filter -- add it to the dialog */
|
||||
|
||||
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
|
||||
|
||||
commandArgs[i] = strdup(filterName);
|
||||
|
||||
filterName[0] = '\0';
|
||||
|
||||
if ( *p_filterList == '\0' )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) )
|
||||
{
|
||||
*p_typebuf = *p_filterList;
|
||||
p_typebuf++;
|
||||
}
|
||||
|
||||
p_filterList++;
|
||||
}
|
||||
|
||||
/* always append a wildcard option to the end*/
|
||||
|
||||
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
|
||||
|
||||
commandArgs[i] = strdup("--file-filter=*.*");
|
||||
}
|
||||
|
||||
static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defaultPath, const char* filterList, char** stdOut)
|
||||
{
|
||||
if(defaultPath != NULL)
|
||||
{
|
||||
char* prefix = "--filename=";
|
||||
int len = strlen(prefix) + strlen(defaultPath) + 1;
|
||||
|
||||
char* tmp = (char*) calloc(len, 1);
|
||||
strcat(tmp, prefix);
|
||||
strcat(tmp, defaultPath);
|
||||
|
||||
int i;
|
||||
for(i = 0; command[i] != NULL && i < commandLen; i++);
|
||||
|
||||
command[i] = tmp;
|
||||
}
|
||||
|
||||
AddFiltersToCommandArgs(command, commandLen, filterList);
|
||||
|
||||
int byteCount = 0;
|
||||
int exitCode = 0;
|
||||
int processInvokeError = runCommandArray(stdOut, &byteCount, &exitCode, 0, command);
|
||||
|
||||
for(int i = 0; command[i] != NULL && i < commandLen; i++)
|
||||
free(command[i]);
|
||||
|
||||
nfdresult_t result = NFD_OKAY;
|
||||
|
||||
if(processInvokeError == COMMAND_NOT_FOUND)
|
||||
{
|
||||
NFDi_SetError(NO_ZENITY_MSG);
|
||||
result = NFD_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(exitCode == 1)
|
||||
result = NFD_CANCEL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
|
||||
{
|
||||
assert(zenityList);
|
||||
assert(pathSet);
|
||||
|
||||
size_t len = strlen(zenityList) + 1;
|
||||
pathSet->buf = NFDi_Malloc(len);
|
||||
|
||||
int numEntries = 1;
|
||||
|
||||
for(size_t i = 0; i < len; i++)
|
||||
{
|
||||
char ch = zenityList[i];
|
||||
|
||||
if(ch == '|')
|
||||
{
|
||||
numEntries++;
|
||||
ch = '\0';
|
||||
}
|
||||
|
||||
pathSet->buf[i] = ch;
|
||||
}
|
||||
|
||||
pathSet->count = numEntries;
|
||||
assert( pathSet->count > 0 );
|
||||
|
||||
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count );
|
||||
|
||||
int entry = 0;
|
||||
pathSet->indices[0] = 0;
|
||||
for(size_t i = 0; i < len; i++)
|
||||
{
|
||||
char ch = zenityList[i];
|
||||
|
||||
if(ch == '|')
|
||||
{
|
||||
entry++;
|
||||
pathSet->indices[entry] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NFD_OKAY;
|
||||
}
|
||||
|
||||
/* public */
|
||||
|
||||
nfdresult_t NFD_OpenDialog( const char *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
int commandLen = 100;
|
||||
char* command[commandLen];
|
||||
memset(command, 0, commandLen * sizeof(char*));
|
||||
|
||||
command[0] = strdup("zenity");
|
||||
command[1] = strdup("--file-selection");
|
||||
command[2] = strdup("--title=Open File");
|
||||
|
||||
char* stdOut = NULL;
|
||||
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
||||
|
||||
if(stdOut != NULL)
|
||||
{
|
||||
size_t len = strlen(stdOut);
|
||||
*outPath = NFDi_Malloc(len);
|
||||
memcpy(*outPath, stdOut, len);
|
||||
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
||||
free(stdOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outPath = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdpathset_t *outPaths )
|
||||
{
|
||||
int commandLen = 100;
|
||||
char* command[commandLen];
|
||||
memset(command, 0, commandLen * sizeof(char*));
|
||||
|
||||
command[0] = strdup("zenity");
|
||||
command[1] = strdup("--file-selection");
|
||||
command[2] = strdup("--title=Open Files");
|
||||
command[3] = strdup("--multiple");
|
||||
|
||||
char* stdOut = NULL;
|
||||
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
||||
|
||||
if(stdOut != NULL)
|
||||
{
|
||||
size_t len = strlen(stdOut);
|
||||
stdOut[len-1] = '\0'; // remove trailing newline
|
||||
|
||||
if ( AllocPathSet( stdOut, outPaths ) == NFD_ERROR )
|
||||
result = NFD_ERROR;
|
||||
|
||||
free(stdOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = NFD_ERROR;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
||||
const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath )
|
||||
{
|
||||
int commandLen = 100;
|
||||
char* command[commandLen];
|
||||
memset(command, 0, commandLen * sizeof(char*));
|
||||
|
||||
command[0] = strdup("zenity");
|
||||
command[1] = strdup("--file-selection");
|
||||
command[2] = strdup("--title=Save File");
|
||||
command[3] = strdup("--save");
|
||||
|
||||
char* stdOut = NULL;
|
||||
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
||||
|
||||
if(stdOut != NULL)
|
||||
{
|
||||
size_t len = strlen(stdOut);
|
||||
*outPath = NFDi_Malloc(len);
|
||||
memcpy(*outPath, stdOut, len);
|
||||
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
||||
free(stdOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outPath = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
|
||||
nfdchar_t **outPath)
|
||||
{
|
||||
int commandLen = 100;
|
||||
char* command[commandLen];
|
||||
memset(command, 0, commandLen * sizeof(char*));
|
||||
|
||||
command[0] = strdup("zenity");
|
||||
command[1] = strdup("--file-selection");
|
||||
command[2] = strdup("--directory");
|
||||
command[3] = strdup("--title=Select folder");
|
||||
|
||||
char* stdOut = NULL;
|
||||
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, "", &stdOut);
|
||||
|
||||
if(stdOut != NULL)
|
||||
{
|
||||
size_t len = strlen(stdOut);
|
||||
*outPath = NFDi_Malloc(len);
|
||||
memcpy(*outPath, stdOut, len);
|
||||
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
||||
free(stdOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outPath = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
218
extern/nfd-modified/src/simple_exec.h
vendored
Normal file
218
extern/nfd-modified/src/simple_exec.h
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
// copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
|
||||
|
||||
// simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
|
||||
//
|
||||
// do this:
|
||||
// #define SIMPLE_EXEC_IMPLEMENTATION
|
||||
// before you include this file in *one* C or C++ file to create the implementation.
|
||||
// i.e. it should look like this:
|
||||
// #define SIMPLE_EXEC_IMPLEMENTATION
|
||||
// #include "simple_exec.h"
|
||||
|
||||
#ifndef SIMPLE_EXEC_H
|
||||
#define SIMPLE_EXEC_H
|
||||
|
||||
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
|
||||
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
|
||||
|
||||
#endif // SIMPLE_EXEC_H
|
||||
|
||||
#ifdef SIMPLE_EXEC_IMPLEMENTATION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define release_assert(exp) { if (!(exp)) { abort(); } }
|
||||
|
||||
enum PIPE_FILE_DESCRIPTORS
|
||||
{
|
||||
READ_FD = 0,
|
||||
WRITE_FD = 1
|
||||
};
|
||||
|
||||
enum RUN_COMMAND_ERROR
|
||||
{
|
||||
COMMAND_RAN_OK = 0,
|
||||
COMMAND_NOT_FOUND = 1
|
||||
};
|
||||
|
||||
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
|
||||
{
|
||||
// adapted from: https://stackoverflow.com/a/479103
|
||||
|
||||
int bufferSize = 256;
|
||||
char buffer[bufferSize + 1];
|
||||
|
||||
int dataReadFromChildDefaultSize = bufferSize * 5;
|
||||
int dataReadFromChildSize = dataReadFromChildDefaultSize;
|
||||
int dataReadFromChildUsed = 0;
|
||||
char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
|
||||
|
||||
|
||||
int parentToChild[2];
|
||||
release_assert(pipe(parentToChild) == 0);
|
||||
|
||||
int childToParent[2];
|
||||
release_assert(pipe(childToParent) == 0);
|
||||
|
||||
int errPipe[2];
|
||||
release_assert(pipe(errPipe) == 0);
|
||||
|
||||
pid_t pid;
|
||||
switch( pid = fork() )
|
||||
{
|
||||
case -1:
|
||||
{
|
||||
release_assert(0 && "Fork failed");
|
||||
break;
|
||||
}
|
||||
|
||||
case 0: // child
|
||||
{
|
||||
release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
|
||||
release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
|
||||
|
||||
if(includeStdErr)
|
||||
{
|
||||
release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int devNull = open("/dev/null", O_WRONLY);
|
||||
release_assert(dup2(devNull, STDERR_FILENO) != -1);
|
||||
}
|
||||
|
||||
// unused
|
||||
release_assert(close(parentToChild[WRITE_FD]) == 0);
|
||||
release_assert(close(childToParent[READ_FD ]) == 0);
|
||||
release_assert(close(errPipe[READ_FD]) == 0);
|
||||
|
||||
const char* command = allArgs[0];
|
||||
execvp(command, allArgs);
|
||||
|
||||
char err = 1;
|
||||
ssize_t result = write(errPipe[WRITE_FD], &err, 1);
|
||||
release_assert(result != -1);
|
||||
|
||||
close(errPipe[WRITE_FD]);
|
||||
close(parentToChild[READ_FD]);
|
||||
close(childToParent[WRITE_FD]);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
default: // parent
|
||||
{
|
||||
// unused
|
||||
release_assert(close(parentToChild[READ_FD]) == 0);
|
||||
release_assert(close(childToParent[WRITE_FD]) == 0);
|
||||
release_assert(close(errPipe[WRITE_FD]) == 0);
|
||||
|
||||
while(1)
|
||||
{
|
||||
ssize_t bytesRead = 0;
|
||||
switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
|
||||
{
|
||||
case 0: // End-of-File, or non-blocking read.
|
||||
{
|
||||
int status = 0;
|
||||
release_assert(waitpid(pid, &status, 0) == pid);
|
||||
|
||||
// done with these now
|
||||
release_assert(close(parentToChild[WRITE_FD]) == 0);
|
||||
release_assert(close(childToParent[READ_FD]) == 0);
|
||||
|
||||
char errChar = 0;
|
||||
ssize_t result = read(errPipe[READ_FD], &errChar, 1);
|
||||
release_assert(result != -1);
|
||||
close(errPipe[READ_FD]);
|
||||
|
||||
if(errChar)
|
||||
{
|
||||
free(dataReadFromChild);
|
||||
return COMMAND_NOT_FOUND;
|
||||
}
|
||||
|
||||
// free any un-needed memory with realloc + add a null terminator for convenience
|
||||
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
|
||||
dataReadFromChild[dataReadFromChildUsed] = '\0';
|
||||
|
||||
if(stdOut != NULL)
|
||||
*stdOut = dataReadFromChild;
|
||||
else
|
||||
free(dataReadFromChild);
|
||||
|
||||
if(stdOutByteCount != NULL)
|
||||
*stdOutByteCount = dataReadFromChildUsed;
|
||||
if(returnCode != NULL)
|
||||
*returnCode = WEXITSTATUS(status);
|
||||
|
||||
return COMMAND_RAN_OK;
|
||||
}
|
||||
case -1:
|
||||
{
|
||||
release_assert(0 && "read() failed");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
|
||||
{
|
||||
dataReadFromChildSize += dataReadFromChildDefaultSize;
|
||||
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
|
||||
}
|
||||
|
||||
memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
|
||||
dataReadFromChildUsed += bytesRead;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, command);
|
||||
|
||||
char* currArg = NULL;
|
||||
|
||||
int allArgsInitialSize = 16;
|
||||
int allArgsSize = allArgsInitialSize;
|
||||
char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
|
||||
allArgs[0] = command;
|
||||
|
||||
int i = 1;
|
||||
do
|
||||
{
|
||||
currArg = va_arg(vl, char*);
|
||||
allArgs[i] = currArg;
|
||||
|
||||
i++;
|
||||
|
||||
if(i >= allArgsSize)
|
||||
{
|
||||
allArgsSize += allArgsInitialSize;
|
||||
allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
|
||||
}
|
||||
|
||||
} while(currArg != NULL);
|
||||
|
||||
va_end(vl);
|
||||
|
||||
int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
|
||||
free(allArgs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif //SIMPLE_EXEC_IMPLEMENTATION
|
||||
Loading…
Add table
Add a link
Reference in a new issue