Merge commit '6e9eb898f5c6e241591d4953ae0b36668cd59f94' as 'external/nfd'

This commit is contained in:
SimoZ64
2025-05-04 00:37:23 +02:00
48 changed files with 8052 additions and 0 deletions

149
external/nfd/src/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,149 @@
set(TARGET_NAME nfd)
set(PUBLIC_HEADER_FILES
include/nfd.h
include/nfd.hpp
include/nfd_sdl2.h
include/nfd_glfw3.h)
set(SOURCE_FILES ${PUBLIC_HEADER_FILES})
if(nfd_PLATFORM STREQUAL PLATFORM_WIN32)
list(APPEND SOURCE_FILES nfd_win.cpp)
endif()
if(nfd_PLATFORM STREQUAL PLATFORM_LINUX)
find_package(PkgConfig REQUIRED)
# for Linux, we support GTK3 and xdg-desktop-portal
option(NFD_PORTAL "Use xdg-desktop-portal instead of GTK" OFF)
if(NOT NFD_PORTAL)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
message("Using GTK version: ${GTK3_VERSION}")
list(APPEND SOURCE_FILES nfd_gtk.cpp)
else()
pkg_check_modules(DBUS REQUIRED dbus-1)
message("Using DBUS version: ${DBUS_VERSION}")
list(APPEND SOURCE_FILES nfd_portal.cpp)
endif()
endif()
if(nfd_PLATFORM STREQUAL PLATFORM_MACOS)
# For setting the filter list, macOS introduced allowedContentTypes in version 11.0 and deprecated allowedFileTypes in 12.0.
# By default (set to ON), NFDe will use allowedContentTypes when targeting macOS >= 11.0.
# Set this option to OFF to always use allowedFileTypes regardless of the target macOS version.
# This is mainly needed for applications that are built on macOS >= 11.0 but should be able to run on lower versions
# and should not be used otherwise.
option(NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE "Use allowedContentTypes for filter lists on macOS >= 11.0" ON)
find_library(APPKIT_LIBRARY AppKit)
if(NFD_USE_ALLOWEDCONTENTTYPES_IF_AVAILABLE)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
"
#include <Availability.h>
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
static_assert(false);
#endif
int main() { return 0; }
"
NFD_USE_ALLOWEDCONTENTTYPES
)
if(NFD_USE_ALLOWEDCONTENTTYPES)
find_library(UNIFORMTYPEIDENTIFIERS_LIBRARY UniformTypeIdentifiers)
if(NOT UNIFORMTYPEIDENTIFIERS_LIBRARY)
message(FATAL_ERROR "UniformTypeIdentifiers framework is not available even though we are targeting macOS >= 11.0")
endif()
endif()
endif()
list(APPEND SOURCE_FILES nfd_cocoa.m)
endif()
# Define the library
add_library(${TARGET_NAME} ${SOURCE_FILES})
# Define alias library to fail early in dependent projects
add_library(${TARGET_NAME}::${TARGET_NAME} ALIAS ${TARGET_NAME})
if (BUILD_SHARED_LIBS)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_EXPORT INTERFACE NFD_SHARED)
endif ()
# Allow includes from include/
target_include_directories(${TARGET_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
if(nfd_PLATFORM STREQUAL PLATFORM_LINUX)
if(NOT NFD_PORTAL)
target_include_directories(${TARGET_NAME}
PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME}
PRIVATE ${GTK3_LINK_LIBRARIES})
else()
target_include_directories(${TARGET_NAME}
PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME}
PRIVATE ${DBUS_LINK_LIBRARIES})
target_compile_definitions(${TARGET_NAME}
PUBLIC NFD_PORTAL)
endif()
option(NFD_APPEND_EXTENSION "Automatically append file extension to an extensionless selection in SaveDialog()" OFF)
if(NFD_APPEND_EXTENSION)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_APPEND_EXTENSION)
endif()
option(NFD_CASE_SENSITIVE_FILTER "Make filters case sensitive" OFF)
if(NFD_CASE_SENSITIVE_FILTER)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_CASE_SENSITIVE_FILTER)
endif()
endif()
if(nfd_PLATFORM STREQUAL PLATFORM_MACOS)
if(NFD_USE_ALLOWEDCONTENTTYPES)
target_link_libraries(${TARGET_NAME} PRIVATE ${APPKIT_LIBRARY} ${UNIFORMTYPEIDENTIFIERS_LIBRARY})
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_MACOS_ALLOWEDCONTENTTYPES=1)
else()
target_link_libraries(${TARGET_NAME} PRIVATE ${APPKIT_LIBRARY})
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_MACOS_ALLOWEDCONTENTTYPES=0)
endif()
endif()
if(nfd_COMPILER STREQUAL COMPILER_MSVC)
string(REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/GR" "/GR-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY STATIC_LIBRARY_OPTIONS /NODEFAULTLIB)
endif()
if(nfd_COMPILER STREQUAL COMPILER_CLANGCL)
string(REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/GR" "/GR-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()
if(nfd_COMPILER STREQUAL COMPILER_GNU)
target_compile_options(${TARGET_NAME} PRIVATE -nostdlib -fno-exceptions -fno-rtti)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADER_FILES}"
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
if (NFD_INSTALL)
include(GNUInstallDirs)
install(TARGETS ${TARGET_NAME} EXPORT ${TARGET_NAME}-export
LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT ${TARGET_NAME}-export
DESTINATION lib/cmake/${TARGET_NAME}
NAMESPACE ${TARGET_NAME}::
FILE ${TARGET_NAME}-config.cmake
)
endif()
option(NFD_OVERRIDE_RECENT_WITH_DEFAULT "Use defaultPath instead of recent folder on Windows" OFF)
if (NFD_OVERRIDE_RECENT_WITH_DEFAULT)
target_compile_definitions(${TARGET_NAME} PRIVATE NFD_OVERRIDE_RECENT_WITH_DEFAULT)
endif()

564
external/nfd/src/include/nfd.h vendored Normal file
View File

