Merge remote-tracking branch 'upstream/master' into es5506_alt

This commit is contained in:
cam900 2022-06-26 00:47:34 +09:00
commit 3119ed8cc5
326 changed files with 8954 additions and 1452 deletions

View File

@ -243,7 +243,7 @@ jobs:
cp -v ../LICENSE LICENSE.txt
cp -v ../README.md README.txt
cp -vr ../{papers,demos} ../${binPath}/furnace.exe ./
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
popd
@ -278,6 +278,7 @@ jobs:
popd
- name: Upload artifact
if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }}
uses: actions/upload-artifact@v3
with:
name: ${{ steps.package-identify.outputs.id }}

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.vscode/
build/
clangbuild/
nosdl/
release/
t/

View File

@ -252,7 +252,7 @@ src/audio/midi.cpp
)
if (USE_SDL2)
list(APPEND AUDIO_SOURCES src/audio/sdl.cpp)
list(APPEND AUDIO_SOURCES src/audio/sdlAudio.cpp)
endif()
if (WITH_JACK)
@ -502,6 +502,8 @@ src/gui/doAction.cpp
src/gui/editing.cpp
src/gui/editControls.cpp
src/gui/effectList.cpp
src/gui/findReplace.cpp
src/gui/gradient.cpp
src/gui/insEdit.cpp
src/gui/log.cpp
src/gui/mixer.cpp
@ -517,6 +519,7 @@ src/gui/sampleEdit.cpp
src/gui/settings.cpp
src/gui/songInfo.cpp
src/gui/songNotes.cpp
src/gui/spoiler.cpp
src/gui/stats.cpp
src/gui/subSongs.cpp
src/gui/sysConf.cpp
@ -527,12 +530,39 @@ src/gui/volMeter.cpp
src/gui/gui.cpp
)
if (WIN32 OR APPLE)
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_common.cpp)
endif()
if (WIN32)
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_win.cpp)
endif()
if (APPLE)
list(APPEND GUI_SOURCES src/gui/macstuff.m)
list(APPEND GUI_SOURCES extern/nfd-modified/src/nfd_cocoa.mm)
endif()
if (NOT WIN32 AND NOT APPLE)
list(APPEND GUI_SOURCES src/gui/icon.c)
include(CheckIncludeFile)
CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND)
CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND)
CHECK_INCLUDE_FILE(linux/kd.h LINUX_KD_FOUND)
if (SYS_IO_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_SYS_IO)
message(STATUS "PC speaker output: outb()")
endif()
if (LINUX_INPUT_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_LINUX_INPUT)
message(STATUS "PC speaker output: evdev")
endif()
if (LINUX_KD_FOUND)
list(APPEND DEPENDENCIES_DEFINES HAVE_LINUX_KD)
message(STATUS "PC speaker output: KIOCSOUND")
endif()
endif()
set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} src/main.cpp)
@ -556,6 +586,11 @@ if (BUILD_GUI)
extern/IconFontCppHeaders
extern/igfd
)
if (WIN32 OR APPLE)
list(APPEND DEPENDENCIES_INCLUDE_DIRS
extern/nfd-modified/src/include
)
endif()
list(APPEND DEPENDENCIES_DEFINES HAVE_GUI)
message(STATUS "Building GUI")
else()
@ -577,7 +612,11 @@ endif()
if (NOT MSVC)
set(WARNING_FLAGS -Wall -Wextra -Wno-unused-parameter)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND WARNING_FLAGS -Wno-cast-function-type)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0.0)
# nothing
else()
list(APPEND WARNING_FLAGS -Wno-cast-function-type)
endif()
endif()
if (WARNINGS_ARE_ERRORS)
list(APPEND WARNING_FLAGS -Werror)
@ -640,6 +679,7 @@ if (NOT ANDROID OR TERMUX)
install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR})
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace)
install(DIRECTORY demos DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
foreach(num 16 32 64 128 256 512)
set(res ${num}x${num})
install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps)

10
TODO.md
View File

@ -6,14 +6,14 @@
# to-do for 0.6pre1
- rewrite the system name detection function anyway
- add another FM editor layout
- if macros have release, note off should release them
- add ability to move selection by dragging
- find and replace
- implement Defle slide bug when using E1xy/E2xy and repeating origin note (requires format change)
# to-do for 0.6pre2 (as this requires new data structures)
- rewrite the system name detection function anyway
- this involves the addition of a new "system" field in the song (which solves the problem)
- songs made in older versions will go through old system name detection for compatibility
- Game Boy envelope macro/sequence
- volume commands should work on Game Boy
- ability to customize `OFF`, `===` and `REL`
- stereo separation control for AY

Binary file not shown.

BIN
demos/E1M4OPL2.fur Normal file

Binary file not shown.

BIN
demos/FDS TEST.fur Normal file

Binary file not shown.

BIN
demos/Fake Gameboy.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/Fusion.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/atmosphere.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/c64 ring test.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/game boy thing.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/the_erfngjt.fur Normal file

Binary file not shown.

BIN
demos/thick bass test.fur Normal file

Binary file not shown.

BIN
demos/wolf3d.fur Normal file

Binary file not shown.

View File

