mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 09:04:38 +00:00
update NFD to 67345b80eb
This commit is contained in:
parent
a5f3e70653
commit
166128dd73
7 changed files with 170 additions and 171 deletions
|
|
@ -47,16 +47,20 @@ int main( void )
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See [NFD.h](src/include/nfd.h) for more options.
|
See self-documenting API [NFD.h](src/include/nfd.h) for more options.
|
||||||
|
|
||||||
# Screenshots #
|
# Screenshots #
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Changelog ##
|
## Changelog ##
|
||||||
|
|
||||||
|
- **Major** version increments denote API or ABI departure.
|
||||||
|
- **Minor** version increments denote build or trivial departures.
|
||||||
|
- **Micro** version increments just recompile and drop-in.
|
||||||
|
|
||||||
release | what's new | date
|
release | what's new | date
|
||||||
--------|-----------------------------|---------
|
--------|-----------------------------|---------
|
||||||
1.0.0 | initial | oct 2014
|
1.0.0 | initial | oct 2014
|
||||||
|
|
@ -64,11 +68,15 @@ release | what's new | date
|
||||||
1.1.1 | mingw support, build fixes | aug 2016
|
1.1.1 | mingw support, build fixes | aug 2016
|
||||||
1.1.2 | test_pickfolder() added | aug 2016
|
1.1.2 | test_pickfolder() added | aug 2016
|
||||||
1.1.3 | zenity linux backend added | nov 2017
|
1.1.3 | zenity linux backend added | nov 2017
|
||||||
1.1.3 | fix char type in decls | nov 2017
|
<i></i> | fix char type in decls | nov 2017
|
||||||
1.1.4 | fix win32 memleaks | dec 2018
|
1.1.4 | fix win32 memleaks | dec 2018
|
||||||
1.1.4 | improve win32 errorhandling | dec 2018
|
<i></i> | improve win32 errorhandling | dec 2018
|
||||||
1.1.4 | macos fix focus bug | dec 2018
|
<i></i> | macos fix focus bug | dec 2018
|
||||||
|
1.1.5 | win32 fix com reinitialize | aug 2019
|
||||||
|
1.1.6 | fix osx filter bug | aug 2019
|
||||||
|
<i></i> | remove deprecated scons | aug 2019
|
||||||
|
<i></i> | fix mingw compilation | aug 2019
|
||||||
|
<i></i> | -Wextra warning cleanup | aug 2019
|
||||||
|
|
||||||
## Building ##
|
## Building ##
|
||||||
|
|
||||||
|
|
@ -76,13 +84,13 @@ NFD uses [Premake5](https://premake.github.io/download.html) generated Makefiles
|
||||||
|
|
||||||
If you need to run Premake5 directly, further [build documentation](docs/build.md) is available.
|
If you need to run Premake5 directly, further [build documentation](docs/build.md) is available.
|
||||||
|
|
||||||
Previously, NFD used SCons to build. It still works, but is now deprecated; updates to it are discouraged. Opt to use the native build system where possible.
|
Previously, NFD used SCons to build. As of 1.1.6, SCons support has been removed entirely.
|
||||||
|
|
||||||
`nfd.a` will be built for release builds, and `nfd_d.a` will be built for debug builds.
|
`nfd.a` will be built for release builds, and `nfd_d.a` will be built for debug builds.
|
||||||
|
|
||||||
### Makefiles ###
|
### Makefiles ###
|
||||||
|
|
||||||
The makefile offers five options, with `release_x64` as the default.
|
The makefile offers up to four options, with `release_x64` as the default.
|
||||||
|
|
||||||
make config=release_x86
|
make config=release_x86
|
||||||
make config=release_x64
|
make config=release_x64
|
||||||
|
|
@ -96,6 +104,7 @@ The makefile offers five options, with `release_x64` as the default.
|
||||||
3. Add `build/<debug|release>/<arch>` to the library search path.
|
3. Add `build/<debug|release>/<arch>` to the library search path.
|
||||||
|
|
||||||
#### Linux GTK ####
|
#### Linux GTK ####
|
||||||
|
|
||||||
`apt-get libgtk-3-dev` installs the gtk dependency for library compilation.
|
`apt-get libgtk-3-dev` installs the gtk dependency for library compilation.
|
||||||
|
|
||||||
On Linux, you have the option of compiling and linking against GTK. If you use it, the recommended way to compile is to include the arguments of `pkg-config --cflags --libs gtk+-3.0`.
|
On Linux, you have the option of compiling and linking against GTK. If you use it, the recommended way to compile is to include the arguments of `pkg-config --cflags --libs gtk+-3.0`.
|
||||||
|
|
@ -105,16 +114,18 @@ On Linux, you have the option of compiling and linking against GTK. If you use
|
||||||
Alternatively, you can use the Zenity backend by running the Makefile in `build/gmake_linux_zenity`. Zenity runs the dialog in its own address space, but requires the user to have Zenity correctly installed and configured on their system.
|
Alternatively, you can use the Zenity backend by running the Makefile in `build/gmake_linux_zenity`. Zenity runs the dialog in its own address space, but requires the user to have Zenity correctly installed and configured on their system.
|
||||||
|
|
||||||
#### MacOS ####
|
#### MacOS ####
|
||||||
|
|
||||||
On Mac OS, add `AppKit` to the list of frameworks.
|
On Mac OS, add `AppKit` to the list of frameworks.
|
||||||
|
|
||||||
#### Windows ####
|
#### Windows ####
|
||||||
On Windows, ensure you are building against `comctl32.lib`.
|
|
||||||
|
On Windows, ensure you are linking against `comctl32.lib`.
|
||||||
|
|
||||||
## Usage ##
|
## Usage ##
|
||||||
|
|
||||||
See `NFD.h` for API calls. See `tests/*.c` for example code.
|
See `NFD.h` for API calls. See `tests/*.c` for example code.
|
||||||
|
|
||||||
After compiling, `build/bin` contains compiled test programs.
|
After compiling, `build/bin` contains compiled test programs. The appropriate subdirectory under `build/lib` contains the built library.
|
||||||
|
|
||||||
## File Filter Syntax ##
|
## File Filter Syntax ##
|
||||||
|
|
||||||
|
|
@ -141,14 +152,16 @@ See [test_opendialogmultiple.c](test/test_opendialogmultiple.c).
|
||||||
|
|
||||||
# Known Limitations #
|
# Known Limitations #
|
||||||
|
|
||||||
I accept quality code patches, or will resolve these and other matters through support. See [submitting pull requests](docs/submitting_pull_requests.md) for details.
|
I accept quality code patches, or will resolve these and other matters through support. See [contributing](docs/contributing.md) for details.
|
||||||
|
|
||||||
- No support for Windows XP's legacy dialogs such as `GetOpenFileName`.
|
- No support for Windows XP's legacy dialogs such as `GetOpenFileName`.
|
||||||
- No support for file filter names -- ex: "Image Files" (*.png, *.jpg). Nameless filters are supported, however.
|
- No support for file filter names -- ex: "Image Files" (*.png, *.jpg). Nameless filters are supported, however.
|
||||||
|
- GTK Zenity implementation's process exec error handling does not gracefully handle numerous error cases, choosing to abort rather than cleanup and return.
|
||||||
|
- GTK 3 spams one warning per dialog created.
|
||||||
|
|
||||||
# Copyright and Credit #
|
# Copyright and Credit #
|
||||||
|
|
||||||
Copyright © 2014-2017 [Frogtoss Games](http://www.frogtoss.com), Inc.
|
Copyright © 2014-2019 [Frogtoss Games](http://www.frogtoss.com), Inc.
|
||||||
File [LICENSE](LICENSE) covers all files in this repo.
|
File [LICENSE](LICENSE) covers all files in this repo.
|
||||||
|
|
||||||
Native File Dialog by Michael Labbe
|
Native File Dialog by Michael Labbe
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ static NSArray *BuildAllowedFileTypes( const char *filterList )
|
||||||
{
|
{
|
||||||
if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' )
|
if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' )
|
||||||
{
|
{
|
||||||
++p_typebuf;
|
if (filterList[i] != '\0')
|
||||||
|
++p_typebuf;
|
||||||
*p_typebuf = '\0';
|
*p_typebuf = '\0';
|
||||||
|
|
||||||
NSString *thisType = [NSString stringWithUTF8String: typebuf];
|
NSString *thisType = [NSString stringWithUTF8String: typebuf];
|
||||||
[buildFilterList addObject:thisType];
|
[buildFilterList addObject:thisType];
|
||||||
p_typebuf = typebuf;
|
p_typebuf = typebuf;
|
||||||
|
|
|
||||||
|
|
@ -92,13 +92,13 @@ int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy )
|
||||||
|
|
||||||
|
|
||||||
/* adapted from microutf8 */
|
/* adapted from microutf8 */
|
||||||
size_t NFDi_UTF8_Strlen( const nfdchar_t *str )
|
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str )
|
||||||
{
|
{
|
||||||
/* This function doesn't properly check validity of UTF-8 character
|
/* This function doesn't properly check validity of UTF-8 character
|
||||||
sequence, it is supposed to use only with valid UTF-8 strings. */
|
sequence, it is supposed to use only with valid UTF-8 strings. */
|
||||||
|
|
||||||
size_t character_count = 0;
|
int32_t character_count = 0;
|
||||||
size_t i = 0; /* Counter used to iterate over string. */
|
int32_t i = 0; /* Counter used to iterate over string. */
|
||||||
nfdchar_t maybe_bom[4];
|
nfdchar_t maybe_bom[4];
|
||||||
|
|
||||||
/* If there is UTF-8 BOM ignore it. */
|
/* If there is UTF-8 BOM ignore it. */
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include "nfd.h"
|
#include "nfd.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -26,7 +28,7 @@ void *NFDi_Malloc( size_t bytes );
|
||||||
void NFDi_Free( void *ptr );
|
void NFDi_Free( void *ptr );
|
||||||
void NFDi_SetError( const char *msg );
|
void NFDi_SetError( const char *msg );
|
||||||
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
|
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
|
||||||
size_t NFDi_UTF8_Strlen( const nfdchar_t *str );
|
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str );
|
||||||
int NFDi_IsFilterSegmentChar( char ch );
|
int NFDi_IsFilterSegmentChar( char ch );
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,6 @@
|
||||||
http://www.frogtoss.com/labs
|
http://www.frogtoss.com/labs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _CRTDBG_MAP_ALLOC
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <crtdbg.h>
|
|
||||||
|
|
||||||
/* only locally define UNICODE in this compilation unit */
|
|
||||||
#ifndef UNICODE
|
|
||||||
#define UNICODE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
|
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
|
||||||
|
|
@ -19,6 +11,15 @@
|
||||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
||||||
#endif
|
#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 <wchar.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -27,6 +28,33 @@
|
||||||
#include "nfd_common.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()
|
// allocs the space in outPath -- call free()
|
||||||
static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr )
|
static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr )
|
||||||
{
|
{
|
||||||
|
|
@ -58,11 +86,9 @@ static size_t GetUTF8ByteCountForWChar( const wchar_t *str )
|
||||||
return bytesNeeded+1;
|
return bytesNeeded+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to outPtr -- no free() necessary. No memory stomp tests are done -- they must be done
|
// write to outPtr -- no free() necessary.
|
||||||
// before entering this function.
|
|
||||||
static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr )
|
static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr )
|
||||||
{
|
{
|
||||||
int inStrCharacterCount = static_cast<int>(wcslen(inStr));
|
|
||||||
int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr ));
|
int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr ));
|
||||||
|
|
||||||
/* invocation copies null term */
|
/* invocation copies null term */
|
||||||
|
|
@ -120,7 +146,8 @@ static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t spec
|
||||||
|
|
||||||
char extWildcard[NFD_MAX_STRLEN];
|
char extWildcard[NFD_MAX_STRLEN];
|
||||||
int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
|
int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
|
||||||
assert( bytesWritten == strlen(ext)+2 );
|
assert( bytesWritten == (int)(strlen(ext)+2) );
|
||||||
|
_NFD_UNUSED(bytesWritten);
|
||||||
|
|
||||||
strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
|
strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
|
||||||
|
|
||||||
|
|
@ -129,7 +156,6 @@ static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t spec
|
||||||
|
|
||||||
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
|
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
|
||||||
{
|
{
|
||||||
const wchar_t EMPTY_WSTR[] = L"";
|
|
||||||
const wchar_t WILDCARD[] = L"*.*";
|
const wchar_t WILDCARD[] = L"*.*";
|
||||||
|
|
||||||
if ( !filterList || strlen(filterList) == 0 )
|
if ( !filterList || strlen(filterList) == 0 )
|
||||||
|
|
@ -167,7 +193,6 @@ static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char
|
||||||
p_filterList = filterList;
|
p_filterList = filterList;
|
||||||
char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */
|
char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */
|
||||||
char *p_typebuf = typebuf;
|
char *p_typebuf = typebuf;
|
||||||
char filterName[NFD_MAX_STRLEN] = {0};
|
|
||||||
|
|
||||||
char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
|
char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
|
||||||
|
|
||||||
|
|
@ -364,22 +389,17 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
|
||||||
nfdchar_t **outPath )
|
nfdchar_t **outPath )
|
||||||
{
|
{
|
||||||
nfdresult_t nfdResult = NFD_ERROR;
|
nfdresult_t nfdResult = NFD_ERROR;
|
||||||
|
|
||||||
|
|
||||||
// Init COM library.
|
HRESULT coResult = COMInit();
|
||||||
HRESULT coResult = ::CoInitializeEx(NULL,
|
if (!COMIsInitialized(coResult))
|
||||||
::COINIT_APARTMENTTHREADED |
|
{
|
||||||
::COINIT_DISABLE_OLE1DDE );
|
|
||||||
|
|
||||||
::IFileOpenDialog *fileOpenDialog(NULL);
|
|
||||||
|
|
||||||
if ( !SUCCEEDED(coResult))
|
|
||||||
{
|
|
||||||
fileOpenDialog = NULL;
|
|
||||||
NFDi_SetError("Could not initialize COM.");
|
NFDi_SetError("Could not initialize COM.");
|
||||||
goto end;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create dialog
|
// Create dialog
|
||||||
|
::IFileOpenDialog *fileOpenDialog(NULL);
|
||||||
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
|
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
|
||||||
CLSCTX_ALL, ::IID_IFileOpenDialog,
|
CLSCTX_ALL, ::IID_IFileOpenDialog,
|
||||||
reinterpret_cast<void**>(&fileOpenDialog) );
|
reinterpret_cast<void**>(&fileOpenDialog) );
|
||||||
|
|
@ -449,8 +469,7 @@ end:
|
||||||
if (fileOpenDialog)
|
if (fileOpenDialog)
|
||||||
fileOpenDialog->Release();
|
fileOpenDialog->Release();
|
||||||
|
|
||||||
if (SUCCEEDED(coResult))
|
COMUninit(coResult);
|
||||||
::CoUninitialize();
|
|
||||||
|
|
||||||
return nfdResult;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
@ -460,20 +479,17 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
||||||
nfdpathset_t *outPaths )
|
nfdpathset_t *outPaths )
|
||||||
{
|
{
|
||||||
nfdresult_t nfdResult = NFD_ERROR;
|
nfdresult_t nfdResult = NFD_ERROR;
|
||||||
|
|
||||||
// Init COM library.
|
|
||||||
HRESULT coResult = ::CoInitializeEx(NULL,
|
HRESULT coResult = COMInit();
|
||||||
::COINIT_APARTMENTTHREADED |
|
if (!COMIsInitialized(coResult))
|
||||||
::COINIT_DISABLE_OLE1DDE );
|
|
||||||
if ( !SUCCEEDED(coResult))
|
|
||||||
{
|
{
|
||||||
NFDi_SetError("Could not initialize COM.");
|
NFDi_SetError("Could not initialize COM.");
|
||||||
return NFD_ERROR;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
::IFileOpenDialog *fileOpenDialog(NULL);
|
|
||||||
|
|
||||||
// Create dialog
|
// Create dialog
|
||||||
|
::IFileOpenDialog *fileOpenDialog(NULL);
|
||||||
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
|
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
|
||||||
CLSCTX_ALL, ::IID_IFileOpenDialog,
|
CLSCTX_ALL, ::IID_IFileOpenDialog,
|
||||||
reinterpret_cast<void**>(&fileOpenDialog) );
|
reinterpret_cast<void**>(&fileOpenDialog) );
|
||||||
|
|
@ -547,8 +563,7 @@ end:
|
||||||
if ( fileOpenDialog )
|
if ( fileOpenDialog )
|
||||||
fileOpenDialog->Release();
|
fileOpenDialog->Release();
|
||||||
|
|
||||||
if (SUCCEEDED(coResult))
|
COMUninit(coResult);
|
||||||
::CoUninitialize();
|
|
||||||
|
|
||||||
return nfdResult;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
@ -558,20 +573,16 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
||||||
nfdchar_t **outPath )
|
nfdchar_t **outPath )
|
||||||
{
|
{
|
||||||
nfdresult_t nfdResult = NFD_ERROR;
|
nfdresult_t nfdResult = NFD_ERROR;
|
||||||
|
|
||||||
// Init COM library.
|
HRESULT coResult = COMInit();
|
||||||
HRESULT coResult = ::CoInitializeEx(NULL,
|
if (!COMIsInitialized(coResult))
|
||||||
::COINIT_APARTMENTTHREADED |
|
|
||||||
::COINIT_DISABLE_OLE1DDE );
|
|
||||||
if ( !SUCCEEDED(coResult))
|
|
||||||
{
|
{
|
||||||
NFDi_SetError("Could not initialize COM.");
|
NFDi_SetError("Could not initialize COM.");
|
||||||
return NFD_ERROR;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
::IFileSaveDialog *fileSaveDialog(NULL);
|
|
||||||
|
|
||||||
// Create dialog
|
// Create dialog
|
||||||
|
::IFileSaveDialog *fileSaveDialog(NULL);
|
||||||
HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL,
|
HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL,
|
||||||
CLSCTX_ALL, ::IID_IFileSaveDialog,
|
CLSCTX_ALL, ::IID_IFileSaveDialog,
|
||||||
reinterpret_cast<void**>(&fileSaveDialog) );
|
reinterpret_cast<void**>(&fileSaveDialog) );
|
||||||
|
|
@ -642,139 +653,110 @@ end:
|
||||||
if ( fileSaveDialog )
|
if ( fileSaveDialog )
|
||||||
fileSaveDialog->Release();
|
fileSaveDialog->Release();
|
||||||
|
|
||||||
if (SUCCEEDED(coResult))
|
COMUninit(coResult);
|
||||||
::CoUninitialize();
|
|
||||||
|
|
||||||
return nfdResult;
|
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,
|
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
|
||||||
nfdchar_t **outPath)
|
nfdchar_t **outPath)
|
||||||
{
|
{
|
||||||
// Init COM
|
nfdresult_t nfdResult = NFD_ERROR;
|
||||||
AutoCoInit autoCoInit;
|
DWORD dwOptions = 0;
|
||||||
if (!SUCCEEDED(autoCoInit.Result()))
|
|
||||||
|
HRESULT coResult = COMInit();
|
||||||
|
if (!COMIsInitialized(coResult))
|
||||||
{
|
{
|
||||||
NFDi_SetError("CoInitializeEx failed.");
|
NFDi_SetError("CoInitializeEx failed.");
|
||||||
return NFD_ERROR;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the file dialog COM object
|
// Create dialog
|
||||||
ComPtr<IFileDialog> pFileDialog;
|
::IFileOpenDialog *fileDialog(NULL);
|
||||||
if (!SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog,
|
HRESULT result = CoCreateInstance(CLSID_FileOpenDialog,
|
||||||
NULL,
|
NULL,
|
||||||
CLSCTX_ALL,
|
CLSCTX_ALL,
|
||||||
IID_PPV_ARGS(&pFileDialog))))
|
IID_PPV_ARGS(&fileDialog));
|
||||||
{
|
if ( !SUCCEEDED(result) )
|
||||||
|
{
|
||||||
NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed.");
|
NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed.");
|
||||||
return NFD_ERROR;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default path
|
// Set the default path
|
||||||
if (SetDefaultPath(pFileDialog.Ptr(), defaultPath) != NFD_OKAY)
|
if (SetDefaultPath(fileDialog, defaultPath) != NFD_OKAY)
|
||||||
{
|
{
|
||||||
NFDi_SetError("SetDefaultPath failed.");
|
NFDi_SetError("SetDefaultPath failed.");
|
||||||
return NFD_ERROR;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the dialogs options
|
// Get the dialogs options
|
||||||
DWORD dwOptions = 0;
|
if (!SUCCEEDED(fileDialog->GetOptions(&dwOptions)))
|
||||||
if (!SUCCEEDED(pFileDialog->GetOptions(&dwOptions)))
|
|
||||||
{
|
{
|
||||||
NFDi_SetError("GetOptions for IFileDialog failed.");
|
NFDi_SetError("GetOptions for IFileDialog failed.");
|
||||||
return NFD_ERROR;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add in FOS_PICKFOLDERS which hides files and only allows selection of folders
|
// Add in FOS_PICKFOLDERS which hides files and only allows selection of folders
|
||||||
if (!SUCCEEDED(pFileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS)))
|
if (!SUCCEEDED(fileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS)))
|
||||||
{
|
{
|
||||||
NFDi_SetError("SetOptions for IFileDialog failed.");
|
NFDi_SetError("SetOptions for IFileDialog failed.");
|
||||||
return NFD_ERROR;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the dialog to the user
|
// Show the dialog to the user
|
||||||
const HRESULT result = pFileDialog->Show(NULL);
|
result = fileDialog->Show(NULL);
|
||||||
if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
if ( SUCCEEDED(result) )
|
||||||
{
|
{
|
||||||
return NFD_CANCEL;
|
// 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 (!SUCCEEDED(result))
|
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
|
||||||
|
{
|
||||||
|
nfdResult = NFD_CANCEL;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
NFDi_SetError("Show for IFileDialog failed.");
|
NFDi_SetError("Show for IFileDialog failed.");
|
||||||
return NFD_ERROR;
|
nfdResult = NFD_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the shell item result
|
end:
|
||||||
ComPtr<IShellItem> pShellItem;
|
|
||||||
if (!SUCCEEDED(pFileDialog->GetResult(&pShellItem)))
|
|
||||||
{
|
|
||||||
NFDi_SetError("Could not get shell item from dialog.");
|
|
||||||
return NFD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally get the path
|
if (fileDialog)
|
||||||
wchar_t *path = NULL;
|
fileDialog->Release();
|
||||||
if (!SUCCEEDED(pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path)))
|
|
||||||
{
|
|
||||||
NFDi_SetError("GetDisplayName for IShellItem failed.");
|
|
||||||
return NFD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert string
|
COMUninit(coResult);
|
||||||
CopyWCharToNFDChar(path, outPath);
|
|
||||||
CoTaskMemFree(path);
|
|
||||||
if (!*outPath)
|
|
||||||
{
|
|
||||||
// error is malloc-based, error message would be redundant
|
|
||||||
return NFD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NFD_OKAY;
|
return nfdResult;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defa
|
||||||
{
|
{
|
||||||
if(defaultPath != NULL)
|
if(defaultPath != NULL)
|
||||||
{
|
{
|
||||||
char* prefix = "--filename";
|
char* prefix = "--filename=";
|
||||||
int len = strlen(prefix) + strlen(defaultPath) + 1;
|
int len = strlen(prefix) + strlen(defaultPath) + 1;
|
||||||
|
|
||||||
char* tmp = (char*) calloc(len, 1);
|
char* tmp = (char*) calloc(len, 1);
|
||||||
|
|
@ -133,10 +133,6 @@ static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defa
|
||||||
|
|
||||||
static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
|
static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
|
||||||
{
|
{
|
||||||
size_t bufSize = 0;
|
|
||||||
nfdchar_t *p_buf;
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
assert(zenityList);
|
assert(zenityList);
|
||||||
assert(pathSet);
|
assert(pathSet);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#define release_assert(x) do { int __release_assert_tmp__ = (x); assert(__release_assert_tmp__); } while(0)
|
#define release_assert(exp) { if (!(exp)) { abort(); } }
|
||||||
|
|
||||||
enum PIPE_FILE_DESCRIPTORS
|
enum PIPE_FILE_DESCRIPTORS
|
||||||
{
|
{
|
||||||
|
|
@ -70,6 +70,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
|
||||||
case -1:
|
case -1:
|
||||||
{
|
{
|
||||||
release_assert(0 && "Fork failed");
|
release_assert(0 && "Fork failed");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0: // child
|
case 0: // child
|
||||||
|
|
@ -96,7 +97,8 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
|
||||||
execvp(command, allArgs);
|
execvp(command, allArgs);
|
||||||
|
|
||||||
char err = 1;
|
char err = 1;
|
||||||
write(errPipe[WRITE_FD], &err, 1);
|
ssize_t result = write(errPipe[WRITE_FD], &err, 1);
|
||||||
|
release_assert(result != -1);
|
||||||
|
|
||||||
close(errPipe[WRITE_FD]);
|
close(errPipe[WRITE_FD]);
|
||||||
close(parentToChild[READ_FD]);
|
close(parentToChild[READ_FD]);
|
||||||
|
|
@ -128,7 +130,8 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
|
||||||
release_assert(close(childToParent[READ_FD]) == 0);
|
release_assert(close(childToParent[READ_FD]) == 0);
|
||||||
|
|
||||||
char errChar = 0;
|
char errChar = 0;
|
||||||
read(errPipe[READ_FD], &errChar, 1);
|
ssize_t result = read(errPipe[READ_FD], &errChar, 1);
|
||||||
|
release_assert(result != -1);
|
||||||
close(errPipe[READ_FD]);
|
close(errPipe[READ_FD]);
|
||||||
|
|
||||||
if(errChar)
|
if(errChar)
|
||||||
|
|
@ -156,6 +159,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
|
||||||
case -1:
|
case -1:
|
||||||
{
|
{
|
||||||
release_assert(0 && "read() failed");
|
release_assert(0 && "read() failed");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue