From 436bac83ecffd45a37cce7538b5a9ccfb920cc6f Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Fri, 30 Jun 2023 20:05:58 -0500 Subject: [PATCH] game: Improve OpenGL version detection and make requirement errors more obvious to the user (#2787) --- CMakeLists.txt | 3 + common/CMakeLists.txt | 3 +- common/util/dialogs.cpp | 10 + common/util/dialogs.h | 9 + game/CMakeLists.txt | 1 + game/graphics/gfx_test.cpp | 50 + game/graphics/gfx_test.h | 16 + game/graphics/pipelines/opengl.cpp | 19 + game/main.cpp | 21 + game/system/hid/sdl_util.cpp | 5 + game/system/hid/sdl_util.h | 1 + third-party/libtinyfiledialogs/CMakeLists.txt | 4 + third-party/libtinyfiledialogs/README.md | 307 + third-party/libtinyfiledialogs/files.xml | 12 + .../libtinyfiledialogs/tinyfiledialogs.c | 6072 +++++++++++++++++ .../libtinyfiledialogs/tinyfiledialogs.h | 301 + vendor.yaml | 4 + 17 files changed, 6837 insertions(+), 1 deletion(-) create mode 100644 common/util/dialogs.cpp create mode 100644 common/util/dialogs.h create mode 100644 game/graphics/gfx_test.cpp create mode 100644 game/graphics/gfx_test.h create mode 100644 third-party/libtinyfiledialogs/CMakeLists.txt create mode 100644 third-party/libtinyfiledialogs/README.md create mode 100644 third-party/libtinyfiledialogs/files.xml create mode 100644 third-party/libtinyfiledialogs/tinyfiledialogs.c create mode 100644 third-party/libtinyfiledialogs/tinyfiledialogs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a76f803535..43900c3f843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,9 @@ include_directories(third-party/tree-sitter/tree-sitter/lib/include) include_directories(third-party/tree-sitter/tree-sitter-opengoal/include) add_subdirectory(third-party/tree-sitter EXCLUDE_FROM_ALL) +# native OS dialogs for error messages +add_subdirectory(third-party/libtinyfiledialogs) + string(REPLACE " ${THIRDPARTY_IGNORED_WARNINGS} " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # build common library diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fc2e2085341..4bf56064e56 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -70,6 +70,7 @@ add_library(common util/dgo_util.cpp util/DgoReader.cpp util/DgoWriter.cpp + util/dialogs.cpp util/diff.cpp util/FileUtil.cpp util/FontUtils.cpp @@ -85,7 +86,7 @@ add_library(common util/unicode_util.cpp versions/versions.cpp) -target_link_libraries(common fmt lzokay replxx libzstd_static tree-sitter sqlite3) +target_link_libraries(common fmt lzokay replxx libzstd_static tree-sitter sqlite3 libtinyfiledialogs) if(WIN32) target_link_libraries(common wsock32 ws2_32 windowsapp) diff --git a/common/util/dialogs.cpp b/common/util/dialogs.cpp new file mode 100644 index 00000000000..fd4861b2c09 --- /dev/null +++ b/common/util/dialogs.cpp @@ -0,0 +1,10 @@ +#include "dialogs.h" + +namespace dialogs { +// char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ +// char const * const aIconType , /* "info" "warning" "error" "question" */ +// int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +void create_error_message_dialog(const std::string& title, const std::string& message) { + tinyfd_messageBox(title.data(), message.data(), "ok", "error", 1); +} +} // namespace dialogs diff --git a/common/util/dialogs.h b/common/util/dialogs.h new file mode 100644 index 00000000000..ff8708045d2 --- /dev/null +++ b/common/util/dialogs.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "third-party/libtinyfiledialogs/tinyfiledialogs.h" + +namespace dialogs { +void create_error_message_dialog(const std::string& title, const std::string& message); +} diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index ab48c6c1ce7..aa55d6100c5 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -11,6 +11,7 @@ set(RUNTIME_SOURCE external/discord_jak2.cpp graphics/display.cpp graphics/gfx.cpp + graphics/gfx_test.cpp graphics/jak2_texture_remap.cpp graphics/opengl_renderer/background/background_common.cpp graphics/opengl_renderer/background/Shrub.cpp diff --git a/game/graphics/gfx_test.cpp b/game/graphics/gfx_test.cpp new file mode 100644 index 00000000000..5b5be7c2bac --- /dev/null +++ b/game/graphics/gfx_test.cpp @@ -0,0 +1,50 @@ +#include "gfx_test.h" + +#include "game/system/hid/sdl_util.h" + +namespace tests { +void to_json(json& j, const GPUTestOutput& obj) { + j = json{ + {"success", obj.success}, + {"error", obj.error}, + {"errorCause", obj.errorCause}, + }; +} + +GPUTestOutput run_gpu_test(const std::string& test_type) { + lg::info("Running GPU Test - {}", test_type); + GPUTestOutput output = {false, "", ""}; + if (test_type == "opengl") { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + output = {false, "SDL initialization failed", + sdl_util::log_and_return_error("SDL initialization failed")}; + return output; + } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_Window* window = + SDL_CreateWindow("OpenGL Version", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, + 600, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (!window) { + output = {false, "SDL window creation failed", + sdl_util::log_and_return_error("SDL initialization failed")}; + SDL_Quit(); + return output; + } + SDL_GLContext glContext = SDL_GL_CreateContext(window); + if (!glContext) { + output = {false, "Required OpenGL Version is not supported", + sdl_util::log_and_return_error("SDL initialization failed")}; + } else { + output = {true, "", ""}; + SDL_GL_DeleteContext(glContext); + } + SDL_DestroyWindow(window); + SDL_Quit(); + } else { + lg::error("Invalid GPU test type - {}", test_type); + } + return output; +} +}; // namespace tests diff --git a/game/graphics/gfx_test.h b/game/graphics/gfx_test.h new file mode 100644 index 00000000000..6fbc6bc0d08 --- /dev/null +++ b/game/graphics/gfx_test.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "common/util/json_util.h" + +namespace tests { +struct GPUTestOutput { + bool success; + std::string error; + std::string errorCause; +}; +void to_json(json& j, const GPUTestOutput& obj); + +GPUTestOutput run_gpu_test(const std::string& test_type); +} // namespace tests diff --git a/game/graphics/pipelines/opengl.cpp b/game/graphics/pipelines/opengl.cpp index 079c31b0486..455f161134f 100644 --- a/game/graphics/pipelines/opengl.cpp +++ b/game/graphics/pipelines/opengl.cpp @@ -36,6 +36,7 @@ #include "third-party/imgui/imgui_impl_sdl.h" #include "third-party/imgui/imgui_style.h" #define STBI_WINDOWS_UTF8 +#include "common/util/dialogs.h" #include "common/util/string_util.h" #include "third-party/stb_image/stb_image.h" @@ -99,6 +100,8 @@ static int gl_init(GfxGlobalSettings& settings) { SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { sdl_util::log_error("Could not initialize SDL, exiting"); + dialogs::create_error_message_dialog("Critical Error Encountered", + "Could not initialize SDL, exiting"); return 1; } } @@ -216,6 +219,10 @@ static std::shared_ptr gl_make_display(int width, prof().end_event(); if (!window) { sdl_util::log_error("gl_make_display failed - Could not create display window"); + dialogs::create_error_message_dialog( + "Critical Error Encountered", + "Unable to create OpenGL window.\nOpenGOAL requires OpenGL 4.3.\nEnsure your GPU " + "supports this and your drivers are up to date."); return NULL; } @@ -225,6 +232,10 @@ static std::shared_ptr gl_make_display(int width, prof().end_event(); if (!gl_context) { sdl_util::log_error("gl_make_display failed - Could not create OpenGL Context"); + dialogs::create_error_message_dialog( + "Critical Error Encountered", + "Unable to create OpenGL context.\nOpenGOAL requires OpenGL 4.3.\nEnsure your GPU " + "supports this and your drivers are up to date."); return NULL; } @@ -232,6 +243,10 @@ static std::shared_ptr gl_make_display(int width, auto p = scoped_prof("startup::sdl::assign_context"); if (SDL_GL_MakeCurrent(window, gl_context) != 0) { sdl_util::log_error("gl_make_display failed - Could not associated context with window"); + dialogs::create_error_message_dialog("Critical Error Encountered", + "Unable to create OpenGL window with context.\nOpenGOAL " + "requires OpenGL 4.3.\nEnsure your GPU " + "supports this and your drivers are up to date."); return NULL; } } @@ -242,6 +257,10 @@ static std::shared_ptr gl_make_display(int width, gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress); if (!gladLoadGL()) { lg::error("GL init fail"); + dialogs::create_error_message_dialog("Critical Error Encountered", + "Unable to initialize OpenGL API.\nOpenGOAL requires " + "OpenGL 4.3.\nEnsure your GPU " + "supports this and your drivers are up to date."); return NULL; } } diff --git a/game/main.cpp b/game/main.cpp index ba4de755021..d32af6bbfea 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -12,11 +12,13 @@ #include "common/global_profiler/GlobalProfiler.h" #include "common/log/log.h" #include "common/util/FileUtil.h" +#include "common/util/dialogs.h" #include "common/util/os.h" #include "common/util/unicode_util.h" #include "common/versions/versions.h" #include "game/common/game_common_types.h" +#include "graphics/gfx_test.h" #include "third-party/CLI11.hpp" @@ -90,6 +92,8 @@ int main(int argc, char** argv) { bool disable_display = false; bool enable_debug_vm = false; bool enable_profiling = false; + std::string gpu_test = ""; + std::string gpu_test_out_path = ""; int port_number = -1; fs::path project_path_override; std::vector game_args; @@ -104,6 +108,10 @@ int main(int argc, char** argv) { app.add_flag("--no-display", disable_display, "Disable video display"); app.add_flag("--vm", enable_debug_vm, "Enable debug PS2 VM (defaulted to off)"); app.add_flag("--profile", enable_profiling, "Enables profiling immediately from startup"); + app.add_option("--gpu-test", gpu_test, + "Tests for minimum graphics requirements. Valid Options are: [opengl]"); + app.add_option("--gpu-test-out-path", gpu_test_out_path, + "Where to store the gpu test result file"); app.add_option("--proj-path", project_path_override, "Specify the location of the 'data/' folder"); app.footer(game_arg_documentation()); @@ -117,6 +125,17 @@ int main(int argc, char** argv) { return 0; } + if (!gpu_test.empty() && !gpu_test_out_path.empty()) { + const auto output = tests::run_gpu_test(gpu_test); + json data = output; + try { + file_util::write_text_file(gpu_test_out_path, data.dump(2)); + } catch (std::exception& e) { + return 1; + } + return 0; + } + prof().set_enable(enable_profiling); // Create struct with all non-kmachine handled args to pass to the runtime @@ -132,6 +151,8 @@ int main(int argc, char** argv) { // If the CPU doesn't have AVX, GOAL code won't work and we exit. if (!get_cpu_info().has_avx) { lg::info("Your CPU does not support AVX, which is required for OpenGOAL."); + dialogs::create_error_message_dialog( + "Unmet Requirements", "Your CPU does not support AVX, which is required for OpenGOAL."); return -1; } diff --git a/game/system/hid/sdl_util.cpp b/game/system/hid/sdl_util.cpp index 2b82a595522..11130ae5c67 100644 --- a/game/system/hid/sdl_util.cpp +++ b/game/system/hid/sdl_util.cpp @@ -9,6 +9,11 @@ void log_error(const std::string& msg) { std::string sdl_cause = SDL_GetError(); lg::error("SDL Error: {} - Cause: {}", msg, sdl_cause.empty() ? "n/a" : sdl_cause); } +std::string log_and_return_error(const std::string& msg) { + std::string sdl_cause = SDL_GetError(); + lg::error("SDL Error: {} - Cause: {}", msg, sdl_cause.empty() ? "n/a" : sdl_cause); + return sdl_cause; +} bool is_any_event_type(uint32_t event_type, const std::vector& allowed_types) { for (const auto& allowed_type : allowed_types) { if (allowed_type == event_type) { diff --git a/game/system/hid/sdl_util.h b/game/system/hid/sdl_util.h index bbda8882601..84dabba7301 100644 --- a/game/system/hid/sdl_util.h +++ b/game/system/hid/sdl_util.h @@ -9,6 +9,7 @@ namespace sdl_util { void log_error(const std::string& msg = ""); +std::string log_and_return_error(const std::string& msg = ""); bool is_any_event_type(uint32_t event_type, const std::vector& allowed_types); SDL_bool sdl_bool(const bool val); bool from_sdl_bool(const SDL_bool val); diff --git a/third-party/libtinyfiledialogs/CMakeLists.txt b/third-party/libtinyfiledialogs/CMakeLists.txt new file mode 100644 index 00000000000..b0b2bf89f1b --- /dev/null +++ b/third-party/libtinyfiledialogs/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(libtinyfiledialogs + tinyfiledialogs.c + tinyfiledialogs.h +) diff --git a/third-party/libtinyfiledialogs/README.md b/third-party/libtinyfiledialogs/README.md new file mode 100644 index 00000000000..0387e59aed7 --- /dev/null +++ b/third-party/libtinyfiledialogs/README.md @@ -0,0 +1,307 @@ +## Official version hosted at https://sourceforge.net/projects/tinyfiledialogs/ + +This version is _not_ official and is currently updated as required by https://github.com/haxelime/lime only + +# tinyfiledialogs + +
+ _________
+/         \   tiny file dialogs ( cross-platform C C++ )
+|tiny file|   v2.9.3 [July 12, 2017] zlib licence
+| dialogs |   InputBox PasswordBox MessageBox ColorPicker
+\____  ___/   OpenFileDialog SaveFileDialog SelectFolderDialog		
+     \|       ASCII UTF-8 (and also MBCS UTF-16 for windows)
+              Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE
+  
+SSH supported via automatic switch to console mode or X11 forwarding
+
+tested with C & C++ compilers on 
+    Visual Studio MinGW OSX LINUX FREEBSD OPENBSD ILLUMOS SOLARIS MINIX RASPBIAN
+using
+    Gnome Kde Mate Cinnamon Unity Lxde Lxqt Xfce Enlightenment
+    WindowMaker IceWm Cde Jds OpenBox Awesome Jwm
+
+bindings for LUA and C# dll
+included in LWJGL(java), rust, Allegrobasic
+
+                   http://tinyfiledialogs.sourceforge.net
+                git://git.code.sf.net/p/tinyfiledialogs/code
+ _________________________________________________________________________
+|                                                                         |
+| CONTACT me directly via the email address at the top of the header file |
+|_________________________________________________________________________|
+
+if you absolutely want them, the windows only wchar_t prototypes are in the header file
+
+int tinyfd_messageBox (
+    char const * const aTitle , // ""
+    char const * const aMessage , // "" may contain \n \t
+    char const * const aDialogType , // "ok" "okcancel" "yesno" "yesnocancel"
+    char const * const aIconType , // "info" "warning" "error" "question"
+    int const aDefaultButton ) ;
+        // 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel
+
+char const * tinyfd_inputBox (
+    char const * const aTitle , // ""
+    char const * const aMessage , // "" may NOT contain \n \t on windows
+    char const * const aDefaultInput ) ; // "" , if NULL it's a passwordBox
+        // returns NULL on cancel
+
+char const * tinyfd_saveFileDialog (
+    char const * const aTitle , // ""
+    char const * const aDefaultPathAndFile , // ""
+    int const aNumOfFilterPatterns , // 0
+    char const * const * const aFilterPatterns , // NULL | {"*.txt"}
+    char const * const aSingleFilterDescription ) ; // NULL | "text files"
+        // returns NULL on cancel
+
+char const * tinyfd_openFileDialog (
+    char const * const aTitle , // ""
+    char const * const aDefaultPathAndFile , // ""
+    int const aNumOfFilterPatterns , // 0
+    char const * const * const aFilterPatterns , // NULL {"*.jpg","*.png"}
+    char const * const aSingleFilterDescription , // NULL | "image files"
+    int const aAllowMultipleSelects ) ; // 0
+        // in case of multiple files, the separator is |
+        // returns NULL on cancel
+
+char const * tinyfd_selectFolderDialog (
+    char const * const aTitle , // ""
+    char const * const aDefaultPath ) ; // ""
+        // returns NULL on cancel
+
+char const * tinyfd_colorChooser(
+    char const * const aTitle , // ""
+    char const * const aDefaultHexRGB , // NULL or "#FF0000”
+    unsigned char const aDefaultRGB[3] , // { 0 , 255 , 255 }
+    unsigned char aoResultRGB[3] ) ; // { 0 , 0 , 0 }
+        // returns the hexcolor as a string "#FF0000"
+        // aoResultRGB also contains the result
+        // aDefaultRGB is used only if aDefaultHexRGB is NULL
+        // aDefaultRGB and aoResultRGB can be the same array
+        // returns NULL on cancel
+
+- This is not for android nor ios.
+- The code is pure C, perfectly compatible with C++.
+- the windows only wchar_t (utf-16) prototypes are in the header file
+- windows is fully supported from XP to 10 (maybe even older versions)
+- C# & LUA via dll, see files in the folder EXTRAS 
+- OSX supported from 10.4 to 10.12 (maybe even older versions)
+- Avoid using " and ' in titles and messages.
+- There's one file filter only, it may contain several patterns.
+- If no filter description is provided,
+  the list of patterns will become the description.
+- char const * filterPatterns[3] = { "*.obj" , "*.stl" , "*.dxf" } ;
+- On windows link against Comdlg32.lib and Ole32.lib
+  (on windows the no linking claim is a lie)
+  This linking is not compulsary for console mode (see header file).
+- On unix: it tries command line calls, so no such need (NO LINKING).
+- On unix you need applescript, zenity, matedialog, qarma, kdialog, Xdialog,
+  python2/tkinter or dialog (will open a terminal if running without console).
+- One of those is already included on most (if not all) desktops.
+- In the absence of those it will use gdialog, gxmessage or whiptail
+  with a textinputbox.
+- If nothing is found, it switches to basic console input,
+  it opens a console if needed (requires xterm + bash).
+- Use windows separators on windows and unix separators on unix.
+- String memory is preallocated statically for all the returned values.
+- File and path names are tested before return, they are valid.
+- If you pass only a path instead of path + filename,
+  make sure it ends with a separator.
+- tinyfd_forceConsole=1; at run time, forces dialogs into console mode.
+- On windows, console mode only make sense for console applications.
+- Mutiple selects are not allowed in console mode.
+- The package dialog must be installed to run in enhanced console mode.
+  It is already installed on most unix systems.
+- On osx, the package dialog can be installed via http://macports.org
+- On windows, for enhanced console mode,
+  dialog.exe should be copied somewhere on your executable path.
+  It can be found at the bottom of the following page:
+  http://andrear.altervista.org/home/cdialog.php
+- If dialog is missing, it will switch to basic console input.
+- You can query the type of dialog that will be use.
+- MinGW needs gcc >= v4.9 otherwise some headers are incomplete.
+
+- Here is the Hello World:
+            if a console is missing, it will use graphic dialogs
+            if a graphical display is absent, it will use console dialogs
+
+
+hello.c
+#include 
+#include 
+#include "tinyfiledialogs.h"
+int main()
+{
+	char const * lTmp;
+	char const * lTheSaveFileName;
+	char const * lTheOpenFileName;
+	char const * lTheSelectFolderName;
+	char const * lTheHexColor;
+	char const * lWillBeGraphicMode;
+	unsigned char lRgbColor[3];
+	FILE * lIn;
+	char lBuffer[1024];
+	char lThePassword[1024];
+	char const * lFilterPatterns[2] = { "*.txt", "*.text" };
+
+	lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL);
+
+	if (lWillBeGraphicMode)
+	{
+		strcpy(lBuffer, "graphic mode: ");
+	}
+	else
+	{
+		strcpy(lBuffer, "console mode: ");
+	}
+
+	strcat(lBuffer, tinyfd_response);
+	strcpy(lThePassword, "tinyfiledialogs v");
+	strcat(lThePassword, tinyfd_version);
+	tinyfd_messageBox(lThePassword, lBuffer, "ok", "info", 0);
+
+	if ( lWillBeGraphicMode && ! tinyfd_forceConsole )
+	{
+		tinyfd_forceConsole = ! tinyfd_messageBox("Hello World",
+			"graphic dialogs [yes] / console mode [no]?",
+			"yesno", "question", 1);
+	}
+
+	lTmp = tinyfd_inputBox(
+		"a password box", "your password will be revealed", NULL);
+
+	if (!lTmp) return 1 ;
+
+	/* copy lTmp because saveDialog would overwrites
+	inputBox static buffer in basicinput mode */
+
+	strcpy(lThePassword, lTmp);
+
+	lTheSaveFileName = tinyfd_saveFileDialog(
+		"let us save this password",
+		"passwordFile.txt",
+		2,
+		lFilterPatterns,
+		NULL);
+
+	if (! lTheSaveFileName)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"Save file name is NULL",
+			"ok",
+			"error",
+			1);
+		return 1 ;
+	}
+
+	lIn = fopen(lTheSaveFileName, "w");
+	if (!lIn)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"Can not open this file in write mode",
+			"ok",
+			"error",
+			1);
+		return 1 ;
+	}
+	fputs(lThePassword, lIn);
+	fclose(lIn);
+
+	lTheOpenFileName = tinyfd_openFileDialog(
+		"let us read the password back",
+		"",
+		2,
+		lFilterPatterns,
+		NULL,
+		0);
+
+	if (! lTheOpenFileName)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"Open file name is NULL",
+			"ok",
+			"error",
+			1);
+		return 1 ;
+	}
+
+	lIn = fopen(lTheOpenFileName, "r");
+
+	if (!lIn)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"Can not open this file in read mode",
+			"ok",
+			"error",
+			1);
+		return(1);
+	}
+	lBuffer[0] = '\0';
+	fgets(lBuffer, sizeof(lBuffer), lIn);
+	fclose(lIn);
+
+	tinyfd_messageBox("your password is",
+			lBuffer, "ok", "info", 1);
+
+	lTheSelectFolderName = tinyfd_selectFolderDialog(
+		"let us just select a directory", NULL);
+
+	if (!lTheSelectFolderName)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"Select folder name is NULL",
+			"ok",
+			"error",
+			1);
+		return 1;
+	}
+
+	tinyfd_messageBox("The selected folder is",
+		lTheSelectFolderName, "ok", "info", 1);
+
+	lTheHexColor = tinyfd_colorChooser(
+		"choose a nice color",
+		"#FF0077",
+		lRgbColor,
+		lRgbColor);
+
+	if (!lTheHexColor)
+	{
+		tinyfd_messageBox(
+			"Error",
+			"hexcolor is NULL",
+			"ok",
+			"error",
+			1);
+		return 1;
+	}
+
+	tinyfd_messageBox("The selected hexcolor is",
+		lTheHexColor, "ok", "info", 1);
+
+	return 0;
+}
+
+
+OSX :
+$ gcc -o hello.app hello.c tinyfiledialogs.c
+
+UNIX :
+$ gcc -o hello hello.c tinyfiledialogs.c
+( or clang tcc cc CC )
+
+MinGW (needs gcc >= v4.9 otherwise some headers are incomplete):
+> gcc -o hello.exe hello.c tinyfiledialogs.c -LC:/mingw/lib -lcomdlg32 -lole32
+(unfortunately some headers are missing with tcc)
+
+VisualStudio :
+    Create a console application project,
+    it links against Comdlg32.lib & Ole32.lib.
+
+
diff --git a/third-party/libtinyfiledialogs/files.xml b/third-party/libtinyfiledialogs/files.xml new file mode 100644 index 00000000000..6a79cc8728c --- /dev/null +++ b/third-party/libtinyfiledialogs/files.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/third-party/libtinyfiledialogs/tinyfiledialogs.c b/third-party/libtinyfiledialogs/tinyfiledialogs.c new file mode 100644 index 00000000000..e34f431128b --- /dev/null +++ b/third-party/libtinyfiledialogs/tinyfiledialogs.c @@ -0,0 +1,6072 @@ +/*_________ + / \ tinyfiledialogs.c v2.9.3 [July 12, 2017] zlib licence + |tiny file| Unique code file created [November 9, 2014] + | dialogs | Copyright (c) 2014 - 2017 Guillaume Vareille http://ysengrin.com + \____ ___/ http://tinyfiledialogs.sourceforge.net + \| + git://git.code.sf.net/p/tinyfiledialogs/code + ______________________________________________ + | | + | email: tinyfiledialogs@ysengrin.com | + |______________________________________________| + +A big thank you to Don Heyse http://ldglite.sf.net for + his code contributions, bug corrections & thorough testing! + +Please + 1) let me know + - if you are including tiny file dialogs, + I'll be happy to add your link to the list of projects using it. + - If you are using it on different hardware / OS / compiler. + 2) leave a review on Sourceforge. Thanks. + +tiny file dialogs (cross-platform C C++) +InputBox PasswordBox MessageBox ColorPicker +OpenFileDialog SaveFileDialog SelectFolderDialog +Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more +SSH supported via automatic switch to console mode or X11 forwarding + +One C file (add it to your C or C++ project) with 6 functions: +- message & question +- input & password +- save file +- open file(s) +- select folder +- color picker + +Complements OpenGL GLFW GLUT GLUI VTK SFML TGUI SDL Ogre Unity3d ION OpenCV +CEGUI MathGL GLM CPW GLOW IMGUI MyGUI GLT NGL STB & GUI less programs + +NO INIT +NO MAIN LOOP +NO LINKING +NO INCLUDE + +The dialogs can be forced into console mode + +Windows (XP to 10) ASCII MBCS UTF-8 UTF-16 +- native code & vbs create the graphic dialogs +- enhanced console mode can use dialog.exe from +http://andrear.altervista.org/home/cdialog.php +- basic console input + +Unix (command line calls) ASCII UTF-8 +- applescript +- zenity / matedialog / qarma (zenity for qt) +- kdialog +- Xdialog +- python2 tkinter +- dialog (opens a console if needed) +- basic console input +The same executable can run across desktops & distributions + +tested with C & C++ compilers +on VisualStudio MinGW Mac Linux Bsd Solaris Minix Raspbian +using Gnome Kde Enlightenment Mate Cinnamon Unity +Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox Awesome Jwm + +bindings for LUA and C# dll +included in LWJGL(java), rust, Allegrobasic + +- License - + +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. +*/ + +#include +#include +#include +#include +#include + +#include "tinyfiledialogs.h" +/* #define TINYFD_NOLIB */ + +#ifdef _WIN32 + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 + #endif + #ifndef TINYFD_NOLIB + #include + /*#define TINYFD_NOSELECTFOLDERWIN*/ + #ifndef TINYFD_NOSELECTFOLDERWIN + #include + #endif /*TINYFD_NOSELECTFOLDERWIN*/ + #endif + #include + /*#include */ + #define SLASH "\\" + int tinyfd_winUtf8 = 0 ; /* on windows string char can be 0:MBSC or 1:UTF-8 */ +#else + #include + #include + #include /* on old systems try instead */ + #include + #include + #define SLASH "/" +#endif /* _WIN32 */ + +#define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */ +#define MAX_MULTIPLE_FILES 32 + +char tinyfd_version [8] = "2.9.3"; + +static int tinyfd_verbose = 0 ; /* print on unix the command line calls */ + +#if defined(TINYFD_NOLIB) && defined(_WIN32) +int tinyfd_forceConsole = 1 ; +#else +int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */ +#endif +/* for unix & windows: 0 (graphic mode) or 1 (console mode). +0: try to use a graphic solution, if it fails then it uses console mode. +1: forces all dialogs into console mode even when the X server is present, + if the package dialog (and a console is present) or dialog.exe is installed. + on windows it only make sense for console applications */ + +char tinyfd_response[1024]; +/* if you pass "tinyfd_query" as aTitle, +the functions will not display the dialogs +but and return 0 for console mode, 1 for graphic mode. +tinyfd_response is then filled with the retain solution. +possible values for tinyfd_response are (all lowercase) +for the graphic mode: + windows applescript zenity zenity3 matedialog qarma kdialog + tkinter gxmessage gmessage xmessage xdialog gdialog +for the console mode: + dialog whiptail basicinput */ + +#if defined(TINYFD_NOLIB) && defined(_WIN32) +static int gWarningDisplayed = 1 ; +#else +static int gWarningDisplayed = 0 ; +#endif + +static char gTitle[]="missing software! (we will try basic console input)"; + +#ifdef _WIN32 +static char gMessageWin[] = "\ + ___________\n\ +/ \\ \n\ +| tiny file |\n\ +| dialogs |\n\ +\\_____ ____/\n\ + \\|\ +tiny file dialogs on Windows needs:\ +\n\ta graphic display\ +\nor\tdialog.exe (enhanced console mode)\ +\nor\ta console for basic input"; +#else +static char gMessageUnix[] = "\ + ___________\n\ +/ \\ \n\ +| tiny file |\n\ +| dialogs |\n\ +\\_____ ____/\n\ + \\|\ +\ntiny file dialogs on UNIX needs:\n\tapplescript\ +\nor\tzenity / matedialog\ +\nor\tqarma (zenity for qt)\ +\nor\tkdialog\ +\nor\tXdialog\ +\nor\tpython 2 + tkinter\ +\nor\tdialog (opens a console xterm if needed)\ +\nor\txterm + bash (opens a console for basic input)\ +\nor\tit will use the existing console for basic input"; +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4996) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ +#pragma warning(disable:4100) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ +#pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ +#endif + +static char * getPathWithoutFinalSlash( + char * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ + char const * const aSource) /* aoDestination and aSource can be the same */ +{ + char const * lTmp ; + if ( aSource ) + { + lTmp = strrchr(aSource, '/'); + if (!lTmp) + { + lTmp = strrchr(aSource, '\\'); + } + if (lTmp) + { + strncpy(aoDestination, aSource, lTmp - aSource ); + aoDestination[lTmp - aSource] = '\0'; + } + else + { + * aoDestination = '\0'; + } + } + else + { + * aoDestination = '\0'; + } + return aoDestination; +} + + +static char * getLastName( + char * const aoDestination, /* make sure it is allocated */ + char const * const aSource) +{ + /* copy the last name after '/' or '\' */ + char const * lTmp ; + if ( aSource ) + { + lTmp = strrchr(aSource, '/'); + if (!lTmp) + { + lTmp = strrchr(aSource, '\\'); + } + if (lTmp) + { + strcpy(aoDestination, lTmp + 1); + } + else + { + strcpy(aoDestination, aSource); + } + } + else + { + * aoDestination = '\0'; + } + return aoDestination; +} + + +static void ensureFinalSlash ( char * const aioString ) +{ + if ( aioString && strlen ( aioString ) ) + { + char * lastcar = aioString + strlen ( aioString ) - 1 ; + if ( strncmp ( lastcar , SLASH , 1 ) ) + { + strcat ( lastcar , SLASH ) ; + } + } +} + + +static void Hex2RGB( char const aHexRGB [8] , + unsigned char aoResultRGB [3] ) +{ + char lColorChannel [8] ; + if ( aoResultRGB ) + { + if ( aHexRGB ) + { + strcpy(lColorChannel, aHexRGB ) ; + aoResultRGB[2] = (unsigned char)strtoul(lColorChannel+5,NULL,16); + lColorChannel[5] = '\0'; + aoResultRGB[1] = (unsigned char)strtoul(lColorChannel+3,NULL,16); + lColorChannel[3] = '\0'; + aoResultRGB[0] = (unsigned char)strtoul(lColorChannel+1,NULL,16); +/* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ + } + else + { + aoResultRGB[0]=0; + aoResultRGB[1]=0; + aoResultRGB[2]=0; + } + } +} + +static void RGB2Hex( unsigned char const aRGB [3] , + char aoResultHexRGB [8] ) +{ + if ( aoResultHexRGB ) + { + if ( aRGB ) + { +#if defined(__GNUC__) && defined(_WIN32) + sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", +#else + sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", +#endif + aRGB[0], aRGB[1], aRGB[2]); + /* printf("aoResultHexRGB %s\n", aoResultHexRGB); */ + } + else + { + aoResultHexRGB[0]=0; + aoResultHexRGB[1]=0; + aoResultHexRGB[2]=0; + } + } +} + + +static void replaceSubStr ( char const * const aSource , + char const * const aOldSubStr , + char const * const aNewSubStr , + char * const aoDestination ) +{ + char const * pOccurence ; + char const * p ; + char const * lNewSubStr = "" ; + int lOldSubLen = strlen ( aOldSubStr ) ; + + if ( ! aSource ) + { + * aoDestination = '\0' ; + return ; + } + if ( ! aOldSubStr ) + { + strcpy ( aoDestination , aSource ) ; + return ; + } + if ( aNewSubStr ) + { + lNewSubStr = aNewSubStr ; + } + p = aSource ; + * aoDestination = '\0' ; + while ( ( pOccurence = strstr ( p , aOldSubStr ) ) != NULL ) + { + strncat ( aoDestination , p , pOccurence - p ) ; + strcat ( aoDestination , lNewSubStr ) ; + p = pOccurence + lOldSubLen ; + } + strcat ( aoDestination , p ) ; +} + + +static int filenameValid( char const * const aFileNameWithoutPath ) +{ + if ( ! aFileNameWithoutPath + || ! strlen(aFileNameWithoutPath) + || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) + { + return 0 ; + } + return 1 ; +} + + +static int fileExists( char const * const aFilePathAndName ) +{ + FILE * lIn ; + if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) + { + return 0 ; + } + lIn = fopen( aFilePathAndName , "r" ) ; + if ( ! lIn ) + { + return 0 ; + } + fclose ( lIn ) ; + return 1 ; +} + + +/* source and destination can be the same or ovelap*/ +static char const * ensureFilesExist( char * const aDestination , + char const * const aSourcePathsAndNames) +{ + char * lDestination = aDestination ; + char const * p ; + char const * p2 ; + int lLen ; + + if ( ! aSourcePathsAndNames ) + { + return NULL ; + } + lLen = strlen( aSourcePathsAndNames ) ; + if ( ! lLen ) + { + return NULL ; + } + + p = aSourcePathsAndNames ; + while ( (p2 = strchr(p, '|')) != NULL ) + { + lLen = p2-p ; + memmove(lDestination,p,lLen); + lDestination[lLen] = '\0'; + if ( fileExists ( lDestination ) ) + { + lDestination += lLen ; + * lDestination = '|'; + lDestination ++ ; + } + p = p2 + 1 ; + } + if ( fileExists ( p ) ) + { + lLen = strlen(p) ; + memmove(lDestination,p,lLen); + lDestination[lLen] = '\0'; + } + else + { + * (lDestination-1) = '\0'; + } + return aDestination ; +} + + +static void wipefile(char const * const aFilename) +{ + int i; + struct stat st; + FILE * lIn; + + if (stat(aFilename, &st) == 0) + { + if ((lIn = fopen(aFilename, "w"))) + { + for (i = 0; i < st.st_size; i++) + { + fputc('A', lIn); + } + } + fclose(lIn); + } +} + +#ifdef _WIN32 + +static int replaceChr ( char * const aString , + char const aOldChr , + char const aNewChr ) +{ + char * p ; + int lRes = 0 ; + + if ( ! aString ) + { + return 0 ; + } + + if ( aOldChr == aNewChr ) + { + return 0 ; + } + + p = aString ; + while ( (p = strchr ( p , aOldChr )) ) + { + * p = aNewChr ; + p ++ ; + lRes = 1 ; + } + return lRes ; +} + + +static int dirExists ( char const * const aDirPath ) +{ + struct stat lInfo; + if ( ! aDirPath || ! strlen ( aDirPath ) ) + return 0 ; + if ( stat ( aDirPath , & lInfo ) != 0 ) + return 0 ; + else if ( lInfo.st_mode & S_IFDIR ) + return 1 ; + else + return 0 ; +} + +#ifndef TINYFD_NOLIB + +static wchar_t * getPathWithoutFinalSlashW( + wchar_t * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ + wchar_t const * const aSource) /* aoDestination and aSource can be the same */ +{ + wchar_t const * lTmp; + if (aSource) + { + lTmp = wcsrchr(aSource, L'/'); + if (!lTmp) + { + lTmp = wcsrchr(aSource, L'\\'); + } + if (lTmp) + { + wcsncpy(aoDestination, aSource, lTmp - aSource); + aoDestination[lTmp - aSource] = L'\0'; + } + else + { + *aoDestination = L'\0'; + } + } + else + { + *aoDestination = L'\0'; + } + return aoDestination; +} + + +static wchar_t * getLastNameW( + wchar_t * const aoDestination, /* make sure it is allocated */ + wchar_t const * const aSource) +{ + /* copy the last name after '/' or '\' */ + wchar_t const * lTmp; + if (aSource) + { + lTmp = wcsrchr(aSource, L'/'); + if (!lTmp) + { + lTmp = wcsrchr(aSource, L'\\'); + } + if (lTmp) + { + wcscpy(aoDestination, lTmp + 1); + } + else + { + wcscpy(aoDestination, aSource); + } + } + else + { + *aoDestination = L'\0'; + } + return aoDestination; +} + + +static void Hex2RGBW(wchar_t const aHexRGB[8], + unsigned char aoResultRGB[3]) +{ + wchar_t lColorChannel[8]; + if (aoResultRGB) + { + if (aHexRGB) + { + wcscpy(lColorChannel, aHexRGB); + aoResultRGB[2] = (unsigned char)wcstoul(lColorChannel + 5, NULL, 16); + lColorChannel[5] = '\0'; + aoResultRGB[1] = (unsigned char)wcstoul(lColorChannel + 3, NULL, 16); + lColorChannel[3] = '\0'; + aoResultRGB[0] = (unsigned char)wcstoul(lColorChannel + 1, NULL, 16); + /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ + } + else + { + aoResultRGB[0] = 0; + aoResultRGB[1] = 0; + aoResultRGB[2] = 0; + } + } +} + + +static void RGB2HexW( + unsigned char const aRGB[3], + wchar_t aoResultHexRGB[8]) +{ + if (aoResultHexRGB) + { + if (aRGB) + { +#if defined(__GNUC__) && __GNUC__ < 5 +swprintf(aoResultHexRGB, L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); +#else +swprintf(aoResultHexRGB, 8, L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); +#endif + /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */ + } + else + { + aoResultHexRGB[0] = 0; + aoResultHexRGB[1] = 0; + aoResultHexRGB[2] = 0; + } + } +} + + +#if !defined(WC_ERR_INVALID_CHARS) +/* undefined prior to Vista, so not yet in MINGW header file */ +#define WC_ERR_INVALID_CHARS 0x00000080 +#endif + + +static int sizeUtf16(char const * const aUtf8string) +{ + return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, NULL, 0); +} + + +static int sizeUtf8(wchar_t const * const aUtf16string) +{ + return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + aUtf16string, -1, NULL, 0, NULL, NULL); +} + + +static wchar_t * utf8to16(char const * const aUtf8string) +{ + wchar_t * lUtf16string ; + int lSize = sizeUtf16(aUtf8string); + lUtf16string = (wchar_t *) malloc( lSize * sizeof(wchar_t) ); + lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, lUtf16string, lSize); + if (lSize == 0) + { + free(lUtf16string); + return NULL; + } + return lUtf16string; +} + + +static char * utf16to8(wchar_t const * const aUtf16string) +{ + char * lUtf8string ; + int lSize = sizeUtf8(aUtf16string); + lUtf8string = (char *) malloc( lSize ); + lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + aUtf16string, -1, lUtf8string, lSize, NULL, NULL); + if (lSize == 0) + { + free(lUtf8string); + return NULL; + } + return lUtf8string; +} + + +static void runSilentA(char const * const aString) +{ + STARTUPINFOA StartupInfo; + PROCESS_INFORMATION ProcessInfo; + char * lArgs; + char * pEnvCMD = NULL; + char * pDefaultCMD = "CMD.EXE"; + ULONG rc; + int lStringLen = 0; + + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFOA); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + + if ( aString ) + { + lStringLen = strlen(aString); + } + lArgs = (char *) malloc( MAX_PATH_OR_CMD + lStringLen ); + + pEnvCMD = getenv("COMSPEC"); + + if (pEnvCMD){ + + strcpy(lArgs, pEnvCMD); + } + else{ + strcpy(lArgs, pDefaultCMD); + } + + /* c to execute then terminate the command window */ + strcat(lArgs, " /c "); + + /* application and parameters to run from the command window */ + strcat(lArgs, aString); + + if (!CreateProcessA(NULL, lArgs, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE, NULL, NULL, + &StartupInfo, &ProcessInfo)) + { + free(lArgs); + return; /* GetLastError(); */ + } + + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + if (!GetExitCodeProcess(ProcessInfo.hProcess, &rc)) + rc = 0; + + CloseHandle(ProcessInfo.hThread); + CloseHandle(ProcessInfo.hProcess); + + free(lArgs); + return; /* rc */ +} + + +static void runSilentW(wchar_t const * const aString) +{ + STARTUPINFOW StartupInfo; + PROCESS_INFORMATION ProcessInfo; + ULONG rc; + wchar_t * lArgs; + wchar_t * pEnvCMD; + wchar_t * pDefaultCMD = L"CMD.EXE"; + int lStringLen = 0; + + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFOW); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + + if ( aString ) + { + lStringLen = wcslen(aString); + } + lArgs = (wchar_t *) malloc( (MAX_PATH_OR_CMD + lStringLen) * sizeof(wchar_t) ); + + pEnvCMD = utf8to16( getenv("COMSPEC") ); + if (pEnvCMD) + { + wcscpy(lArgs, pEnvCMD); + free(pEnvCMD); + } + else + { + wcscpy(lArgs, pDefaultCMD); + } + + /* c to execute then terminate the command window */ + wcscat(lArgs, L" /c "); + + /* application and parameters to run from the command window */ + wcscat(lArgs, aString); + + if (!CreateProcessW(NULL, lArgs, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE, NULL, NULL, + &StartupInfo, &ProcessInfo)) + { + free(lArgs); + return; /* GetLastError(); */ + } + + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + if (!GetExitCodeProcess(ProcessInfo.hProcess, &rc)) + { + rc = 0; + } + + CloseHandle(ProcessInfo.hThread); + CloseHandle(ProcessInfo.hProcess); + + free(lArgs); + return; /* rc */ +} + + +int tinyfd_messageBoxW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aMessage, /* NULL or "" may contain \n and \t */ + wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ + wchar_t const * const aIconType, /* "info" "warning" "error" "question" */ + int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + int lBoxReturnValue; + UINT aCode; + + if (aIconType && !wcscmp(L"warning", aIconType)) + { + aCode = MB_ICONWARNING; + } + else if (aIconType && !wcscmp(L"error", aIconType)) + { + aCode = MB_ICONERROR; + } + else if (aIconType && !wcscmp(L"question", aIconType)) + { + aCode = MB_ICONQUESTION; + } + else + { + aCode = MB_ICONINFORMATION; + } + + if (aDialogType && !wcscmp(L"okcancel", aDialogType)) + { + aCode += MB_OKCANCEL; + if (!aDefaultButton) + { + aCode += MB_DEFBUTTON2; + } + } + else if (aDialogType && !wcscmp(L"yesno", aDialogType)) + { + aCode += MB_YESNO; + if (!aDefaultButton) + { + aCode += MB_DEFBUTTON2; + } + } + else + { + aCode += MB_OK; + } + + lBoxReturnValue = MessageBoxW(NULL, aMessage, aTitle, aCode); + if (((aDialogType + && wcscmp(L"okcancel", aDialogType) + && wcscmp(L"yesno", aDialogType))) + || (lBoxReturnValue == IDOK) + || (lBoxReturnValue == IDYES)) + { + return 1; + } + else + { + return 0; + } +} + + +static int messageBoxWinGui8( + char const * const aTitle, /* NULL or "" */ + char const * const aMessage, /* NULL or "" may contain \n and \t */ + char const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType, /* "info" "warning" "error" "question" */ + int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + int lIntRetVal; + wchar_t * lTitle; + wchar_t * lMessage; + wchar_t * lDialogType; + wchar_t * lIconType; + + lTitle = utf8to16(aTitle); + lMessage = utf8to16(aMessage); + lDialogType = utf8to16(aDialogType); + lIconType = utf8to16(aIconType); + + lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, + lDialogType, lIconType, aDefaultButton ); + + free(lTitle); + free(lMessage); + free(lDialogType); + free(lIconType); + + return lIntRetVal ; +} + + +static char const * inputBoxWinGui( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + char * lDialogString; + FILE * lIn; + int lResult; + int lTitleLen; + int lMessageLen; + wchar_t * lDialogStringW; + + lTitleLen = aTitle ? strlen(aTitle) : 0 ; + lMessageLen = aMessage ? strlen(aMessage) : 0 ; + lDialogString = (char *)malloc(3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen); + + if (aDefaultInput) + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.vbs", + getenv("USERPROFILE")); + } + else + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", + getenv("USERPROFILE")); + } + lIn = fopen(lDialogString, "w"); + if (!lIn) + { + free(lDialogString); + return NULL; + } + + if ( aDefaultInput ) + { + strcpy(lDialogString, "Dim result:result=InputBox(\""); + if (aMessage && strlen(aMessage)) + { + strcat(lDialogString, aMessage); + } + strcat(lDialogString, "\",\""); + if (aTitle && strlen(aTitle)) + { + strcat(lDialogString, aTitle); + } + strcat(lDialogString, "\",\""); + if (aDefaultInput && strlen(aDefaultInput)) + { + strcat(lDialogString, aDefaultInput); + } + strcat(lDialogString, "\"):If IsEmpty(result) then:WScript.Echo 0"); + strcat(lDialogString, ":Else: WScript.Echo \"1\" & result : End If"); + } + else + { + sprintf(lDialogString, "\n\ +\n\ +\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +%s\n\ +\n\ +\n\ +\n\ +
\n\ +