@ -130,7 +130,7 @@ static void YMPSG_ClockInternal1(ympsg_t *chip)
else if (noise_of && !chip->noise_of)
{
noise_bit1 = (chip->noise >> chip->noise_tap2) & 1;
noise_bit2 = (chip->noise >> 12) & 1;
noise_bit2 = (chip->noise >> chip->noise_tap1) & 1;
noise_bit1 ^= noise_bit2;
noise_next = ((noise_bit1 && ((chip->noise_data >> 2) & 1)) || ((chip->noise & chip->noise_size) == 0));
chip->noise <<= 1;
@ -257,13 +257,14 @@ uint16_t YMPSG_Read(ympsg_t *chip)
return data;
}
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn)
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t noise_tap2, uint32_t noise_size)
{
uint32_t i;
memset(chip, 0, sizeof(ympsg_t));
YMPSG_SetIC(chip, 1);
chip->noise_tap2 = real_sn ? 13 : 15;
chip->noise_size = real_sn ? 16383 : 32767;
chip->noise_tap1 = noise_tap1;
chip->noise_tap2 = noise_tap2;
chip->noise_size = noise_size;
for (i = 0; i < 17; i++)
{
chip->vol_table[i]=(real_sn?tipsg_vol[i]:ympsg_vol[i]) * 8192.0f;

View File

@ -46,8 +46,9 @@ typedef struct {
uint8_t sign_l;
uint8_t noise_sign_l;
uint16_t noise;
uint8_t noise_tap1;
uint8_t noise_tap2;
uint16_t noise_size;
uint32_t noise_size;
uint8_t test;
uint8_t volume_out[4];
@ -68,7 +69,7 @@ typedef struct {
void YMPSG_Write(ympsg_t *chip, uint8_t data);
uint16_t YMPSG_Read(ympsg_t *chip);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t noise_tap2, uint32_t noise_size);
void YMPSG_SetIC(ympsg_t *chip, uint32_t ic);
void YMPSG_Clock(ympsg_t *chip);
int YMPSG_GetOutput(ympsg_t *chip);

View File

@ -1287,8 +1287,6 @@ namespace IGFD
std::sort(prFileList.begin(), prFileList.end(),
[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
{
if (a==NULL || b==NULL)
return false;
if (!a.use_count() || !b.use_count())
return false;
@ -1760,7 +1758,7 @@ namespace IGFD
struct stat statInfos = {};
char timebuf[100];
int result = stat(fpn.c_str(), &statInfos);
if (!result)
if (result!=-1)
{
if (vInfos->fileType != 'd')
{
@ -1781,7 +1779,11 @@ namespace IGFD
{
vInfos->fileModifDate = std::string(timebuf, len);
}
}
} else {
vInfos->fileSize=0;
vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize);
vInfos->fileModifDate="???";
}
}
}

View File

@ -4545,27 +4545,52 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
// without any storage on user's side.
IM_ASSERT(apply_new_text_length >= 0);
if (is_resizable)
{
ImGuiInputTextCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
callback_data.Flags = flags;
callback_data.Buf = buf;
callback_data.BufTextLen = apply_new_text_length;
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
callback_data.UserData = callback_user_data;
callback(&callback_data);
buf = callback_data.Buf;
buf_size = callback_data.BufSize;
apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
IM_ASSERT(apply_new_text_length <= buf_size);
}
//IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length);
// If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
value_changed = true;
// don't assert because maybe the user passes an invalid UTF-8 string...
if (apply_new_text_length >= 0) {
if (is_resizable)
{
ImGuiInputTextCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
callback_data.Flags = flags;
callback_data.Buf = buf;
callback_data.BufTextLen = apply_new_text_length;
callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
callback_data.UserData = callback_user_data;
callback(&callback_data);
buf = callback_data.Buf;
buf_size = callback_data.BufSize;
apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
IM_ASSERT(apply_new_text_length <= buf_size);
}
//IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length);
// If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
value_changed = true;
} else {
printf("invalid buffer!\n");
if (is_resizable)
{
ImGuiInputTextCallbackData callback_data;
callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
callback_data.Flags = flags;
callback_data.Buf = buf;
callback_data.BufTextLen = 0;
callback_data.BufSize = ImMax(buf_size, 1);
callback_data.UserData = callback_user_data;
callback(&callback_data);
buf = callback_data.Buf;
buf_size = callback_data.BufSize;
apply_new_text_length = 0;
IM_ASSERT(apply_new_text_length <= buf_size);
}
//IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length);
// clear the buffer
ImStrncpy(buf, "", 1);
value_changed = true;
}
}
// Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)

181
extern/nfd-modified/.gitignore vendored Normal file
View File

@ -0,0 +1,181 @@
.sconsign.dblite
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific folders
*.sln.ide/
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# If using the old MSBuild-Integrated Package Restore, uncomment this:
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/

16
extern/nfd-modified/LICENSE vendored Normal file
View File

@ -0,0 +1,16 @@
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

7
extern/nfd-modified/MODIFIED.md vendored Normal file
View File

@ -0,0 +1,7 @@
# MODIFIED
this is a modified, altered, edited and revised version of the Native File Dialog library used to display native-feeling, original and operating-system specific file archive dialog picker selectors, which is done by the Native File Dialog library.
it should not and shall NOT be mistaken for the original, authentic or actual version and revision of the library.
this is a version tailored for Furnace.

182
extern/nfd-modified/README.md vendored Normal file
View File

@ -0,0 +1,182 @@
# Native File Dialog Modified Version!!! #
A tiny, neat C library that portably invokes native file open, folder select and save dialogs. Write dialog code once and have it pop up native dialogs on all supported platforms. Avoid linking large dependencies like wxWidgets and qt.
This is a modified version of Native File Dialog, tailored for Furnace.
Features:
- Lean C API, static library -- no ObjC, no C++, no STL.
- Zlib licensed.
- Consistent UTF-8 support on all platforms.
- Simple universal file filter syntax.
- Paid support available.
- Multiple file selection support.
- 64-bit and 32-bit friendly.
- GCC, Clang, Xcode, Mingw and Visual Studio supported.
- No third party dependencies for building or linking.
- Support for Vista's modern `IFileDialog` on Windows.
- Support for non-deprecated Cocoa APIs on OS X.
- GTK3 dialog on Linux.
- Optional Zenity support on Linux to avoid linking GTK.
- Tested, works alongside [http://www.libsdl.org](SDL2) on all platforms, for the game developers out there.
# Example Usage #
```C
#include <nfd.h>
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
nfdchar_t *outPath = NULL;
nfdresult_t result = NFD_OpenDialog( NULL, NULL, &outPath );
if ( result == NFD_OKAY ) {
puts("Success!");
puts(outPath);
free(outPath);
}
else if ( result == NFD_CANCEL ) {
puts("User pressed cancel.");
}
else {
printf("Error: %s\n", NFD_GetError() );
}
return 0;
}
```
See self-documenting API [NFD.h](src/include/nfd.h) for more options.
# Screenshots #
![Windows rendering a dialog](screens/open_win.png?raw=true)
![GTK3 on Linux rendering a dialog](screens/open_gtk3.png?raw=true)
![Cocoa on MacOS rendering a dialog](screens/open_cocoa.png?raw=true)
## Changelog ##
- **Major** version increments denote API or ABI departure.
- **Minor** version increments denote build or trivial departures.
- **Micro** version increments just recompile and drop-in.
release | what's new | date
--------|-----------------------------|---------
1.0.0 | initial | oct 2014
1.1.0 | premake5; scons deprecated | aug 2016
1.1.1 | mingw support, build fixes | aug 2016
1.1.2 | test_pickfolder() added | aug 2016
1.1.3 | zenity linux backend added | nov 2017
<i></i> | fix char type in decls | nov 2017
1.1.4 | fix win32 memleaks | dec 2018
<i></i> | improve win32 errorhandling | dec 2018
<i></i> | macos fix focus bug | dec 2018
1.1.5 | win32 fix com reinitialize | aug 2019
1.1.6 | fix osx filter bug | aug 2019
<i></i> | remove deprecated scons | aug 2019
<i></i> | fix mingw compilation | aug 2019
<i></i> | -Wextra warning cleanup | aug 2019
## Building ##
NFD uses [Premake5](https://premake.github.io/download.html) generated Makefiles and IDE project files. The generated project files are checked in under `build/` so you don't have to download and use Premake in most cases.
If you need to run Premake5 directly, further [build documentation](docs/build.md) is available.
Previously, NFD used SCons to build. As of 1.1.6, SCons support has been removed entirely.
`nfd.a` will be built for release builds, and `nfd_d.a` will be built for debug builds.
### Makefiles ###
The makefile offers up to four options, with `release_x64` as the default.
make config=release_x86
make config=release_x64
make config=debug_x86
make config=debug_x64
### Compiling Your Programs ###
1. Add `src/include` to your include search path.
2. Add `nfd.lib` or `nfd_d.lib` to the list of list of static libraries to link against (for release or debug, respectively).
3. Add `build/<debug|release>/<arch>` to the library search path.
#### Linux GTK ####
`apt-get libgtk-3-dev` installs the gtk dependency for library compilation.
On Linux, you have the option of compiling and linking against GTK. If you use it, the recommended way to compile is to include the arguments of `pkg-config --cflags --libs gtk+-3.0`.
#### Linux Zenity ####
Alternatively, you can use the Zenity backend by running the Makefile in `build/gmake_linux_zenity`. Zenity runs the dialog in its own address space, but requires the user to have Zenity correctly installed and configured on their system.
#### MacOS ####
On Mac OS, add `AppKit` to the list of frameworks.
#### Windows ####
On Windows, ensure you are linking against `comctl32.lib`.
## Usage ##
See `NFD.h` for API calls. See `tests/*.c` for example code.
After compiling, `build/bin` contains compiled test programs. The appropriate subdirectory under `build/lib` contains the built library.
## File Filter Syntax ##
There is a form of file filtering in every file dialog API, but no consistent means of supporting it. NFD provides support for filtering files by groups of extensions, providing its own descriptions (where applicable) for the extensions.
A wildcard filter is always added to every dialog.
### Separators ###
- `;` Begin a new filter.
- `,` Add a separate type to the filter.
#### Examples ####
`txt` The default filter is for text files. There is a wildcard option in a dropdown.
`png,jpg;psd` The default filter is for png and jpg files. A second filter is available for psd files. There is a wildcard option in a dropdown.
`NULL` Wildcard only.
## Iterating Over PathSets ##
See [test_opendialogmultiple.c](test/test_opendialogmultiple.c).
# Known Limitations #
I accept quality code patches, or will resolve these and other matters through support. See [contributing](docs/contributing.md) for details.
- No support for Windows XP's legacy dialogs such as `GetOpenFileName`.
- No support for file filter names -- ex: "Image Files" (*.png, *.jpg). Nameless filters are supported, however.
- GTK Zenity implementation's process exec error handling does not gracefully handle numerous error cases, choosing to abort rather than cleanup and return.
- GTK 3 spams one warning per dialog created.
# Copyright and Credit #
Copyright &copy; 2014-2019 [Frogtoss Games](http://www.frogtoss.com), Inc.
File [LICENSE](LICENSE) covers all files in this repo.
Native File Dialog by Michael Labbe
<mike@frogtoss.com>
Tomasz Konojacki for [microutf8](http://puszcza.gnu.org.ua/software/microutf8/)
[Denis Kolodin](https://github.com/DenisKolodin) for mingw support.
[Tom Mason](https://github.com/wheybags) for Zenity support.
## Support ##
Directed support for this work is available from the original author under a paid agreement.
[Contact Frogtoss Games](http://www.frogtoss.com/pages/contact.html).

39
extern/nfd-modified/docs/build.md vendored Normal file
View File

@ -0,0 +1,39 @@
# Building NFD #
Most of the building instructions are included in [README.md](/README.md). This file just contains apocrypha.
## Running Premake5 Directly ##
*You shouldn't have to run Premake5 directly to build Native File Dialog. This is for package maintainers or people with exotic demands only!*
1. [Clone premake-core](https://github.com/premake/premake-core)
2. [Follow instructions on how to build premake](https://github.com/premake/premake-core/wiki/Building-Premake)
3. `cd` to `build`
4. Type `premake5 <type>`, where <type> is the build you want to create.
### Package Maintainer Only ###
I support a custom Premake action: `premake5 dist`, which generates all of the checked in project types in subdirectories. It is useful to run this command if you are submitting a pull request to test all of the supported premake configurations. Do not check in the built projects; I will do so while accepting your pull request.
## SCons build (deprecated) ##
As of 1.1.6, the deprecated and unmaintained SCons support is removed.
## Compiling with Mingw ##
Use the Makefile in `build/gmake_windows` to build Native File Dialog with mingw. Mingw has many distributions and not all of them are reliable. Here is what worked for me, the primary author of Native File Dialog:
1. Use mingw64, not mingw32. Downloaded from [sourceforge.net](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe/download).
2. When prompted in the intsaller, install the basic compiler and g++.
3. Add the installed bin dir to command prompt path.
4. Run `set CC=g++` to enforce `g++` instead of the default, `cc` for compiling and linking.
5. In `build/gmake_windows`, run `mingw32-make config=release_x64 clean`. Running clean ensures no Visual Studio build products conflict which can cause link errors.
6. Now run `mingw32-make config=release_x64`.
The author has not attempted to build or even install an x86 toolchain for mingw.
If you report an issue, be sure to run make with `verbose=1` so commands are visible.
## Adding NFD source directly to your project ##
Lots of developers add NFD source directly to their projects instead of using the included build scripts. As of 1.1.6, this is an acknowledged approach to building. Of course, everyone has a slightly different toolchain with various warnings and linters enabled. If you run a linter or catch a warning, please consider submitting a pull request to help NFD build cleanly for everyone.

View File

@ -0,0 +1,25 @@
# Pull Requests #
I have had to turn away a number of pull requests due to avoidable circumstances. Please read this file before submitting a pull request. Also look at existing, rejected pull requests which state the reason for rejection.
Here are the rules:
- **Submit pull requests to the devel branch**. The library must be tested on every compiler and OS, so there is no way I am going to just put your change in the master before it has been sync'd and tested on a number of machines. Master branch is depended upon by hundreds of projects.
- **Test your changes on all platforms and compilers that you can.** Also, state which platforms you have tested your code on. 32-bit or 64-bit. Clang or GCC. Visual Studio or Mingw. I have to test all these to accept pull requests, so I prioritize changes that respect my time.
- **Submit Premake build changes only**. As of 1.1, SCons is deprecated. Also, do not submit altered generated projects. I will re-run Premake to re-generate them to ensure that I can still generate the project prior to admitting your pull request.
- **Do not alter existing behavior to support your desired behavior**. For instance, rewriting file open dialogs to behave differently, while trading off functionality for compatibility, will get you rejected. Consider creating an additional code path. Instead of altering `nfd_win.cpp` to support Windows XP, create `nfd_win_legacy.cpp`, which exists alongside the newer file dialog.
- **Do not submit anything I can't verify or maintain**. If you add support for a compiler, include from-scratch install instructions that you have tested yourself. Accepting a pull request means I am now the maintainer of your code, so I must understand what it does and how to test it.
- **Do not change the externally facing API**. NFD needs to maintain ABI compatibility.
## Submitting Cloud Autobuild systems ##
I have received a few pull requests for Travis and AppVeyor-based autobuilding which I have not accepted. NativeFileDialog is officially covered by my private BuildBot network which supports all three target OSes, both CPU architectures and four compilers. I take the view that having a redundant, lesser autobuild system does not improve coverage: it gives a false positive when partial building succeeds. Please do not invest time into cloud-based building with the hope of a pull request being accepted.
## Contact Me ##
Despite all of the "do nots" above, I am happy to recieve new pull requests! If you have any questions about style, or what I would need to accept your specific request, please contact me ahead of submitting the pull request by opening an issue on Github with your question. I will do my best to answer you.

21
extern/nfd-modified/src/common.h vendored Normal file
View File

@ -0,0 +1,21 @@
/*
Native File Dialog
Internal, common across platforms
http://www.frogtoss.com/labs
*/
#ifndef _NFD_COMMON_H
#define _NFD_COMMON_H
#define NFD_MAX_STRLEN 256
#define _NFD_UNUSED(x) ((void)x)
void *NFDi_Malloc( size_t bytes );
void NFDi_Free( void *ptr );
void NFDi_SetError( const char *msg );
void NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
#endif

73
extern/nfd-modified/src/include/nfd.h vendored Normal file
View File

@ -0,0 +1,73 @@
/*
Native File Dialog
User API
http://www.frogtoss.com/labs
*/
#ifndef _NFD_H
#define _NFD_H
#include <stddef.h>
#include <functional>
#include <string>
#include <vector>
/* denotes UTF-8 char */
typedef char nfdchar_t;
typedef std::function<void(const char*)> nfdselcallback_t;
/* opaque data structure -- see NFD_PathSet_* */
typedef struct {
nfdchar_t *buf;
size_t *indices; /* byte offsets into buf */
size_t count; /* number of indices into buf */
}nfdpathset_t;
typedef enum {
NFD_ERROR, /* programmatic error */
NFD_OKAY, /* user pressed okay, or successful return */
NFD_CANCEL /* user pressed cancel */
}nfdresult_t;
/* nfd_<targetplatform>.c */
/* single file open dialog */
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback = NULL );
/* multiple file open dialog */
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback = NULL );
/* save dialog */
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback = NULL );
/* select folder dialog */
nfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath,
nfdchar_t **outPath);
/* nfd_common.c */
/* get last error -- set when nfdresult_t returns NFD_ERROR */
const char *NFD_GetError( void );
/* get the number of entries stored in pathSet */
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathSet );
/* Get the UTF-8 path at offset index */
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index );
/* Free the pathSet */
void NFD_PathSet_Free( nfdpathset_t *pathSet );
#endif

296
extern/nfd-modified/src/nfd_cocoa.mm vendored Normal file
View File

@ -0,0 +1,296 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
#include <AppKit/AppKit.h>
#include "nfd.h"
#include "nfd_common.h"
// this language is a mess!
// who thought it was a good idea to combine Objective-C and C++ together
// when you could just have used C++ and call it a day!!!
//
// might as well make Objective-Ruswift++...
static NSArray *BuildAllowedFileTypes( const std::vector<std::string>& filterList )
{
// Commas and semicolons are the same thing on this platform
// like what about THIS INSTEAD!
// NSMutableArray *buildFilterList = NSMutableArray::alloc()->init();
NSMutableArray *buildFilterList = [[NSMutableArray alloc] init];
std::string typebuf;
for (const std::string& i: filterList) {
typebuf="";
for (const char& j: i) {
if (j==' ' || j==',' || j ==';') {
// or this: NSString::stringWithUTF8String(typebuf);
// buildFilterList->addObject(thisType);
// really? did you have to make this mess?!
const char* typebufC=typebuf.c_str();
NSString *thisType = [NSString stringWithUTF8String:typebufC];
[buildFilterList addObject:thisType];
typebuf="";
} else if (j!='.' && j!='*') {
typebuf+=j;
}
}
if (!typebuf.empty()) {
// I don't think this will work, but come on...
const char* typebufC=typebuf.c_str();
NSString *thisType = [NSString stringWithUTF8String:typebufC];
[buildFilterList addObject:thisType];
}
}
NSArray *returnArray = [NSArray arrayWithArray:buildFilterList];
[buildFilterList release];
return returnArray;
}
static void AddFilterListToDialog( NSSavePanel *dialog, const std::vector<std::string>& filterList )
{
if ( filterList.size()&1 )
return;
NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList );
if ( [allowedFileTypes count] != 0 )
{
[dialog setAllowedFileTypes:allowedFileTypes];
}
}
static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath )
{
if ( !defaultPath || strlen(defaultPath) == 0 )
return;
NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath];
NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES];
[dialog setDirectoryURL:url];
}
/* fixme: pathset should be pathSet */
static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset )
{
assert(pathset);
assert([urls count]);
pathset->count = (size_t)[urls count];
pathset->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathset->count );
if ( !pathset->indices )
{
return NFD_ERROR;
}
// count the total space needed for buf
size_t bufsize = 0;
for ( NSURL *url in urls )
{
NSString *path = [url path];
bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
}
pathset->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufsize );
if ( !pathset->buf )
{
return NFD_ERROR;
}
// fill buf
nfdchar_t *p_buf = pathset->buf;
size_t count = 0;
for ( NSURL *url in urls )
{
NSString *path = [url path];
const nfdchar_t *utf8Path = [path UTF8String];
size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
memcpy( p_buf, utf8Path, byteLen );
ptrdiff_t index = p_buf - pathset->buf;
assert( index >= 0 );
pathset->indices[count] = (size_t)index;
p_buf += byteLen;
++count;
}
return NFD_OKAY;
}
/* public */
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
NSOpenPanel *dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:NO];
// Build the filter list
AddFilterListToDialog(dialog, filterList);
// Set the starting directory
SetDefaultPath(dialog, defaultPath);
nfdresult_t nfdResult = NFD_CANCEL;
if ( [dialog runModal] == NSModalResponseOK )
{
NSURL *url = [dialog URL];
const char *utf8Path = [[url path] UTF8String];
// byte count, not char count
size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path);
*outPath = (nfdchar_t*)NFDi_Malloc( len+1 );
if ( !*outPath )
{
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return NFD_ERROR;
}
memcpy( *outPath, utf8Path, len+1 ); /* copy null term */
nfdResult = NFD_OKAY;
}
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return nfdResult;
}
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
NSOpenPanel *dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:YES];
// Build the fiter list.
AddFilterListToDialog(dialog, filterList);
// Set the starting directory
SetDefaultPath(dialog, defaultPath);
nfdresult_t nfdResult = NFD_CANCEL;
if ( [dialog runModal] == NSModalResponseOK )
{
NSArray *urls = [dialog URLs];
if ( [urls count] == 0 )
{
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return NFD_CANCEL;
}
if ( AllocPathSet( urls, outPaths ) == NFD_ERROR )
{
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return NFD_ERROR;
}
nfdResult = NFD_OKAY;
}
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return nfdResult;
}
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
NSSavePanel *dialog = [NSSavePanel savePanel];
[dialog setExtensionHidden:NO];
// Build the filter list.
AddFilterListToDialog(dialog, filterList);
// Set the starting directory
SetDefaultPath(dialog, defaultPath);
nfdresult_t nfdResult = NFD_CANCEL;
if ( [dialog runModal] == NSModalResponseOK )
{
NSURL *url = [dialog URL];
const char *utf8Path = [[url path] UTF8String];
size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
*outPath = (nfdchar_t*)NFDi_Malloc( byteLen );
if ( !*outPath )
{
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return NFD_ERROR;
}
memcpy( *outPath, utf8Path, byteLen );
nfdResult = NFD_OKAY;
}
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return nfdResult;
}
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
nfdchar_t **outPath)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
NSOpenPanel *dialog = [NSOpenPanel openPanel];
[dialog setAllowsMultipleSelection:NO];
[dialog setCanChooseDirectories:YES];
[dialog setCanCreateDirectories:YES];
[dialog setCanChooseFiles:NO];
// Set the starting directory
SetDefaultPath(dialog, defaultPath);
nfdresult_t nfdResult = NFD_CANCEL;
if ( [dialog runModal] == NSModalResponseOK )
{
NSURL *url = [dialog URL];
const char *utf8Path = [[url path] UTF8String];
// byte count, not char count
size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path);
*outPath = (nfdchar_t*)NFDi_Malloc( len+1 );
if ( !*outPath )
{
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return NFD_ERROR;
}
memcpy( *outPath, utf8Path, len+1 ); /* copy null term */
nfdResult = NFD_OKAY;
}
[pool release];
[keyWindow makeKeyAndOrderFront:nil];
return nfdResult;
}