@@ -0,0 +1,564 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo, Michael Labbe
This header contains the functions that can be called by user code.
*/
#ifndef _NFD_H
#define _NFD_H
#if defined(_WIN32)
#if defined(NFD_EXPORT)
#define NFD_API __declspec(dllexport)
#elif defined(NFD_SHARED)
#define NFD_API __declspec(dllimport)
#endif
#else
#if defined(NFD_EXPORT) || defined(NFD_SHARED)
#if defined(__GNUC__) || defined(__clang__)
#define NFD_API __attribute__((visibility("default")))
#endif
#endif
#endif
#ifndef NFD_API
#define NFD_API
#endif
#ifdef __cplusplus
extern "C" {
#define NFD_INLINE inline
#else
#define NFD_INLINE static inline
#endif // __cplusplus
#include <stddef.h>
typedef char nfdu8char_t;
#ifdef _WIN32
/** @typedef UTF-16 character */
typedef wchar_t nfdnchar_t;
#else
/** @typedef UTF-8 character */
typedef nfdu8char_t nfdnchar_t;
#endif // _WIN32
/** @typedef Opaque data structure -- see NFD_PathSet_* */
typedef void nfdpathset_t;
#ifndef NFD_PORTAL
typedef struct {
void* ptr;
} nfdpathsetenum_t;
#else
typedef struct {
void* d1;
void* d2;
unsigned int d3;
int d4;
int d5;
int d6;
int d7;
int d8;
int d9;
int d10;
int d11;
int p1;
void* p2;
void* p3;
} nfdpathsetenum_t;
#endif
typedef unsigned int nfdfiltersize_t;
typedef enum {
NFD_ERROR, /**< Programmatic error */
NFD_OKAY, /**< User pressed okay, or successful return */
NFD_CANCEL /**< User pressed cancel */
} nfdresult_t;
/** @typedef UTF-8 Filter Item */
typedef struct {
const nfdu8char_t* name;
const nfdu8char_t* spec;
} nfdu8filteritem_t;
#ifdef _WIN32
/** @typedef UTF-16 Filter Item */
typedef struct {
const nfdnchar_t* name;
const nfdnchar_t* spec;
} nfdnfilteritem_t;
#else
/** @typedef UTF-8 Filter Item */
typedef nfdu8filteritem_t nfdnfilteritem_t;
#endif // _WIN32
// The native window handle type.
enum {
NFD_WINDOW_HANDLE_TYPE_UNSET = 0,
// Windows: handle is HWND (the Windows API typedefs this to void*)
NFD_WINDOW_HANDLE_TYPE_WINDOWS = 1,
// Cocoa: handle is NSWindow*
NFD_WINDOW_HANDLE_TYPE_COCOA = 2,
// X11: handle is Window
NFD_WINDOW_HANDLE_TYPE_X11 = 3,
// Wayland support will be implemented separately in the future
};
// The native window handle. If using a platform abstraction framework (e.g. SDL2), this should be
// obtained using the corresponding NFD glue header (e.g. nfd_sdl2.h).
typedef struct {
size_t type; // this is one of the values of the enum above
void* handle;
} nfdwindowhandle_t;
typedef size_t nfdversion_t;
typedef struct {
const nfdu8filteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdopendialogu8args_t;
#ifdef _WIN32
typedef struct {
const nfdnfilteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdnchar_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdopendialognargs_t;
#else
typedef nfdopendialogu8args_t nfdopendialognargs_t;
#endif // _WIN32
typedef struct {
const nfdu8filteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
const nfdu8char_t* defaultName;
nfdwindowhandle_t parentWindow;
} nfdsavedialogu8args_t;
#ifdef _WIN32
typedef struct {
const nfdnfilteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdnchar_t* defaultPath;
const nfdnchar_t* defaultName;
nfdwindowhandle_t parentWindow;
} nfdsavedialognargs_t;
#else
typedef nfdsavedialogu8args_t nfdsavedialognargs_t;
#endif // _WIN32
typedef struct {
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdpickfolderu8args_t;
#ifdef _WIN32
typedef struct {
const nfdnchar_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdpickfoldernargs_t;
#else
typedef nfdpickfolderu8args_t nfdpickfoldernargs_t;
#endif // _WIN32
// This is a unique identifier tagged to all the NFD_*With() function calls, for backward
// compatibility purposes. There is usually no need to use this directly, unless you want to use
// NFD differently depending on the version you're building with.
#define NFD_INTERFACE_VERSION 1
/** Free a file path that was returned by the dialogs.
*
* Note: use NFD_PathSet_FreePathN() to free path from pathset instead of this function. */
NFD_API void NFD_FreePathN(nfdnchar_t* filePath);
/** Free a file path that was returned by the dialogs.
*
* Note: use NFD_PathSet_FreePathU8() to free path from pathset instead of this function. */
NFD_API void NFD_FreePathU8(nfdu8char_t* filePath);
/** Initialize NFD. Call this for every thread that might use NFD, before calling any other NFD
* functions on that thread. */
NFD_API nfdresult_t NFD_Init(void);
/** Call this to de-initialize NFD, if NFD_Init returned NFD_OKAY. */
NFD_API void NFD_Quit(void);
/** Single file open dialog
*
* It's the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY.
* @param[out] outPath
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
/** Single file open dialog
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY.
* @param[out] outPath
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath);
/** This function is a library implementation detail. Please use NFD_OpenDialogN_With() instead. */
NFD_API nfdresult_t NFD_OpenDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdopendialognargs_t* args);
/** Single file open dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function
* returns NFD_OKAY. See documentation of nfdopendialognargs_t for details. */
NFD_INLINE nfdresult_t NFD_OpenDialogN_With(nfdnchar_t** outPath,
const nfdopendialognargs_t* args) {
return NFD_OpenDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** This function is a library implementation detail. Please use NFD_OpenDialogU8_With() instead.
*/
NFD_API nfdresult_t NFD_OpenDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdopendialogu8args_t* args);
/** Single file open dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY. See documentation of nfdopendialogu8args_t for details. */
NFD_INLINE nfdresult_t NFD_OpenDialogU8_With(nfdu8char_t** outPath,
const nfdopendialogu8args_t* args) {
return NFD_OpenDialogU8_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** Multiple file open dialog
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeN() if this function
* returns NFD_OKAY.
* @param[out] outPaths
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath);
/** Multiple file open dialog
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeU8() if this function
* returns NFD_OKAY.
* @param[out] outPaths
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath);
/** This function is a library implementation detail. Please use NFD_OpenDialogMultipleN_With()
* instead. */
NFD_API nfdresult_t NFD_OpenDialogMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialognargs_t* args);
/** Multiple file open dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeN() if this function
* returns NFD_OKAY. See documentation of nfdopendialognargs_t for details. */
NFD_INLINE nfdresult_t NFD_OpenDialogMultipleN_With(const nfdpathset_t** outPaths,
const nfdopendialognargs_t* args) {
return NFD_OpenDialogMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, args);
}
/** This function is a library implementation detail. Please use NFD_OpenDialogU8_With() instead.
*/
NFD_API nfdresult_t NFD_OpenDialogMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialogu8args_t* args);
/** Multiple file open dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeU8() if this function
* returns NFD_OKAY. See documentation of nfdopendialogu8args_t for details. */
NFD_INLINE nfdresult_t NFD_OpenDialogMultipleU8_With(const nfdpathset_t** outPaths,
const nfdopendialogu8args_t* args) {
return NFD_OpenDialogMultipleU8_With_Impl(NFD_INTERFACE_VERSION, outPaths, args);
}
/** Save dialog
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY.
* @param[out] outPath
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath,
const nfdnchar_t* defaultName);
/** Save dialog
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY.
* @param[out] outPath
* @param filterCount If zero, filterList is ignored (you can use null).
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath,
const nfdu8char_t* defaultName);
/** This function is a library implementation detail. Please use NFD_SaveDialogN_With() instead. */
NFD_API nfdresult_t NFD_SaveDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdsavedialognargs_t* args);
/** Single file save dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function
* returns NFD_OKAY. See documentation of nfdsavedialognargs_t for details. */
NFD_INLINE nfdresult_t NFD_SaveDialogN_With(nfdnchar_t** outPath,
const nfdsavedialognargs_t* args) {
return NFD_SaveDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** This function is a library implementation detail. Please use NFD_SaveDialogU8_With() instead.
*/
NFD_API nfdresult_t NFD_SaveDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdsavedialogu8args_t* args);
/** Single file save dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY. See documentation of nfdsavedialogu8args_t for details. */
NFD_INLINE nfdresult_t NFD_SaveDialogU8_With(nfdu8char_t** outPath,
const nfdsavedialogu8args_t* args) {
return NFD_SaveDialogU8_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** Select single folder dialog
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function returns
* NFD_OKAY.
* @param[out] outPath
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath);
/** Select single folder dialog
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY.
* @param[out] outPath
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath);
/** This function is a library implementation detail. Please use NFD_PickFolderN_With() instead. */
NFD_API nfdresult_t NFD_PickFolderN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdpickfoldernargs_t* args);
/** Select single folder dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathN() if this function
* returns NFD_OKAY. See documentation of nfdpickfoldernargs_t for details. */
NFD_INLINE nfdresult_t NFD_PickFolderN_With(nfdnchar_t** outPath,
const nfdpickfoldernargs_t* args) {
return NFD_PickFolderN_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** This function is a library implementation detail. Please use NFD_PickFolderU8_With() instead.
*/
NFD_API nfdresult_t NFD_PickFolderU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdpickfolderu8args_t* args);
/** Select single folder dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPath` via NFD_FreePathU8() if this function
* returns NFD_OKAY. See documentation of nfdpickfolderu8args_t for details. */
NFD_INLINE nfdresult_t NFD_PickFolderU8_With(nfdu8char_t** outPath,
const nfdpickfolderu8args_t* args) {
return NFD_PickFolderU8_With_Impl(NFD_INTERFACE_VERSION, outPath, args);
}
/** Select multiple folder dialog
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeN() if this function
* returns NFD_OKAY.
* @param[out] outPaths
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_PickFolderMultipleN(const nfdpathset_t** outPaths,
const nfdnchar_t* defaultPath);
/** Select multiple folder dialog
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeU8() if this function
* returns NFD_OKAY.
* @param[out] outPaths
* @param defaultPath If null, the operating system will decide. */
NFD_API nfdresult_t NFD_PickFolderMultipleU8(const nfdpathset_t** outPaths,
const nfdu8char_t* defaultPath);
/** This function is a library implementation detail. Please use NFD_PickFolderMultipleN_With()
* instead. */
NFD_API nfdresult_t NFD_PickFolderMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfoldernargs_t* args);
/** Select multiple folder dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeN() if this function
* returns NFD_OKAY. See documentation of nfdopendialogargs_t for details. */
NFD_INLINE nfdresult_t NFD_PickFolderMultipleN_With(const nfdpathset_t** outPaths,
const nfdpickfoldernargs_t* args) {
return NFD_PickFolderMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, args);
}
/** This function is a library implementation detail. Please use NFD_PickFolderMultipleU8_With()
* instead.
*/
NFD_API nfdresult_t NFD_PickFolderMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfolderu8args_t* args);
/** Select multiple folder dialog, with additional parameters.
*
* It is the caller's responsibility to free `outPaths` via NFD_PathSet_FreeU8() if this function
* returns NFD_OKAY. See documentation of nfdpickfolderargs_t for details. */
NFD_INLINE nfdresult_t NFD_PickFolderMultipleU8_With(const nfdpathset_t** outPaths,
const nfdpickfolderu8args_t* args) {
return NFD_PickFolderMultipleU8_With_Impl(NFD_INTERFACE_VERSION, outPaths, args);
}
/** Get the last error
*
* This is set when a function returns NFD_ERROR.
* The memory is owned by NFD and should not be freed by user code.
* This is *always* ASCII printable characters, so it can be interpreted as UTF-8 without any
* conversion.
* @return The last error that was set, or null if there is no error. */
NFD_API const char* NFD_GetError(void);
/** Clear the error. */
NFD_API void NFD_ClearError(void);
/* path set operations */
#ifdef _WIN32
typedef unsigned long nfdpathsetsize_t;
#elif __APPLE__
typedef unsigned long nfdpathsetsize_t;
#else
typedef unsigned int nfdpathsetsize_t;
#endif // _WIN32, __APPLE__
/** Get the number of entries stored in pathSet.
*
* Note: some paths might be invalid (NFD_ERROR will be returned by NFD_PathSet_GetPath),
* so we might not actually have this number of usable paths. */
NFD_API nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count);
/** Get the UTF-8 path at offset index.
*
* It is the caller's responsibility to free `outPath` via NFD_PathSet_FreePathN() if this function
* returns NFD_OKAY. */
NFD_API nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t** outPath);
/** Get the native path at offset index.
*
* It is the caller's responsibility to free `outPath` via NFD_PathSet_FreePathU8() if this
* function returns NFD_OKAY. */
NFD_API nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t** outPath);
/** Free the path gotten by NFD_PathSet_GetPathN(). */
NFD_API void NFD_PathSet_FreePathN(const nfdnchar_t* filePath);
/** Free the path gotten by NFD_PathSet_GetPathU8(). */
NFD_API void NFD_PathSet_FreePathU8(const nfdu8char_t* filePath);
/** Gets an enumerator of the path set.
*
* It is the caller's responsibility to free `enumerator` via NFD_PathSet_FreeEnum()
* if this function returns NFD_OKAY, and it should be freed before freeing the pathset. */
NFD_API nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet,
nfdpathsetenum_t* outEnumerator);
/** Frees an enumerator of the path set. */
NFD_API void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator);
/** Gets the next item from the path set enumerator.
*
* If there are no more items, then *outPaths will be set to null.
* It is the caller's responsibility to free `*outPath` via NFD_PathSet_FreePathN()
* if this function returns NFD_OKAY and `*outPath` is not null. */
NFD_API nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath);
/** Gets the next item from the path set enumerator.
*
* If there are no more items, then *outPaths will be set to null.
* It is the caller's responsibility to free `*outPath` via NFD_PathSet_FreePathU8()
* if this function returns NFD_OKAY and `*outPath` is not null. */
NFD_API nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath);
/** Free the pathSet */
NFD_API void NFD_PathSet_Free(const nfdpathset_t* pathSet);
#ifdef _WIN32
/* say that the U8 versions of functions are not just __attribute__((alias(""))) to the native
* versions */
#define NFD_DIFFERENT_NATIVE_FUNCTIONS
#endif // _WIN32
#ifdef NFD_NATIVE
typedef nfdnchar_t nfdchar_t;
typedef nfdnfilteritem_t nfdfilteritem_t;
#define NFD_FreePath NFD_FreePathN
#define NFD_OpenDialog NFD_OpenDialogN
#define NFD_OpenDialogMultiple NFD_OpenDialogMultipleN
#define NFD_SaveDialog NFD_SaveDialogN
#define NFD_PickFolder NFD_PickFolderN
#define NFD_PickFolderMultiple NFD_PickFolderMultipleN
#define NFD_PathSet_GetPath NFD_PathSet_GetPathN
#define NFD_PathSet_FreePath NFD_PathSet_FreePathN
#define NFD_PathSet_EnumNext NFD_PathSet_EnumNextN
#else
typedef nfdu8char_t nfdchar_t;
typedef nfdu8filteritem_t nfdfilteritem_t;
#define NFD_FreePath NFD_FreePathU8
#define NFD_OpenDialog NFD_OpenDialogU8
#define NFD_OpenDialogMultiple NFD_OpenDialogMultipleU8
#define NFD_SaveDialog NFD_SaveDialogU8
#define NFD_PickFolder NFD_PickFolderU8
#define NFD_PickFolderMultiple NFD_PickFolderMultipleU8
#define NFD_PathSet_GetPath NFD_PathSet_GetPathU8
#define NFD_PathSet_FreePath NFD_PathSet_FreePathU8
#define NFD_PathSet_EnumNext NFD_PathSet_EnumNextU8
#endif // NFD_NATIVE
#undef NFD_INLINE
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _NFD_H