\n\ +\n\ +
\n\ +
\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +
\n\ +
\n\ +\n\ +\n\ +" , aTitle ? aTitle : "", aMessage ? aMessage : "") ; + } + fputs(lDialogString, lIn); + fclose(lIn); + + strcpy(lDialogString, ""); + + if (aDefaultInput) + { + strcat(lDialogString, "cscript.exe "); + strcat(lDialogString, "//Nologo "); + strcat(lDialogString,"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs"); + strcat(lDialogString, " > %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.txt"); + } + else + { + strcat(lDialogString, + "mshta.exe %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta"); + } + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; */ + + if (tinyfd_winUtf8) + { + lDialogStringW = utf8to16(lDialogString); + runSilentW(lDialogStringW); + free(lDialogStringW); + } + else + { + runSilentA(lDialogString); + } + + /* + if (!(lIn = _popen(lDialogString, "r"))) + { + free(lDialogString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + { + } + _pclose(lIn); + if (aoBuff[strlen(aoBuff) - 1] == '\n') + { + aoBuff[strlen(aoBuff) - 1] = '\0'; + } + */ + + if (aDefaultInput) + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.txt", + getenv("USERPROFILE")); + if (!(lIn = fopen(lDialogString, "r"))) + { + remove(lDialogString); + free(lDialogString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + remove(lDialogString); + + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.vbs", + getenv("USERPROFILE")); + } + else + { + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.txt", + getenv("USERPROFILE")); + if (!(lIn = fopen(lDialogString, "r"))) + { + remove(lDialogString); + free(lDialogString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + + wipefile(lDialogString); + remove(lDialogString); + sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", + getenv("USERPROFILE")); + } + remove(lDialogString); + free(lDialogString); + /* printf ( "aoBuff: %s\n" , aoBuff ) ; */ + lResult = strncmp(aoBuff, "1", 1) ? 0 : 1; + /* printf ( "lResult: %d \n" , lResult ) ; */ + if (!lResult) + { + return NULL ; + } + + if (aoBuff[strlen(aoBuff) - 1] == '\n') + { + aoBuff[strlen(aoBuff) - 1] = '\0'; + } + + /* printf ( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ + return aoBuff + 1; +} + + +wchar_t const * tinyfd_saveFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription) /* NULL or "image files" */ +{ + static wchar_t lBuff[MAX_PATH_OR_CMD]; + wchar_t lDirname[MAX_PATH_OR_CMD]; + wchar_t lDialogString[MAX_PATH_OR_CMD]; + wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; + wchar_t * p; + wchar_t * lRetval; + int i; + HRESULT lHResult; + OPENFILENAMEW ofn = {0}; + + lHResult = CoInitializeEx(NULL, 0); + + getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); + getLastNameW(lBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) + { + wcscpy(lFilterPatterns, aSingleFilterDescription); + wcscat(lFilterPatterns, L"\n"); + } + wcscat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + wcscat(lFilterPatterns, L";"); + wcscat(lFilterPatterns, aFilterPatterns[i]); + } + wcscat(lFilterPatterns, L"\n"); + if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) + { + wcscpy(lDialogString, lFilterPatterns); + wcscat(lFilterPatterns, lDialogString); + } + wcscat(lFilterPatterns, L"All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = wcschr(p, L'\n')) != NULL) + { + *p = L'\0'; + p++; + } + } + + ofn.lStructSize = sizeof(OPENFILENAMEW); + ofn.hwndOwner = 0; + ofn.hInstance = 0; + ofn.lpstrFilter = lFilterPatterns && wcslen(lFilterPatterns) ? lFilterPatterns : NULL; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = lBuff; + + ofn.nMaxFile = MAX_PATH_OR_CMD; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT; + ofn.lpstrInitialDir = lDirname && wcslen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + ofn.lCustData = 0L; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + + if (GetSaveFileNameW(&ofn) == 0) + { + lRetval = NULL; + } + else + { + lRetval = lBuff; + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + + +static char const * saveFileDialogWinGui8( + char * const aoBuff, + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription) /* NULL or "image files" */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPathAndFile; + wchar_t * lSingleFilterDescription; + wchar_t * * lFilterPatterns; + wchar_t const * lTmpWChar; + char * lTmpChar; + int i ; + + lFilterPatterns = (wchar_t **) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); + } + + lTitle = utf8to16(aTitle); + lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); + lSingleFilterDescription = utf8to16(aSingleFilterDescription); + + lTmpWChar = tinyfd_saveFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const** ) /*stupid cast for gcc*/ + lFilterPatterns, + lSingleFilterDescription); + + free(lTitle); + free(lDefaultPathAndFile); + free(lSingleFilterDescription); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} + + +wchar_t const * tinyfd_openFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription, /* NULL or "image files" */ + int const aAllowMultipleSelects) /* 0 or 1 */ +{ + static wchar_t lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; + + size_t lLengths[MAX_MULTIPLE_FILES]; + wchar_t lDirname[MAX_PATH_OR_CMD]; + wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; + wchar_t lDialogString[MAX_PATH_OR_CMD]; + wchar_t * lPointers[MAX_MULTIPLE_FILES]; + wchar_t * lRetval, * p; + int i, j; + size_t lBuffLen; + HRESULT lHResult; + OPENFILENAMEW ofn = { 0 }; + + lHResult = CoInitializeEx(NULL, 0); + + getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); + getLastNameW(lBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) + { + wcscpy(lFilterPatterns, aSingleFilterDescription); + wcscat(lFilterPatterns, L"\n"); + } + wcscat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + wcscat(lFilterPatterns, L";"); + wcscat(lFilterPatterns, aFilterPatterns[i]); + } + wcscat(lFilterPatterns, L"\n"); + if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) + { + wcscpy(lDialogString, lFilterPatterns); + wcscat(lFilterPatterns, lDialogString); + } + wcscat(lFilterPatterns, L"All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = wcschr(p, L'\n')) != NULL) + { + *p = L'\0'; + p++; + } + } + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = 0; + ofn.hInstance = 0; + ofn.lpstrFilter = lFilterPatterns && wcslen(lFilterPatterns) ? lFilterPatterns : NULL; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = lBuff; + ofn.nMaxFile = MAX_PATH_OR_CMD; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT; + ofn.lpstrInitialDir = lDirname && wcslen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + ofn.lCustData = 0L; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + + if (aAllowMultipleSelects) + { + ofn.Flags |= OFN_ALLOWMULTISELECT; + } + + if (GetOpenFileNameW(&ofn) == 0) + { + lRetval = NULL; + } + else + { + lBuffLen = wcslen(lBuff); + lPointers[0] = lBuff + lBuffLen + 1; + if (!aAllowMultipleSelects || (lPointers[0][0] == L'\0')) + { + lRetval = lBuff; + } + else + { + i = 0; + do + { + lLengths[i] = wcslen(lPointers[i]); + lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; + i++; + } while (lPointers[i][0] != L'\0'); + i--; + p = lBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1; + *p = L'\0'; + for (j = i; j >= 0; j--) + { + p -= lLengths[j]; + memmove(p, lPointers[j], lLengths[j]*sizeof(wchar_t)); + p--; + *p = L'\\'; + p -= lBuffLen; + memmove(p, lBuff, lBuffLen*sizeof(wchar_t)); + p--; + *p = L'|'; + } + p++; + lRetval = p; + } + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + + +static char const * openFileDialogWinGui8( + char * const aoBuff, + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription, /* NULL or "image files" */ + int const aAllowMultipleSelects) /* 0 or 1 */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPathAndFile; + wchar_t * lSingleFilterDescription; + wchar_t * * lFilterPatterns; + wchar_t const * lTmpWChar; + char * lTmpChar; + int i; + + lFilterPatterns = (wchar_t * *) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); + } + + lTitle = utf8to16(aTitle); + lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); + lSingleFilterDescription = utf8to16(aSingleFilterDescription); + + lTmpWChar = tinyfd_openFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const**) /*stupid cast for gcc*/ + lFilterPatterns, + lSingleFilterDescription, + aAllowMultipleSelects); + + free(lTitle); + free(lDefaultPathAndFile); + free(lSingleFilterDescription); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} + +#ifndef TINYFD_NOSELECTFOLDERWIN +static int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + if (uMsg == BFFM_INITIALIZED) + { + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData); + } + return 0; +} + +static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + if (uMsg == BFFM_INITIALIZED) + { + SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData); + } + return 0; +} + +wchar_t const * tinyfd_selectFolderDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPath) /* NULL or "" */ +{ + static wchar_t lBuff[MAX_PATH_OR_CMD]; + + BROWSEINFOW bInfo; + LPITEMIDLIST lpItem; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + bInfo.hwndOwner = 0; + bInfo.pidlRoot = NULL; + bInfo.pszDisplayName = lBuff; + bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; + if (lHResult == S_OK || lHResult == S_FALSE) + { + bInfo.ulFlags = BIF_USENEWUI; + } + bInfo.lpfn = BrowseCallbackProcW; + bInfo.lParam = (LPARAM)aDefaultPath; + bInfo.iImage = -1; + + lpItem = SHBrowseForFolderW(&bInfo); + if (lpItem) + { + SHGetPathFromIDListW(lpItem, lBuff); + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + + if (lpItem) + { + return lBuff; + } + else + { + return NULL; + } +} + + +static char const * selectFolderDialogWinGui8 ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + wchar_t * lTitle; + wchar_t * lDefaultPath; + wchar_t const * lTmpWChar; + char * lTmpChar; + + lTitle = utf8to16(aTitle); + lDefaultPath = utf8to16(aDefaultPath); + + lTmpWChar = tinyfd_selectFolderDialogW( + lTitle, + lDefaultPath); + + free(lTitle); + free(lDefaultPath); + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(aoBuff, lTmpChar); + free(lTmpChar); + + return aoBuff; +} +#endif /*TINYFD_NOSELECTFOLDERWIN*/ + + +wchar_t const * tinyfd_colorChooserW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + static wchar_t lResultHexRGB[8]; + CHOOSECOLORW cc; + COLORREF crCustColors[16]; + unsigned char lDefaultRGB[3]; + int lRet; + + HRESULT lHResult; + lHResult = CoInitializeEx(NULL, 0); + + if (aDefaultHexRGB) + { + Hex2RGBW(aDefaultHexRGB, lDefaultRGB); + } + else + { + lDefaultRGB[0] = aDefaultRGB[0]; + lDefaultRGB[1] = aDefaultRGB[1]; + lDefaultRGB[2] = aDefaultRGB[2]; + } + + /* we can't use aTitle */ + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = NULL; + cc.hInstance = NULL; + cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); + cc.lpCustColors = crCustColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN; + cc.lCustData = 0; + cc.lpfnHook = NULL; + cc.lpTemplateName = NULL; + + lRet = ChooseColorW(&cc); + + if (!lRet) + { + return NULL; + } + + aoResultRGB[0] = GetRValue(cc.rgbResult); + aoResultRGB[1] = GetGValue(cc.rgbResult); + aoResultRGB[2] = GetBValue(cc.rgbResult); + + RGB2HexW(aoResultRGB, lResultHexRGB); + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + + return lResultHexRGB; +} + + +static char const * colorChooserWinGui8( + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + static char lResultHexRGB[8]; + + wchar_t * lTitle; + wchar_t * lDefaultHexRGB; + wchar_t const * lTmpWChar; + char * lTmpChar; + + lTitle = utf8to16(aTitle); + lDefaultHexRGB = utf8to16(aDefaultHexRGB); + + lTmpWChar = tinyfd_colorChooserW( + lTitle, + lDefaultHexRGB, + aDefaultRGB, + aoResultRGB ); + + free(lTitle); + free(lDefaultHexRGB); + if (!lTmpWChar) + { + return NULL; + } + + lTmpChar = utf16to8(lTmpWChar); + strcpy(lResultHexRGB, lTmpChar); + free(lTmpChar); + + return lResultHexRGB; +} + + +static int messageBoxWinGuiA ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + int lBoxReturnValue; + UINT aCode ; + + if ( aIconType && ! strcmp( "warning" , aIconType ) ) + { + aCode = MB_ICONWARNING ; + } + else if ( aIconType && ! strcmp("error", aIconType)) + { + aCode = MB_ICONERROR ; + } + else if ( aIconType && ! strcmp("question", aIconType)) + { + aCode = MB_ICONQUESTION ; + } + else + { + aCode = MB_ICONINFORMATION ; + } + + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + aCode += MB_OKCANCEL ; + if ( ! aDefaultButton ) + { + aCode += MB_DEFBUTTON2 ; + } + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + aCode += MB_YESNO ; + if ( ! aDefaultButton ) + { + aCode += MB_DEFBUTTON2 ; + } + } + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + aCode += MB_YESNOCANCEL; + if (!aDefaultButton) + { + aCode += MB_DEFBUTTON3; + } + else if (aDefaultButton == 2) + { + aCode += MB_DEFBUTTON2; + } + } + else + { + aCode += MB_OK ; + } + + lBoxReturnValue = MessageBoxA(NULL, aMessage, aTitle, aCode); + + if (((aDialogType && !strcmp("yesnocancel", aDialogType)) + && (lBoxReturnValue == IDNO))) + { + return 2; + } + + if ( ( ( aDialogType + && strcmp("yesnocancel", aDialogType) + && strcmp("okcancel", aDialogType) + && strcmp("yesno", aDialogType))) + || (lBoxReturnValue == IDOK) + || (lBoxReturnValue == IDYES) ) + { + return 1 ; + } + else + { + return 0 ; + } +} + + +static char const * saveFileDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + char lDirname [MAX_PATH_OR_CMD] ; + char lDialogString[MAX_PATH_OR_CMD]; + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + int i ; + char * p; + char * lRetval; + HRESULT lHResult; + OPENFILENAMEA ofn = { 0 }; + + lHResult = CoInitializeEx(NULL,0); + + getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); + getLastName(aoBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcpy(lFilterPatterns, aSingleFilterDescription); + strcat(lFilterPatterns, "\n"); + } + strcat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + strcat(lFilterPatterns, ";"); + strcat(lFilterPatterns, aFilterPatterns[i]); + } + strcat(lFilterPatterns, "\n"); + if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) + { + strcpy(lDialogString, lFilterPatterns); + strcat(lFilterPatterns, lDialogString); + } + strcat(lFilterPatterns, "All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = strchr(p, '\n')) != NULL) + { + *p = '\0'; + p ++ ; + } + } + + ofn.lStructSize = sizeof(OPENFILENAME) ; + ofn.hwndOwner = 0 ; + ofn.hInstance = 0 ; + ofn.lpstrFilter = lFilterPatterns && strlen(lFilterPatterns) ? lFilterPatterns : NULL; + ofn.lpstrCustomFilter = NULL ; + ofn.nMaxCustFilter = 0 ; + ofn.nFilterIndex = 1 ; + ofn.lpstrFile = aoBuff; + + ofn.nMaxFile = MAX_PATH_OR_CMD ; + ofn.lpstrFileTitle = NULL ; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ; + ofn.lpstrInitialDir = lDirname && strlen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR ; + ofn.nFileOffset = 0 ; + ofn.nFileExtension = 0 ; + ofn.lpstrDefExt = NULL ; + ofn.lCustData = 0L ; + ofn.lpfnHook = NULL ; + ofn.lpTemplateName = NULL ; + + if ( GetSaveFileNameA ( & ofn ) == 0 ) + { + lRetval = NULL ; + } + else + { + lRetval = aoBuff ; + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return lRetval ; +} + + +static char const * openFileDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lDirname [MAX_PATH_OR_CMD] ; + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + char lDialogString[MAX_PATH_OR_CMD] ; + char * lPointers[MAX_MULTIPLE_FILES]; + size_t lLengths[MAX_MULTIPLE_FILES]; + int i , j ; + char * p; + size_t lBuffLen ; + char * lRetval; + HRESULT lHResult; + OPENFILENAMEA ofn = {0}; + + lHResult = CoInitializeEx(NULL,0); + + getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); + getLastName(aoBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcpy(lFilterPatterns, aSingleFilterDescription); + strcat(lFilterPatterns, "\n"); + } + strcat(lFilterPatterns, aFilterPatterns[0]); + for (i = 1; i < aNumOfFilterPatterns; i++) + { + strcat(lFilterPatterns, ";"); + strcat(lFilterPatterns, aFilterPatterns[i]); + } + strcat(lFilterPatterns, "\n"); + if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) + { + strcpy(lDialogString, lFilterPatterns); + strcat(lFilterPatterns, lDialogString); + } + strcat(lFilterPatterns, "All Files\n*.*\n"); + p = lFilterPatterns; + while ((p = strchr(p, '\n')) != NULL) + { + *p = '\0'; + p ++ ; + } + } + + ofn.lStructSize = sizeof ( OPENFILENAME ) ; + ofn.hwndOwner = 0 ; + ofn.hInstance = 0 ; + ofn.lpstrFilter = lFilterPatterns && strlen(lFilterPatterns) ? lFilterPatterns : NULL; + ofn.lpstrCustomFilter = NULL ; + ofn.nMaxCustFilter = 0 ; + ofn.nFilterIndex = 1 ; + ofn.lpstrFile = aoBuff ; + ofn.nMaxFile = MAX_PATH_OR_CMD ; + ofn.lpstrFileTitle = NULL ; + ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ; + ofn.lpstrInitialDir = lDirname && strlen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR ; + ofn.nFileOffset = 0 ; + ofn.nFileExtension = 0 ; + ofn.lpstrDefExt = NULL ; + ofn.lCustData = 0L ; + ofn.lpfnHook = NULL ; + ofn.lpTemplateName = NULL ; + + if ( aAllowMultipleSelects ) + { + ofn.Flags |= OFN_ALLOWMULTISELECT; + } + + if ( GetOpenFileNameA ( & ofn ) == 0 ) + { + lRetval = NULL ; + } + else + { + lBuffLen = strlen(aoBuff) ; + lPointers[0] = aoBuff + lBuffLen + 1 ; + if ( !aAllowMultipleSelects || (lPointers[0][0] == '\0') ) + { + lRetval = aoBuff ; + } + else + { + i = 0 ; + do + { + lLengths[i] = strlen(lPointers[i]); + lPointers[i+1] = lPointers[i] + lLengths[i] + 1 ; + i ++ ; + } + while ( lPointers[i][0] != '\0' ); + i--; + p = aoBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1 ; + * p = '\0'; + for ( j = i ; j >=0 ; j-- ) + { + p -= lLengths[j]; + memmove(p, lPointers[j], lLengths[j]); + p--; + *p = '\\'; + p -= lBuffLen ; + memmove(p, aoBuff, lBuffLen); + p--; + *p = '|'; + } + p++; + lRetval = p ; + } + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + +#ifndef TINYFD_NOSELECTFOLDERWIN +static char const * selectFolderDialogWinGuiA ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + BROWSEINFOA bInfo ; + LPITEMIDLIST lpItem ; + HRESULT lHResult; + + lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* we can't use aDefaultPath */ + bInfo.hwndOwner = 0 ; + bInfo.pidlRoot = NULL ; + bInfo.pszDisplayName = aoBuff ; + bInfo.lpszTitle = aTitle && strlen(aTitle) ? aTitle : NULL; + if (lHResult == S_OK || lHResult == S_FALSE) + { + bInfo.ulFlags = BIF_USENEWUI; + } + bInfo.lpfn = BrowseCallbackProc; + bInfo.lParam = (LPARAM)aDefaultPath; + bInfo.iImage = -1 ; + + lpItem = SHBrowseForFolderA ( & bInfo ) ; + if ( lpItem ) + { + SHGetPathFromIDListA ( lpItem , aoBuff ) ; + } + + if (lHResult==S_OK || lHResult==S_FALSE) + { + CoUninitialize(); + } + return aoBuff ; +} +#endif /*TINYFD_NOSELECTFOLDERWIN*/ + + +static char const * colorChooserWinGuiA( + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + static char lResultHexRGB[8]; + + CHOOSECOLORA cc; + COLORREF crCustColors[16]; + unsigned char lDefaultRGB[3]; + int lRet; + + if ( aDefaultHexRGB ) + { + Hex2RGB(aDefaultHexRGB, lDefaultRGB); + } + else + { + lDefaultRGB[0]=aDefaultRGB[0]; + lDefaultRGB[1]=aDefaultRGB[1]; + lDefaultRGB[2]=aDefaultRGB[2]; + } + + /* we can't use aTitle */ + cc.lStructSize = sizeof ( CHOOSECOLOR ) ; + cc.hwndOwner = NULL ; + cc.hInstance = NULL ; + cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); + cc.lpCustColors = crCustColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN; + cc.lCustData = 0; + cc.lpfnHook = NULL; + cc.lpTemplateName = NULL; + + lRet = ChooseColorA(&cc); + + if ( ! lRet ) + { + return NULL; + } + + aoResultRGB[0] = GetRValue(cc.rgbResult); + aoResultRGB[1] = GetGValue(cc.rgbResult); + aoResultRGB[2] = GetBValue(cc.rgbResult); + + RGB2Hex(aoResultRGB, lResultHexRGB); + + return lResultHexRGB; +} + +#endif /* TINYFD_NOLIB */ + +static int dialogPresent ( ) +{ + static int lDialogPresent = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + char const * lString = "dialog.exe"; + if ( lDialogPresent < 0 ) + { + if (!(lIn = _popen("where dialog.exe","r"))) + { + lDialogPresent = 0 ; + return 0 ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + _pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + if ( strcmp(lBuff+strlen(lBuff)-strlen(lString),lString) ) + { + lDialogPresent = 0 ; + } + else + { + lDialogPresent = 1 ; + } + } + return lDialogPresent; +} + + +static int messageBoxWinConsole ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + char lDialogString[MAX_PATH_OR_CMD]; + char lDialogFile[MAX_PATH_OR_CMD]; + FILE * lIn; + char lBuff [MAX_PATH_OR_CMD] = ""; + + strcpy ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) + || !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, "tab: move focus") ; + strcat(lDialogString, "\" ") ; + } + + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + if ( ! aDefaultButton ) + { + strcat ( lDialogString , "--defaultno " ) ; + } + strcat ( lDialogString , + "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + if ( ! aDefaultButton ) + { + strcat ( lDialogString , "--defaultno " ) ; + } + strcat ( lDialogString , "--yesno " ) ; + } + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + if (!aDefaultButton) + { + strcat(lDialogString, "--defaultno "); + } + strcat(lDialogString, "--menu "); + } + else + { + strcat ( lDialogString , "--msgbox " ) ; + } + + strcat ( lDialogString , "\"" ) ; + if ( aMessage && strlen(aMessage) ) + { + replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; + strcat(lDialogString, lBuff) ; + lBuff[0]='\0'; + } + strcat(lDialogString, "\" "); + + if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + strcat(lDialogString, "0 60 0 Yes \"\" No \"\""); + strcat(lDialogString, "2>>"); + } + else + { + strcat(lDialogString, "10 60"); + strcat(lDialogString, " && echo 1 > "); + } + + strcpy(lDialogFile, getenv("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lDialogFile); + + /*if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ;*/ + system ( lDialogString ) ; + + if (!(lIn = fopen(lDialogFile, "r"))) + { + remove(lDialogFile); + return 0 ; + } + while (fgets(lBuff, sizeof(lBuff), lIn) != NULL) + {} + fclose(lIn); + remove(lDialogFile); + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + + /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff); */ + if ( ! strlen(lBuff) ) + { + return 0; + } + + if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + if (lBuff[0] == 'Y') return 1; + else return 2; + } + + return 1; +} + + +static char const * inputBoxWinConsole( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + char lDialogString[MAX_PATH_OR_CMD]; + char lDialogFile[MAX_PATH_OR_CMD]; + FILE * lIn; + int lResult; + + strcpy(lDialogFile, getenv("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcpy(lDialogString , "echo|set /p=1 >" ) ; + strcat(lDialogString, lDialogFile); + strcat( lDialogString , " & " ) ; + + strcat ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, "tab: move focus") ; + if ( ! aDefaultInput ) + { + strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; + } + + strcat(lDialogString, "\" ") ; + + if ( ! aDefaultInput ) + { + strcat ( lDialogString , "--insecure --passwordbox" ) ; + } + else + { + strcat ( lDialogString , "--inputbox" ) ; + } + strcat ( lDialogString , " \"" ) ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString,"\" 10 60 ") ; + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, "\"") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "\" ") ; + } + + strcat(lDialogString, "2>>"); + strcpy(lDialogFile, getenv("USERPROFILE")); + strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lDialogFile); + strcat(lDialogString, " || echo 0 > "); + strcat(lDialogString, lDialogFile); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; */ + system ( lDialogString ) ; + + if (!(lIn = fopen(lDialogFile, "r"))) + { + remove(lDialogFile); + return 0 ; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + + wipefile(lDialogFile); + remove(lDialogFile); + if ( aoBuff[strlen ( aoBuff ) -1] == '\n' ) + { + aoBuff[strlen ( aoBuff ) -1] = '\0' ; + } + /* printf ( "aoBuff: %s\n" , aoBuff ) ; */ + + /* printf ( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */ + lResult = strncmp ( aoBuff , "1" , 1) ? 0 : 1 ; + /* printf ( "lResult: %d \n" , lResult ) ; */ + if ( ! lResult ) + { + return NULL ; + } + /* printf ( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ + return aoBuff+3 ; +} + + +static char const * saveFileDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile ) /* NULL or "" */ +{ + char lDialogString[MAX_PATH_OR_CMD]; + char lPathAndFile[MAX_PATH_OR_CMD] = ""; + FILE * lIn; + + strcpy ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + /* dialog.exe uses unix separators even on windows */ + strcpy(lPathAndFile, aDefaultPathAndFile); + replaceChr ( lPathAndFile , '\\' , '/' ) ; + } + + /* dialog.exe needs at least one separator */ + if ( ! strchr(lPathAndFile, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, lPathAndFile) ; + strcat(lDialogString, "\" 0 60 2>"); + strcpy(lPathAndFile, getenv("USERPROFILE")); + strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lPathAndFile); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; */ + system ( lDialogString ) ; + + if (!(lIn = fopen(lPathAndFile, "r"))) + { + remove(lPathAndFile); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + remove(lPathAndFile); + replaceChr ( aoBuff , '/' , '\\' ) ; + /* printf ( "aoBuff: %s\n" , aoBuff ) ; */ + getLastName(lDialogString,aoBuff); + if ( ! strlen(lDialogString) ) + { + return NULL; + } + return aoBuff; +} + + +static char const * openFileDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + char lDialogString[MAX_PATH_OR_CMD] ; + FILE * lIn; + + strcpy ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + /* dialog.exe uses unix separators even on windows */ + strcpy(lFilterPatterns, aDefaultPathAndFile); + replaceChr ( lFilterPatterns , '\\' , '/' ) ; + } + + /* dialog.exe needs at least one separator */ + if ( ! strchr(lFilterPatterns, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, lFilterPatterns) ; + strcat(lDialogString, "\" 0 60 2>"); + strcpy(lFilterPatterns, getenv("USERPROFILE")); + strcat(lFilterPatterns, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lFilterPatterns); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; */ + system ( lDialogString ) ; + + if (!(lIn = fopen(lFilterPatterns, "r"))) + { + remove(lFilterPatterns); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + remove(lFilterPatterns); + replaceChr ( aoBuff , '/' , '\\' ) ; + /* printf ( "aoBuff: %s\n" , aoBuff ) ; */ + return aoBuff; +} + + +static char const * selectFolderDialogWinConsole ( + char * const aoBuff , + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + char lDialogString [MAX_PATH_OR_CMD] ; + char lString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + + strcpy ( lDialogString , "dialog " ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + + strcat ( lDialogString , "--dselect \"" ) ; + if ( aDefaultPath && strlen(aDefaultPath) ) + { + /* dialog.exe uses unix separators even on windows */ + strcpy(lString, aDefaultPath) ; + ensureFinalSlash(lString); + replaceChr ( lString , '\\' , '/' ) ; + strcat(lDialogString, lString) ; + } + else + { + /* dialog.exe needs at least one separator */ + strcat(lDialogString, "./") ; + } + strcat(lDialogString, "\" 0 60 2>"); + strcpy(lString, getenv("USERPROFILE")); + strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt"); + strcat(lDialogString, lString); + + /* printf ( "lDialogString: %s\n" , lDialogString ) ; */ + system ( lDialogString ) ; + + if (!(lIn = fopen(lString, "r"))) + { + remove(lString); + return NULL; + } + while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) + {} + fclose(lIn); + remove(lString); + replaceChr ( aoBuff , '/' , '\\' ) ; + /* printf ( "aoBuff: %s\n" , aoBuff ) ; */ + return aoBuff; +} + + +int tinyfd_messageBox ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + char lChar ; + +#ifndef TINYFD_NOLIB + if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) + && (!getenv("SSH_CLIENT") || getenv("DISPLAY"))) + { + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; } + if (tinyfd_winUtf8) + { + return messageBoxWinGui8( + aTitle, aMessage, aDialogType, aIconType, aDefaultButton); + } + else + { + return messageBoxWinGuiA( + aTitle, aMessage, aDialogType, aIconType, aDefaultButton); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} + return messageBoxWinConsole( + aTitle,aMessage,aDialogType,aIconType,aDefaultButton); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} + if (!gWarningDisplayed && !tinyfd_forceConsole ) + { + gWarningDisplayed = 1; + printf("\n\n%s\n", gTitle); + printf("%s\n\n", gMessageWin); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("\n%s\n\n", aTitle); + } + if ( aDialogType && !strcmp("yesno",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("y/n: "); + lChar = (char) tolower ( _getch() ) ; + printf("\n\n"); + } + while ( lChar != 'y' && lChar != 'n' ) ; + return lChar == 'y' ? 1 : 0 ; + } + else if ( aDialogType && !strcmp("okcancel",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("[O]kay/[C]ancel: "); + lChar = (char) tolower ( _getch() ) ; + printf("\n\n"); + } + while ( lChar != 'o' && lChar != 'c' ) ; + return lChar == 'o' ? 1 : 0 ; + } + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + do + { + if (aMessage && strlen(aMessage)) + { + printf("%s\n", aMessage); + } + printf("[Y]es/[N]o/[C]ancel: "); + lChar = (char)tolower(_getch()); + printf("\n\n"); + } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); + return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ; + } + else + { + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n\n",aMessage); + } + printf("press enter to continue "); + lChar = (char) _getch() ; + printf("\n\n"); + return 1 ; + } + } +} + + +/* returns NULL on cancel */ +char const * tinyfd_inputBox( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char * lEOF; + +#ifndef TINYFD_NOLIB + DWORD mode = 0; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + + if ((!tinyfd_forceConsole || !( + GetConsoleWindow() || + dialogPresent())) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + lBuff[0]='\0'; + return inputBoxWinGui(lBuff,aTitle,aMessage,aDefaultInput); + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + lBuff[0]='\0'; + return inputBoxWinConsole(lBuff,aTitle,aMessage,aDefaultInput); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + lBuff[0]='\0'; + if (!gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf("\n\n%s\n", gTitle); + printf("%s\n\n", gMessageWin); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("\n%s\n\n", aTitle); + } + if ( aMessage && strlen(aMessage) ) + { + printf("%s\n",aMessage); + } + printf("(ctrl-Z + enter to cancel): "); +#ifndef TINYFD_NOLIB + if ( ! aDefaultInput ) + { + GetConsoleMode(hStdin,&mode); + SetConsoleMode(hStdin,mode & (~ENABLE_ECHO_INPUT) ); + } +#endif /* TINYFD_NOLIB */ + lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); + if ( ! lEOF ) + { + return NULL; + } +#ifndef TINYFD_NOLIB + if ( ! aDefaultInput ) + { + SetConsoleMode(hStdin,mode); + printf ("\n"); + } +#endif /* TINYFD_NOLIB */ + printf ("\n"); + if ( strchr(lBuff,27) ) + { + return NULL ; + } + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + return lBuff ; + } +} + + +char const * tinyfd_saveFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lString[MAX_PATH_OR_CMD] ; + char const * p ; + lBuff[0]='\0'; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + p = saveFileDialogWinGui8(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + } + else + { + p = saveFileDialogWinGuiA(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = saveFileDialogWinConsole(lBuff,aTitle,aDefaultPathAndFile); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Save file",""); + } + + if ( ! p || ! strlen ( p ) ) + { + return NULL; + } + getPathWithoutFinalSlash ( lString , p ) ; + if ( strlen ( lString ) && ! dirExists ( lString ) ) + { + return NULL ; + } + getLastName(lString,p); + if ( ! filenameValid(lString) ) + { + return NULL; + } + return p ; +} + + +/* in case of multiple files, the separator is | */ +char const * tinyfd_openFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + static char lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; + char const * p ; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + p = openFileDialogWinGui8(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, + aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + } + else + { + p = openFileDialogWinGuiA(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, + aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = openFileDialogWinConsole(lBuff, + aTitle,aDefaultPathAndFile,aAllowMultipleSelects); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Open file",""); + } + + if ( ! p || ! strlen ( p ) ) + { + return NULL; + } + if ( aAllowMultipleSelects && strchr(p, '|') ) + { + p = ensureFilesExist( lBuff , p ) ; + } + else if ( ! fileExists (p) ) + { + return NULL ; + } + /* printf ( "lBuff3: %s\n" , p ) ; */ + return p ; +} + + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPath ) /* NULL or "" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char const * p ; +#ifndef TINYFD_NOLIB + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { +#ifndef TINYFD_NOSELECTFOLDERWIN + p = selectFolderDialogWinGui8(lBuff, aTitle, aDefaultPath); + } + else + { + p = selectFolderDialogWinGuiA(lBuff, aTitle, aDefaultPath); +#endif /*TINYFD_NOSELECTFOLDERWIN*/ + } + } + else +#endif /* TINYFD_NOLIB */ + if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + p = selectFolderDialogWinConsole(lBuff,aTitle,aDefaultPath); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + p = tinyfd_inputBox(aTitle, "Select folder",""); + } + + if ( ! p || ! strlen ( p ) || ! dirExists ( p ) ) + { + return NULL ; + } + return p ; +} + + +/* returns the hexcolor as a string "#FF0000" */ +/* aoResultRGB also contains the result */ +/* aDefaultRGB is used only if aDefaultHexRGB is NULL */ +/* aDefaultRGB and aoResultRGB can be the same array */ +char const * tinyfd_colorChooser( + char const * const aTitle, /* NULL or "" */ + char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + char lDefaultHexRGB[8]; + char * lpDefaultHexRGB; + int i; + char const * p ; + +#ifndef TINYFD_NOLIB + if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) + && (!getenv("SSH_CLIENT") || getenv("DISPLAY")) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} + if (tinyfd_winUtf8) + { + return colorChooserWinGui8( + aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); + } + else + { + return colorChooserWinGuiA( + aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); + } + } + else +#endif /* TINYFD_NOLIB */ + if ( aDefaultHexRGB ) + { + lpDefaultHexRGB = (char *) aDefaultHexRGB ; + } + else + { + RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; + lpDefaultHexRGB = (char *) lDefaultHexRGB ; + } + p = tinyfd_inputBox(aTitle, + "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); + if (aTitle&&!strcmp(aTitle,"tinyfd_query")) return p; + + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) + { + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + return p ; +} + +#else /* unix */ + +static char gPython2Name[16]; + +static int isDarwin ( ) +{ + static int lsIsDarwin = -1 ; + struct utsname lUtsname ; + if ( lsIsDarwin < 0 ) + { + lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ; + } + return lsIsDarwin ; +} + + +static int dirExists ( char const * const aDirPath ) +{ + DIR * lDir ; + if ( ! aDirPath || ! strlen ( aDirPath ) ) + return 0 ; + lDir = opendir ( aDirPath ) ; + if ( ! lDir ) + { + return 0 ; + } + closedir ( lDir ) ; + return 1 ; +} + + +static int detectPresence ( char const * const aExecutable ) +{ + char lBuff [MAX_PATH_OR_CMD] ; + char lTestedString [MAX_PATH_OR_CMD] = "which " ; + FILE * lIn ; + + strcat ( lTestedString , aExecutable ) ; + strcat( lTestedString, " 2>/dev/null "); + lIn = popen ( lTestedString , "r" ) ; + if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + && ( ! strchr ( lBuff , ':' ) ) + && ( strncmp(lBuff, "no ", 3) ) ) + { /* present */ + pclose ( lIn ) ; + if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 1); + return 1 ; + } + else + { + pclose ( lIn ) ; + if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0); + return 0 ; + } +} + + +static char const * getVersion ( char const * const aExecutable ) /*version # must follow :*/ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lTestedString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + char * lTmp ; + + strcpy ( lTestedString , aExecutable ) ; + strcat ( lTestedString , " --version" ) ; + + lIn = popen ( lTestedString , "r" ) ; + lTmp = fgets ( lBuff , sizeof ( lBuff ) , lIn ) ; + pclose ( lIn ) ; + if ( ! lTmp || !(lTmp = strchr ( lBuff , ':' )) ) return 0 ; + lTmp ++ ; + /* printf("lTmp %s\n", lTmp); */ + return lTmp ; +} + + +static int tryCommand ( char const * const aCommand ) +{ + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + + lIn = popen ( aCommand , "r" ) ; + if ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) == NULL ) + { /* present */ + pclose ( lIn ) ; + return 1 ; + } + else + { + pclose ( lIn ) ; + return 0 ; + } + +} + + +static int isTerminalRunning() +{ + return isatty(1); +} + + +static char const * dialogNameOnly ( ) +{ + static char lDialogName[128] = "*" ; + if ( lDialogName[0] == '*' ) + { + if ( isDarwin() && strcpy(lDialogName , "/opt/local/bin/dialog" ) + && detectPresence ( lDialogName ) ) + {} + else if ( strcpy(lDialogName , "dialog" ) + && detectPresence ( lDialogName ) ) + {} + else + { + strcpy(lDialogName , "" ) ; + } + } + return lDialogName ; +} + + +int isDialogVersionBetter09b ( ) +{ + char const * lDialogName ; + char * lVersion ; + int lMajor ; + int lMinor ; + int lDate ; + int lResult ; + char * lMinorP ; + char * lLetter ; + char lBuff[128] ; + + /*char lTest[128] = " 0.9b-20031126" ;*/ + + lDialogName = dialogNameOnly ( ) ; + if ( ! lDialogName || !(lVersion = (char *) getVersion(lDialogName)) ) return 0 ; + /*lVersion = lTest ;*/ + /*printf("lVersion %s\n", lVersion);*/ + strcpy(lBuff,lVersion); + lMajor = atoi ( strtok(lVersion," ,.-") ) ; + /*printf("lMajor %d\n", lMajor);*/ + lMinorP = strtok(0," ,.-abcdefghijklmnopqrstuvxyz"); + lMinor = atoi ( lMinorP ) ; + /*printf("lMinor %d\n", lMinor );*/ + lDate = atoi ( strtok(0," ,.-") ) ; + if (lDate<0) lDate = - lDate; + /*printf("lDate %d\n", lDate);*/ + lLetter = lMinorP + strlen(lMinorP) ; + strcpy(lVersion,lBuff); + strtok(lLetter," ,.-"); + /*printf("lLetter %s\n", lLetter);*/ + lResult = (lMajor > 0) || ( ( lMinor == 9 ) && (*lLetter == 'b') && (lDate >= 20031126) ); + /*printf("lResult %d\n", lResult);*/ + return lResult; +} + + +static int whiptailPresentOnly ( ) +{ + static int lWhiptailPresent = -1 ; + if ( lWhiptailPresent < 0 ) + { + lWhiptailPresent = detectPresence ( "whiptail" ) ; + } + return lWhiptailPresent ; +} + + +static char const * terminalName ( ) +{ + static char lTerminalName[128] = "*" ; + char lShellName[64] = "*" ; + + if ( lTerminalName[0] == '*' ) + { + if ( detectPresence ( "bash" ) ) + { + strcpy(lShellName , "bash -c " ) ; /*good for basic input*/ + } + else if ( dialogNameOnly() || whiptailPresentOnly() ) + { + strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/ + } + else + { + return NULL ; + } + + if ( isDarwin() ) + { + if ( strcpy(lTerminalName , "/opt/X11/bin/xterm" ) + && detectPresence ( lTerminalName ) ) + { + strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else + { + strcpy(lTerminalName , "" ) ; + } + } + else if ( strcpy(lTerminalName,"xterm") /*good (small without parameters)*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"terminator") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"lxterminal") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"konsole") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"kterm") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"xfce4-terminal") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"mate-terminal") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -x " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"Eterm") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"evilvte") /*good*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else if ( strcpy(lTerminalName,"pterm") /*good (only letters)*/ + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " -e " ) ; + strcat(lTerminalName , lShellName ) ; + } + else + { + strcpy(lTerminalName , "" ) ; + } + /*else if ( strcpy(lTerminalName,"gnome-terminal") //bad (good if version < 3) + && detectPresence(lTerminalName) ) + { + strcat(lTerminalName , " --disable-factory -x " ) ; + strcat(lTerminalName , lShellName ) ; + }*/ + /* bad: koi rxterm guake tilda vala-terminal qterminal + aterm Terminal terminology sakura lilyterm weston-terminal + roxterm termit xvt rxvt mrxvt urxvt */ + } + if ( strlen(lTerminalName) ) + { + return lTerminalName ; + } + else + { + return NULL ; + } +} + + +static char const * dialogName ( ) +{ + char const * lDialogName ; + lDialogName = dialogNameOnly ( ) ; + if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) ) + { + return lDialogName ; + } + else + { + return NULL ; + } +} + + +static int whiptailPresent ( ) +{ + int lWhiptailPresent ; + lWhiptailPresent = whiptailPresentOnly ( ) ; + if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) ) + { + return lWhiptailPresent ; + } + else + { + return 0 ; + } +} + + + +static int graphicMode() +{ + return !( tinyfd_forceConsole && (isTerminalRunning() || terminalName()) ) + && ( getenv("DISPLAY") + || (isDarwin() && (!getenv("SSH_TTY") || getenv("DISPLAY") ) ) ) ; +} + + +static int xmessagePresent ( ) +{ + static int lXmessagePresent = -1 ; + if ( lXmessagePresent < 0 ) + { + lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ + } + return lXmessagePresent && graphicMode ( ) ; +} + + +static int gxmessagePresent ( ) +{ + static int lGxmessagePresent = -1 ; + if ( lGxmessagePresent < 0 ) + { + lGxmessagePresent = detectPresence("gxmessage") ; + } + return lGxmessagePresent && graphicMode ( ) ; +} + + +static int gmessagePresent ( ) +{ + static int lGmessagePresent = -1 ; + if ( lGmessagePresent < 0 ) + { + lGmessagePresent = detectPresence("gmessage") ; + } + return lGmessagePresent && graphicMode ( ) ; +} + + +static int notifysendPresent ( ) +{ + static int lNotifysendPresent = -1 ; + if ( lNotifysendPresent < 0 ) + { + lNotifysendPresent = detectPresence("notify-send") ; + } + return lNotifysendPresent && graphicMode ( ) ; +} + + +static int xdialogPresent ( ) +{ + static int lXdialogPresent = -1 ; + if ( lXdialogPresent < 0 ) + { + lXdialogPresent = detectPresence("Xdialog") ; + } + return lXdialogPresent && graphicMode ( ) ; +} + + +static int gdialogPresent ( ) +{ + static int lGdialoglPresent = -1 ; + if ( lGdialoglPresent < 0 ) + { + lGdialoglPresent = detectPresence ( "gdialog" ) ; + } + return lGdialoglPresent && graphicMode ( ) ; +} + + +static int osascriptPresent ( ) +{ + static int lOsascriptPresent = -1 ; + if ( lOsascriptPresent < 0 ) + { + gWarningDisplayed |= !!getenv("SSH_TTY"); + lOsascriptPresent = detectPresence ( "osascript" ) ; + } + return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ; +} + + +static int kdialogPresent ( ) +{ + static int lKdialogPresent = -1 ; + if ( lKdialogPresent < 0 ) + { + lKdialogPresent = detectPresence("kdialog") ; + } + return lKdialogPresent && graphicMode ( ) ; +} + + +static int qarmaPresent ( ) +{ + static int lQarmaPresent = -1 ; + if ( lQarmaPresent < 0 ) + { + lQarmaPresent = detectPresence("qarma") ; + } + return lQarmaPresent && graphicMode ( ) ; +} + + +static int matedialogPresent ( ) +{ + static int lMatedialogPresent = -1 ; + if ( lMatedialogPresent < 0 ) + { + lMatedialogPresent = detectPresence("matedialog") ; + } + return lMatedialogPresent && graphicMode ( ) ; +} + + +static int zenityPresent ( ) +{ + static int lZenityPresent = -1 ; + if ( lZenityPresent < 0 ) + { + lZenityPresent = detectPresence("zenity") ; + } + return lZenityPresent && graphicMode ( ) ; +} + + +static int osx9orBetter ( ) +{ + static int lOsx9orBetter = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + int V,v; + + if ( lOsx9orBetter < 0 ) + { + lOsx9orBetter = 0 ; + lIn = popen ( "osascript -e 'set osver to system version of (system info)'" , "r" ) ; + if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + && ( 2 == sscanf(lBuff, "%d.%d", &V, &v) ) ) + { + V = V * 100 + v; + if ( V >= 1009 ) + { + lOsx9orBetter = 1 ; + } + } + pclose ( lIn ) ; + /* printf ("Osx10 = %d, %d = <%s>\n", lOsx9orBetter, V, lBuff) ; */ + } + return lOsx9orBetter ; +} + + +static int zenity3Present ( ) +{ + static int lZenity3Present = -1 ; + char lBuff [MAX_PATH_OR_CMD] ; + FILE * lIn ; + + if ( lZenity3Present < 0 ) + { + lZenity3Present = 0 ; + if ( zenityPresent() ) + { + lIn = popen ( "zenity --version" , "r" ) ; + if ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + { + if ( atoi(lBuff) >= 3 ) + { + lZenity3Present = 3 ; + } + else if ( ( atoi(lBuff) == 2 ) && ( atoi(strtok(lBuff,".")+2 ) >= 32 ) ) + { + lZenity3Present = 2 ; + } + } + pclose ( lIn ) ; + } + } + return lZenity3Present && graphicMode ( ) ; +} + + +static int tkinter2Present ( ) +{ + static int lTkinter2Present = -1 ; + char lPythonCommand[256]; + char lPythonParams[256] = +"-c \"try:\n\timport Tkinter;\nexcept:\n\tprint(0);\""; + int i; + + if ( lTkinter2Present < 0 ) + { + lTkinter2Present = 0 ; + strcpy(gPython2Name , "python" ) ; + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + if ( ! detectPresence(gPython2Name) + || ! (lTkinter2Present = tryCommand(lPythonCommand)) ) + { + strcpy(gPython2Name , "python2" ) ; + if ( detectPresence(gPython2Name) ) + { + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand); + } + else + { + for ( i = 9 ; i >= 0 ; i -- ) + { + sprintf ( gPython2Name , "python2.%d" , i ) ; + if ( detectPresence(gPython2Name) ) + { + sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand); + break ; + } + } + } + } + } + /* printf ("lTkinter2Present %d\n", lTkinter2Present) ; */ + /* printf ("gPython2Name %s\n", gPython2Name) ; */ + return lTkinter2Present && graphicMode() && !(isDarwin() && getenv("SSH_TTY") ); +} + + +int tinyfd_messageBox ( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may contain \n and \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + char lBuff [MAX_PATH_OR_CMD] ; + char * lDialogString = NULL ; + char * lpDialogString; + FILE * lIn ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + int lResult ; + char lChar ; + struct termios infoOri; + struct termios info; + int lTitleLen ; + int lMessageLen ; + + lBuff[0]='\0'; + + lTitleLen = aTitle ? strlen(aTitle) : 0 ; + lMessageLen = aMessage ? strlen(aMessage) : 0 ; + if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) + { + lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); + } + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;} + + strcpy ( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat ( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"") ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString, "\" ") ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + strcat(lDialogString, "with icon ") ; + if ( aIconType && ! strcmp( "error" , aIconType ) ) + { + strcat(lDialogString, "stop " ) ; + } + else if ( aIconType && ! strcmp( "warning" , aIconType ) ) + { + strcat(lDialogString, "caution " ) ; + } + else /* question or info */ + { + strcat(lDialogString, "note " ) ; + } + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + if ( ! aDefaultButton ) + { + strcat ( lDialogString ,"default button \"Cancel\" " ) ; + } + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + strcat ( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ; + if (aDefaultButton) + { + strcat ( lDialogString ,"default button \"Yes\" " ) ; + } + else + { + strcat ( lDialogString ,"default button \"No\" " ) ; + } + strcat ( lDialogString ,"cancel button \"No\"" ) ; + } + else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString ,"buttons {\"No\", \"Yes\", \"Cancel\"} " ) ; + switch (aDefaultButton) + { + case 1: strcat ( lDialogString ,"default button \"Yes\" " ) ; break; + case 2: strcat ( lDialogString ,"default button \"No\" " ) ; break; + case 0: strcat ( lDialogString ,"default button \"Cancel\" " ) ; break; + } + strcat ( lDialogString ,"cancel button \"Cancel\"" ) ; + } + else + { + strcat ( lDialogString ,"buttons {\"OK\"} " ) ; + strcat ( lDialogString ,"default button \"OK\" " ) ; + } + strcat ( lDialogString, ")' ") ; + + strcat ( lDialogString, +"-e 'if vButton is \"Yes\" then' -e 'return 1' -e 'else if vButton is \"No\" then' -e 'return 2' -e 'else' -e 'return 0' -e 'end if' " ); + + strcat ( lDialogString, "-e 'on error number -128' " ) ; + strcat ( lDialogString, "-e '0' " ); + + strcat ( lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat ( lDialogString, " -e 'end tell'") ; + } + else if ( zenityPresent() || matedialogPresent() || qarmaPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} + strcpy ( lDialogString , "szAnswer=$(zenity --" ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} + strcpy ( lDialogString , "szAnswer=$(matedialog --" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;} + strcpy ( lDialogString , "szAnswer=$(qarma --" ) ; + } + + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + strcat ( lDialogString , + "question --ok-label=Ok --cancel-label=Cancel" ) ; + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + strcat ( lDialogString , "question" ) ; + } + else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString , "list --column \"\" --hide-header \"Yes\" \"No\"" ) ; + } + else if ( aIconType && ! strcmp( "error" , aIconType ) ) + { + strcat ( lDialogString , "error" ) ; + } + else if ( aIconType && ! strcmp( "warning" , aIconType ) ) + { + strcat ( lDialogString , "warning" ) ; + } + else + { + strcat ( lDialogString , "info" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, " --text=\"") ; + strcat(lDialogString, aMessage) ; + strcat(lDialogString, "\"") ; + } + if ( zenity3Present ( ) >= 3 ) + { + strcat ( lDialogString , " --icon-name=dialog-" ) ; + if ( aIconType && (! strcmp( "question" , aIconType ) + || ! strcmp( "error" , aIconType ) + || ! strcmp( "warning" , aIconType ) ) ) + { + strcat ( lDialogString , aIconType ) ; + } + else + { + strcat ( lDialogString , "information" ) ; + } + } + + if ( ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString , +");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi"); + } + else + { + strcat ( lDialogString , ");if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} + + strcpy ( lDialogString , "kdialog --" ) ; + if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType ) + || ! strcmp( "yesno" , aDialogType ) || ! strcmp( "yesnocancel" , aDialogType ) ) ) + { + if ( aIconType && ( ! strcmp( "warning" , aIconType ) + || ! strcmp( "error" , aIconType ) ) ) + { + strcat ( lDialogString , "warning" ) ; + } + if ( ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString , "yesnocancel" ) ; + } + else + { + strcat ( lDialogString , "yesno" ) ; + } + } + else if ( aIconType && ! strcmp( "error" , aIconType ) ) + { + strcat ( lDialogString , "error" ) ; + } + else if ( aIconType && ! strcmp( "warning" , aIconType ) ) + { + strcat ( lDialogString , "sorry" ) ; + } + else + { + strcat ( lDialogString , "msgbox" ) ; + } + strcat ( lDialogString , " \"" ) ; + if ( aMessage ) + { + strcat ( lDialogString , aMessage ) ; + } + strcat ( lDialogString , "\"" ) ; + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + strcat ( lDialogString , + " --yes-label Ok --no-label Cancel" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + + if ( ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString , "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi"); + } + else + { + strcat ( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + } + else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return 1;} + + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ +frontmost of process \\\"Python\\\" to true' ''');"); + } + + strcat ( lDialogString ,"res=tkMessageBox." ) ; + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + strcat ( lDialogString , "askokcancel(" ) ; + if ( aDefaultButton ) + { + strcat ( lDialogString , "default=tkMessageBox.OK," ) ; + } + else + { + strcat ( lDialogString , "default=tkMessageBox.CANCEL," ) ; + } + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + strcat ( lDialogString , "askyesno(" ) ; + if ( aDefaultButton ) + { + strcat ( lDialogString , "default=tkMessageBox.YES," ) ; + } + else + { + strcat ( lDialogString , "default=tkMessageBox.NO," ) ; + } + } + else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat ( lDialogString , "askyesnocancel(" ) ; + switch ( aDefaultButton ) + { + case 1: strcat ( lDialogString , "default=tkMessageBox.YES," ); break; + case 2: strcat ( lDialogString , "default=tkMessageBox.NO," ); break; + case 0: strcat ( lDialogString , "default=tkMessageBox.CANCEL," ); break; + } + } + else + { + strcat ( lDialogString , "showinfo(" ) ; + } + + strcat ( lDialogString , "icon='" ) ; + if ( aIconType && (! strcmp( "question" , aIconType ) + || ! strcmp( "error" , aIconType ) + || ! strcmp( "warning" , aIconType ) ) ) + { + strcat ( lDialogString , aIconType ) ; + } + else + { + strcat ( lDialogString , "info" ) ; + } + + strcat(lDialogString, "',") ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, "message='") ; + lpDialogString = lDialogString + strlen(lDialogString); + replaceSubStr ( aMessage , "\n" , "\\n" , lpDialogString ) ; + strcat(lDialogString, "'") ; + } + + if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat(lDialogString, ");\n\ +if res is None :\n\tprint 0\n\ +elif res is False :\n\tprint 2\n\ +else :\n\tprint 1\n\"" ) ; + } + else + { + strcat(lDialogString, ");\n\ +if res is False :\n\tprint 0\n\ +else :\n\tprint 1\n\"" ) ; + } + } + else if ( gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()) ) + { + if ( gxmessagePresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;} + strcpy ( lDialogString , "gxmessage"); + } + else if ( gmessagePresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return 1;} + strcpy ( lDialogString , "gmessage"); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;} + strcpy ( lDialogString , "xmessage"); + } + + if ( aDialogType && ! strcmp("okcancel" , aDialogType) ) + { + strcat ( lDialogString , " -buttons Ok:1,Cancel:0"); + switch ( aDefaultButton ) + { + case 1: strcat ( lDialogString , " -default Ok"); break; + case 0: strcat ( lDialogString , " -default Cancel"); break; + } + } + else if ( aDialogType && ! strcmp("yesno" , aDialogType) ) + { + strcat ( lDialogString , " -buttons Yes:1,No:0"); + switch ( aDefaultButton ) + { + case 1: strcat ( lDialogString , " -default Yes"); break; + case 0: strcat ( lDialogString , " -default No"); break; + } + } + else if ( aDialogType && ! strcmp("yesnocancel" , aDialogType) ) + { + strcat ( lDialogString , " -buttons Yes:1,No:2,Cancel:0"); + switch ( aDefaultButton ) + { + case 1: strcat ( lDialogString , " -default Yes"); break; + case 2: strcat ( lDialogString , " -default No"); break; + case 0: strcat ( lDialogString , " -default Cancel"); break; + } + } + else + { + strcat ( lDialogString , " -buttons Ok:1"); + strcat ( lDialogString , " -default Ok"); + } + + strcat ( lDialogString , " -center \""); + if ( aMessage && strlen(aMessage) ) + { + strcat ( lDialogString , aMessage ) ; + } + strcat(lDialogString, "\"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat ( lDialogString , " -title \""); + strcat ( lDialogString , aTitle ) ; + strcat ( lDialogString, "\"" ) ; + } + strcat ( lDialogString , " ; echo $? "); + } + else if ( xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent() ) + { + if ( gdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(gdialog " ) ; + } + else if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( dialogName ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} + if ( isTerminalRunning ( ) ) + { + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + } + else if ( isTerminalRunning ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} + strcpy ( lDialogString , "(whiptail " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(whiptail " ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( !xdialogPresent() && !gdialogPresent() ) + { + if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , aDialogType ) + || !strcmp( "yesnocancel" , aDialogType ) ) ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, "tab: move focus") ; + strcat(lDialogString, "\" ") ; + } + } + + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + if ( ! aDefaultButton ) + { + strcat ( lDialogString , "--defaultno " ) ; + } + strcat ( lDialogString , + "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + if ( ! aDefaultButton ) + { + strcat ( lDialogString , "--defaultno " ) ; + } + strcat ( lDialogString , "--yesno " ) ; + } + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + if (!aDefaultButton) + { + strcat(lDialogString, "--defaultno "); + } + strcat(lDialogString, "--menu "); + } + else + { + strcat ( lDialogString , "--msgbox " ) ; + + } + strcat ( lDialogString , "\"" ) ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString, "\" "); + + if ( lWasGraphicDialog ) + { + if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + strcat(lDialogString,"0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;\ +if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ +tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; + } + else + { + strcat(lDialogString, + "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); + } + } + else + { + if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + strcat(lDialogString,"0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;\ + if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ + tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; + + if ( lWasXterm ) + { + strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); + } + else + { + strcat(lDialogString, "; clear >/dev/tty") ; + } + } + else + { + strcat(lDialogString, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];"); + if ( lWasXterm ) + { + strcat ( lDialogString , +"then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + } + else + { + strcat(lDialogString, + "then echo 1;else echo 0;fi;clear >/dev/tty"); + } + } + } + } + else if ( ! isTerminalRunning ( ) && terminalName() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'" ) ; + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gTitle) ; + strcat ( lDialogString , "\";" ) ; + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, gMessageUnix) ; + strcat ( lDialogString , "\";echo;echo;" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, aTitle) ; + strcat ( lDialogString , "\";echo;" ) ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, aMessage) ; + strcat ( lDialogString , "\"; " ) ; + } + if ( aDialogType && !strcmp("yesno",aDialogType) ) + { + strcat ( lDialogString , "echo -n \"y/n: \"; " ) ; + strcat ( lDialogString , "stty sane -echo;" ) ; + strcat ( lDialogString , + "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);"); + strcat ( lDialogString , + "if echo \"$answer\" | grep -iq \"^y\";then\n"); + strcat ( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; + } + else if ( aDialogType && !strcmp("okcancel",aDialogType) ) + { + strcat ( lDialogString , "echo -n \"[O]kay/[C]ancel: \"; " ) ; + strcat ( lDialogString , "stty sane -echo;" ) ; + strcat ( lDialogString , + "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);"); + strcat ( lDialogString , + "if echo \"$answer\" | grep -iq \"^o\";then\n"); + strcat ( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; + } + else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) + { + strcat ( lDialogString , "echo -n \"[Y]es/[N]o/[C]ancel: \"; " ) ; + strcat ( lDialogString , "stty sane -echo;" ) ; + strcat ( lDialogString , + "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);"); + strcat ( lDialogString , + "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n"); + strcat ( lDialogString , "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n" ) ; + strcat ( lDialogString , "else\n\techo 0\nfi" ) ; + } + else + { + strcat(lDialogString , "echo -n \"press enter to continue \"; "); + strcat ( lDialogString , "stty sane -echo;" ) ; + strcat ( lDialogString , + "answer=$( while ! head -c 1;do true ;done);echo 1"); + } + strcat ( lDialogString , + " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + } + else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notify");return 1;} + + strcpy ( lDialogString , "notify-send \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + strcat ( lDialogString , " | " ) ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat ( lDialogString , "\"" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf ("\n\n%s\n", gTitle); + printf ("%s\n\n", gMessageUnix); + } + if ( aTitle && strlen(aTitle) ) + { + printf ("\n%s\n", aTitle); + } + + tcgetattr(0, &infoOri); + tcgetattr(0, &info); + info.c_lflag &= ~ICANON; + info.c_cc[VMIN] = 1; + info.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &info); + if ( aDialogType && !strcmp("yesno",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("\n%s\n",aMessage); + } + printf("y/n: "); fflush(stdout); + lChar = tolower ( getchar() ) ; + printf("\n\n"); + } + while ( lChar != 'y' && lChar != 'n' ); + lResult = lChar == 'y' ? 1 : 0 ; + } + else if ( aDialogType && !strcmp("okcancel",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("\n%s\n",aMessage); + } + printf("[O]kay/[C]ancel: "); fflush(stdout); + lChar = tolower ( getchar() ) ; + printf("\n\n"); + } + while ( lChar != 'o' && lChar != 'c' ); + lResult = lChar == 'o' ? 1 : 0 ; + } + else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) + { + do + { + if ( aMessage && strlen(aMessage) ) + { + printf("\n%s\n",aMessage); + } + printf("[Y]es/[N]o/[C]ancel: "); fflush(stdout); + lChar = tolower ( getchar() ) ; + printf("\n\n"); + } + while ( lChar != 'y' && lChar != 'n' && lChar != 'c' ); + lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ; + } + else + { + if ( aMessage && strlen(aMessage) ) + { + printf("\n%s\n\n",aMessage); + } + printf("press enter to continue "); fflush(stdout); + getchar() ; + printf("\n\n"); + lResult = 1 ; + } + tcsetattr(0, TCSANOW, &infoOri); + free(lDialogString); + return lResult ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + free(lDialogString); + return 0 ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + + pclose ( lIn ) ; + + /* printf ( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ + + if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + if ( lBuff[0]=='1' ) + { + if ( !strcmp ( lBuff+1 , "Yes" )) strcpy(lBuff,"1"); + else if ( !strcmp ( lBuff+1 , "No" )) strcpy(lBuff,"2"); + } + } + /* printf ( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ + + lResult = !strcmp ( lBuff , "2" ) ? 2 : !strcmp ( lBuff , "1" ) ? 1 : 0; + + /* printf ( "lResult: %d\n" , lResult ) ; */ + free(lDialogString); + return lResult ; +} + + +/* returns NULL on cancel */ +char const * tinyfd_inputBox( + char const * const aTitle , /* NULL or "" */ + char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ + char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ +{ + static char lBuff[MAX_PATH_OR_CMD]; + char * lDialogString = NULL; + char * lpDialogString; + FILE * lIn ; + int lResult ; + int lWasGdialog = 0 ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + int lWasBasicXterm = 0 ; + struct termios oldt ; + struct termios newt ; + char * lEOF; + int lTitleLen ; + int lMessageLen ; + + lBuff[0]='\0'; + + lTitleLen = aTitle ? strlen(aTitle) : 0 ; + lMessageLen = aMessage ? strlen(aMessage) : 0 ; + if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) + { + lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); + } + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} + strcpy ( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat ( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e 'display dialog \"") ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString, "\" ") ; + strcat(lDialogString, "default answer \"") ; + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, aDefaultInput) ; + } + strcat(lDialogString, "\" ") ; + if ( ! aDefaultInput ) + { + strcat(lDialogString, "hidden answer true ") ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + strcat(lDialogString, "with icon note' ") ; + strcat(lDialogString, "-e '\"1\" & text returned of result' " ); + strcat(lDialogString, "-e 'on error number -128' " ) ; + strcat(lDialogString, "-e '0' " ); + strcat(lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat(lDialogString, " -e 'end tell'") ; + } + else if ( zenityPresent() || matedialogPresent() || qarmaPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(zenity --entry" ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(matedialog --entry" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(qarma --entry" ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, " --text=\"") ; + strcat(lDialogString, aMessage) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, " --entry-text=\"") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "\"") ; + } + else + { + strcat(lDialogString, " --hide-text") ; + } + strcat ( lDialogString , + ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(kdialog" ) ; + if ( ! aDefaultInput ) + { + strcat(lDialogString, " --password ") ; + } + else + { + strcat(lDialogString, " --inputbox ") ; + + } + strcat(lDialogString, "\"") ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage ) ; + } + strcat(lDialogString , "\" \"" ) ; + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, aDefaultInput ) ; + } + strcat(lDialogString , "\"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + strcat ( lDialogString , + ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); + } + else if ( gxmessagePresent() || gmessagePresent() ) + { + if ( gxmessagePresent() ) { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \""); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char const *)1;} + strcpy ( lDialogString , "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \""); + } + + if ( aMessage && strlen(aMessage) ) + { + strcat ( lDialogString , aMessage ) ; + } + strcat(lDialogString, "\"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat ( lDialogString , " -title \""); + strcat ( lDialogString , aTitle ) ; + strcat(lDialogString, "\" " ) ; + } + strcat(lDialogString, " -entrytext \"" ) ; + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat ( lDialogString , aDefaultInput ) ; + } + strcat(lDialogString, "\"" ) ; + strcat ( lDialogString , ");echo $?$szAnswer"); + } + else if ( !gdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ +frontmost of process \\\"Python\\\" to true' ''');"); + } + + strcat ( lDialogString ,"res=tkSimpleDialog.askstring(" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aMessage && strlen(aMessage) ) + { + + strcat(lDialogString, "prompt='") ; + lpDialogString = lDialogString + strlen(lDialogString); + replaceSubStr ( aMessage , "\n" , "\\n" , lpDialogString ) ; + strcat(lDialogString, "',") ; + } + if ( aDefaultInput ) + { + if ( strlen(aDefaultInput) ) + { + strcat(lDialogString, "initialvalue='") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "',") ; + } + } + else + { + strcat(lDialogString, "show='*'") ; + } + strcat(lDialogString, ");\nif res is None :\n\tprint 0"); + strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ; + } + else if ( gdialogPresent() || xdialogPresent() + || dialogName() || whiptailPresent() ) + { + if ( gdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + lWasGdialog = 1 ; + strcpy ( lDialogString , "(gdialog " ) ; + } + else if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( dialogName ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + if ( isTerminalRunning ( ) ) + { + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + } + else if ( isTerminalRunning ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;} + strcpy ( lDialogString , "(whiptail " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;} + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(whiptail " ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( !xdialogPresent() && !gdialogPresent() ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, "tab: move focus") ; + if ( ! aDefaultInput && !lWasGdialog ) + { + strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; + } + strcat(lDialogString, "\" ") ; + } + + if ( aDefaultInput || lWasGdialog ) + { + strcat ( lDialogString , "--inputbox" ) ; + } + else + { + if ( !lWasGraphicDialog && dialogName() && isDialogVersionBetter09b() ) + { + strcat ( lDialogString , "--insecure " ) ; + } + strcat ( lDialogString , "--passwordbox" ) ; + } + strcat ( lDialogString , " \"" ) ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString,"\" 10 60 ") ; + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, "\"") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "\" ") ; + } + if ( lWasGraphicDialog ) + { + strcat(lDialogString,") 2>/tmp/tinyfd.txt;\ + if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ + tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; + } + else + { + strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\ + if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ + tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; + + if ( lWasXterm ) + { + strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); + } + else + { + strcat(lDialogString, "; clear >/dev/tty") ; + } + } + } + else if ( ! isTerminalRunning ( ) && terminalName() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + lWasBasicXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'" ) ; + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + tinyfd_messageBox(gTitle,gMessageUnix,"ok","warning",0); + gWarningDisplayed = 1 ; + } + if ( aTitle && strlen(aTitle) && !tinyfd_forceConsole) + { + strcat ( lDialogString , "echo \"" ) ; + strcat ( lDialogString, aTitle) ; + strcat ( lDialogString , "\";echo;" ) ; + } + + strcat ( lDialogString , "echo \"" ) ; + if ( aMessage && strlen(aMessage) ) + { + strcat ( lDialogString, aMessage) ; + } + strcat ( lDialogString , "\";read " ) ; + if ( ! aDefaultInput ) + { + strcat ( lDialogString , "-s " ) ; + } + strcat ( lDialogString , "-p \"" ) ; + strcat ( lDialogString , "(esc+enter to cancel): \" ANSWER " ) ; + strcat ( lDialogString , ";echo 1$ANSWER >/tmp/tinyfd.txt';" ) ; + strcat ( lDialogString , "cat -v /tmp/tinyfd.txt"); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + tinyfd_messageBox(gTitle,gMessageUnix,"ok","warning",0); + gWarningDisplayed = 1 ; + } + if ( aTitle && strlen(aTitle) ) + { + printf ("\n%s\n", aTitle); + } + if ( aMessage && strlen(aMessage) ) + { + printf("\n%s\n",aMessage); + } + printf("(esc+enter to cancel): "); fflush(stdout); + if ( ! aDefaultInput ) + { + tcgetattr(STDIN_FILENO, & oldt) ; + newt = oldt ; + newt.c_lflag &= ~ECHO ; + tcsetattr(STDIN_FILENO, TCSANOW, & newt); + } + + lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); + /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ + if ( ! lEOF || (lBuff[0] == '\0') ) + { + free(lDialogString); + return NULL; + } + + if ( lBuff[0] == '\n' ) + { + lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); + /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ + if ( ! lEOF || (lBuff[0] == '\0') ) + { + free(lDialogString); + return NULL; + } + } + + if ( ! aDefaultInput ) + { + tcsetattr(STDIN_FILENO, TCSANOW, & oldt); + printf ("\n"); + } + printf ("\n"); + if ( strchr(lBuff,27) ) + { + free(lDialogString); + return NULL ; + } + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + free(lDialogString); + return lBuff ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + lIn = popen ( lDialogString , "r" ); + if ( ! lIn ) + { + if ( fileExists("/tmp/tinyfd.txt") ) + { + wipefile("/tmp/tinyfd.txt"); + remove("/tmp/tinyfd.txt"); + } + if ( fileExists("/tmp/tinyfd0.txt") ) + { + wipefile("/tmp/tinyfd0.txt"); + remove("/tmp/tinyfd0.txt"); + } + free(lDialogString); + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + + pclose ( lIn ) ; + + if ( fileExists("/tmp/tinyfd.txt") ) + { + wipefile("/tmp/tinyfd.txt"); + remove("/tmp/tinyfd.txt"); + } + if ( fileExists("/tmp/tinyfd0.txt") ) + { + wipefile("/tmp/tinyfd0.txt"); + remove("/tmp/tinyfd0.txt"); + } + + /* printf ( "len Buff: %lu\n" , strlen(lBuff) ) ; */ + /* printf ( "lBuff0: %s\n" , lBuff ) ; */ + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ + if ( lWasBasicXterm ) + { + if ( strstr(lBuff,"^[") ) /* esc was pressed */ + { + free(lDialogString); + return NULL ; + } + } + + lResult = strncmp ( lBuff , "1" , 1) ? 0 : 1 ; + /* printf ( "lResult: %d \n" , lResult ) ; */ + if ( ! lResult ) + { + free(lDialogString); + return NULL ; + } + /* printf ( "lBuff+1: %s\n" , lBuff+1 ) ; */ + free(lDialogString); + + return lBuff+1 ; +} + + +char const * tinyfd_saveFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) /* NULL or "image files" */ +{ + + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lString [MAX_PATH_OR_CMD] ; + int i ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + char const * p ; + FILE * lIn ; + lBuff[0]='\0'; + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} + strcpy ( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat ( lDialogString , " -e 'tell application \"Finder\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e 'POSIX path of ( choose file name " ); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with prompt \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "default location \"") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "\" " ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "default name \"") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "\" " ) ; + } + strcat ( lDialogString , ")' " ) ; + strcat(lDialogString, "-e 'on error number -128' " ) ; + strcat(lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat ( lDialogString, " -e 'end tell'") ; + } + else if ( zenityPresent() || matedialogPresent() || qarmaPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity" ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog" ) ; + + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} + strcpy ( lDialogString , "qarma" ) ; + + } + strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ; + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPathAndFile) ; + strcat(lDialogString, "\"") ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat ( lDialogString , " --file-filter='" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + strcat ( lDialogString , " | " ) ; + } + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + strcat ( lDialogString , "' --file-filter='All files | *'" ) ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "kdialog --getsavefilename" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , " | " ) ; + strcat ( lDialogString , aSingleFilterDescription ) ; + } + strcat ( lDialogString , "\"" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( )) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set\ + frontmost of process \\\"Python\\\" to true' ''');"); + } + + strcat ( lDialogString , "print tkFileDialog.asksaveasfilename("); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialfile='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + } + if ( ( aNumOfFilterPatterns > 1 ) + || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ + && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) + { + strcat(lDialogString , "filetypes=(" ) ; + strcat ( lDialogString , "('" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + } + strcat ( lDialogString , "',(" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , "'" ) ; + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , "'," ) ; + } + strcat ( lDialogString , "))," ) ; + strcat ( lDialogString , "('All files','*'))" ) ; + } + strcat ( lDialogString , ")\"" ) ; + } + else if ( xdialogPresent() || dialogName() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( !xdialogPresent() && !gdialogPresent() ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( ! strchr(aDefaultPathAndFile, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, aDefaultPathAndFile) ; + } + else if ( ! isTerminalRunning ( ) && !lWasGraphicDialog ) + { + strcat(lDialogString, getenv("HOME")) ; + strcat(lDialogString, "/") ; + } + else + { + strcat(lDialogString, "./") ; + } + + if ( lWasGraphicDialog ) + { + strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; + } + else + { + strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; + if ( lWasXterm ) + { + strcat ( lDialogString , + "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + } + else + { + strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + } + } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox (aTitle,NULL,NULL);} + p = tinyfd_inputBox ( aTitle , "Save file" , "" ) ; + getPathWithoutFinalSlash ( lString , p ) ; + if ( strlen ( lString ) && ! dirExists ( lString ) ) + { + return NULL ; + } + getLastName(lString,p); + if ( ! strlen(lString) ) + { + return NULL; + } + return p ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + if ( ! strlen(lBuff) ) + { + return NULL; + } + getPathWithoutFinalSlash ( lString , lBuff ) ; + if ( strlen ( lString ) && ! dirExists ( lString ) ) + { + return NULL ; + } + getLastName(lString,lBuff); + if ( ! filenameValid(lString) ) + { + return NULL; + } + return lBuff ; +} + + +/* in case of multiple files, the separator is | */ +char const * tinyfd_openFileDialog ( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultPathAndFile , /* NULL or "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL or "image files" */ + int const aAllowMultipleSelects ) /* 0 or 1 */ +{ + static char lBuff [MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lString [MAX_PATH_OR_CMD] ; + int i ; + FILE * lIn ; + char * p ; + char const * p2 ; + int lWasKdialog = 0 ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + lBuff[0]='\0'; + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} + strcpy ( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat ( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e '" ); + if ( ! aAllowMultipleSelects ) + { + + + strcat ( lDialogString , "POSIX path of ( " ); + } + else + { + strcat ( lDialogString , "set mylist to " ); + } + strcat ( lDialogString , "choose file " ); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with prompt \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "default location \"") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "\" " ) ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , "of type {\"" ); + strcat ( lDialogString , aFilterPatterns [0] + 2 ) ; + strcat ( lDialogString , "\"" ) ; + for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , ",\"" ) ; + strcat ( lDialogString , aFilterPatterns [i] + 2) ; + strcat ( lDialogString , "\"" ) ; + } + strcat ( lDialogString , "} " ) ; + } + if ( aAllowMultipleSelects ) + { + strcat ( lDialogString , "multiple selections allowed true ' " ) ; + strcat ( lDialogString , + "-e 'set mystring to POSIX path of item 1 of mylist' " ); + strcat ( lDialogString , + "-e 'repeat with i from 2 to the count of mylist' " ); + strcat ( lDialogString , "-e 'set mystring to mystring & \"|\"' " ); + strcat ( lDialogString , + "-e 'set mystring to mystring & POSIX path of item i of mylist' " ); + strcat ( lDialogString , "-e 'end repeat' " ); + strcat ( lDialogString , "-e 'mystring' " ); + } + else + { + strcat ( lDialogString , ")' " ) ; + } + strcat(lDialogString, "-e 'on error number -128' " ) ; + strcat(lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat ( lDialogString, " -e 'end tell'") ; + } + else if ( zenityPresent() || matedialogPresent() || qarmaPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --file-selection" ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog --file-selection" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} + strcpy ( lDialogString , "qarma --file-selection" ) ; + } + + if ( aAllowMultipleSelects ) + { + strcat ( lDialogString , " --multiple" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPathAndFile) ; + strcat(lDialogString, "\"") ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat ( lDialogString , " --file-filter='" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + strcat ( lDialogString , " | " ) ; + } + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + strcat ( lDialogString , "' --file-filter='All files | *'" ) ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + lWasKdialog = 1 ; + strcpy ( lDialogString , "kdialog --getopenfilename" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , " " ) ; + } + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , " | " ) ; + strcat ( lDialogString , aSingleFilterDescription ) ; + } + strcat ( lDialogString , "\"" ) ; + } + if ( aAllowMultipleSelects ) + { + strcat ( lDialogString , " --multiple --separate-output" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ +frontmost of process \\\"Python\\\" to true' ''');"); + } + strcat ( lDialogString , "lFiles=tkFileDialog.askopenfilename("); + if ( aAllowMultipleSelects ) + { + strcat ( lDialogString , "multiple=1," ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + getPathWithoutFinalSlash ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + getLastName ( lString , aDefaultPathAndFile ) ; + if ( strlen(lString) ) + { + strcat(lDialogString, "initialfile='") ; + strcat(lDialogString, lString ) ; + strcat(lDialogString , "'," ) ; + } + } + if ( ( aNumOfFilterPatterns > 1 ) + || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ + && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) + { + strcat(lDialogString , "filetypes=(" ) ; + strcat ( lDialogString , "('" ) ; + if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + { + strcat ( lDialogString , aSingleFilterDescription ) ; + } + strcat ( lDialogString , "',(" ) ; + for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat ( lDialogString , "'" ) ; + strcat ( lDialogString , aFilterPatterns [i] ) ; + strcat ( lDialogString , "'," ) ; + } + strcat ( lDialogString , "))," ) ; + strcat ( lDialogString , "('All files','*'))" ) ; + } + strcat ( lDialogString , ");\ +\nif not isinstance(lFiles, tuple):\n\tprint lFiles\nelse:\ +\n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\ +\n\tprint lFilesString[:-1]\n\"" ) ; + } + else if ( xdialogPresent() || dialogName() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( !xdialogPresent() && !gdialogPresent() ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--fselect \"" ) ; + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( ! strchr(aDefaultPathAndFile, '/') ) + { + strcat(lDialogString, "./") ; + } + strcat(lDialogString, aDefaultPathAndFile) ; + } + else if ( ! isTerminalRunning ( ) && !lWasGraphicDialog ) + { + strcat(lDialogString, getenv("HOME")) ; + strcat(lDialogString, "/"); + } + else + { + strcat(lDialogString, "./") ; + } + + if ( lWasGraphicDialog ) + { + strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; + } + else + { + strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; + if ( lWasXterm ) + { + strcat ( lDialogString , + "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + } + else + { + strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + } + } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox (aTitle,NULL,NULL);} + p2 = tinyfd_inputBox(aTitle, "Open file",""); + if ( ! fileExists (p2) ) + { + return NULL ; + } + return p2 ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + lBuff[0]='\0'; + p=lBuff; + while ( fgets ( p , sizeof ( lBuff ) , lIn ) != NULL ) + { + p += strlen ( p ); + } + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + if ( lWasKdialog && aAllowMultipleSelects ) + { + p = lBuff ; + while ( ( p = strchr ( p , '\n' ) ) ) + * p = '|' ; + } + /* printf ( "lBuff2: %s\n" , lBuff ) ; */ + if ( ! strlen ( lBuff ) ) + { + return NULL; + } + if ( aAllowMultipleSelects && strchr(lBuff, '|') ) + { + p2 = ensureFilesExist( lBuff , lBuff ) ; + } + else if ( fileExists (lBuff) ) + { + p2 = lBuff ; + } + else + { + return NULL ; + } + /* printf ( "lBuff3: %s\n" , p2 ) ; */ + + return p2 ; +} + + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPath ) /* "" */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + char const * p ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + lBuff[0]='\0'; + + if ( osascriptPresent ( )) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} + strcpy ( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat ( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e 'POSIX path of ( choose folder "); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with prompt \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, "default location \"") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "\" " ) ; + } + strcat ( lDialogString , ")' " ) ; + strcat(lDialogString, "-e 'on error number -128' " ) ; + strcat(lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat ( lDialogString, " -e 'end tell'") ; + } + else if ( zenityPresent() || matedialogPresent() || qarmaPresent() ) + { + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --file-selection --directory" ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + strcpy ( lDialogString , "matedialog --file-selection --directory" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} + strcpy ( lDialogString , "qarma --file-selection --directory" ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPath) ; + strcat(lDialogString, "\"") ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + strcpy ( lDialogString , "kdialog --getexistingdirectory" ) ; + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, " \"") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, " :" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( ! xdialogPresent() && tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat ( lDialogString , +" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ +frontmost of process \\\"Python\\\" to true' ''');"); + } + + strcat ( lDialogString , "print tkFileDialog.askdirectory("); + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; + } + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "'" ) ; + } + strcat ( lDialogString , ")\"" ) ; + } + else if ( xdialogPresent() || dialogName() ) + { + if ( xdialogPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasGraphicDialog = 1 ; + strcpy ( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + strcpy ( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} + lWasXterm = 1 ; + strcpy ( lDialogString , terminalName() ) ; + strcat ( lDialogString , "'(" ) ; + strcat ( lDialogString , dialogName() ) ; + strcat ( lDialogString , " " ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "--title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + if ( !xdialogPresent() && !gdialogPresent() ) + { + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, + "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; + strcat(lDialogString, "\" ") ; + } + + strcat ( lDialogString , "--dselect \"" ) ; + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, aDefaultPath) ; + ensureFinalSlash(lDialogString); + } + else if ( ! isTerminalRunning ( ) && !lWasGraphicDialog ) + { + strcat(lDialogString, getenv("HOME")) ; + strcat(lDialogString, "/"); + } + else + { + strcat(lDialogString, "./") ; + } + + if ( lWasGraphicDialog ) + { + strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; + } + else + { + strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; + if ( lWasXterm ) + { + strcat ( lDialogString , + "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + } + else + { + strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + } + } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox (aTitle,NULL,NULL);} + p = tinyfd_inputBox(aTitle, "Select folder",""); + if ( !p || ! strlen ( p ) || ! dirExists ( p ) ) + { + return NULL ; + } + return p ; + } + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + if ( ! strlen ( lBuff ) || ! dirExists ( lBuff ) ) + { + return NULL ; + } + return lBuff ; +} + + +/* returns the hexcolor as a string "#FF0000" */ +/* aoResultRGB also contains the result */ +/* aDefaultRGB is used only if aDefaultHexRGB is NULL */ +/* aDefaultRGB and aoResultRGB can be the same array */ +char const * tinyfd_colorChooser( + char const * const aTitle , /* NULL or "" */ + char const * const aDefaultHexRGB , /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */ +{ + static char lBuff [128] ; + char lTmp [128] ; + char lDialogString [MAX_PATH_OR_CMD] ; + char lDefaultHexRGB[8]; + char * lpDefaultHexRGB; + unsigned char lDefaultRGB[3]; + char const * p; + FILE * lIn ; + int i ; + int lWasZenity3 = 0 ; + int lWasOsascript = 0 ; + int lWasXdialog = 0 ; + lBuff[0]='\0'; + + if ( aDefaultHexRGB ) + { + Hex2RGB ( aDefaultHexRGB , lDefaultRGB ) ; + lpDefaultHexRGB = (char *) aDefaultHexRGB ; + } + else + { + lDefaultRGB[0]=aDefaultRGB[0]; + lDefaultRGB[1]=aDefaultRGB[1]; + lDefaultRGB[2]=aDefaultRGB[2]; + RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; + lpDefaultHexRGB = (char *) lDefaultHexRGB ; + } + + if ( osascriptPresent ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} + lWasOsascript = 1 ; + strcpy ( lDialogString , "osascript"); + + if ( ! osx9orBetter() ) + { + strcat ( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat ( lDialogString , " -e 'try' -e 'set mycolor to choose color default color {"); + } + else + { + strcat ( lDialogString , +" -e 'try' -e 'tell app (path to frontmost application as Unicode text) \ +to set mycolor to choose color default color {"); + } + + sprintf(lTmp, "%d", 256 * lDefaultRGB[0] ) ; + strcat(lDialogString, lTmp ) ; + strcat(lDialogString, "," ) ; + sprintf(lTmp, "%d", 256 * lDefaultRGB[1] ) ; + strcat(lDialogString, lTmp ) ; + strcat(lDialogString, "," ) ; + sprintf(lTmp, "%d", 256 * lDefaultRGB[2] ) ; + strcat(lDialogString, lTmp ) ; + strcat(lDialogString, "}' " ) ; + strcat ( lDialogString , +"-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' " ); + strcat ( lDialogString , +"-e 'repeat with i from 2 to the count of mycolor' " ); + strcat ( lDialogString , +"-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' " ); + strcat ( lDialogString , "-e 'end repeat' " ); + strcat ( lDialogString , "-e 'mystring' "); + strcat(lDialogString, "-e 'on error number -128' " ) ; + strcat(lDialogString, "-e 'end try'") ; + if ( ! osx9orBetter() ) strcat ( lDialogString, " -e 'end tell'") ; + } + else if ( zenity3Present() || matedialogPresent() || qarmaPresent() ) + { + lWasZenity3 = 1 ; + if ( zenity3Present() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char const *)1;} + sprintf ( lDialogString , +"zenity --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; + } + else if ( matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} + sprintf ( lDialogString , +"matedialog --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} + sprintf ( lDialogString , +"qarma --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} + sprintf ( lDialogString , +"kdialog --getcolor --default '%s'" , lpDefaultHexRGB ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( xdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} + lWasXdialog = 1 ; + strcpy ( lDialogString , "Xdialog --colorsel \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + } + strcat(lDialogString, "\" 0 60 ") ; + sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0], + lDefaultRGB[1],lDefaultRGB[2]); + strcat(lDialogString, lTmp) ; + strcat(lDialogString, " 2>&1"); + } + else if ( tkinter2Present ( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"tkinter");return (char const *)1;} + strcpy ( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning ( ) && isDarwin ( ) ) + { + strcat ( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat ( lDialogString , +" -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); + + if ( isDarwin ( ) ) + { + strcat ( lDialogString , +"import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set \ +frontmost of process \\\"Python\\\" to true' ''');"); + } + + strcat ( lDialogString , "res=tkColorChooser.askcolor(color='" ) ; + strcat(lDialogString, lpDefaultHexRGB ) ; + strcat(lDialogString, "'") ; + + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, ",title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "'") ; + } + strcat ( lDialogString , ");\ +\nif res[1] is not None:\n\tprint res[1]\"" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox (aTitle,NULL,NULL);} + p = tinyfd_inputBox(aTitle, + "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) + { + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + return p ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + { + } + pclose ( lIn ) ; + if ( ! strlen ( lBuff ) ) + { + return NULL ; + } + /* printf ( "len Buff: %lu\n" , strlen(lBuff) ) ; */ + /* printf ( "lBuff0: %s\n" , lBuff ) ; */ + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + if ( lWasZenity3 ) + { + if ( lBuff[0] == '#' ) { + lBuff[3]=lBuff[5]; + lBuff[4]=lBuff[6]; + lBuff[5]=lBuff[9]; + lBuff[6]=lBuff[10]; + lBuff[7]='\0'; + Hex2RGB(lBuff,aoResultRGB); + } + else if ( lBuff[3] == '(' ) { + sscanf(lBuff,"rgb(%hhu,%hhu,%hhu", + & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); + RGB2Hex(aoResultRGB,lBuff); + } + else if ( lBuff[4] == '(' ) { + sscanf(lBuff,"rgba(%hhu,%hhu,%hhu", + & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); + RGB2Hex(aoResultRGB,lBuff); + } + } + else if ( lWasOsascript || lWasXdialog ) + { + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + sscanf(lBuff,"%hhu %hhu %hhu", + & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); + RGB2Hex(aoResultRGB,lBuff); + } + else + { + Hex2RGB(lBuff,aoResultRGB); + } + /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */ + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + return lBuff ; +} + + +/* not cross platform - zenity only */ +/* contributed by Attila Dusnoki */ +char const * tinyfd_arrayDialog ( + char const * const aTitle , /* "" */ + int const aNumOfColumns , /* 2 */ + char const * const * const aColumns , /* {"Column 1","Column 2"} */ + int const aNumOfRows , /* 2 */ + char const * const * const aCells ) + /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ +{ + static char lBuff [MAX_PATH_OR_CMD] ; + char lDialogString [MAX_PATH_OR_CMD] ; + FILE * lIn ; + lBuff[0]='\0'; + int i ; + + if ( zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} + strcpy ( lDialogString , "zenity --list --print-column=ALL" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title=\"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + + if ( aColumns && (aNumOfColumns > 0) ) + { + for ( i = 0 ; i < aNumOfColumns ; i ++ ) + { + strcat ( lDialogString , " --column=\"" ) ; + strcat ( lDialogString , aColumns [i] ) ; + strcat ( lDialogString , "\"" ) ; + } + } + + if ( aCells && (aNumOfRows > 0) ) + { + strcat ( lDialogString , " " ) ; + for ( i = 0 ; i < aNumOfRows*aNumOfColumns ; i ++ ) + { + strcat ( lDialogString , "\"" ) ; + strcat ( lDialogString , aCells [i] ) ; + strcat ( lDialogString , "\" " ) ; + } + } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"");return (char const *)0;} + return NULL ; + } + + if (tinyfd_verbose) printf ( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + { + return NULL ; + } + while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + {} + pclose ( lIn ) ; + if ( lBuff[strlen ( lBuff ) -1] == '\n' ) + { + lBuff[strlen ( lBuff ) -1] = '\0' ; + } + /* printf ( "lBuff: %s\n" , lBuff ) ; */ + if ( ! strlen ( lBuff ) ) + { + return NULL ; + } + return lBuff ; +} +#endif /* _WIN32 */ + + +/* +int main(void) +{ +char const * lTmp; +char const * lTheSaveFileName; +char const * lTheOpenFileName; +char const * lTheSelectFolderName; +char const * lTheHexColor; +char const * lWillBeGraphicMode; +unsigned char lRgbColor[3]; +FILE * lIn; +char lBuffer[1024]; +char lThePassword[1024]; +char const * lFilterPatterns[2] = { "*.txt", "*.text" }; + +lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL); + +if (lWillBeGraphicMode) +{ + strcpy(lBuffer, "graphic mode: "); +} +else +{ + strcpy(lBuffer, "console mode: "); +} + +strcat(lBuffer, tinyfd_response); +strcpy(lThePassword, "tinyfiledialogs v"); +strcat(lThePassword, tinyfd_version); +tinyfd_messageBox(lThePassword, lBuffer, "ok", "info", 0); + +if (lWillBeGraphicMode && !tinyfd_forceConsole) +{ + tinyfd_forceConsole = ! tinyfd_messageBox("Hello World", + "graphic dialogs [yes] / console mode [no]?", + "yesno", "question", 1); +} + +lTmp = tinyfd_inputBox( + "a password box", "your password will be revealed", NULL); + +if (!lTmp) return 1; + +strcpy(lThePassword, lTmp); + +lTheSaveFileName = tinyfd_saveFileDialog( + "let us save this password", + "passwordFile.txt", + 2, + lFilterPatterns, + NULL); + +if (!lTheSaveFileName) +{ + tinyfd_messageBox( + "Error", + "Save file name is NULL", + "ok", + "error", + 1); + return 1; +} + +lIn = fopen(lTheSaveFileName, "w"); +if (!lIn) +{ + tinyfd_messageBox( + "Error", + "Can not open this file in write mode", + "ok", + "error", + 1); + return 1; +} +fputs(lThePassword, lIn); +fclose(lIn); + +lTheOpenFileName = tinyfd_openFileDialog( + "let us read the password back", + "", + 2, + lFilterPatterns, + NULL, + 0); + +if (!lTheOpenFileName) +{ + tinyfd_messageBox( + "Error", + "Open file name is NULL", + "ok", + "error", + 1); + return 1; +} + +lIn = fopen(lTheOpenFileName, "r"); + +if (!lIn) +{ + tinyfd_messageBox( + "Error", + "Can not open this file in read mode", + "ok", + "error", + 1); + return(1); +} +lBuffer[0] = '\0'; +fgets(lBuffer, sizeof(lBuffer), lIn); +fclose(lIn); + +tinyfd_messageBox("your password is", + lBuffer, "ok", "info", 1); + +lTheSelectFolderName = tinyfd_selectFolderDialog( + "let us just select a directory", NULL); + +if (!lTheSelectFolderName) +{ + tinyfd_messageBox( + "Error", + "Select folder name is NULL", + "ok", + "error", + 1); + return 1; +} + +tinyfd_messageBox("The selected folder is", + lTheSelectFolderName, "ok", "info", 1); + +lTheHexColor = tinyfd_colorChooser( + "choose a nice color", + "#FF0077", + lRgbColor, + lRgbColor); + +if (!lTheHexColor) +{ + tinyfd_messageBox( + "Error", + "hexcolor is NULL", + "ok", + "error", + 1); + return 1; +} + +tinyfd_messageBox("The selected hexcolor is", + lTheHexColor, "ok", "info", 1); + + return 0; +} +*/ + +#ifdef _MSC_VER +#pragma warning(default:4996) +#pragma warning(default:4100) +#pragma warning(default:4706) +#endif diff --git a/third-party/libtinyfiledialogs/tinyfiledialogs.h b/third-party/libtinyfiledialogs/tinyfiledialogs.h new file mode 100644 index 00000000000..65e6dffb467 --- /dev/null +++ b/third-party/libtinyfiledialogs/tinyfiledialogs.h @@ -0,0 +1,301 @@ +/*_________ + / \ tinyfiledialogs.h v2.9.3 [July 12, 2017] zlib licence + |tiny file| Unique header file created [November 9, 2014] + | dialogs | Copyright (c) 2014 - 2017 Guillaume Vareille http://ysengrin.com + \____ ___/ http://tinyfiledialogs.sourceforge.net + \| + git://git.code.sf.net/p/tinyfiledialogs/code + ______________________________________________ + | | + | email: tinyfiledialogs@ysengrin.com | + |______________________________________________| + +A big thank you to Don Heyse http://ldglite.sf.net for + his code contributions, bug corrections & thorough testing! + + git://git.code.sf.net/p/tinyfiledialogs/code + +Please + 1) let me know + - if you are including tiny file dialogs, + I'll be happy to add your link to the list of projects using it. + - If you are using it on different hardware / OS / compiler. + 2) leave a review on Sourceforge. Thanks. + +tiny file dialogs (cross-platform C C++) +InputBox PasswordBox MessageBox ColorPicker +OpenFileDialog SaveFileDialog SelectFolderDialog +Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more +SSH supported via automatic switch to console mode or X11 forwarding + +One C file (add it to your C or C++ project) with 6 functions: +- message & question +- input & password +- save file +- open file(s) +- select folder +- color picker + +Complements OpenGL GLFW GLUT GLUI VTK SFML TGUI SDL Ogre Unity3d ION OpenCV +CEGUI MathGL GLM CPW GLOW IMGUI MyGUI GLT NGL STB & GUI less programs + +NO INIT +NO MAIN LOOP +NO LINKING +NO INCLUDE + +The dialogs can be forced into console mode + +Windows (XP to 10) ASCII MBCS UTF-8 UTF-16 +- native code & vbs create the graphic dialogs +- enhanced console mode can use dialog.exe from +http://andrear.altervista.org/home/cdialog.php +- basic console input + +Unix (command line calls) ASCII UTF-8 +- applescript +- zenity / matedialog / qarma (zenity for qt) +- kdialog +- Xdialog +- python2 tkinter +- dialog (opens a console if needed) +- basic console input +The same executable can run across desktops & distributions + +tested with C & C++ compilers +on VisualStudio MinGW Mac Linux Bsd Solaris Minix Raspbian +using Gnome Kde Enlightenment Mate Cinnamon Unity +Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox Awesome Jwm + +bindings for LUA and C# dll +included in LWJGL(java), rust, Allegrobasic + +- License - + +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. +*/ + +#ifndef TINYFILEDIALOGS_H +#define TINYFILEDIALOGS_H + +/* #define TINYFD_NOLIB */ +/* On windows, define TINYFD_NOLIB here +if you don't want to include the code creating the graphic dialogs. +Then you won't need to link against Comdlg32.lib and Ole32.lib */ + +/* if tinydialogs.c is compiled with a C++ compiler rather than with a C compiler +(ie. you change the extension from .c to .cpp), you need to comment out: +extern "C" { +and the corresponding closing bracket near the end of this file: +} +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern char tinyfd_version[8]; /* contains tinyfd current version number */ + +#ifdef _WIN32 +/* for UTF-16 use the functions at the end of this files */ +extern int tinyfd_winUtf8; /* 0 (default) or 1 */ +/* on windows string char can be 0:MBSC or 1:UTF-8 +unless your code is really prepared for UTF-8 on windows, leave this on MBSC. +Or you can use the UTF-16 (wchar) prototypes at the end of ths file.*/ +#endif + +extern int tinyfd_forceConsole ; /* 0 (default) or 1 */ +/* for unix & windows: 0 (graphic mode) or 1 (console mode). +0: try to use a graphic solution, if it fails then it uses console mode. +1: forces all dialogs into console mode even when an X server is present, + if the package dialog (and a console is present) or dialog.exe is installed. + on windows it only make sense for console applications */ + +extern char tinyfd_response[1024]; +/* if you pass "tinyfd_query" as aTitle, +the functions will not display the dialogs +but will return 0 for console mode, 1 for graphic mode. +tinyfd_response is then filled with the retain solution. +possible values for tinyfd_response are (all lowercase) +for the graphic mode: + windows applescript zenity zenity3 matedialog qarma kdialog + xdialog tkinter gdialog gxmessage xmessage +for the console mode: + dialog whiptail basicinput */ + +int tinyfd_messageBox ( + char const * const aTitle , /* "" */ + char const * const aMessage , /* "" may contain \n \t */ + char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * const aIconType , /* "info" "warning" "error" "question" */ + int const aDefaultButton ) ; + /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ + +char const * tinyfd_inputBox ( + char const * const aTitle , /* "" */ + char const * const aMessage , /* "" may NOT contain \n \t on windows */ + char const * const aDefaultInput ) ; /* "" , if NULL it's a passwordBox */ + /* returns NULL on cancel */ + +char const * tinyfd_saveFileDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPathAndFile , /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL | {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription ) ; /* NULL | "text files" */ + /* returns NULL on cancel */ + +char const * tinyfd_openFileDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPathAndFile , /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + char const * const * const aFilterPatterns , /* NULL {"*.jpg","*.png"} */ + char const * const aSingleFilterDescription , /* NULL | "image files" */ + int const aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +char const * tinyfd_selectFolderDialog ( + char const * const aTitle , /* "" */ + char const * const aDefaultPath ) ; /* "" */ + /* returns NULL on cancel */ + +char const * tinyfd_colorChooser( + char const * const aTitle , /* "" */ + char const * const aDefaultHexRGB , /* NULL or "#FF0000" */ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ + /* returns the hexcolor as a string "#FF0000" */ + /* aoResultRGB also contains the result */ + /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ + /* aDefaultRGB and aoResultRGB can be the same array */ + /* returns NULL on cancel */ + + +/************ NOT CROSS PLATFORM SECTION STARTS HERE ************************/ +#ifdef _WIN32 +#ifndef TINYFD_NOLIB + +/* windows only - utf-16 version */ +int tinyfd_messageBoxW( + wchar_t const * const aTitle , + wchar_t const * const aMessage, /* "" may contain \n \t */ + wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" */ + wchar_t const * const aIconType, /* "info" "warning" "error" "question" */ + int const aDefaultButton ); /* 0 for cancel/no , 1 for ok/yes */ + /* returns 0 for cancel/no , 1 for ok/yes */ + +/* windows only - utf-16 version */ +wchar_t const * tinyfd_saveFileDialogW( + wchar_t const * const aTitle, /* NULL or "" */ + wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ + int const aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription); /* NULL or "image files" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t const * tinyfd_openFileDialogW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultPathAndFile, /* "" */ + int const aNumOfFilterPatterns , /* 0 */ + wchar_t const * const * const aFilterPatterns, /* NULL {"*.jpg","*.png"} */ + wchar_t const * const aSingleFilterDescription, /* NULL | "image files" */ + int const aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ + wchar_t const * tinyfd_selectFolderDialogW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultPath); /* "" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t const * tinyfd_colorChooserW( + wchar_t const * const aTitle, /* "" */ + wchar_t const * const aDefaultHexRGB, /* NULL or "#FF0000" */ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ + /* returns the hexcolor as a string "#FF0000" */ + /* aoResultRGB also contains the result */ + /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ + /* aDefaultRGB and aoResultRGB can be the same array */ + /* returns NULL on cancel */ + + +#endif /*TINYFD_NOLIB*/ +#else /*_WIN32*/ + +/* unix zenity only */ +char const * tinyfd_arrayDialog( + char const * const aTitle , /* "" */ + int const aNumOfColumns , /* 2 */ + char const * const * const aColumns, /* {"Column 1","Column 2"} */ + int const aNumOfRows, /* 2*/ + char const * const * const aCells); + /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ + +#endif /*_WIN32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* TINYFILEDIALOGS_H */ + +/* +- This is not for android nor ios. +- The code is pure C, perfectly compatible with C++. +- the windows only wchar_t (utf-16) prototypes are in the header file +- windows is fully supported from XP to 10 (maybe even older versions) +- C# & LUA via dll, see example files +- OSX supported from 10.4 to 10.11 (maybe even older versions) +- Avoid using " and ' in titles and messages. +- There's one file filter only, it may contain several patterns. +- If no filter description is provided, + the list of patterns will become the description. +- char const * filterPatterns[3] = { "*.obj" , "*.stl" , "*.dxf" } ; +- On windows link against Comdlg32.lib and Ole32.lib + This linking is not compulsary for console mode (see above). +- On unix: it tries command line calls, so no such need. +- On unix you need applescript, zenity, matedialog, qarma, kdialog, Xdialog, + python2/tkinter or dialog (will open a terminal if running without console). +- One of those is already included on most (if not all) desktops. +- In the absence of those it will use gdialog, gxmessage or whiptail + with a textinputbox. +- If nothing is found, it switches to basic console input, + it opens a console if needed (requires xterm + bash). +- Use windows separators on windows and unix separators on unix. +- String memory is preallocated statically for all the returned values. +- File and path names are tested before return, they are valid. +- If you pass only a path instead of path + filename, + make sure it ends with a separator. +- tinyfd_forceConsole=1; at run time, forces dialogs into console mode. +- On windows, console mode only make sense for console applications. +- Mutiple selects are not allowed in console mode. +- The package dialog must be installed to run in enhanced console mode. + It is already installed on most unix systems. +- On osx, the package dialog can be installed via http://macports.org +- On windows, for enhanced console mode, + dialog.exe should be copied somewhere on your executable path. + It can be found at the bottom of the following page: + http://andrear.altervista.org/home/cdialog.php +- If dialog is missing, it will switch to basic console input. +- You can query the type of dialog that will be use. +- MinGW needs gcc >= v4.9 otherwise some headers are incomplete. +- The Hello World (and a bit more) is on the sourceforge site: +*/ diff --git a/vendor.yaml b/vendor.yaml index c0f2d771d7e..dc67da70e1a 100644 --- a/vendor.yaml +++ b/vendor.yaml @@ -35,3 +35,7 @@ third-party/tree-sitter: git: https://github.com/tree-sitter/tree-sitter/tree/v0.20.8 third-party/replxx: git: https://github.com/AmokHuginnsson/replxx/commit/1f149bfe20bf6e49c1afd4154eaf0032c8c2fda2 +third-party/libtinyfiledialogs: + git: https://github.com/native-toolkit/libtinyfiledialogs/commit/cc6b593c029110af8045826ce691f540c85e850c + alternatives: + - https://github.com/btzy/nativefiledialog-extended (only file dialog support though!)