142
extern/nfd-modified/src/nfd_common.cpp vendored Normal file
View File

@ -0,0 +1,142 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "nfd_common.h"
static char g_errorstr[NFD_MAX_STRLEN] = {0};
/* public routines */
const char *NFD_GetError( void )
{
return g_errorstr;
}
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathset )
{
assert(pathset);
return pathset->count;
}
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathset, size_t num )
{
assert(pathset);
assert(num < pathset->count);
return pathset->buf + pathset->indices[num];
}
void NFD_PathSet_Free( nfdpathset_t *pathset )
{
assert(pathset);
NFDi_Free( pathset->indices );
NFDi_Free( pathset->buf );
}
/* internal routines */
void *NFDi_Malloc( size_t bytes )
{
void *ptr = malloc(bytes);
if ( !ptr )
NFDi_SetError("NFDi_Malloc failed.");
return ptr;
}
void NFDi_Free( void *ptr )
{
assert(ptr);
free(ptr);
}
void NFDi_SetError( const char *msg )
{
int bTruncate = NFDi_SafeStrncpy( g_errorstr, msg, NFD_MAX_STRLEN );
assert( !bTruncate ); _NFD_UNUSED(bTruncate);
}
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy )
{
size_t n = maxCopy;
char *d = dst;
assert( src );
assert( dst );
while ( n > 0 && *src != '\0' )
{
*d++ = *src++;
--n;
}
/* Truncation case -
terminate string and return true */
if ( n == 0 )
{
dst[maxCopy-1] = '\0';
return 1;
}
/* No truncation. Append a single NULL and return. */
*d = '\0';
return 0;
}
/* adapted from microutf8 */
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str )
{
/* This function doesn't properly check validity of UTF-8 character
sequence, it is supposed to use only with valid UTF-8 strings. */
int32_t character_count = 0;
int32_t i = 0; /* Counter used to iterate over string. */
nfdchar_t maybe_bom[4];
/* If there is UTF-8 BOM ignore it. */
if (strlen(str) > 2)
{
strncpy(maybe_bom, str, 3);
maybe_bom[3] = 0;
if (strcmp(maybe_bom, (nfdchar_t*)NFD_UTF8_BOM) == 0)
i += 3;
}
while(str[i])
{
if (str[i] >> 7 == 0)
{
/* If bit pattern begins with 0 we have ascii character. */
++character_count;
}
else if (str[i] >> 6 == 3)
{
/* If bit pattern begins with 11 it is beginning of UTF-8 byte sequence. */
++character_count;
}
else if (str[i] >> 6 == 2)
; /* If bit pattern begins with 10 it is middle of utf-8 byte sequence. */
else
{
/* In any other case this is not valid UTF-8. */
return -1;
}
++i;
}
return character_count;
}
int NFDi_IsFilterSegmentChar( char ch )
{
return (ch==','||ch==';'||ch=='\0');
}