375
external/nfd/src/include/nfd.hpp vendored Normal file
View File

@@ -0,0 +1,375 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Author: Bernard Teo
This header is a thin C++ wrapper for nfd.h.
C++ projects can choose to use this header instead of nfd.h directly.
Refer to documentation on nfd.h for instructions on how to use these functions.
*/
#ifndef _NFD_HPP
#define _NFD_HPP
#include <nfd.h>
#include <cstddef> // for std::size_t
#include <memory> // for std::unique_ptr
#ifdef NFD_THROWS_EXCEPTIONS
#include <stdexcept>
#endif
namespace NFD {
inline nfdresult_t Init() noexcept {
return ::NFD_Init();
}
inline void Quit() noexcept {
::NFD_Quit();
}
inline void FreePath(nfdnchar_t* outPath) noexcept {
::NFD_FreePathN(outPath);
}
inline nfdresult_t OpenDialog(nfdnchar_t*& outPath,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdopendialognargs_t args{filterList, filterCount, defaultPath, parentWindow};
return ::NFD_OpenDialogN_With(&outPath, &args);
}
inline nfdresult_t OpenDialogMultiple(const nfdpathset_t*& outPaths,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdopendialognargs_t args{filterList, filterCount, defaultPath, parentWindow};
return ::NFD_OpenDialogMultipleN_With(&outPaths, &args);
}
inline nfdresult_t SaveDialog(nfdnchar_t*& outPath,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
const nfdnchar_t* defaultName = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdsavedialognargs_t args{
filterList, filterCount, defaultPath, defaultName, parentWindow};
return ::NFD_SaveDialogN_With(&outPath, &args);
}
inline nfdresult_t PickFolder(nfdnchar_t*& outPath,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpickfoldernargs_t args{defaultPath, parentWindow};
return ::NFD_PickFolderN_With(&outPath, &args);
}
inline nfdresult_t PickFolderMultiple(const nfdpathset_t*& outPaths,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpickfoldernargs_t args{defaultPath, parentWindow};
return ::NFD_PickFolderMultipleN_With(&outPaths, &args);
}
inline const char* GetError() noexcept {
return ::NFD_GetError();
}
inline void ClearError() noexcept {
::NFD_ClearError();
}
namespace PathSet {
inline nfdresult_t Count(const nfdpathset_t* pathSet, nfdpathsetsize_t& count) noexcept {
return ::NFD_PathSet_GetCount(pathSet, &count);
}
inline nfdresult_t GetPath(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t*& outPath) noexcept {
return ::NFD_PathSet_GetPathN(pathSet, index, &outPath);
}
inline void FreePath(nfdnchar_t* filePath) noexcept {
::NFD_PathSet_FreePathN(filePath);
}
inline void Free(const nfdpathset_t* pathSet) noexcept {
::NFD_PathSet_Free(pathSet);
}
} // namespace PathSet
#ifdef NFD_DIFFERENT_NATIVE_FUNCTIONS
/* we need the C++ bindings for the UTF-8 functions as well, because there are different functions
* for them */
inline void FreePath(nfdu8char_t* outPath) noexcept {
::NFD_FreePathU8(outPath);
}
inline nfdresult_t OpenDialog(nfdu8char_t*& outPath,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdopendialogu8args_t args{filterList, filterCount, defaultPath, parentWindow};
return ::NFD_OpenDialogU8_With(&outPath, &args);
}
inline nfdresult_t OpenDialogMultiple(const nfdpathset_t*& outPaths,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdopendialogu8args_t args{filterList, filterCount, defaultPath, parentWindow};
return ::NFD_OpenDialogMultipleU8_With(&outPaths, &args);
}
inline nfdresult_t SaveDialog(nfdu8char_t*& outPath,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
const nfdu8char_t* defaultName = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdsavedialogu8args_t args{
filterList, filterCount, defaultPath, defaultName, parentWindow};
return ::NFD_SaveDialogU8_With(&outPath, &args);
}
inline nfdresult_t PickFolder(nfdu8char_t*& outPath,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpickfolderu8args_t args{defaultPath, parentWindow};
return ::NFD_PickFolderU8_With(&outPath, &args);
}
inline nfdresult_t PickFolderMultiple(const nfdpathset_t*& outPaths,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpickfolderu8args_t args{defaultPath, parentWindow};
return ::NFD_PickFolderMultipleU8_With(&outPaths, &args);
}
namespace PathSet {
inline nfdresult_t GetPath(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t*& outPath) noexcept {
return ::NFD_PathSet_GetPathU8(pathSet, index, &outPath);
}
inline void FreePath(nfdu8char_t* filePath) noexcept {
::NFD_PathSet_FreePathU8(filePath);
}
} // namespace PathSet
#endif
// smart objects
class Guard {
public:
#ifndef NFD_THROWS_EXCEPTIONS
inline Guard() noexcept {
Init(); // always assume that initialization succeeds
}
#else
inline Guard() {
if (!Init()) {
throw std::runtime_error(GetError());
}
}
#endif
inline ~Guard() noexcept { Quit(); }
// Not allowed to copy or move this class
Guard(const Guard&) = delete;
Guard& operator=(const Guard&) = delete;
};
template <typename T>
struct PathDeleter {
inline void operator()(T* ptr) const noexcept { FreePath(ptr); }
};
typedef std::unique_ptr<nfdchar_t, PathDeleter<nfdchar_t>> UniquePath;
typedef std::unique_ptr<nfdnchar_t, PathDeleter<nfdnchar_t>> UniquePathN;
typedef std::unique_ptr<nfdu8char_t, PathDeleter<nfdu8char_t>> UniquePathU8;
struct PathSetDeleter {
inline void operator()(const nfdpathset_t* ptr) const noexcept { PathSet::Free(ptr); }
};
typedef std::unique_ptr<const nfdpathset_t, PathSetDeleter> UniquePathSet;
template <typename T>
struct PathSetPathDeleter {
inline void operator()(T* ptr) const noexcept { PathSet::FreePath(ptr); }
};
typedef std::unique_ptr<nfdchar_t, PathSetPathDeleter<nfdchar_t>> UniquePathSetPath;
typedef std::unique_ptr<nfdnchar_t, PathSetPathDeleter<nfdnchar_t>> UniquePathSetPathN;
typedef std::unique_ptr<nfdu8char_t, PathSetPathDeleter<nfdu8char_t>> UniquePathSetPathU8;
inline nfdresult_t OpenDialog(UniquePathN& outPath,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdnchar_t* out;
nfdresult_t res = OpenDialog(out, filterList, filterCount, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t OpenDialogMultiple(UniquePathSet& outPaths,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpathset_t* out;
nfdresult_t res = OpenDialogMultiple(out, filterList, filterCount, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPaths.reset(out);
}
return res;
}
inline nfdresult_t SaveDialog(UniquePathN& outPath,
const nfdnfilteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdnchar_t* defaultPath = nullptr,
const nfdnchar_t* defaultName = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdnchar_t* out;
nfdresult_t res =
SaveDialog(out, filterList, filterCount, defaultPath, defaultName, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t PickFolder(UniquePathN& outPath,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdnchar_t* out;
nfdresult_t res = PickFolder(out, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t PickFolderMultiple(UniquePathSet& outPaths,
const nfdnchar_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpathset_t* out;
nfdresult_t res = PickFolderMultiple(out, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPaths.reset(out);
}
return res;
}
#ifdef NFD_DIFFERENT_NATIVE_FUNCTIONS
inline nfdresult_t OpenDialog(UniquePathU8& outPath,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdu8char_t* out;
nfdresult_t res = OpenDialog(out, filterList, filterCount, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t OpenDialogMultiple(UniquePathSet& outPaths,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpathset_t* out;
nfdresult_t res = OpenDialogMultiple(out, filterList, filterCount, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPaths.reset(out);
}
return res;
}
inline nfdresult_t SaveDialog(UniquePathU8& outPath,
const nfdu8filteritem_t* filterList = nullptr,
nfdfiltersize_t filterCount = 0,
const nfdu8char_t* defaultPath = nullptr,
const nfdu8char_t* defaultName = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdu8char_t* out;
nfdresult_t res =
SaveDialog(out, filterList, filterCount, defaultPath, defaultName, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t PickFolder(UniquePathU8& outPath,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
nfdu8char_t* out;
nfdresult_t res = PickFolder(out, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
inline nfdresult_t PickFolderMultiple(UniquePathSet& outPaths,
const nfdu8char_t* defaultPath = nullptr,
nfdwindowhandle_t parentWindow = {}) noexcept {
const nfdpathset_t* out;
nfdresult_t res = PickFolderMultiple(out, defaultPath, parentWindow);
if (res == NFD_OKAY) {
outPaths.reset(out);
}
return res;
}
#endif
namespace PathSet {
inline nfdresult_t Count(const UniquePathSet& uniquePathSet, nfdpathsetsize_t& count) noexcept {
return Count(uniquePathSet.get(), count);
}
inline nfdresult_t GetPath(const UniquePathSet& uniquePathSet,
nfdpathsetsize_t index,
UniquePathSetPathN& outPath) noexcept {
nfdnchar_t* out;
nfdresult_t res = GetPath(uniquePathSet.get(), index, out);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
#ifdef NFD_DIFFERENT_NATIVE_FUNCTIONS
inline nfdresult_t GetPath(const UniquePathSet& uniquePathSet,
nfdpathsetsize_t index,
UniquePathSetPathU8& outPath) noexcept {
nfdu8char_t* out;
nfdresult_t res = GetPath(uniquePathSet.get(), index, out);
if (res == NFD_OKAY) {
outPath.reset(out);
}
return res;
}
#endif
} // namespace PathSet
} // namespace NFD
#endif

85
external/nfd/src/include/nfd_glfw3.h vendored Normal file
View File

@@ -0,0 +1,85 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo
This header contains a function to convert a GLFW window handle to a native window handle for
passing to NFDe.
*/
#ifndef _NFD_GLFW3_H
#define _NFD_GLFW3_H
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <nfd.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#define NFD_INLINE inline
#else
#define NFD_INLINE static inline
#endif // __cplusplus
/**
* Converts a GLFW window handle to a native window handle that can be passed to NFDe.
* @param sdlWindow The GLFW window handle.
* @param[out] nativeWindow The output native window handle, populated if and only if this function
* returns true.
* @return Either true to indicate success, or false to indicate failure. It is intended that
* users ignore the error and simply pass a value-initialized nfdwindowhandle_t to NFDe if this
* function fails. */
NFD_INLINE bool NFD_GetNativeWindowFromGLFWWindow(GLFWwindow* glfwWindow,
nfdwindowhandle_t* nativeWindow) {
GLFWerrorfun oldCallback = glfwSetErrorCallback(NULL);
bool success = false;
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
if (!success) {
const HWND hwnd = glfwGetWin32Window(glfwWindow);
if (hwnd) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WINDOWS;
nativeWindow->handle = (void*)hwnd;
success = true;
}
}
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
if (!success) {
const id cocoa_window = glfwGetCocoaWindow(glfwWindow);
if (cocoa_window) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_COCOA;
nativeWindow->handle = (void*)cocoa_window;
success = true;
}
}
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
if (!success) {
const Window x11_window = glfwGetX11Window(glfwWindow);
if (x11_window != None) {
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11;
nativeWindow->handle = (void*)x11_window;
success = true;
}
}
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
// For now we don't support Wayland, but we intend to support it eventually.
// Silence the warnings.
{
(void)glfwWindow;
(void)nativeWindow;
}
#endif
glfwSetErrorCallback(oldCallback);
return success;
}
#undef NFD_INLINE
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _NFD_GLFW3_H

76
external/nfd/src/include/nfd_sdl2.h vendored Normal file
View File

@@ -0,0 +1,76 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo
This header contains a function to convert an SDL window handle to a native window handle for
passing to NFDe.
This is meant to be used with SDL2, but if there are incompatibilities with future SDL versions,
we can conditionally compile based on SDL_MAJOR_VERSION.
*/
#ifndef _NFD_SDL2_H
#define _NFD_SDL2_H
#include <SDL_error.h>
#include <SDL_syswm.h>
#include <nfd.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#define NFD_INLINE inline
#else
#define NFD_INLINE static inline
#endif // __cplusplus
/**
* Converts an SDL window handle to a native window handle that can be passed to NFDe.
* @param sdlWindow The SDL window handle.
* @param[out] nativeWindow The output native window handle, populated if and only if this function
* returns true.
* @return Either true to indicate success, or false to indicate failure. If false is returned,
* you can call SDL_GetError() for more information. However, it is intended that users ignore the
* error and simply pass a value-initialized nfdwindowhandle_t to NFDe if this function fails. */
NFD_INLINE bool NFD_GetNativeWindowFromSDLWindow(SDL_Window* sdlWindow,
nfdwindowhandle_t* nativeWindow) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(sdlWindow, &info)) {
return false;
}
switch (info.subsystem) {
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
case SDL_SYSWM_WINDOWS:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_WINDOWS;
nativeWindow->handle = (void*)info.info.win.window;
return true;
#endif
#if defined(SDL_VIDEO_DRIVER_COCOA)
case SDL_SYSWM_COCOA:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_COCOA;
nativeWindow->handle = (void*)info.info.cocoa.window;
return true;
#endif
#if defined(SDL_VIDEO_DRIVER_X11)
case SDL_SYSWM_X11:
nativeWindow->type = NFD_WINDOW_HANDLE_TYPE_X11;
nativeWindow->handle = (void*)info.info.x11.window;
return true;
#endif
default:
// Silence the warning in case we are not using a supported backend.
(void)nativeWindow;
SDL_SetError("Unsupported native window type.");
return false;
}
}
#undef NFD_INLINE
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _NFD_SDL2_H

615
external/nfd/src/nfd_cocoa.m vendored Normal file
View File

@@ -0,0 +1,615 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo, Michael Labbe
*/
#include <AppKit/AppKit.h>
#include <Availability.h>
#include "nfd.h"
// MacOS is deprecating the allowedFileTypes property in favour of allowedContentTypes, so we have
// to introduce this breaking change. Define NFD_MACOS_ALLOWEDCONTENTTYPES to 1 to have it set the
// allowedContentTypes property of the SavePanel or OpenPanel. Define
// NFD_MACOS_ALLOWEDCONTENTTYPES to 0 to have it set the allowedFileTypes property of the SavePanel
// or OpenPanel. If NFD_MACOS_ALLOWEDCONTENTTYPES is undefined, then it will set it to 1 if
// __MAC_OS_X_VERSION_MIN_REQUIRED >= 11.0, and 0 otherwise.
#if !defined(NFD_MACOS_ALLOWEDCONTENTTYPES)
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || \
__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
#define NFD_MACOS_ALLOWEDCONTENTTYPES 0
#else
#define NFD_MACOS_ALLOWEDCONTENTTYPES 1
#endif
#endif
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#endif
static const char* g_errorstr = NULL;
static void NFDi_SetError(const char* msg) {
g_errorstr = msg;
}
static void* NFDi_Malloc(size_t bytes) {
void* ptr = malloc(bytes);
if (!ptr) NFDi_SetError("NFDi_Malloc failed.");
return ptr;
}
static void NFDi_Free(void* ptr) {
assert(ptr);
free(ptr);
}
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
// Returns an NSArray of UTType representing the content types.
static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
NSMutableArray* buildFilterList = [[NSMutableArray alloc] init];
for (nfdfiltersize_t filterIndex = 0; filterIndex != filterCount; ++filterIndex) {
// this is the spec to parse (we don't use the friendly name on OS X)
const nfdnchar_t* filterSpec = filterList[filterIndex].spec;
const nfdnchar_t* p_currentFilterBegin = filterSpec;
for (const nfdnchar_t* p_filterSpec = filterSpec; *p_filterSpec; ++p_filterSpec) {
if (*p_filterSpec == ',') {
// add the extension to the array
NSString* filterStr = [[NSString alloc]
initWithBytes:(const void*)p_currentFilterBegin
length:(sizeof(nfdnchar_t) * (p_filterSpec - p_currentFilterBegin))
encoding:NSUTF8StringEncoding];
UTType* filterType = [UTType typeWithFilenameExtension:filterStr
conformingToType:UTTypeData];
[filterStr release];
if (filterType) [buildFilterList addObject:filterType];
p_currentFilterBegin = p_filterSpec + 1;
}
}
// add the extension to the array
NSString* filterStr = [[NSString alloc] initWithUTF8String:p_currentFilterBegin];
UTType* filterType = [UTType typeWithFilenameExtension:filterStr
conformingToType:UTTypeData];
[filterStr release];
if (filterType) [buildFilterList addObject:filterType];
}
NSArray* returnArray = [NSArray arrayWithArray:buildFilterList];
[buildFilterList release];
assert([returnArray count] != 0);
return returnArray;
}
#else
// Returns an NSArray of NSString representing the file types.
static NSArray* BuildAllowedFileTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
NSMutableArray* buildFilterList = [[NSMutableArray alloc] init];
for (nfdfiltersize_t filterIndex = 0; filterIndex != filterCount; ++filterIndex) {
// this is the spec to parse (we don't use the friendly name on OS X)
const nfdnchar_t* filterSpec = filterList[filterIndex].spec;
const nfdnchar_t* p_currentFilterBegin = filterSpec;
for (const nfdnchar_t* p_filterSpec = filterSpec; *p_filterSpec; ++p_filterSpec) {
if (*p_filterSpec == ',') {
// add the extension to the array
NSString* filterStr = [[[NSString alloc]
initWithBytes:(const void*)p_currentFilterBegin
length:(sizeof(nfdnchar_t) * (p_filterSpec - p_currentFilterBegin))
encoding:NSUTF8StringEncoding] autorelease];
[buildFilterList addObject:filterStr];
p_currentFilterBegin = p_filterSpec + 1;
}
}
// add the extension to the array
NSString* filterStr = [NSString stringWithUTF8String:p_currentFilterBegin];
[buildFilterList addObject:filterStr];
}
NSArray* returnArray = [NSArray arrayWithArray:buildFilterList];
[buildFilterList release];
assert([returnArray count] != 0);
return returnArray;
}
#endif
static void AddFilterListToDialog(NSSavePanel* dialog,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
// note: NSOpenPanel inherits from NSSavePanel.
if (!filterCount) return;
assert(filterList);
// Make NSArray of file types and set it on the dialog
// We use setAllowedFileTypes or setAllowedContentTypes depending on the deployment target
#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
[dialog setAllowedContentTypes:allowedContentTypes];
#else
NSArray* allowedFileTypes = BuildAllowedFileTypes(filterList, filterCount);
[dialog setAllowedFileTypes:allowedFileTypes];
#endif
}
static void SetDefaultPath(NSSavePanel* dialog, const nfdnchar_t* defaultPath) {
if (!defaultPath || !*defaultPath) return;
NSString* defaultPathString = [NSString stringWithUTF8String:defaultPath];
NSURL* url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES];
[dialog setDirectoryURL:url];
}
static void SetDefaultName(NSSavePanel* dialog, const nfdnchar_t* defaultName) {
if (!defaultName || !*defaultName) return;
NSString* defaultNameString = [NSString stringWithUTF8String:defaultName];
[dialog setNameFieldStringValue:defaultNameString];
}
static nfdresult_t CopyUtf8String(const char* utf8Str, nfdnchar_t** out) {
// byte count, not char count
size_t len = strlen(utf8Str);
// Too bad we have to use additional memory for all the result paths,
// because we cannot reconstitute an NSString from a char* to release it properly.
*out = (nfdnchar_t*)NFDi_Malloc(len + 1);
if (*out) {
strcpy(*out, utf8Str);
return NFD_OKAY;
}
return NFD_ERROR;
}
static NSWindow* GetNativeWindowHandle(const nfdwindowhandle_t* parentWindow) {
if (parentWindow->type != NFD_WINDOW_HANDLE_TYPE_COCOA) {
return NULL;
}
return (NSWindow*)parentWindow->handle;
}
/* public */
const char* NFD_GetError(void) {
return g_errorstr;
}
void NFD_ClearError(void) {
NFDi_SetError(NULL);
}
void NFD_FreePathN(nfdnchar_t* filePath) {
NFDi_Free((void*)filePath);
}
void NFD_FreePathU8(nfdu8char_t* filePath) {
NFD_FreePathN(filePath);
}
static NSApplicationActivationPolicy old_app_policy;
nfdresult_t NFD_Init(void) {
NSApplication* app = [NSApplication sharedApplication];
old_app_policy = [app activationPolicy];
if (old_app_policy == NSApplicationActivationPolicyProhibited) {
if (![app setActivationPolicy:NSApplicationActivationPolicyAccessory]) {
NFDi_SetError("Failed to set activation policy.");
return NFD_ERROR;
}
}
return NFD_OKAY;
}
/* call this to de-initialize NFD, if NFD_Init returned NFD_OKAY */
void NFD_Quit(void) {
[[NSApplication sharedApplication] setActivationPolicy:old_app_policy];
}
nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
nfdopendialognargs_t args = {0};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
return NFD_OpenDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_OpenDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdopendialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
nfdresult_t result = NFD_CANCEL;
@autoreleasepool {
NSWindow* keyWindow = GetNativeWindowHandle(&args->parentWindow);
if (keyWindow) {
[keyWindow makeKeyAndOrderFront:nil];
} else {
keyWindow = [[NSApplication sharedApplication] keyWindow];
}
NSOpenPanel* dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:NO];
// Build the filter list
AddFilterListToDialog(dialog, args->filterList, args->filterCount);
// Set the starting directory
SetDefaultPath(dialog, args->defaultPath);
if ([dialog runModal] == NSModalResponseOK) {
const NSURL* url = [dialog URL];
const char* utf8Path = [[url path] UTF8String];
result = CopyUtf8String(utf8Path, outPath);
}
// return focus to the key window (i.e. main window)
[keyWindow makeKeyAndOrderFront:nil];
}
return result;
}
nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath) {
return NFD_OpenDialogN(outPath, filterList, filterCount, defaultPath);
}
nfdresult_t NFD_OpenDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdopendialogu8args_t* args) {
return NFD_OpenDialogN_With_Impl(version, outPath, args);
}
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
nfdopendialognargs_t args = {0};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
return NFD_OpenDialogMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, &args);
}
nfdresult_t NFD_OpenDialogMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
nfdresult_t result = NFD_CANCEL;
@autoreleasepool {
NSWindow* keyWindow = GetNativeWindowHandle(&args->parentWindow);
if (keyWindow) {
[keyWindow makeKeyAndOrderFront:nil];
} else {
keyWindow = [[NSApplication sharedApplication] keyWindow];
}
NSOpenPanel* dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:YES];
// Build the filter list
AddFilterListToDialog(dialog, args->filterList, args->filterCount);
// Set the starting directory
SetDefaultPath(dialog, args->defaultPath);
if ([dialog runModal] == NSModalResponseOK) {
const NSArray* urls = [dialog URLs];
if ([urls count] > 0) {
// have at least one URL, we return this NSArray
[urls retain];
*outPaths = (const nfdpathset_t*)urls;
result = NFD_OKAY;
}
}
// return focus to the key window (i.e. main window)
[keyWindow makeKeyAndOrderFront:nil];
}
return result;
}
nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath) {
return NFD_OpenDialogMultipleN(outPaths, filterList, filterCount, defaultPath);
}
nfdresult_t NFD_OpenDialogMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialogu8args_t* args) {
return NFD_OpenDialogMultipleN_With_Impl(version, outPaths, args);
}
nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath,
const nfdnchar_t* defaultName) {
nfdsavedialognargs_t args = {0};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
args.defaultName = defaultName;
return NFD_SaveDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_SaveDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdsavedialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
nfdresult_t result = NFD_CANCEL;
@autoreleasepool {
NSWindow* keyWindow = GetNativeWindowHandle(&args->parentWindow);
if (keyWindow) {
[keyWindow makeKeyAndOrderFront:nil];
} else {
keyWindow = [[NSApplication sharedApplication] keyWindow];
}
NSSavePanel* dialog = [NSSavePanel savePanel];
[dialog setExtensionHidden:NO];
// allow other file types, to give the user an escape hatch since you can't select "*.*" on
// Mac
[dialog setAllowsOtherFileTypes:TRUE];
// Build the filter list
AddFilterListToDialog(dialog, args->filterList, args->filterCount);
// Set the starting directory
SetDefaultPath(dialog, args->defaultPath);
// Set the default file name
SetDefaultName(dialog, args->defaultName);
if ([dialog runModal] == NSModalResponseOK) {
const NSURL* url = [dialog URL];
const char* utf8Path = [[url path] UTF8String];
result = CopyUtf8String(utf8Path, outPath);
}
// return focus to the key window (i.e. main window)
[keyWindow makeKeyAndOrderFront:nil];
}
return result;
}
nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath,
const nfdu8char_t* defaultName) {
return NFD_SaveDialogN(outPath, filterList, filterCount, defaultPath, defaultName);
}
nfdresult_t NFD_SaveDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdsavedialogu8args_t* args) {
return NFD_SaveDialogN_With_Impl(version, outPath, args);
}
nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) {
nfdpickfoldernargs_t args = {0};
args.defaultPath = defaultPath;
return NFD_PickFolderN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_PickFolderN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdpickfoldernargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
nfdresult_t result = NFD_CANCEL;
@autoreleasepool {
NSWindow* keyWindow = GetNativeWindowHandle(&args->parentWindow);
if (keyWindow) {
[keyWindow makeKeyAndOrderFront:nil];
} else {
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, args->defaultPath);
if ([dialog runModal] == NSModalResponseOK) {
const NSURL* url = [dialog URL];
const char* utf8Path = [[url path] UTF8String];
result = CopyUtf8String(utf8Path, outPath);
}
// return focus to the key window (i.e. main window)
[keyWindow makeKeyAndOrderFront:nil];
}
return result;
}
nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath) {
return NFD_PickFolderN(outPath, defaultPath);
}
nfdresult_t NFD_PickFolderU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdpickfolderu8args_t* args) {
return NFD_PickFolderN_With_Impl(version, outPath, args);
}
nfdresult_t NFD_PickFolderMultipleN(const nfdpathset_t** outPaths, const nfdnchar_t* defaultPath) {
nfdpickfoldernargs_t args = {0};
args.defaultPath = defaultPath;
return NFD_PickFolderMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, &args);
}
nfdresult_t NFD_PickFolderMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfoldernargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
nfdresult_t result = NFD_CANCEL;
@autoreleasepool {
NSWindow* keyWindow = GetNativeWindowHandle(&args->parentWindow);
if (keyWindow) {
[keyWindow makeKeyAndOrderFront:nil];
} else {
keyWindow = [[NSApplication sharedApplication] keyWindow];
}
NSOpenPanel* dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:YES];
[dialog setCanChooseDirectories:YES];
[dialog setCanCreateDirectories:YES];
[dialog setCanChooseFiles:NO];
// Set the starting directory
SetDefaultPath(dialog, args->defaultPath);
if ([dialog runModal] == NSModalResponseOK) {
const NSArray* urls = [dialog URLs];
if ([urls count] > 0) {
// have at least one URL, we return this NSArray
[urls retain];
*outPaths = (const nfdpathset_t*)urls;
result = NFD_OKAY;
}
}
// return focus to the key window (i.e. main window)
[keyWindow makeKeyAndOrderFront:nil];
}
return result;
}
nfdresult_t NFD_PickFolderMultipleU8(const nfdpathset_t** outPaths,
const nfdu8char_t* defaultPath) {
return NFD_PickFolderMultipleN(outPaths, defaultPath);
}
nfdresult_t NFD_PickFolderMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfolderu8args_t* args) {
return NFD_PickFolderMultipleN_With_Impl(version, outPaths, args);
}
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) {
const NSArray* urls = (const NSArray*)pathSet;
*count = [urls count];
return NFD_OKAY;
}
nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t** outPath) {
const NSArray* urls = (const NSArray*)pathSet;
@autoreleasepool {
// autoreleasepool needed because UTF8String method might use the pool
const NSURL* url = [urls objectAtIndex:index];
const char* utf8Path = [[url path] UTF8String];
return CopyUtf8String(utf8Path, outPath);
}
}
nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t** outPath) {
return NFD_PathSet_GetPathN(pathSet, index, outPath);
}
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) {
// const_cast not supported on Mac
union {
const nfdnchar_t* constPath;
nfdnchar_t* nonConstPath;
} pathUnion;
pathUnion.constPath = filePath;
NFD_FreePathN(pathUnion.nonConstPath);
}
void NFD_PathSet_FreePathU8(const nfdu8char_t* filePath) {
// const_cast not supported on Mac
union {
const nfdu8char_t* constPath;
nfdu8char_t* nonConstPath;
} pathUnion;
pathUnion.constPath = filePath;
NFD_FreePathU8(pathUnion.nonConstPath);
}
void NFD_PathSet_Free(const nfdpathset_t* pathSet) {
const NSArray* urls = (const NSArray*)pathSet;
[urls release];
}
nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t* outEnumerator) {
const NSArray* urls = (const NSArray*)pathSet;
@autoreleasepool {
// autoreleasepool needed because NSEnumerator uses it
NSEnumerator* enumerator = [urls objectEnumerator];
[enumerator retain];
outEnumerator->ptr = (void*)enumerator;
}
return NFD_OKAY;
}
void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator) {
NSEnumerator* real_enum = (NSEnumerator*)enumerator->ptr;
[real_enum release];
}
nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath) {
NSEnumerator* real_enum = (NSEnumerator*)enumerator->ptr;
@autoreleasepool {
// autoreleasepool needed because NSURL uses it
const NSURL* url = [real_enum nextObject];
if (url) {
const char* utf8Path = [[url path] UTF8String];
return CopyUtf8String(utf8Path, outPath);
} else {
*outPath = NULL;
return NFD_OKAY;
}
}
}
nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath) {
return NFD_PathSet_EnumNextN(enumerator, outPath);
}

