Merge commit '6e9eb898f5c6e241591d4953ae0b36668cd59f94' as 'external/nfd'
This commit is contained in:
149
external/nfd/src/CMakeLists.txt
vendored
Normal file
149
external/nfd/src/CMakeLists.txt
vendored
Normal 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
564
external/nfd/src/include/nfd.h
vendored
Normal 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
375
external/nfd/src/include/nfd.hpp
vendored
Normal 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
85
external/nfd/src/include/nfd_glfw3.h
vendored
Normal 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
76
external/nfd/src/include/nfd_sdl2.h
vendored
Normal 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
615
external/nfd/src/nfd_cocoa.m
vendored
Normal 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
974
external/nfd/src/nfd_gtk.cpp
vendored
Normal 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
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
1185
external/nfd/src/nfd_win.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user