31
extern/nfd-modified/src/nfd_common.h vendored Normal file
View File

@ -0,0 +1,31 @@
/*
Native File Dialog
Internal, common across platforms
http://www.frogtoss.com/labs
*/
#ifndef _NFD_COMMON_H
#define _NFD_COMMON_H
#include "nfd.h"
#include <stdint.h>
#define NFD_MAX_STRLEN 256
#define _NFD_UNUSED(x) ((void)x)
#define NFD_UTF8_BOM "\xEF\xBB\xBF"
void *NFDi_Malloc( size_t bytes );
void NFDi_Free( void *ptr );
void NFDi_SetError( const char *msg );
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
int32_t NFDi_UTF8_Strlen( const nfdchar_t *str );
int NFDi_IsFilterSegmentChar( char ch );
#endif

379
extern/nfd-modified/src/nfd_gtk.cpp vendored Normal file
View File

@ -0,0 +1,379 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <gtk/gtk.h>
#include "nfd.h"
#include "nfd_common.h"
const char INIT_FAIL_MSG[] = "gtk_init_check failed to initilaize GTK+";
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize )
{
const char SEP[] = ", ";
size_t len = strlen(filterName);
if ( len != 0 )
{
strncat( filterName, SEP, bufsize - len - 1 );
len += strlen(SEP);
}
strncat( filterName, typebuf, bufsize - len - 1 );
}
static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList )
{
GtkFileFilter *filter;
char typebuf[NFD_MAX_STRLEN] = {0};
const char *p_filterList = filterList;
char *p_typebuf = typebuf;
char filterName[NFD_MAX_STRLEN] = {0};
if ( !filterList || strlen(filterList) == 0 )
return;
filter = gtk_file_filter_new();
while ( 1 )
{
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
{
char typebufWildcard[NFD_MAX_STRLEN];
/* add another type to the filter */
assert( strlen(typebuf) > 0 );
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 );
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf );
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN );
gtk_file_filter_add_pattern( filter, typebufWildcard );
p_typebuf = typebuf;
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN );
}
if ( *p_filterList == ';' || *p_filterList == '\0' )
{
/* end of filter -- add it to the dialog */
gtk_file_filter_set_name( filter, filterName );
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
filterName[0] = '\0';
if ( *p_filterList == '\0' )
break;
filter = gtk_file_filter_new();
}
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) )
{
*p_typebuf = *p_filterList;
p_typebuf++;
}
p_filterList++;
}
/* always append a wildcard option to the end*/
filter = gtk_file_filter_new();
gtk_file_filter_set_name( filter, "*.*" );
gtk_file_filter_add_pattern( filter, "*" );
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
}
static void SetDefaultPath( GtkWidget *dialog, const char *defaultPath )
{
if ( !defaultPath || strlen(defaultPath) == 0 )
return;
/* GTK+ manual recommends not specifically setting the default path.
We do it anyway in order to be consistent across platforms.
If consistency with the native OS is preferred, this is the line
to comment out. -ml */
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), defaultPath );
}
static nfdresult_t AllocPathSet( GSList *fileList, nfdpathset_t *pathSet )
{
size_t bufSize = 0;
GSList *node;
nfdchar_t *p_buf;
size_t count = 0;
assert(fileList);
assert(pathSet);
pathSet->count = (size_t)g_slist_length( fileList );
assert( pathSet->count > 0 );
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count );
if ( !pathSet->indices )
{
return NFD_ERROR;
}
/* count the total space needed for buf */
for ( node = fileList; node; node = node->next )
{
assert(node->data);
bufSize += strlen( (const gchar*)node->data ) + 1;
}
pathSet->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufSize );
/* fill buf */
p_buf = pathSet->buf;
for ( node = fileList; node; node = node->next )
{
nfdchar_t *path = (nfdchar_t*)(node->data);
size_t byteLen = strlen(path)+1;
ptrdiff_t index;
memcpy( p_buf, path, byteLen );
g_free(node->data);
index = p_buf - pathSet->buf;
assert( index >= 0 );
pathSet->indices[count] = (size_t)index;
p_buf += byteLen;
++count;
}
g_slist_free( fileList );
return NFD_OKAY;
}
static void WaitForCleanup(void)
{
while (gtk_events_pending())
gtk_main_iteration();
}
/* public */
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath )
{
GtkWidget *dialog;
nfdresult_t result;
if ( !gtk_init_check( NULL, NULL ) )
{
NFDi_SetError(INIT_FAIL_MSG);
return NFD_ERROR;
}
dialog = gtk_file_chooser_dialog_new( "Open File",
NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT,
NULL );
/* Build the filter list */
AddFiltersToDialog(dialog, filterList);
/* Set the default path */
SetDefaultPath(dialog, defaultPath);
result = NFD_CANCEL;
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
{
char *filename;
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
{
size_t len = strlen(filename);
*outPath = NFDi_Malloc( len + 1 );
memcpy( *outPath, filename, len + 1 );
if ( !*outPath )
{
g_free( filename );
gtk_widget_destroy(dialog);
return NFD_ERROR;
}
}
g_free( filename );
result = NFD_OKAY;
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
return result;
}
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths )
{
GtkWidget *dialog;
nfdresult_t result;
if ( !gtk_init_check( NULL, NULL ) )
{
NFDi_SetError(INIT_FAIL_MSG);
return NFD_ERROR;
}
dialog = gtk_file_chooser_dialog_new( "Open Files",
NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT,
NULL );
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE );
/* Build the filter list */
AddFiltersToDialog(dialog, filterList);
/* Set the default path */
SetDefaultPath(dialog, defaultPath);
result = NFD_CANCEL;
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
{
GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
if ( AllocPathSet( fileList, outPaths ) == NFD_ERROR )
{
gtk_widget_destroy(dialog);
return NFD_ERROR;
}
result = NFD_OKAY;
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
return result;
}
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath )
{
GtkWidget *dialog;
nfdresult_t result;
if ( !gtk_init_check( NULL, NULL ) )
{
NFDi_SetError(INIT_FAIL_MSG);
return NFD_ERROR;
}
dialog = gtk_file_chooser_dialog_new( "Save File",
NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT,
NULL );
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
/* Build the filter list */
AddFiltersToDialog(dialog, filterList);
/* Set the default path */
SetDefaultPath(dialog, defaultPath);
result = NFD_CANCEL;
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
{
char *filename;
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
{
size_t len = strlen(filename);
*outPath = NFDi_Malloc( len + 1 );
memcpy( *outPath, filename, len + 1 );
if ( !*outPath )
{
g_free( filename );
gtk_widget_destroy(dialog);
return NFD_ERROR;
}
}
g_free(filename);
result = NFD_OKAY;
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
return result;
}
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
nfdchar_t **outPath)
{
GtkWidget *dialog;
nfdresult_t result;
if (!gtk_init_check(NULL, NULL))
{
NFDi_SetError(INIT_FAIL_MSG);
return NFD_ERROR;
}
dialog = gtk_file_chooser_dialog_new( "Select folder",
NULL,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Select", GTK_RESPONSE_ACCEPT,
NULL );
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
/* Set the default path */
SetDefaultPath(dialog, defaultPath);
result = NFD_CANCEL;
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
{
char *filename;
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
{
size_t len = strlen(filename);
*outPath = NFDi_Malloc( len + 1 );
memcpy( *outPath, filename, len + 1 );
if ( !*outPath )
{
g_free( filename );
gtk_widget_destroy(dialog);
return NFD_ERROR;
}
}
g_free(filename);
result = NFD_OKAY;
}
WaitForCleanup();
gtk_widget_destroy(dialog);
WaitForCleanup();
return result;
}