974
external/nfd/src/nfd_gtk.cpp vendored Normal file
View File

@@ -0,0 +1,974 @@
/*
Native File Dialog Extended
Repository: https://github.com/btzy/nativefiledialog-extended
License: Zlib
Authors: Bernard Teo, Michael Labbe
Note: We do not check for malloc failure on Linux - Linux overcommits memory!
*/
#include <assert.h>
#include <gtk/gtk.h>
#if defined(GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nfd.h"
/*
Define NFD_CASE_SENSITIVE_FILTER if you want file filters to be case-sensitive. The default
is case-insensitive. While Linux uses a case-sensitive filesystem and is designed for
case-sensitive file extensions, perhaps in the vast majority of cases users actually expect the file
filters to be case-insensitive.
*/
namespace {
template <typename T>
struct Free_Guard {
T* data;
Free_Guard(T* freeable) noexcept : data(freeable) {}
~Free_Guard() { NFDi_Free(data); }
};
template <typename T>
struct FreeCheck_Guard {
T* data;
FreeCheck_Guard(T* freeable = nullptr) noexcept : data(freeable) {}
~FreeCheck_Guard() {
if (data) NFDi_Free(data);
}
};
/* current error */
const char* g_errorstr = nullptr;
void NFDi_SetError(const char* msg) {
g_errorstr = msg;
}
template <typename T = void>
T* NFDi_Malloc(size_t bytes) {
void* ptr = malloc(bytes);
if (!ptr) NFDi_SetError("NFDi_Malloc failed.");
return static_cast<T*>(ptr);
}
template <typename T>
void NFDi_Free(T* ptr) {
assert(ptr);
free(static_cast<void*>(ptr));
}
template <typename T>
T* copy(const T* begin, const T* end, T* out) {
for (; begin != end; ++begin) {
*out++ = *begin;
}
return out;
}
#ifndef NFD_CASE_SENSITIVE_FILTER
nfdnchar_t* emit_case_insensitive_glob(const nfdnchar_t* begin,
const nfdnchar_t* end,
nfdnchar_t* out) {
// this code will only make regular Latin characters case-insensitive; other
// characters remain case sensitive
for (; begin != end; ++begin) {
if ((*begin >= 'A' && *begin <= 'Z') || (*begin >= 'a' && *begin <= 'z')) {
*out++ = '[';
*out++ = *begin;
// invert the case of the original character
*out++ = *begin ^ static_cast<nfdnchar_t>(0x20);
*out++ = ']';
} else {
*out++ = *begin;
}
}
return out;
}
#endif
// Does not own the filter and extension.
struct Pair_GtkFileFilter_FileExtension {
GtkFileFilter* filter;
const nfdnchar_t* extensionBegin;
const nfdnchar_t* extensionEnd;
};
struct ButtonClickedArgs {
Pair_GtkFileFilter_FileExtension* map;
GtkFileChooser* chooser;
};
void AddFiltersToDialog(GtkFileChooser* chooser,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
if (filterCount) {
assert(filterList);
// we have filters to add ... format and add them
for (nfdfiltersize_t index = 0; index != filterCount; ++index) {
GtkFileFilter* filter = gtk_file_filter_new();
// count number of file extensions
size_t sep = 1;
for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) {
if (*p_spec == ',') {
++sep;
}
}
// friendly name conversions: "png,jpg" -> "Image files
// (png, jpg)"
// calculate space needed (including the trailing '\0')
size_t nameSize =
sep + strlen(filterList[index].spec) + 3 + strlen(filterList[index].name);
// malloc the required memory
nfdnchar_t* nameBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) * nameSize);
nfdnchar_t* p_nameBuf = nameBuf;
for (const nfdnchar_t* p_filterName = filterList[index].name; *p_filterName;
++p_filterName) {
*p_nameBuf++ = *p_filterName;
}
*p_nameBuf++ = ' ';
*p_nameBuf++ = '(';
const nfdnchar_t* p_extensionStart = filterList[index].spec;
for (const nfdnchar_t* p_spec = filterList[index].spec; true; ++p_spec) {
if (*p_spec == ',' || !*p_spec) {
if (*p_spec == ',') {
*p_nameBuf++ = ',';
*p_nameBuf++ = ' ';
}
#ifdef NFD_CASE_SENSITIVE_FILTER
// +1 for the trailing '\0'
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) *
(p_spec - p_extensionStart + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#else
// Each character in the Latin alphabet is converted into 4 characters. E.g.
// 'a' is converted into "[Aa]". Other characters are preserved. Then we +1
// for the trailing '\0'.
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(
sizeof(nfdnchar_t) * ((p_spec - p_extensionStart) * 4 + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd =
emit_case_insensitive_glob(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#endif
if (*p_spec) {
// update the extension start point
p_extensionStart = p_spec + 1;
} else {
// reached the '\0' character
break;
}
} else {
*p_nameBuf++ = *p_spec;
}
}
*p_nameBuf++ = ')';
*p_nameBuf++ = '\0';
assert((size_t)(p_nameBuf - nameBuf) == sizeof(nfdnchar_t) * nameSize);
// add to the filter
gtk_file_filter_set_name(filter, nameBuf);
// free the memory
NFDi_Free(nameBuf);
// add filter to chooser
gtk_file_chooser_add_filter(chooser, filter);
}
}
/* always append a wildcard option to the end*/
GtkFileFilter* filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "All files");
gtk_file_filter_add_pattern(filter, "*");
gtk_file_chooser_add_filter(chooser, filter);
}
// returns null-terminated map (trailing .filter is null)
Pair_GtkFileFilter_FileExtension* AddFiltersToDialogWithMap(GtkFileChooser* chooser,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
Pair_GtkFileFilter_FileExtension* map = NFDi_Malloc<Pair_GtkFileFilter_FileExtension>(
sizeof(Pair_GtkFileFilter_FileExtension) * (filterCount + 1));
if (filterCount) {
assert(filterList);
// we have filters to add ... format and add them
for (nfdfiltersize_t index = 0; index != filterCount; ++index) {
GtkFileFilter* filter = gtk_file_filter_new();
// store filter in map
map[index].filter = filter;
map[index].extensionBegin = filterList[index].spec;
map[index].extensionEnd = nullptr;
// count number of file extensions
size_t sep = 1;
for (const nfdnchar_t* p_spec = filterList[index].spec; *p_spec; ++p_spec) {
if (*p_spec == ',') {
++sep;
}
}
// friendly name conversions: "png,jpg" -> "Image files
// (png, jpg)"
// calculate space needed (including the trailing '\0')
size_t nameSize =
sep + strlen(filterList[index].spec) + 3 + strlen(filterList[index].name);
// malloc the required memory
nfdnchar_t* nameBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) * nameSize);
nfdnchar_t* p_nameBuf = nameBuf;
for (const nfdnchar_t* p_filterName = filterList[index].name; *p_filterName;
++p_filterName) {
*p_nameBuf++ = *p_filterName;
}
*p_nameBuf++ = ' ';
*p_nameBuf++ = '(';
const nfdnchar_t* p_extensionStart = filterList[index].spec;
for (const nfdnchar_t* p_spec = filterList[index].spec; true; ++p_spec) {
if (*p_spec == ',' || !*p_spec) {
if (*p_spec == ',') {
*p_nameBuf++ = ',';
*p_nameBuf++ = ' ';
}
#ifdef NFD_CASE_SENSITIVE_FILTER
// +1 for the trailing '\0'
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(sizeof(nfdnchar_t) *
(p_spec - p_extensionStart + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd = copy(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#else
// Each character in the Latin alphabet is converted into 4 characters. E.g.
// 'a' is converted into "[Aa]". Other characters are preserved. Then we +1
// for the trailing '\0'.
nfdnchar_t* extnBuf = NFDi_Malloc<nfdnchar_t>(
sizeof(nfdnchar_t) * ((p_spec - p_extensionStart) * 4 + 3));
nfdnchar_t* p_extnBufEnd = extnBuf;
*p_extnBufEnd++ = '*';
*p_extnBufEnd++ = '.';
p_extnBufEnd =
emit_case_insensitive_glob(p_extensionStart, p_spec, p_extnBufEnd);
*p_extnBufEnd++ = '\0';
gtk_file_filter_add_pattern(filter, extnBuf);
NFDi_Free(extnBuf);
#endif
// store current pointer in map (if it's
// the first one)
if (map[index].extensionEnd == nullptr) {
map[index].extensionEnd = p_spec;
}
if (*p_spec) {
// update the extension start point
p_extensionStart = p_spec + 1;
} else {
// reached the '\0' character
break;
}
} else {
*p_nameBuf++ = *p_spec;
}
}
*p_nameBuf++ = ')';
*p_nameBuf++ = '\0';
assert((size_t)(p_nameBuf - nameBuf) == sizeof(nfdnchar_t) * nameSize);
// add to the filter
gtk_file_filter_set_name(filter, nameBuf);
// free the memory
NFDi_Free(nameBuf);
// add filter to chooser
gtk_file_chooser_add_filter(chooser, filter);
}
}
// set trailing map index to null
map[filterCount].filter = nullptr;
/* always append a wildcard option to the end*/
GtkFileFilter* filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "All files");
gtk_file_filter_add_pattern(filter, "*");
gtk_file_chooser_add_filter(chooser, filter);
return map;
}
void SetDefaultPath(GtkFileChooser* chooser, const char* defaultPath) {
if (!defaultPath || !*defaultPath) 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(chooser, defaultPath);
}
void SetDefaultName(GtkFileChooser* chooser, const char* defaultName) {
if (!defaultName || !*defaultName) return;
gtk_file_chooser_set_current_name(chooser, defaultName);
}
void WaitForCleanup() {
while (gtk_events_pending()) gtk_main_iteration();
}
struct Widget_Guard {
GtkWidget* data;
Widget_Guard(GtkWidget* widget) : data(widget) {}
~Widget_Guard() {
WaitForCleanup();
gtk_widget_destroy(data);
WaitForCleanup();
}
};
void FileActivatedSignalHandler(GtkButton* saveButton, void* userdata) {
(void)saveButton; // silence the unused arg warning
ButtonClickedArgs* args = static_cast<ButtonClickedArgs*>(userdata);
GtkFileChooser* chooser = args->chooser;
char* currentFileName = gtk_file_chooser_get_current_name(chooser);
if (*currentFileName) { // string is not empty
// find a '.' in the file name
const char* p_period = currentFileName;
for (; *p_period; ++p_period) {
if (*p_period == '.') {
break;
}
}
if (!*p_period) { // there is no '.', so append the default extension
Pair_GtkFileFilter_FileExtension* filterMap =
static_cast<Pair_GtkFileFilter_FileExtension*>(args->map);
GtkFileFilter* currentFilter = gtk_file_chooser_get_filter(chooser);
if (currentFilter) {
for (; filterMap->filter; ++filterMap) {
if (filterMap->filter == currentFilter) break;
}
}
if (filterMap->filter) {
// memory for appended string (including '.' and
// trailing '\0')
char* appendedFileName = NFDi_Malloc<char>(
sizeof(char) * ((p_period - currentFileName) +
(filterMap->extensionEnd - filterMap->extensionBegin) + 2));
char* p_fileName = copy(currentFileName, p_period, appendedFileName);
*p_fileName++ = '.';
p_fileName = copy(filterMap->extensionBegin, filterMap->extensionEnd, p_fileName);
*p_fileName++ = '\0';
assert(p_fileName - appendedFileName ==
(p_period - currentFileName) +
(filterMap->extensionEnd - filterMap->extensionBegin) + 2);
// set the appended file name
gtk_file_chooser_set_current_name(chooser, appendedFileName);
// free the memory
NFDi_Free(appendedFileName);
}
}
}
// free the memory
g_free(currentFileName);
}
// wrapper for gtk_dialog_run() that brings the dialog to the front
// see issues at:
// https://github.com/btzy/nativefiledialog-extended/issues/31
// https://github.com/mlabbe/nativefiledialog/pull/92
// https://github.com/guillaumechereau/noc/pull/11
gint RunDialogWithFocus(GtkDialog* dialog) {
#if defined(GDK_WINDOWING_X11)
gtk_widget_show_all(GTK_WIDGET(dialog)); // show the dialog so that it gets a display
if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(dialog)))) {
GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(dialog));
gdk_window_set_events(
window,
static_cast<GdkEventMask>(gdk_window_get_events(window) | GDK_PROPERTY_CHANGE_MASK));
gtk_window_present_with_time(GTK_WINDOW(dialog), gdk_x11_get_server_time(window));
}
#endif
return gtk_dialog_run(dialog);
}
// Gets the GdkWindow from the given window handle. This function might fail even if parentWindow
// is set correctly, since it calls some failable GDK functions. If it fails, it will return
// nullptr. The caller is responsible for freeing ths returned GdkWindow, if not nullptr.
GdkWindow* GetAllocNativeWindowHandle(const nfdwindowhandle_t& parentWindow) {
switch (parentWindow.type) {
#if defined(GDK_WINDOWING_X11)
case NFD_WINDOW_HANDLE_TYPE_X11: {
const Window x11_handle = reinterpret_cast<Window>(parentWindow.handle);
// AFAIK, _any_ X11 display will do, because Windows are not associated to a specific
// Display. Supposedly, a Display is just a connection to the X server.
// This will contain the X11 display we want to use.
GdkDisplay* x11_display = nullptr;
GdkDisplayManager* display_manager = gdk_display_manager_get();
// If we can find an existing X11 display, use it.
GSList* gdk_display_list = gdk_display_manager_list_displays(display_manager);
while (gdk_display_list) {
GSList* node = gdk_display_list;
GdkDisplay* display = GDK_DISPLAY(node->data);
if (GDK_IS_X11_DISPLAY(display)) {
g_slist_free(node);
x11_display = display;
break;
} else {
gdk_display_list = node->next;
g_slist_free_1(node);
}
}
// Otherwise, we have to create our own X11 display.
if (!x11_display) {
// This is not very nice, because we are always resetting the allowed backends
// setting to NULL (which means all backends are allowed), even though we can't be
// sure that the user didn't call gdk_set_allowed_backends() earlier to force a
// specific backend. But well if the user doesn't have an X11 display already open
// and yet is telling us with have an X11 window as parent, they probably don't use
// GTK in their application at all so they probably won't notice this.
//
// There is no way, AFAIK, to get the allowed backends first so we can restore it
// later, and gdk_x11_display_open() is GTK4-only (the GTK3 version is a private
// implementation detail).
//
// Also, we don't close the display we specially opened, since GTK will need it to
// show the dialog. Though it probably doesn't matter very much if we want to free
// up resources and clean it up.
gdk_set_allowed_backends("x11");
x11_display = gdk_display_manager_open_display(display_manager, NULL);
gdk_set_allowed_backends(NULL);
}
if (!x11_display) return nullptr;
GdkWindow* gdk_window = gdk_x11_window_foreign_new_for_display(x11_display, x11_handle);
return gdk_window;
}
#endif
default:
return nullptr;
}
}
void RealizedSignalHandler(GtkWidget* window, void* userdata) {
GdkWindow* const parentWindow = static_cast<GdkWindow*>(userdata);
gdk_window_set_transient_for(gtk_widget_get_window(window), parentWindow);
}
struct NativeWindowParenter {
NativeWindowParenter(GtkWidget* w, const nfdwindowhandle_t& parentWindow) noexcept : widget(w) {
parent = GetAllocNativeWindowHandle(parentWindow);
if (parent) {
// set the handler to the realize signal to set the transient GDK parent
handlerID = g_signal_connect(G_OBJECT(widget),
"realize",
G_CALLBACK(RealizedSignalHandler),
static_cast<void*>(parent));
// make the dialog window use the same GtkScreen as the parent (so that parenting works)
gtk_window_set_screen(GTK_WINDOW(widget), gdk_window_get_screen(parent));
}
}
~NativeWindowParenter() {
if (parent) {
// unset the handler and delete the parent GdkWindow
g_signal_handler_disconnect(G_OBJECT(widget), handlerID);
g_object_unref(parent);
}
}
GtkWidget* const widget;
GdkWindow* parent;
gulong handlerID;
};
} // namespace
const char* NFD_GetError(void) {
return g_errorstr;
}
void NFD_ClearError(void) {
NFDi_SetError(nullptr);
}
/* public */
nfdresult_t NFD_Init(void) {
// Init GTK
if (!gtk_init_check(NULL, NULL)) {
NFDi_SetError("Failed to initialize GTK+ with gtk_init_check.");
return NFD_ERROR;
}
return NFD_OKAY;
}
void NFD_Quit(void) {
// do nothing, GTK cannot be de-initialized
}
void NFD_FreePathN(nfdnchar_t* filePath) {
assert(filePath);
g_free(filePath);
}
void NFD_FreePathU8(nfdu8char_t* filePath) __attribute__((alias("NFD_FreePathN")));
nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
nfdopendialognargs_t args{};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
return NFD_OpenDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_OpenDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdopendialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
GtkWidget* widget = gtk_file_chooser_dialog_new("Open File",
nullptr,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
nullptr);
// guard to destroy the widget when returning from this function
Widget_Guard widgetGuard(widget);
/* Build the filter list */
AddFiltersToDialog(GTK_FILE_CHOOSER(widget), args->filterList, args->filterCount);
/* Set the default path */
SetDefaultPath(GTK_FILE_CHOOSER(widget), args->defaultPath);
gint result;
{
/* Parent the window properly */
NativeWindowParenter nativeWindowParenter(widget, args->parentWindow);
/* invoke the dialog (blocks until dialog is closed) */
result = RunDialogWithFocus(GTK_DIALOG(widget));
}
if (result == GTK_RESPONSE_ACCEPT) {
// write out the file name
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
return NFD_OKAY;
} else {
return NFD_CANCEL;
}
}
nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath)
__attribute__((alias("NFD_OpenDialogN")));
nfdresult_t NFD_OpenDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdopendialogu8args_t* args)
__attribute__((alias("NFD_OpenDialogN_With_Impl")));
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath) {
nfdopendialognargs_t args{};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
return NFD_OpenDialogMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, &args);
}
nfdresult_t NFD_OpenDialogMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
GtkWidget* widget = gtk_file_chooser_dialog_new("Open Files",
nullptr,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
nullptr);
// guard to destroy the widget when returning from this function
Widget_Guard widgetGuard(widget);
// set select multiple
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(widget), TRUE);
/* Build the filter list */
AddFiltersToDialog(GTK_FILE_CHOOSER(widget), args->filterList, args->filterCount);
/* Set the default path */
SetDefaultPath(GTK_FILE_CHOOSER(widget), args->defaultPath);
gint result;
{
/* Parent the window properly */
NativeWindowParenter nativeWindowParenter(widget, args->parentWindow);
/* invoke the dialog (blocks until dialog is closed) */
result = RunDialogWithFocus(GTK_DIALOG(widget));
}
if (result == GTK_RESPONSE_ACCEPT) {
// write out the file name
GSList* fileList = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
*outPaths = static_cast<void*>(fileList);
return NFD_OKAY;
} else {
return NFD_CANCEL;
}
}
nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath)
__attribute__((alias("NFD_OpenDialogMultipleN")));
nfdresult_t NFD_OpenDialogMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdopendialogu8args_t* args)
__attribute__((alias("NFD_OpenDialogMultipleN_With_Impl")));
nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdnchar_t* defaultPath,
const nfdnchar_t* defaultName) {
nfdsavedialognargs_t args{};
args.filterList = filterList;
args.filterCount = filterCount;
args.defaultPath = defaultPath;
args.defaultName = defaultName;
return NFD_SaveDialogN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_SaveDialogN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdsavedialognargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
GtkWidget* widget = gtk_file_chooser_dialog_new("Save File",
nullptr,
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel",
GTK_RESPONSE_CANCEL,
nullptr);
// guard to destroy the widget when returning from this function
Widget_Guard widgetGuard(widget);
GtkWidget* saveButton = gtk_dialog_add_button(GTK_DIALOG(widget), "_Save", GTK_RESPONSE_ACCEPT);
// Prompt on overwrite
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(widget), TRUE);
/* Build the filter list */
ButtonClickedArgs buttonClickedArgs;
buttonClickedArgs.chooser = GTK_FILE_CHOOSER(widget);
buttonClickedArgs.map =
AddFiltersToDialogWithMap(GTK_FILE_CHOOSER(widget), args->filterList, args->filterCount);
/* Set the default path */
SetDefaultPath(GTK_FILE_CHOOSER(widget), args->defaultPath);
/* Set the default file name */
SetDefaultName(GTK_FILE_CHOOSER(widget), args->defaultName);
/* set the handler to add file extension */
gulong handlerID = g_signal_connect(G_OBJECT(saveButton),
"pressed",
G_CALLBACK(FileActivatedSignalHandler),
static_cast<void*>(&buttonClickedArgs));
gint result;
{
/* Parent the window properly */
NativeWindowParenter nativeWindowParenter(widget, args->parentWindow);
/* invoke the dialog (blocks until dialog is closed) */
result = RunDialogWithFocus(GTK_DIALOG(widget));
}
/* unset the handler */
g_signal_handler_disconnect(G_OBJECT(saveButton), handlerID);
/* free the filter map */
NFDi_Free(buttonClickedArgs.map);
if (result == GTK_RESPONSE_ACCEPT) {
// write out the file name
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
return NFD_OKAY;
} else {
return NFD_CANCEL;
}
}
nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath,
const nfdu8filteritem_t* filterList,
nfdfiltersize_t filterCount,
const nfdu8char_t* defaultPath,
const nfdu8char_t* defaultName)
__attribute__((alias("NFD_SaveDialogN")));
nfdresult_t NFD_SaveDialogU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdsavedialogu8args_t* args)
__attribute__((alias("NFD_SaveDialogN_With_Impl")));
nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) {
nfdpickfoldernargs_t args{};
args.defaultPath = defaultPath;
return NFD_PickFolderN_With_Impl(NFD_INTERFACE_VERSION, outPath, &args);
}
nfdresult_t NFD_PickFolderN_With_Impl(nfdversion_t version,
nfdnchar_t** outPath,
const nfdpickfoldernargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
GtkWidget* widget = gtk_file_chooser_dialog_new("Select Folder",
nullptr,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Select",
GTK_RESPONSE_ACCEPT,
nullptr);
// guard to destroy the widget when returning from this function
Widget_Guard widgetGuard(widget);
/* Set the default path */
SetDefaultPath(GTK_FILE_CHOOSER(widget), args->defaultPath);
gint result;
{
/* Parent the window properly */
NativeWindowParenter nativeWindowParenter(widget, args->parentWindow);
/* invoke the dialog (blocks until dialog is closed) */
result = RunDialogWithFocus(GTK_DIALOG(widget));
}
if (result == GTK_RESPONSE_ACCEPT) {
// write out the file name
*outPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
return NFD_OKAY;
} else {
return NFD_CANCEL;
}
}
nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath)
__attribute__((alias("NFD_PickFolderN")));
nfdresult_t NFD_PickFolderU8_With_Impl(nfdversion_t version,
nfdu8char_t** outPath,
const nfdpickfolderu8args_t* args)
__attribute__((alias("NFD_PickFolderN_With_Impl")));
nfdresult_t NFD_PickFolderMultipleN(const nfdpathset_t** outPaths, const nfdnchar_t* defaultPath) {
nfdpickfoldernargs_t args{};
args.defaultPath = defaultPath;
return NFD_PickFolderMultipleN_With_Impl(NFD_INTERFACE_VERSION, outPaths, &args);
}
nfdresult_t NFD_PickFolderMultipleN_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfoldernargs_t* args) {
// We haven't needed to bump the interface version yet.
(void)version;
GtkWidget* widget = gtk_file_chooser_dialog_new("Select Folders",
nullptr,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Select",
GTK_RESPONSE_ACCEPT,
nullptr);
// guard to destroy the widget when returning from this function
Widget_Guard widgetGuard(widget);
/* Set the default path */
SetDefaultPath(GTK_FILE_CHOOSER(widget), args->defaultPath);
gint result;
{
/* Parent the window properly */
NativeWindowParenter nativeWindowParenter(widget, args->parentWindow);
/* invoke the dialog (blocks until dialog is closed) */
result = RunDialogWithFocus(GTK_DIALOG(widget));
}
if (result == GTK_RESPONSE_ACCEPT) {
// write out the file name
GSList* fileList = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
*outPaths = static_cast<void*>(fileList);
return NFD_OKAY;
} else {
return NFD_CANCEL;
}
}
nfdresult_t NFD_PickFolderMultipleU8(const nfdpathset_t** outPaths, const nfdu8char_t* defaultPath)
__attribute__((alias("NFD_PickFolderMultipleN")));
nfdresult_t NFD_PickFolderMultipleU8_With_Impl(nfdversion_t version,
const nfdpathset_t** outPaths,
const nfdpickfolderu8args_t* args)
__attribute__((alias("NFD_PickFolderMultipleN_With_Impl")));
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) {
assert(pathSet);
// const_cast because methods on GSList aren't const, but it should act
// like const to the caller
GSList* fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
*count = g_slist_length(fileList);
return NFD_OKAY;
}
nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdnchar_t** outPath) {
assert(pathSet);
// const_cast because methods on GSList aren't const, but it should act
// like const to the caller
GSList* fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
// Note: this takes linear time... but should be good enough
*outPath = static_cast<nfdnchar_t*>(g_slist_nth_data(fileList, index));
return NFD_OKAY;
}
nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet,
nfdpathsetsize_t index,
nfdu8char_t** outPath)
__attribute__((alias("NFD_PathSet_GetPathN")));
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) {
assert(filePath);
(void)filePath; // prevent warning in release build
// no-op, because NFD_PathSet_Free does the freeing for us
}
void NFD_PathSet_FreePathU8(const nfdu8char_t* filePath)
__attribute__((alias("NFD_PathSet_FreePathN")));
void NFD_PathSet_Free(const nfdpathset_t* pathSet) {
assert(pathSet);
// const_cast because methods on GSList aren't const, but it should act
// like const to the caller
GSList* fileList = const_cast<GSList*>(static_cast<const GSList*>(pathSet));
// free all the nodes
for (GSList* node = fileList; node; node = node->next) {
assert(node->data);
g_free(node->data);
}
// free the path set memory
g_slist_free(fileList);
}
nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t* outEnumerator) {
// The pathset (GSList) is already a linked list, so the enumeration is itself
outEnumerator->ptr = const_cast<void*>(pathSet);
return NFD_OKAY;
}
void NFD_PathSet_FreeEnum(nfdpathsetenum_t*) {
// Do nothing, because the enumeration is the pathset itself
}
nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath) {
const GSList* fileList = static_cast<const GSList*>(enumerator->ptr);
if (fileList) {
*outPath = static_cast<nfdnchar_t*>(fileList->data);
enumerator->ptr = static_cast<void*>(fileList->next);
} else {
*outPath = nullptr;
}
return NFD_OKAY;
}
nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath)
__attribute__((alias("NFD_PathSet_EnumNextN")));

1843
external/nfd/src/nfd_portal.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

1185
external/nfd/src/nfd_win.cpp vendored Normal file

File diff suppressed because it is too large Load Diff