797
extern/nfd-modified/src/nfd_win.cpp vendored Normal file
View File

@ -0,0 +1,797 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
#ifdef __MINGW32__
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
#define NTDDI_VERSION NTDDI_VISTA
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
/* only locally define UNICODE in this compilation unit */
#ifndef UNICODE
#define UNICODE
#endif
#include <wchar.h>
#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <shobjidl.h>
#include "nfd_common.h"
// hack I know
#include "../../../src/utfutils.h"
class NFDWinEvents: public IFileDialogEvents {
nfdselcallback_t selCallback;
size_t refCount;
virtual ~NFDWinEvents() {
}
public:
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
printf("QueryInterface called DAMN IT\n");
*ppv=NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP_(ULONG) AddRef() {
printf("AddRef() called\n");
return InterlockedIncrement(&refCount);
}
IFACEMETHODIMP_(ULONG) Release() {
printf("Release() called\n");
LONG ret=InterlockedDecrement(&refCount);
if (ret==0) {
printf("Destroying the final object.\n");
delete this;
}
return ret;
}
IFACEMETHODIMP OnFileOk(IFileDialog*) { return E_NOTIMPL; }
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return E_NOTIMPL; }
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return E_NOTIMPL; }
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return E_NOTIMPL; }
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return E_NOTIMPL; }
IFACEMETHODIMP OnTypeChange(IFileDialog*) { return E_NOTIMPL; }
IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) {
// Get the file name
::IShellItem *shellItem(NULL);
HRESULT result = dialog->GetCurrentSelection(&shellItem);
if ( !SUCCEEDED(result) )
{
printf("failure!\n");
return S_OK;
}
wchar_t *filePath(NULL);
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
if ( !SUCCEEDED(result) )
{
printf("GDN failure!\n");
shellItem->Release();
return S_OK;
}
std::string utf8FilePath=utf16To8(filePath);
if (selCallback!=NULL) selCallback(utf8FilePath.c_str());
printf("I got you for a value of %s\n",utf8FilePath.c_str());
shellItem->Release();
return S_OK;
}
NFDWinEvents(nfdselcallback_t callback):
selCallback(callback),
refCount(1) {
}
};
#define COM_INITFLAGS ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE
static BOOL COMIsInitialized(HRESULT coResult)
{
if (coResult == RPC_E_CHANGED_MODE)
{
// If COM was previously initialized with different init flags,
// NFD still needs to operate. Eat this warning.
return TRUE;
}
return SUCCEEDED(coResult);
}
static HRESULT COMInit(void)
{
return ::CoInitializeEx(NULL, COM_INITFLAGS);
}
static void COMUninit(HRESULT coResult)
{
// do not uninitialize if RPC_E_CHANGED_MODE occurred -- this
// case does not refcount COM.
if (SUCCEEDED(coResult))
::CoUninitialize();
}
// allocs the space in outPath -- call free()
static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr )
{
int inStrCharacterCount = static_cast<int>(wcslen(inStr));
int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0,
inStr, inStrCharacterCount,
NULL, 0, NULL, NULL );
assert( bytesNeeded );
bytesNeeded += 1;
*outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded );
if ( !*outStr )
return;
int bytesWritten = WideCharToMultiByte( CP_UTF8, 0,
inStr, -1,
*outStr, bytesNeeded,
NULL, NULL );
assert( bytesWritten ); _NFD_UNUSED( bytesWritten );
}
/* includes NULL terminator byte in return */
static size_t GetUTF8ByteCountForWChar( const wchar_t *str )
{
size_t bytesNeeded = WideCharToMultiByte( CP_UTF8, 0,
str, -1,
NULL, 0, NULL, NULL );
assert( bytesNeeded );
return bytesNeeded+1;
}
// write to outPtr -- no free() necessary.
static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr )
{
int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr ));
/* invocation copies null term */
int bytesWritten = WideCharToMultiByte( CP_UTF8, 0,
inStr, -1,
outPtr, bytesNeeded,
NULL, 0 );
assert( bytesWritten );
return bytesWritten;
}
// allocs the space in outStr -- call free()
static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
{
int inStrByteCount = static_cast<int>(strlen(inStr));
int charsNeeded = MultiByteToWideChar(CP_UTF8, 0,
inStr, inStrByteCount,
NULL, 0 );
assert( charsNeeded );
assert( !*outStr );
charsNeeded += 1; // terminator
*outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) );
if ( !*outStr )
return;
int ret = MultiByteToWideChar(CP_UTF8, 0,
inStr, inStrByteCount,
*outStr, charsNeeded);
(*outStr)[charsNeeded-1] = '\0';
#ifdef _DEBUG
int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr));
assert( ret == inStrCharacterCount );
#else
_NFD_UNUSED(ret);
#endif
}
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const std::vector<std::string>& filterList )
{
const wchar_t WILDCARD[] = L"*.*";
if (filterList.empty())
return NFD_OKAY;
// list size has to be an even number (name/filter)
if (filterList.size()&1)
return NFD_ERROR;
// Count rows to alloc
UINT filterCount = filterList.size()>>1; /* guaranteed to have one filter on a correct, non-empty parse */
assert(filterCount);
if ( !filterCount )
{
NFDi_SetError("Error parsing filters.");
return NFD_ERROR;
}
/* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */
COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount + 1) );
if ( !specList )
{
return NFD_ERROR;
}
for (UINT i = 0; i < filterCount+1; ++i )
{
specList[i].pszName = NULL;
specList[i].pszSpec = NULL;
}
size_t specIdx = 0;
for (size_t i=0; i<filterList.size(); i+=2) {
String name=filterList[i];
String spec=filterList[i+1];
for (char& i: spec) {
if (i==' ') i=',';
}
CopyNFDCharToWChar( name.c_str(), (wchar_t**)&specList[specIdx].pszName );
CopyNFDCharToWChar( spec.c_str(), (wchar_t**)&specList[specIdx].pszSpec );
++specIdx;
}
/* Add wildcard */
specList[specIdx].pszSpec = WILDCARD;
specList[specIdx].pszName = WILDCARD;
fileOpenDialog->SetFileTypes( filterCount+1, specList );
/* free speclist */
for ( size_t i = 0; i < filterCount; ++i )
{
NFDi_Free( (void*)specList[i].pszSpec );
}
NFDi_Free( specList );
return NFD_OKAY;
}
static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet )
{
const char ERRORMSG[] = "Error allocating pathset.";
assert(shellItems);
assert(pathSet);
// How many items in shellItems?
DWORD numShellItems;
HRESULT result = shellItems->GetCount(&numShellItems);
if ( !SUCCEEDED(result) )
{
NFDi_SetError(ERRORMSG);
return NFD_ERROR;
}
pathSet->count = static_cast<size_t>(numShellItems);
assert( pathSet->count > 0 );
pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count );
if ( !pathSet->indices )
{
return NFD_ERROR;
}
/* count the total bytes needed for buf */
size_t bufSize = 0;
for ( DWORD i = 0; i < numShellItems; ++i )
{
::IShellItem *shellItem;
result = shellItems->GetItemAt(i, &shellItem);
if ( !SUCCEEDED(result) )
{
NFDi_SetError(ERRORMSG);
return NFD_ERROR;
}
// Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
SFGAOF attribs;
result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs );
if ( !SUCCEEDED(result) )
{
NFDi_SetError(ERRORMSG);
return NFD_ERROR;
}
if ( !(attribs & SFGAO_FILESYSTEM) )
continue;
LPWSTR name;
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
// Calculate length of name with UTF-8 encoding
bufSize += GetUTF8ByteCountForWChar( name );
CoTaskMemFree(name);
}
assert(bufSize);
pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize );
memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize );
/* fill buf */
nfdchar_t *p_buf = pathSet->buf;
for (DWORD i = 0; i < numShellItems; ++i )
{
::IShellItem *shellItem;
result = shellItems->GetItemAt(i, &shellItem);
if ( !SUCCEEDED(result) )
{
NFDi_SetError(ERRORMSG);
return NFD_ERROR;
}
// Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
SFGAOF attribs;
result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs );
if ( !SUCCEEDED(result) )
{
NFDi_SetError(ERRORMSG);
return NFD_ERROR;
}
if ( !(attribs & SFGAO_FILESYSTEM) )
continue;
LPWSTR name;
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf);
CoTaskMemFree(name);
ptrdiff_t index = p_buf - pathSet->buf;
assert( index >= 0 );
pathSet->indices[i] = static_cast<size_t>(index);
p_buf += bytesWritten;
}
return NFD_OKAY;
}
static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath )
{
if ( !defaultPath || strlen(defaultPath) == 0 )
return NFD_OKAY;
wchar_t *defaultPathW = {0};
CopyNFDCharToWChar( defaultPath, &defaultPathW );
IShellItem *folder;
HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) );
// Valid non results.
if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) )
{
NFDi_Free( defaultPathW );
return NFD_OKAY;
}
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Error creating ShellItem");
NFDi_Free( defaultPathW );
return NFD_ERROR;
}
// Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API.
dialog->SetFolder( folder );
NFDi_Free( defaultPathW );
folder->Release();
return NFD_OKAY;
}
/* public */
nfdresult_t NFD_OpenDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
{
nfdresult_t nfdResult = NFD_ERROR;
NFDWinEvents* winEvents;
bool hasEvents=true;
DWORD eventID=0;
HRESULT coResult = COMInit();
if (!COMIsInitialized(coResult))
{
NFDi_SetError("Could not initialize COM.");
return nfdResult;
}
// Create dialog
::IFileOpenDialog *fileOpenDialog(NULL);
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
CLSCTX_ALL, ::IID_IFileOpenDialog,
reinterpret_cast<void**>(&fileOpenDialog) );
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not create dialog.");
goto end;
}
// Build the filter list
if ( !AddFiltersToDialog( fileOpenDialog, filterList ) )
{
goto end;
}
// Set the default path
if ( !SetDefaultPath( fileOpenDialog, defaultPath ) )
{
goto end;
}
// Pass the callback
winEvents=new NFDWinEvents(selCallback);
if ( !SUCCEEDED(fileOpenDialog->Advise(winEvents,&eventID)) ) {
// error... ignore
hasEvents=false;
winEvents->Release();
} else {
winEvents->Release();
}
// Show the dialog.
// TODO: pass the Furnace window here
result = fileOpenDialog->Show(NULL);
if ( SUCCEEDED(result) )
{
// Get the file name
::IShellItem *shellItem(NULL);
result = fileOpenDialog->GetResult(&shellItem);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get shell item from dialog.");
goto end;
}
wchar_t *filePath(NULL);
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get file path for selected.");
shellItem->Release();
goto end;
}
CopyWCharToNFDChar( filePath, outPath );
CoTaskMemFree(filePath);
if ( !*outPath )
{
/* error is malloc-based, error message would be redundant */
shellItem->Release();
goto end;
}
nfdResult = NFD_OKAY;
shellItem->Release();
}
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
nfdResult = NFD_CANCEL;
}
else
{
NFDi_SetError("File dialog box show failed.");
nfdResult = NFD_ERROR;
}
end:
if (fileOpenDialog) {
if (hasEvents) {
fileOpenDialog->Unadvise(eventID);
}
fileOpenDialog->Release();
}
COMUninit(coResult);
return nfdResult;
}
nfdresult_t NFD_OpenDialogMultiple( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths,
nfdselcallback_t selCallback )
{
nfdresult_t nfdResult = NFD_ERROR;
HRESULT coResult = COMInit();
if (!COMIsInitialized(coResult))
{
NFDi_SetError("Could not initialize COM.");
return nfdResult;
}
// Create dialog
::IFileOpenDialog *fileOpenDialog(NULL);
HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
CLSCTX_ALL, ::IID_IFileOpenDialog,
reinterpret_cast<void**>(&fileOpenDialog) );
if ( !SUCCEEDED(result) )
{
fileOpenDialog = NULL;
NFDi_SetError("Could not create dialog.");
goto end;
}
// Build the filter list
if ( !AddFiltersToDialog( fileOpenDialog, filterList ) )
{
goto end;
}
// Set the default path
if ( !SetDefaultPath( fileOpenDialog, defaultPath ) )
{
goto end;
}
// Set a flag for multiple options
DWORD dwFlags;
result = fileOpenDialog->GetOptions(&dwFlags);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get options.");
goto end;
}
result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not set options.");
goto end;
}
// Show the dialog.
result = fileOpenDialog->Show(NULL);
if ( SUCCEEDED(result) )
{
IShellItemArray *shellItems;
result = fileOpenDialog->GetResults( &shellItems );
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get shell items.");
goto end;
}
if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR )
{
shellItems->Release();
goto end;
}
shellItems->Release();
nfdResult = NFD_OKAY;
}
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
nfdResult = NFD_CANCEL;
}
else
{
NFDi_SetError("File dialog box show failed.");
nfdResult = NFD_ERROR;
}
end:
if ( fileOpenDialog )
fileOpenDialog->Release();
COMUninit(coResult);
return nfdResult;
}
nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath,
nfdselcallback_t selCallback )
{
nfdresult_t nfdResult = NFD_ERROR;
HRESULT coResult = COMInit();
if (!COMIsInitialized(coResult))
{
NFDi_SetError("Could not initialize COM.");
return nfdResult;
}
// Create dialog
::IFileSaveDialog *fileSaveDialog(NULL);
HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL,
CLSCTX_ALL, ::IID_IFileSaveDialog,
reinterpret_cast<void**>(&fileSaveDialog) );
if ( !SUCCEEDED(result) )
{
fileSaveDialog = NULL;
NFDi_SetError("Could not create dialog.");
goto end;
}
// Build the filter list
if ( !AddFiltersToDialog( fileSaveDialog, filterList ) )
{
goto end;
}
// Set the default path
if ( !SetDefaultPath( fileSaveDialog, defaultPath ) )
{
goto end;
}
// Show the dialog.
result = fileSaveDialog->Show(NULL);
if ( SUCCEEDED(result) )
{
// Get the file name
::IShellItem *shellItem;
result = fileSaveDialog->GetResult(&shellItem);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get shell item from dialog.");
goto end;
}
wchar_t *filePath(NULL);
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
if ( !SUCCEEDED(result) )
{
shellItem->Release();
NFDi_SetError("Could not get file path for selected.");
goto end;
}
CopyWCharToNFDChar( filePath, outPath );
CoTaskMemFree(filePath);
if ( !*outPath )
{
/* error is malloc-based, error message would be redundant */
shellItem->Release();
goto end;
}
nfdResult = NFD_OKAY;
shellItem->Release();
}
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
nfdResult = NFD_CANCEL;
}
else
{
NFDi_SetError("File dialog box show failed.");
nfdResult = NFD_ERROR;
}
end:
if ( fileSaveDialog )
fileSaveDialog->Release();
COMUninit(coResult);
return nfdResult;
}
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
nfdchar_t **outPath)
{
nfdresult_t nfdResult = NFD_ERROR;
DWORD dwOptions = 0;
HRESULT coResult = COMInit();
if (!COMIsInitialized(coResult))
{
NFDi_SetError("CoInitializeEx failed.");
return nfdResult;
}
// Create dialog
::IFileOpenDialog *fileDialog(NULL);
HRESULT result = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_ALL,
IID_PPV_ARGS(&fileDialog));
if ( !SUCCEEDED(result) )
{
NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed.");
goto end;
}
// Set the default path
if (SetDefaultPath(fileDialog, defaultPath) != NFD_OKAY)
{
NFDi_SetError("SetDefaultPath failed.");
goto end;
}
// Get the dialogs options
if (!SUCCEEDED(fileDialog->GetOptions(&dwOptions)))
{
NFDi_SetError("GetOptions for IFileDialog failed.");
goto end;
}
// Add in FOS_PICKFOLDERS which hides files and only allows selection of folders
if (!SUCCEEDED(fileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS)))
{
NFDi_SetError("SetOptions for IFileDialog failed.");
goto end;
}
// Show the dialog to the user
result = fileDialog->Show(NULL);
if ( SUCCEEDED(result) )
{
// Get the folder name
::IShellItem *shellItem(NULL);
result = fileDialog->GetResult(&shellItem);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get file path for selected.");
shellItem->Release();
goto end;
}
wchar_t *path = NULL;
result = shellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("GetDisplayName for IShellItem failed.");
shellItem->Release();
goto end;
}
CopyWCharToNFDChar(path, outPath);
CoTaskMemFree(path);
if ( !*outPath )
{
shellItem->Release();
goto end;
}
nfdResult = NFD_OKAY;
shellItem->Release();
}
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
{
nfdResult = NFD_CANCEL;
}
else
{
NFDi_SetError("Show for IFileDialog failed.");
nfdResult = NFD_ERROR;
}
end:
if (fileDialog)
fileDialog->Release();
COMUninit(coResult);
return nfdResult;
}

307
extern/nfd-modified/src/nfd_zenity.cpp vendored Normal file
View File

@ -0,0 +1,307 @@
/*
Native File Dialog
http://www.frogtoss.com/labs
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "nfd.h"
#include "nfd_common.h"
#define SIMPLE_EXEC_IMPLEMENTATION
#include "simple_exec.h"
const char NO_ZENITY_MSG[] = "zenity not installed";
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize )
{
size_t len = strlen(filterName);
if( len > 0 )
strncat( filterName, " *.", bufsize - len - 1 );
else
strncat( filterName, "--file-filter=*.", bufsize - len - 1 );
len = strlen(filterName);
strncat( filterName, typebuf, bufsize - len - 1 );
}
static void AddFiltersToCommandArgs(char** commandArgs, int commandArgsLen, const char *filterList )
{
char typebuf[NFD_MAX_STRLEN] = {0};
const char *p_filterList = filterList;
char *p_typebuf = typebuf;
char filterName[NFD_MAX_STRLEN] = {0};
int i;
if ( !filterList || strlen(filterList) == 0 )
return;
while ( 1 )
{
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
{
char typebufWildcard[NFD_MAX_STRLEN];
/* add another type to the filter */
assert( strlen(typebuf) > 0 );
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 );
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf );
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN );
p_typebuf = typebuf;
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN );
}
if ( *p_filterList == ';' || *p_filterList == '\0' )
{
/* end of filter -- add it to the dialog */
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
commandArgs[i] = strdup(filterName);
filterName[0] = '\0';
if ( *p_filterList == '\0' )
break;
}
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) )
{
*p_typebuf = *p_filterList;
p_typebuf++;
}
p_filterList++;
}
/* always append a wildcard option to the end*/
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
commandArgs[i] = strdup("--file-filter=*.*");
}
static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defaultPath, const char* filterList, char** stdOut)
{
if(defaultPath != NULL)
{
char* prefix = "--filename=";
int len = strlen(prefix) + strlen(defaultPath) + 1;
char* tmp = (char*) calloc(len, 1);
strcat(tmp, prefix);
strcat(tmp, defaultPath);
int i;
for(i = 0; command[i] != NULL && i < commandLen; i++);
command[i] = tmp;
}
AddFiltersToCommandArgs(command, commandLen, filterList);
int byteCount = 0;
int exitCode = 0;
int processInvokeError = runCommandArray(stdOut, &byteCount, &exitCode, 0, command);
for(int i = 0; command[i] != NULL && i < commandLen; i++)
free(command[i]);
nfdresult_t result = NFD_OKAY;
if(processInvokeError == COMMAND_NOT_FOUND)
{
NFDi_SetError(NO_ZENITY_MSG);
result = NFD_ERROR;
}
else
{
if(exitCode == 1)
result = NFD_CANCEL;
}
return result;
}
static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
{
assert(zenityList);
assert(pathSet);
size_t len = strlen(zenityList) + 1;
pathSet->buf = NFDi_Malloc(len);
int numEntries = 1;
for(size_t i = 0; i < len; i++)
{
char ch = zenityList[i];
if(ch == '|')
{
numEntries++;
ch = '\0';
}
pathSet->buf[i] = ch;
}
pathSet->count = numEntries;
assert( pathSet->count > 0 );
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count );
int entry = 0;
pathSet->indices[0] = 0;
for(size_t i = 0; i < len; i++)
{
char ch = zenityList[i];
if(ch == '|')
{
entry++;
pathSet->indices[entry] = i + 1;
}
}
return NFD_OKAY;
}
/* public */
nfdresult_t NFD_OpenDialog( const char *filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath )
{
int commandLen = 100;
char* command[commandLen];
memset(command, 0, commandLen * sizeof(char*));
command[0] = strdup("zenity");
command[1] = strdup("--file-selection");
command[2] = strdup("--title=Open File");
char* stdOut = NULL;
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
if(stdOut != NULL)
{
size_t len = strlen(stdOut);
*outPath = NFDi_Malloc(len);
memcpy(*outPath, stdOut, len);
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
free(stdOut);
}
else
{
*outPath = NULL;
}
return result;
}
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
const nfdchar_t *defaultPath,
nfdpathset_t *outPaths )
{
int commandLen = 100;
char* command[commandLen];
memset(command, 0, commandLen * sizeof(char*));
command[0] = strdup("zenity");
command[1] = strdup("--file-selection");
command[2] = strdup("--title=Open Files");
command[3] = strdup("--multiple");
char* stdOut = NULL;
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
if(stdOut != NULL)
{
size_t len = strlen(stdOut);
stdOut[len-1] = '\0'; // remove trailing newline
if ( AllocPathSet( stdOut, outPaths ) == NFD_ERROR )
result = NFD_ERROR;
free(stdOut);
}
else
{
result = NFD_ERROR;
}
return result;
}
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
const nfdchar_t *defaultPath,
nfdchar_t **outPath )
{
int commandLen = 100;
char* command[commandLen];
memset(command, 0, commandLen * sizeof(char*));
command[0] = strdup("zenity");
command[1] = strdup("--file-selection");
command[2] = strdup("--title=Save File");
command[3] = strdup("--save");
char* stdOut = NULL;
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
if(stdOut != NULL)
{
size_t len = strlen(stdOut);
*outPath = NFDi_Malloc(len);
memcpy(*outPath, stdOut, len);
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
free(stdOut);
}
else
{
*outPath = NULL;
}
return result;
}
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
nfdchar_t **outPath)
{
int commandLen = 100;
char* command[commandLen];
memset(command, 0, commandLen * sizeof(char*));
command[0] = strdup("zenity");
command[1] = strdup("--file-selection");
command[2] = strdup("--directory");
command[3] = strdup("--title=Select folder");
char* stdOut = NULL;
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, "", &stdOut);
if(stdOut != NULL)
{
size_t len = strlen(stdOut);
*outPath = NFDi_Malloc(len);
memcpy(*outPath, stdOut, len);
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
free(stdOut);
}
else
{
*outPath = NULL;
}
return result;
}

218
extern/nfd-modified/src/simple_exec.h vendored Normal file
View File

@ -0,0 +1,218 @@
// copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
// simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
//
// do this:
// #define SIMPLE_EXEC_IMPLEMENTATION
// before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
// #define SIMPLE_EXEC_IMPLEMENTATION
// #include "simple_exec.h"
#ifndef SIMPLE_EXEC_H
#define SIMPLE_EXEC_H
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
#endif // SIMPLE_EXEC_H
#ifdef SIMPLE_EXEC_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/wait.h>
#include <stdarg.h>
#include <fcntl.h>
#define release_assert(exp) { if (!(exp)) { abort(); } }
enum PIPE_FILE_DESCRIPTORS
{
READ_FD = 0,
WRITE_FD = 1
};
enum RUN_COMMAND_ERROR
{
COMMAND_RAN_OK = 0,
COMMAND_NOT_FOUND = 1
};
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
{
// adapted from: https://stackoverflow.com/a/479103
int bufferSize = 256;
char buffer[bufferSize + 1];
int dataReadFromChildDefaultSize = bufferSize * 5;
int dataReadFromChildSize = dataReadFromChildDefaultSize;
int dataReadFromChildUsed = 0;
char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
int parentToChild[2];
release_assert(pipe(parentToChild) == 0);
int childToParent[2];
release_assert(pipe(childToParent) == 0);
int errPipe[2];
release_assert(pipe(errPipe) == 0);
pid_t pid;
switch( pid = fork() )
{
case -1:
{
release_assert(0 && "Fork failed");
break;
}
case 0: // child
{
release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
if(includeStdErr)
{
release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
}
else
{
int devNull = open("/dev/null", O_WRONLY);
release_assert(dup2(devNull, STDERR_FILENO) != -1);
}
// unused
release_assert(close(parentToChild[WRITE_FD]) == 0);
release_assert(close(childToParent[READ_FD ]) == 0);
release_assert(close(errPipe[READ_FD]) == 0);
const char* command = allArgs[0];
execvp(command, allArgs);
char err = 1;
ssize_t result = write(errPipe[WRITE_FD], &err, 1);
release_assert(result != -1);
close(errPipe[WRITE_FD]);
close(parentToChild[READ_FD]);
close(childToParent[WRITE_FD]);
exit(0);
}
default: // parent
{
// unused
release_assert(close(parentToChild[READ_FD]) == 0);
release_assert(close(childToParent[WRITE_FD]) == 0);
release_assert(close(errPipe[WRITE_FD]) == 0);
while(1)
{
ssize_t bytesRead = 0;
switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
{
case 0: // End-of-File, or non-blocking read.
{
int status = 0;
release_assert(waitpid(pid, &status, 0) == pid);
// done with these now
release_assert(close(parentToChild[WRITE_FD]) == 0);
release_assert(close(childToParent[READ_FD]) == 0);
char errChar = 0;
ssize_t result = read(errPipe[READ_FD], &errChar, 1);
release_assert(result != -1);
close(errPipe[READ_FD]);
if(errChar)
{
free(dataReadFromChild);
return COMMAND_NOT_FOUND;
}
// free any un-needed memory with realloc + add a null terminator for convenience
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
dataReadFromChild[dataReadFromChildUsed] = '\0';
if(stdOut != NULL)
*stdOut = dataReadFromChild;
else
free(dataReadFromChild);
if(stdOutByteCount != NULL)
*stdOutByteCount = dataReadFromChildUsed;
if(returnCode != NULL)
*returnCode = WEXITSTATUS(status);
return COMMAND_RAN_OK;
}
case -1:
{
release_assert(0 && "read() failed");
break;
}
default:
{
if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
{
dataReadFromChildSize += dataReadFromChildDefaultSize;
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
}
memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
dataReadFromChildUsed += bytesRead;
break;
}
}
}
}
}
}
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
{
va_list vl;
va_start(vl, command);
char* currArg = NULL;
int allArgsInitialSize = 16;
int allArgsSize = allArgsInitialSize;
char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
allArgs[0] = command;
int i = 1;
do
{
currArg = va_arg(vl, char*);
allArgs[i] = currArg;
i++;
if(i >= allArgsSize)
{
allArgsSize += allArgsInitialSize;
allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
}
} while(currArg != NULL);
va_end(vl);
int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
free(allArgs);
return retval;
}
#endif //SIMPLE_EXEC_IMPLEMENTATION

View File

@ -0,0 +1,29 @@
#include "nfd.h"
#include <stdio.h>
#include <stdlib.h>
/* this test should compile on all supported platforms */
int main( void )
{
nfdchar_t *outPath = NULL;
nfdresult_t result = NFD_OpenDialog( "png,jpg;pdf", NULL, &outPath );
if ( result == NFD_OKAY )
{
puts("Success!");
puts(outPath);
free(outPath);
}
else if ( result == NFD_CANCEL )
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError() );
}
return 0;
}

View File

@ -0,0 +1,32 @@
#include "nfd.h"
#include <stdio.h>
#include <stdlib.h>
/* this test should compile on all supported platforms */
int main( void )
{
nfdpathset_t pathSet;
nfdresult_t result = NFD_OpenDialogMultiple( "png,jpg;pdf", NULL, &pathSet );
if ( result == NFD_OKAY )
{
size_t i;
for ( i = 0; i < NFD_PathSet_GetCount(&pathSet); ++i )
{
nfdchar_t *path = NFD_PathSet_GetPath(&pathSet, i);
printf("Path %i: %s\n", (int)i, path );
}
NFD_PathSet_Free(&pathSet);
}
else if ( result == NFD_CANCEL )
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError() );
}
return 0;
}

View File

@ -0,0 +1,29 @@
#include "nfd.h"
#include <stdio.h>
#include <stdlib.h>
/* this test should compile on all supported platforms */
int main( void )
{
nfdchar_t *outPath = NULL;
nfdresult_t result = NFD_PickFolder( NULL, &outPath );
if ( result == NFD_OKAY )
{
puts("Success!");
puts(outPath);
free(outPath);
}
else if ( result == NFD_CANCEL )
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError() );
}
return 0;
}

View File

@ -0,0 +1,28 @@
#include "nfd.h"
#include <stdio.h>
#include <stdlib.h>
/* this test should compile on all supported platforms */
int main( void )
{
nfdchar_t *savePath = NULL;
nfdresult_t result = NFD_SaveDialog( "png,jpg;pdf", NULL, &savePath );
if ( result == NFD_OKAY )
{
puts("Success!");
puts(savePath);
free(savePath);
}
else if ( result == NFD_CANCEL )
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError() );
}
return 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
instruments/FM/Square.fui Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
instruments/FM/default.fui Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
instruments/FM/growl.fui Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More