diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..da476ee --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,71 @@ +name: Run CI +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + RUST_BACKTRACE: 1 + SHELL: /bin/bash + +jobs: + linux-ci: + name: Linux + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + override: true + - name: Build + run: cargo build + - name: Tests + run: cargo test --all + + mac-ci: + name: macOS + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + override: true + - name: Build + run: cargo build + - name: Tests + run: cargo test --all + + windows-ci: + name: Linux + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + override: true + - name: Build + run: cargo build + - name: Tests + run: cargo test --all diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0b2b076..0000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: rust -rust: - - nightly - -os: - - linux - - osx diff --git a/Cargo.toml b/Cargo.toml index 5a06486..53ceb1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,24 @@ [package] name = "tinyfiledialogs" -version = "0.1.0" +description = "High-level Rust binding for the tinyfiledialogs C library." +version = "3.9.1" authors = ["Guillaume Vareille ", "Josh Matthews "] license = "zlib-acknowledgement" +readme = "README.md" +keywords = ["dialog", "ui", "windows", "unix"] +repository = "https://github.com/jdm/tinyfiledialogs-rs" build = "build.rs" +[features] +default = ["windows-hidpi"] +windows-hidpi = [] + +[lib] +name = "tinyfiledialogs" + [dependencies] libc = "0.2.0" +[build-dependencies] +cc = "1" diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e48486 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# tinyfiledialogs-rs + +This is a high-level Rust binding to the excellent [tinyfiledialogs library](https://sourceforge.net/projects/tinyfiledialogs/) +by Guillaume Vareille. The source for the C library is included in +the `libtinyfiledialogs` directory to facilitate an all-in-one package +when using the Rust bindings via Cargo. It is updated sporadically from +the original repository, which should be used as the primary source for +all non-Rust users of the library. + +To use this library, add this to the `dependencies` section in `Cargo.toml`: +``` +tinyfiledialogs = "3.0" +``` + +## Security Warning + +tinyfiledialogs should only be used with trusted input. Using it with +untrusted input, for example as dialog title or message, can in the worst +case lead to execution of arbitrary commands. diff --git a/build.rs b/build.rs index 86f37dd..1d32112 100644 --- a/build.rs +++ b/build.rs @@ -2,17 +2,32 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::process::Command; +extern crate cc; + use std::env; fn main() { let target = env::var("TARGET").unwrap(); - assert!(Command::new("make") - .args(&["-R", "-f", "makefile.cargo", &format!("-j{}", env::var("NUM_JOBS").unwrap())]) - .status() - .unwrap() - .success()); - println!("cargo:rustc-flags=-L native={}", env::var("OUT_DIR").unwrap()); - println!("cargo:rustc-flags=-l tinyfiledialogs -L {}", env::var("OUT_DIR").unwrap()) -} + let windows_hidpi = if cfg!(feature = "windows-hidpi") & !target.ends_with("pc-windows-gnu") { + "USE_WINDOWS_HIDPI" + }else { + "NO_WINDOWS_HIDPI" + }; + + cc::Build::new() + .file("libtinyfiledialogs/tinyfiledialogs.c") + .flag("-v") + .define(windows_hidpi,None) + .compile("libtinyfiledialogs.a"); + + if target.contains("windows") { + println!("cargo:rustc-link-lib=user32"); + println!("cargo:rustc-link-lib=comdlg32"); + println!("cargo:rustc-link-lib=ole32"); + println!("cargo:rustc-link-lib=shell32"); + if !target.ends_with("pc-windows-gnu") { + println!("cargo:rustc-link-lib=shcore"); + } + } +} diff --git a/examples/main.rs b/examples/main.rs new file mode 100644 index 0000000..a7de51b --- /dev/null +++ b/examples/main.rs @@ -0,0 +1,60 @@ +extern crate tinyfiledialogs; + +use tinyfiledialogs::{YesNo, MessageBoxIcon, DefaultColorValue}; + +fn main() { + + let choice = tinyfiledialogs::message_box_yes_no("hello", "yes or no?", + MessageBoxIcon::Question, YesNo::No); + + let user_input: String; + match tinyfiledialogs::input_box("Enter user name", "Username:", "") { + Some(input) => user_input = input, + None => user_input = "null".to_string(), + } + + let user_input_2: String; + match tinyfiledialogs::input_box("Re-enter user name", "Username:", &user_input) { + Some(input) => user_input_2 = input, + None => user_input_2 = "null".to_string(), + } + + let password_input: String; + match tinyfiledialogs::password_box("Enter password", "Password:") { + Some(input) => password_input = input, + None => password_input = "null".to_string(), + } + + let save_file: String; + match tinyfiledialogs::save_file_dialog("Save", "password.txt") { + Some(file) => save_file = file, + None => save_file = "null".to_string(), + } + + let open_file: String; + match tinyfiledialogs::open_file_dialog("Open", "password.txt", None) { + Some(file) => open_file = file, + None => open_file = "null".to_string(), + } + + let folder: String; + match tinyfiledialogs::select_folder_dialog("Select folder", "") { + Some(result) => folder = result, + None => folder = "null".to_string(), + } + + let color: String; + match tinyfiledialogs::color_chooser_dialog("Choose a Color", DefaultColorValue::Hex("#FF0000")) { + Some((hex_result, _rgb)) => color = hex_result, + None => color = "null".to_string(), + } + + println!("Choice {:?}", choice); + println!("User input {:?}", user_input); + println!("User input 2 {:?}", user_input_2); + println!("Password input {:?}", password_input); + println!("Save file {:?}", save_file); + println!("Open file {:?}", open_file); + println!("folder {:?}", folder); + println!("color {:?}", color); +} diff --git a/examples/tinytest/Cargo.toml b/examples/tinytest/Cargo.toml deleted file mode 100644 index f8d4426..0000000 --- a/examples/tinytest/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "test" -version = "0.1.0" -authors = ["daniel "] - -[dependencies] -tinyfiledialogs = { path = "../../" } -libc = "0.2.0" diff --git a/examples/tinytest/src/main.rs b/examples/tinytest/src/main.rs deleted file mode 100644 index 9bd8993..0000000 --- a/examples/tinytest/src/main.rs +++ /dev/null @@ -1,137 +0,0 @@ -extern crate tinyfiledialogs; -extern crate libc; - -use std::ffi::{CString, CStr}; -use libc::c_int; -use libc::c_uchar; - - -fn message_box(title: &CStr, message: &CStr, dialog_type: &CStr, icon: &CStr, button: c_int) { - - unsafe{ - - tinyfiledialogs::tinyfd_messageBox( - title.as_ptr(), - message.as_ptr(), - dialog_type.as_ptr(), - icon.as_ptr(), - button); - } -} -fn input_box(title: &CStr, message: &CStr, default: &CStr) { - - unsafe{ - - tinyfiledialogs::tinyfd_inputBox(title.as_ptr(),message.as_ptr(),default.as_ptr()); - } -} -fn save_file_dialog(title: &CStr, path: &CStr, num_patterns: c_int, filter_patterns: &CStr, des: &CStr) { - - unsafe{ - - tinyfiledialogs::tinyfd_saveFileDialog( - title.as_ptr(), - path.as_ptr(), - num_patterns, - filter_patterns.as_ptr(), - des.as_ptr()); - } -} -fn open_file_dialog(title: &CStr, path: &CStr, num_patterns: c_int, filter_patterns: &CStr, des: &CStr, multi_select: c_int) { - - unsafe{ - - tinyfiledialogs::tinyfd_openFileDialog( - title.as_ptr(), - path.as_ptr(), - num_patterns, - filter_patterns.as_ptr(), - des.as_ptr(), - multi_select); - } -} -fn select_folder_dialog(title: &CStr, path: &CStr) { - - unsafe{ - - tinyfiledialogs::tinyfd_selectFolderDialog(title.as_ptr(),path.as_ptr()); - } - -} -fn color_chooser_dialog(title: &CStr, default_hex: &CStr, default_RGB: &[c_uchar ; 3], result_RGB: &[c_uchar ; 3]) { - - unsafe{ - - tinyfiledialogs::tinyfd_colorChooser(title.as_ptr(),default_hex.as_ptr(),default_RGB,result_RGB); - } - -} -fn main() -{ - let message_box_title = CString::new("Title").unwrap(); - let message_box_message = CString::new("Hello, world!").unwrap(); - let message_box_type = CString::new("yes").unwrap(); - let message_box_icon = CString::new("yes").unwrap(); - let message_box_button : c_int = 0; - - message_box( - &message_box_title, - &message_box_message, - &message_box_type, - &message_box_icon, - message_box_button); - - let input_box_title = CString::new("Title").unwrap(); - let input_box_message = CString::new("Hello, world!").unwrap(); - let input_box_default = CString::new("yes").unwrap(); - - input_box(&input_box_title, &input_box_message, &input_box_default); - - let save_dialog_title = CString::new("Save this").unwrap(); - let save_dialog_path = CString::new("password.txt").unwrap(); - let save_dialog_num_patterns : c_int = 0; - let save_dialog_filter_patterns = CString::new("").unwrap(); - let save_dialog_des = CString::new("").unwrap(); - - save_file_dialog( - &save_dialog_title, - &save_dialog_path, - save_dialog_num_patterns, - &save_dialog_filter_patterns, - &save_dialog_des); - - let open_dialog_title = CString::new("Open this").unwrap(); - let open_dialog_path = CString::new("password.txt").unwrap(); - let open_dialog_num_patterns : c_int = 0; - let open_dialog_filter_patterns = CString::new("").unwrap(); - let open_dialog_des = CString::new("").unwrap(); - let open_dialog_multi : c_int = 0; - - open_file_dialog( - &open_dialog_title, - &open_dialog_path, - open_dialog_num_patterns, - &open_dialog_filter_patterns, - &open_dialog_des, - open_dialog_multi); - - let select_folder_title = CString::new("Select Folder").unwrap(); - let select_folder_path = CString::new("home").unwrap(); - - select_folder_dialog( - &select_folder_title, - &select_folder_path); - - let color_title = CString::new("Color Choose").unwrap(); - let color_default_hex = CString::new("Color Choose").unwrap(); - let color_default_RGB : [c_uchar ; 3] = [250,0,0]; - let color_result_RGB : [c_uchar ; 3] = [100,0,0]; - - color_chooser_dialog( - &color_title, - &color_default_hex, - &color_default_RGB, - &color_result_RGB); - - -} diff --git a/libtinyfiledialogs/tinyfiledialogs.c b/libtinyfiledialogs/tinyfiledialogs.c index b90b334..b7662fd 100644 --- a/libtinyfiledialogs/tinyfiledialogs.c +++ b/libtinyfiledialogs/tinyfiledialogs.c @@ -1,65 +1,38 @@ -/* -tinyfiledialogs.c -Unique code file of "tiny file dialogs" created [November 9, 2014] -Copyright (c) 2014 - 2016 Guillaume Vareille http://ysengrin.com -http://tinyfiledialogs.sourceforge.net - - Let me know here mailto:tinfyfiledialogs@ysengrin.com - - 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 not listed here hardware / OS / compiler. - - and please, 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 -v2.3.1 [January 12, 2016] zlib licence. - -A single C file (add it to your C or C++ project) with 6 modal function calls: -- message box & question box -- input box & password box -- save file dialog -- open file dialog & multiple files -- select folder dialog -- color picker. - -Complement to OpenGL GLFW GLUT GLUI VTK SDL Ogre3D Unity3D -or any GUI-less program, there is NO INIT & NO MAIN LOOP. -The dialogs can be forced into console mode. - -On Windows: -- native code & some vbs create the graphic dialogs -- enhanced console mode can use dialog.exe from - http://andrear.altervista.org/home/cdialog.php -- basic console input. - -On Unix (command line call attempts): -- applescript -- zenity -- kdialog -- Xdialog -- python2 tkinter -- dialog (opens a console if needed) -- whiptail, gdialog, gxmessage -- basic console input. -The same executable can run across desktops & distributions. - -tested with C & C++ compilers -on Windows Visual Studio 2013 MinGW Mac OSX LINUX FREEBSD ILLUMOS SOLARIS -using Gnome Kde Enlightenment Mate Cinnamon Unity -Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox +/* this file can be renamed with extension ".cpp" and compiled as C++. +The code is 100% compatible C C++ +(just comment out << extern "C" >> in the header file) */ + +/*_________ + / \ tinyfiledialogs.c v3.8.8 [Apr 22, 2021] zlib licence + |tiny file| Unique code file created [November 9, 2014] + | dialogs | Copyright (c) 2014 - 2021 Guillaume Vareille http://ysengrin.com + \____ ___/ http://tinyfiledialogs.sourceforge.net + \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd + ____________________________________________ + | | + | email: tinyfiledialogs at ysengrin.com | + |____________________________________________| + _________________________________________________________________________________ + | | + | the windows only wchar_t UTF-16 prototypes are at the bottom of the header file | + |_________________________________________________________________________________| + _________________________________________________________ + | | + | on windows: - since v3.6 char is UTF-8 by default | + | - if you want MBCS set tinyfd_winUtf8 to 0 | + | - functions like fopen expect MBCS | + |_________________________________________________________| + +If you like tinyfiledialogs, please upvote my stackoverflow answer +https://stackoverflow.com/a/47651444 - 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 @@ -67,26 +40,45 @@ 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. +----------- + +Thanks for contributions, bug corrections & thorough testing to: +- Don Heyse http://ldglite.sf.net for bug corrections & thorough testing! +- Paul Rouget */ + +#ifndef __sun +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 2 /* to accept POSIX 2 in old ANSI C standards */ +#endif +#endif + +#if !defined(_WIN32) && ( defined(__GNUC__) || defined(__clang__) ) +#if !defined(_GNU_SOURCE) + #define _GNU_SOURCE /* used only to resolve symbolic links. Can be commented out */ +#endif +#endif + #include #include #include #include - -#include "tinyfiledialogs.h" -/* #define TINYFD_WIN_CONSOLE_ONLY //*/ +#include #ifdef _WIN32 + #ifdef __BORLANDC__ + #define _getch getch + #endif #ifndef _WIN32_WINNT - #define _WIN32_WINNT 0x0500 + #define _WIN32_WINNT 0x0500 #endif - #ifndef TINYFD_WIN_CONSOLE_ONLY - #include - #include - #endif /* TINYFD_WIN_CONSOLE_ONLY */ - #include + #include + #include + #include #include + #include + #define TINYFD_NOCCSUNICODE #define SLASH "\\" #else #include @@ -94,3780 +86,7204 @@ misrepresented as being the original software. #include /* on old systems try instead */ #include #include + #include /* on old systems try instead */ #define SLASH "/" #endif /* _WIN32 */ +#include "tinyfiledialogs.h" + #define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */ -#define MAX_MULTIPLE 32 -#ifdef TINYFD_WIN_CONSOLE_ONLY -/*on windows if you don't compile with the GUI then you must use the console*/ -int tinyfd_forceConsole = 1 ; -#else -int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 -can be modified at run time. -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 */ -#endif /* TINYFD_WIN_CONSOLE_ONLY */ +#ifndef MAX_MULTIPLE_FILES +#define MAX_MULTIPLE_FILES 1024 +#endif +#define LOW_MULTIPLE_FILES 32 + +char tinyfd_version[8] = "3.8.8"; -#pragma warning(disable:4996) -/* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ +/******************************************************************************************************/ +/**************************************** UTF-8 on Windows ********************************************/ +/******************************************************************************************************/ +#ifdef _WIN32 +/* if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of tinyfiledialogs.h ) +Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */ +int tinyfd_winUtf8 = 1; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */ +/* for MBCS change this to 0, here or in your code */ +#endif +/******************************************************************************************************/ +/******************************************************************************************************/ +/******************************************************************************************************/ + +int tinyfd_verbose = 0 ; /* on unix: prints the command line calls */ +int tinyfd_silent = 1 ; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */ + +/* Curses dialogs are difficult to use, on windows they are only ascii and uses the unix backslah */ +int tinyfd_allowCursesDialogs = 0 ; /* 0 (default) or 1 */ +int tinyfd_forceConsole = 0 ; /* 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 the X server is present. + it can use the package dialog or dialog.exe. + on windows it only make sense for console applications */ + +int tinyfd_assumeGraphicDisplay = 0; /* 0 (default) or 1 */ +/* some systems don't set the environment variable DISPLAY even when a graphic display is present. +set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */ + + +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 graphic mode: + windows_wchar windows applescript kdialog zenity zenity3 matedialog + shellementary qarma yad python2-tkinter python3-tkinter python-dbus + perl-dbus gxmessage gmessage xmessage xdialog gdialog +for console mode: + dialog whiptail basicinput no_solution */ static int gWarningDisplayed = 0 ; -static char gTitle[]= "missing software! (so we switch to basic console input)"; -static char gMessageWin[] = "tiny file dialogs on Windows needs:\n\t\ -a graphic display\nor\tdialog.exe (enhanced console mode)\ -\nor\ta console for basic input" ; - -static char gMessageUnix[] = "tiny file dialogs on UNIX needs:\n\tapplescript\ -\nor\tzenity (version 3 for the color chooser)\nor\tkdialog\ -\nor\tXdialog\nor\tpython 2 with tkinter\ -\nor\tdialog (opens a console if needed)\ -\nor\twhiptail, gdialog or gxmessage (really?)\ -\nor\tit will open a console (if needed) for basic input (you had it comming!)"; +static char gTitle[]="missing software! (we will try basic console input)"; + +#ifdef _WIN32 +char tinyfd_needs[] = "\ + ___________\n\ +/ \\ \n\ +| tiny file |\n\ +| dialogs |\n\ +\\_____ ____/\n\ + \\|\ +\ntiny file dialogs on Windows needs:\ +\n a graphic display\ +\nor dialog.exe (curses console mode)\ +\nor a console for basic input"; +#else +char tinyfd_needs[] = "\ + ___________\n\ +/ \\ \n\ +| tiny file |\n\ +| dialogs |\n\ +\\_____ ____/\n\ + \\|\ +\ntiny file dialogs on UNIX needs:\ +\n applescript or kdialog or yad or Xdialog\ +\nor zenity (or matedialog or shellementary or qarma)\ +\nor python (2 or 3) + tkinter + python-dbus (optional)\ +\nor dialog (opens console if needed) ** Disabled by default **/\ +\nor xterm + bash (opens console for basic input)\ +\nor 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 + +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) +#include + +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif // USE_WINDOWS_HIDPI + +static int getenvDISPLAY(void) +{ + return tinyfd_assumeGraphicDisplay || getenv("DISPLAY"); +} + + +static char * getCurDir(void) +{ + static char lCurDir[MAX_PATH_OR_CMD]; + return getcwd(lCurDir, sizeof(lCurDir)); +} + 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 * aoDestination, /* make sure it is allocated, use _MAX_PATH */ + char 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; + 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) + char * aoDestination, /* make sure it is allocated */ + char 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; + /* 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 ) +static void ensureFinalSlash( char * aioString ) { - if ( aioString && strlen ( aioString ) ) - { - char * lastcar = aioString + strlen ( aioString ) - 1 ; - if ( strncmp ( lastcar , SLASH , 1 ) ) - { - strcat ( lastcar , SLASH ) ; - } - } + 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 ] ) +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; - } - } + 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 ] ) +static void RGB2Hex( unsigned char const aRGB[3], char aoResultHexRGB[8] ) { - if ( aoResultHexRGB ) - { - if ( aRGB ) - { - sprintf(aoResultHexRGB,"#%02hhx%02hhx%02hhx", - aRGB[0],aRGB[1],aRGB[2]); - /* printf("aoResultHexRGB %s\n", aoResultHexRGB); //*/ - } - else - { - aoResultHexRGB[0]=0; - aoResultHexRGB[1]=0; - aoResultHexRGB[2]=0; - } - } + if ( aoResultHexRGB ) + { + if ( aRGB ) + { +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); +#else + sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", aRGB[0], aRGB[1], aRGB[2]); +#endif + /*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 ) +void tfd_replaceSubStr( char const * aSource, char const * aOldSubStr, + char const * aNewSubStr, char * 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 ) ; + char const * pOccurence ; + char const * p ; + char const * lNewSubStr = "" ; + size_t 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 replaceChr ( char * const aString , - char const aOldChr , - char const aNewChr ) +static int filenameValid( char const * aFileNameWithoutPath ) { - char * p ; - int lRes = 0 ; + if ( ! aFileNameWithoutPath + || ! strlen(aFileNameWithoutPath) + || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) + { + return 0 ; + } + return 1 ; +} - if ( ! aString ) - { - return 0 ; - } - if ( aOldChr == aNewChr ) - { - return 0 ; - } +#ifndef _WIN32 - p = aString ; - while ( (p = strchr ( p , aOldChr )) ) - { - * p = aNewChr ; - p ++ ; - lRes = 1 ; - } - return lRes ; +static int fileExists( char const * aFilePathAndName ) +{ + FILE * lIn ; + if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) + { + return 0 ; + } + lIn = fopen( aFilePathAndName , "r" ) ; + if ( ! lIn ) + { + return 0 ; + } + fclose( lIn ) ; + return 1 ; } +#endif -static int filenameValid( char const * const aFileNameWithoutPath ) + +static void wipefile(char const * aFilename) { - if ( ! aFileNameWithoutPath - || ! strlen(aFileNameWithoutPath) - || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) - { - return 0 ; - } - return 1 ; + 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); + } + } } -static int fileExists( char const * const aFilePathAndName ) +int tfd_quoteDetected(char const * aString) { - FILE * lIn ; - if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) + char const * p; + + if (!aString) return 0; + + p = aString; + while ((p = strchr(p, '\''))) { - return 0 ; + return 1; } - lIn = fopen( aFilePathAndName , "r" ) ; - if ( ! lIn ) - { - return 0 ; + p = aString; + while ((p = strchr(p, '\"'))) + { + return 1; } - fclose ( lIn ) ; - return 1 ; + + return 0; } -/* source and destination can be the same or ovelap*/ -static char const * ensureFilesExist( char * const aDestination , - char const * const aSourcePathsAndNames) +char const * tinyfd_getGlobalChar(char const * aCharVariableName) /* to be called from C# (you don't need this in C or C++) */ { - char * lDestination = aDestination ; - char const * p ; - char const * p2 ; - int lLen ; + if (!aCharVariableName || !strlen(aCharVariableName)) return NULL; + else if (!strcmp(aCharVariableName, "tinyfd_version")) return tinyfd_version; + else if (!strcmp(aCharVariableName, "tinyfd_needs")) return tinyfd_needs; + else if (!strcmp(aCharVariableName, "tinyfd_response")) return tinyfd_response; + else return NULL ; +} - 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 ; + +int tinyfd_getGlobalInt(char const * aIntVariableName) /* to be called from C# (you don't need this in C or C++) */ +{ + if ( !aIntVariableName || !strlen(aIntVariableName) ) return -1 ; + else if ( !strcmp(aIntVariableName, "tinyfd_verbose") ) return tinyfd_verbose ; + else if ( !strcmp(aIntVariableName, "tinyfd_silent") ) return tinyfd_silent ; + else if ( !strcmp(aIntVariableName, "tinyfd_allowCursesDialogs") ) return tinyfd_allowCursesDialogs ; + else if ( !strcmp(aIntVariableName, "tinyfd_forceConsole") ) return tinyfd_forceConsole ; + else if ( !strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay") ) return tinyfd_assumeGraphicDisplay ; +#ifdef _WIN32 + else if ( !strcmp(aIntVariableName, "tinyfd_winUtf8") ) return tinyfd_winUtf8 ; +#endif + else return -1; +} + + +int tinyfd_setGlobalInt(char const * aIntVariableName, int aValue) /* to be called from C# (you don't need this in C or C++) */ +{ + if (!aIntVariableName || !strlen(aIntVariableName)) return -1 ; + else if (!strcmp(aIntVariableName, "tinyfd_verbose")) { tinyfd_verbose = aValue; return tinyfd_verbose; } + else if (!strcmp(aIntVariableName, "tinyfd_silent")) { tinyfd_silent = aValue; return tinyfd_silent; } + else if (!strcmp(aIntVariableName, "tinyfd_allowCursesDialogs")) { tinyfd_allowCursesDialogs = aValue; return tinyfd_allowCursesDialogs; } + else if (!strcmp(aIntVariableName, "tinyfd_forceConsole")) { tinyfd_forceConsole = aValue; return tinyfd_forceConsole; } + else if (!strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay")) { tinyfd_assumeGraphicDisplay = aValue; return tinyfd_assumeGraphicDisplay; } +#ifdef _WIN32 + else if (!strcmp(aIntVariableName, "tinyfd_winUtf8")) { tinyfd_winUtf8 = aValue; return tinyfd_winUtf8; } +#endif + else return -1; } + #ifdef _WIN32 +static int powershellPresent(void) +{ /*only on vista and above (or installed on xp)*/ + static int lPowershellPresent = -1; + char lBuff[MAX_PATH_OR_CMD]; + FILE* lIn; + char const* lString = "powershell.exe"; + + if (lPowershellPresent < 0) + { + if (!(lIn = _popen("where powershell.exe", "r"))) + { + lPowershellPresent = 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)) + { + lPowershellPresent = 0; + } + else + { + lPowershellPresent = 1; + } + } + return lPowershellPresent; +} -static int dirExists ( char const * const aDirPath ) +static int windowsVersion(void) { - 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 ; +#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) + typedef LONG NTSTATUS ; + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + HMODULE hMod; + RtlGetVersionPtr lFxPtr; + RTL_OSVERSIONINFOW lRovi = { 0 }; + + hMod = GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + lFxPtr = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion"); + if (lFxPtr) + { + lRovi.dwOSVersionInfoSize = sizeof(lRovi); + if (!lFxPtr(&lRovi)) + { + return lRovi.dwMajorVersion; + } + } + } +#endif + if (powershellPresent()) return 6; /*minimum is vista or installed on xp*/ + return 0; } -#ifndef TINYFD_WIN_CONSOLE_ONLY -static int messageBoxWinGui ( - char const * const aTitle , /* NULL or "" */ - char const * const aMessage , /* NULL or "" may contain \n and \t */ - char const * const aDialogType , /* "ok" "okcancel" "yesno" */ - char const * const aIconType , /* "info" "warning" "error" "question" */ - int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +static void replaceChr(char * aString, char aOldChr, char aNewChr) { - 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 ; - } + char * p; - 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 - { - aCode += MB_OK ; - } + if (!aString) return; + if (aOldChr == aNewChr) return; - lBoxReturnValue = MessageBox(NULL, aMessage, aTitle, aCode); - if ( ( ( aDialogType - && strcmp("okcancel", aDialogType) - && strcmp("yesno", aDialogType) ) ) - || (lBoxReturnValue == IDOK) - || (lBoxReturnValue == IDYES) ) + p = aString; + while ((p = strchr(p, aOldChr))) { - return 1 ; - } - else - { - return 0 ; + *p = aNewChr; + p++; } + return; } -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 */ +#if !defined(WC_ERR_INVALID_CHARS) +/* undefined prior to Vista, so not yet in MINGW header file */ +#define WC_ERR_INVALID_CHARS 0x00000000 /* 0x00000080 for MINGW maybe ? */ +#endif + +static int sizeUtf16From8(char const * aUtf8string) { - char lDialogString[4*MAX_PATH_OR_CMD]; - FILE * lIn; - int lResult; + return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, NULL, 0); +} - 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) - { - return NULL; - } - if ( aDefaultInput ) - { - strcpy(lDialogString, "Dim result:result=InputBox(\""); - if (aMessage && strlen(aMessage)) - { - strcat(lDialogString, aMessage); - } - strcat(lDialogString, "\",\""); - if (aTitle && strlen(aTitle)) - { +static int sizeUtf16FromMbcs(char const * aMbcsString) +{ + return MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, + aMbcsString, -1, NULL, 0); +} - 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); - if ( GetConsoleWindow() ) - { - strcpy(lDialogString, ""); - } - else - { - strcpy(lDialogString, "powershell -WindowStyle Hidden -Command \""); - } - if (aDefaultInput) - { - strcat(lDialogString, - "cscript.exe %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs"); - } - else - { - strcat(lDialogString, - "mshta.exe %USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta"); - } - if ( ! GetConsoleWindow() ) - { - strcat(lDialogString, "\""); - } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if (!(lIn = _popen(lDialogString,"r"))) - { - 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.vbs", - getenv("USERPROFILE")); - } - else - { - sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.txt", - getenv("USERPROFILE")); - if (!(lIn = fopen(lDialogString, "r"))) - { - remove(lDialogString); - sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", - getenv("USERPROFILE")); - return NULL; - } - while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) - {} - fclose(lIn); - remove(lDialogString); - sprintf(lDialogString, "%s\\AppData\\Local\\Temp\\tinyfd.hta", - getenv("USERPROFILE")); - } - remove(lDialogString); - /* printf ( "aoBuff: %s\n" , 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 + 1; -} - - -static char const * saveFileDialogWinGui ( - 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; - OPENFILENAME ofn ; - - 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 ; - 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; - ofn.lpstrTitle = aTitle ; - ofn.Flags = OFN_OVERWRITEPROMPT ; - ofn.nFileOffset = 0 ; - ofn.nFileExtension = 0 ; - ofn.lpstrDefExt = NULL ; - ofn.lCustData = 0L ; - ofn.lpfnHook = NULL ; - ofn.lpTemplateName = NULL ; - - if ( GetSaveFileName ( & ofn ) == 0 ) - { - return NULL ; - } - else - { - return aoBuff ; - } +static int sizeUtf8(wchar_t const * aUtf16string) +{ + return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + aUtf16string, -1, NULL, 0, NULL, NULL); } -static char const * openFileDialogWinGui ( - 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 */ +static int sizeMbcs(wchar_t const * aMbcsString) { - char lDirname [ MAX_PATH_OR_CMD ] ; - char lFilterPatterns[MAX_PATH_OR_CMD] = ""; - char lDialogString[MAX_PATH_OR_CMD] ; - char * lPointers[MAX_MULTIPLE]; - size_t lLengths[MAX_MULTIPLE]; - int i , j ; - char * p; - OPENFILENAME ofn; - size_t lBuffLen ; + int lRes = WideCharToMultiByte(CP_ACP, 0, + aMbcsString, -1, NULL, 0, NULL, NULL); + /* DWORD licic = GetLastError(); */ + return lRes; +} - 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 ++ ; - } - } +wchar_t* tinyfd_mbcsTo16(char const* aMbcsString) +{ + static wchar_t* lMbcsString = NULL; + int lSize; - ofn.lStructSize = sizeof ( OPENFILENAME ) ; - ofn.hwndOwner = 0 ; - ofn.hInstance = 0 ; - ofn.lpstrFilter = lFilterPatterns; - 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 ; - ofn.lpstrTitle = aTitle ; - ofn.Flags = OFN_EXPLORER ; - ofn.nFileOffset = 0 ; - ofn.nFileExtension = 0 ; - ofn.lpstrDefExt = NULL ; - ofn.lCustData = 0L ; - ofn.lpfnHook = NULL ; - ofn.lpTemplateName = NULL ; - - if ( aAllowMultipleSelects ) - { - ofn.Flags |= OFN_ALLOWMULTISELECT; - } + free(lMbcsString); + if (!aMbcsString) { lMbcsString = NULL; return NULL; } + lSize = sizeUtf16FromMbcs(aMbcsString); + if (lSize) + { + lMbcsString = (wchar_t*)malloc(lSize * sizeof(wchar_t)); + lSize = MultiByteToWideChar(CP_ACP, 0, aMbcsString, -1, lMbcsString, lSize); + } + else wcscpy(lMbcsString, L""); + return lMbcsString; +} - if ( GetOpenFileName ( & ofn ) == 0 ) - { - return NULL ; - } - else - { - lBuffLen = strlen(aoBuff) ; - lPointers[0] = aoBuff + lBuffLen + 1 ; - if ( !aAllowMultipleSelects || (lPointers[0][0] == '\0') ) - return aoBuff ; - - 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*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++; - return p ; - } + +wchar_t * tinyfd_utf8to16(char const * aUtf8string) +{ + static wchar_t * lUtf16string = NULL; + int lSize; + + free(lUtf16string); + if (!aUtf8string) {lUtf16string = NULL; return NULL;} + lSize = sizeUtf16From8(aUtf8string); + if (lSize) + { + lUtf16string = (wchar_t*)malloc(lSize * sizeof(wchar_t)); + lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + aUtf8string, -1, lUtf16string, lSize); + return lUtf16string; + } + else + { + /* let's try mbcs anyway */ + lUtf16string = NULL; + return tinyfd_mbcsTo16(aUtf8string); + } } -static char const * selectFolderDialogWinGui ( - char * const aoBuff , - char const * const aTitle , /* NULL or "" */ - char const * const aDefaultPath ) /* NULL or "" */ +char * tinyfd_utf16toMbcs(wchar_t const * aUtf16string) { - BROWSEINFO bInfo ; - LPITEMIDLIST lpItem ; - - /* we can't use aDefaultPath */ - bInfo.hwndOwner = 0 ; - bInfo.pidlRoot = NULL ; - bInfo.pszDisplayName = aoBuff ; - bInfo.lpszTitle = aTitle ; - bInfo.ulFlags = 0 ; - bInfo.lpfn = NULL ; - bInfo.lParam = 0 ; - bInfo.iImage = -1 ; + static char * lMbcsString = NULL; + int lSize; - lpItem = SHBrowseForFolder ( & bInfo ) ; - if ( lpItem ) - { - SHGetPathFromIDList ( lpItem , aoBuff ) ; - } - return aoBuff ; + free(lMbcsString); + if (!aUtf16string) { lMbcsString = NULL; return NULL; } + lSize = sizeMbcs(aUtf16string); + if (lSize) + { + lMbcsString = (char*)malloc(lSize); + lSize = WideCharToMultiByte(CP_ACP, 0, aUtf16string, -1, lMbcsString, lSize, NULL, NULL); + } + else strcpy(lMbcsString, ""); + return lMbcsString; } -static char const * colorChooserWinGui( - 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 * tinyfd_utf8toMbcs(char const * aUtf8string) { - static CHOOSECOLOR cc; - static COLORREF crCustColors[16]; - static char lResultHexRGB[8]; - unsigned char lDefaultRGB[3]; - int lRet; + wchar_t const * lUtf16string; + lUtf16string = tinyfd_utf8to16(aUtf8string); + return tinyfd_utf16toMbcs(lUtf16string); +} - 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; +char * tinyfd_utf16to8(wchar_t const * aUtf16string) +{ + static char * lUtf8string = NULL; + int lSize; - lRet = ChooseColor(&cc); + free(lUtf8string); + if (!aUtf16string) { lUtf8string = NULL; return NULL; } + lSize = sizeUtf8(aUtf16string); + if (lSize) + { + lUtf8string = (char*)malloc(lSize); + lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, lUtf8string, lSize, NULL, NULL); + } + else strcpy(lUtf8string, ""); + return lUtf8string; +} - if ( ! lRet ) - { - return NULL; - } - aoResultRGB[0] = GetRValue(cc.rgbResult); - aoResultRGB[1] = GetGValue(cc.rgbResult); - aoResultRGB[2] = GetBValue(cc.rgbResult); +char * tinyfd_mbcsTo8(char const * aMbcsString) +{ + wchar_t const * lUtf16string; + lUtf16string = tinyfd_mbcsTo16(aMbcsString); + return tinyfd_utf16to8(lUtf16string); +} - RGB2Hex(aoResultRGB, lResultHexRGB); - return lResultHexRGB; +void tinyfd_beep(void) +{ + if (windowsVersion() > 5) Beep(440, 300); + else MessageBeep(MB_OK); } -#endif /* TINYFD_WIN_CONSOLE_ONLY */ + +static void wipefileW(wchar_t const * aFilename) +{ + int i; + FILE * lIn; +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + struct _stat st; + if (_wstat(aFilename, &st) == 0) +#else + struct __stat64 st; + if (_wstat64(aFilename, &st) == 0) +#endif + { + if ((lIn = _wfopen(aFilename, L"w"))) + { + for (i = 0; i < st.st_size; i++) + { + fputc('A', lIn); + } + fclose(lIn); + } + } +} -static int dialogPresent ( ) +static wchar_t * getPathWithoutFinalSlashW( + wchar_t * aoDestination, /* make sure it is allocated, use _MAX_PATH */ + wchar_t const * aSource) /* aoDestination and aSource can be the same */ { - 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 ; + 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 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" */ - char const * const aIconType , /* "info" "warning" "error" "question" */ - int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ +static wchar_t * getLastNameW( + wchar_t * aoDestination, /* make sure it is allocated */ + wchar_t const * aSource) { - 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 ) ) - { - 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 - { - strcat ( lDialogString , "--msgbox " ) ; - - } + /* 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; +} - strcat ( lDialogString , "\"" ) ; - if ( aMessage && strlen(aMessage) ) - { - replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; - strcat(lDialogString, lBuff) ; - lBuff[0]='\0'; - } +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; + } + } +} - strcat(lDialogString, "\" 10 60"); - strcat(lDialogString, " && echo 1 > "); - strcpy(lDialogFile, getenv("USERPROFILE")); - strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); - strcat(lDialogString, lDialogFile); +static void RGB2HexW( unsigned char const aRGB[3], wchar_t aoResultHexRGB[8]) +{ +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + wchar_t const * const lPrintFormat = L"#%02hhx%02hhx%02hhx"; +#else + wchar_t const * const lPrintFormat = L"#%02hx%02hx%02hx"; +#endif - /* 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' ; - } - /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ - if ( ! strlen(lBuff) ) - { - return 0; - } - return 1; + if (aoResultHexRGB) + { + if (aRGB) + { + /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */ +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + swprintf(aoResultHexRGB, 8, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]); +#else + swprintf(aoResultHexRGB, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]); +#endif + + } + else + { + aoResultHexRGB[0] = 0; + aoResultHexRGB[1] = 0; + aoResultHexRGB[2] = 0; + } + } } -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 */ +static int dirExists(char const * aDirPath) { - char lDialogString[MAX_PATH_OR_CMD]; - char lDialogFile[MAX_PATH_OR_CMD]; - FILE * lIn; - int lResult; +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + struct _stat lInfo; +#else + struct __stat64 lInfo; +#endif + wchar_t * lTmpWChar; + int lStatRet; + size_t lDirLen; + + if (!aDirPath) + return 0; + lDirLen = strlen(aDirPath); + if (!lDirLen) + return 1; + if ( (lDirLen == 2) && (aDirPath[1] == ':') ) + return 1; + + if (tinyfd_winUtf8) + { + lTmpWChar = tinyfd_utf8to16(aDirPath); +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + lStatRet = _wstat(lTmpWChar, &lInfo); +#else + lStatRet = _wstat64(lTmpWChar, &lInfo); +#endif + if (lStatRet != 0) + return 0; + else if (lInfo.st_mode & S_IFDIR) + return 1; + else + return 0; + } +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + else if (_stat(aDirPath, &lInfo) != 0) +#else + else if (_stat64(aDirPath, &lInfo) != 0) +#endif + return 0; + else if (lInfo.st_mode & S_IFDIR) + return 1; + else + return 0; +} - 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, "\" ") ; - } - if ( ! aDefaultInput ) - { - strcat ( lDialogString , "--passwordbox" ) ; - } - else +static int fileExists(char const * aFilePathAndName) +{ +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + struct _stat lInfo; +#else + struct __stat64 lInfo; +#endif + wchar_t * lTmpWChar; + int lStatRet; + FILE * lIn; + + if (!aFilePathAndName || !strlen(aFilePathAndName)) + { + return 0; + } + + if (tinyfd_winUtf8) + { + lTmpWChar = tinyfd_utf8to16(aFilePathAndName); +#if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) + lStatRet = _wstat(lTmpWChar, &lInfo); +#else + lStatRet = _wstat64(lTmpWChar, &lInfo); +#endif + + if (lStatRet != 0) + return 0; + else if (lInfo.st_mode & _S_IFREG) + return 1; + else + return 0; + } + else + { + lIn = fopen(aFilePathAndName, "r"); + if (!lIn) + { + return 0; + } + fclose(lIn); + return 1; + } +} + +static void replaceWchar(wchar_t * aString, + wchar_t aOldChr, + wchar_t aNewChr) +{ + wchar_t * p; + + if (!aString) { - strcat ( lDialogString , "--inputbox" ) ; + return ; } - strcat ( lDialogString , " \"" ) ; - if ( aMessage && strlen(aMessage) ) + + if (aOldChr == aNewChr) { - strcat(lDialogString, aMessage) ; + return ; } - strcat(lDialogString,"\" 10 60 ") ; - if ( aDefaultInput && strlen(aDefaultInput) ) + + p = aString; + while ((p = wcsrchr(p, aOldChr))) { - strcat(lDialogString, "\"") ; - strcat(lDialogString, aDefaultInput) ; - strcat(lDialogString, "\" ") ; + *p = aNewChr; +#ifdef TINYFD_NOCCSUNICODE + p++; +#endif + p++; } + return ; +} - 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 ) ; +static int quoteDetectedW(wchar_t const * aString) +{ + wchar_t const * p; + + if (!aString) return 0; - if (!(lIn = fopen(lDialogFile, "r"))) + p = aString; + while ((p = wcsrchr(p, L'\''))) { - remove(lDialogFile); - return 0 ; + return 1; } - while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) - {} - fclose(lIn); - 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 ; + p = aString; + while ((p = wcsrchr(p, L'\"'))) + { + return 1; } - /* printf ( "aoBuff+1: %s\n" , aoBuff+1 ) ; //*/ - return aoBuff+3 ; + + return 0; } +#endif /* _WIN32 */ -static char const * saveFileDialogWinConsole ( - char * const aoBuff , - char const * const aTitle , /* NULL or "" */ - char const * const aDefaultPathAndFile ) /* NULL or "" */ +/* source and destination can be the same or ovelap*/ +static char * ensureFilesExist(char * aDestination, + char const * aSourcePathsAndNames) { - char lDialogString[MAX_PATH_OR_CMD]; - char lPathAndFile[MAX_PATH_OR_CMD] = ""; - FILE * lIn; + char * lDestination = aDestination; + char const * p; + char const * p2; + size_t lLen; - strcpy ( lDialogString , "dialog " ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - 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 (!aSourcePathsAndNames) + { + return NULL; + } + lLen = strlen(aSourcePathsAndNames); + if (!lLen) + { + return NULL; + } - 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; + 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; } +#ifdef _WIN32 -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 */ +static int __stdcall EnumThreadWndProc(HWND hwnd, LPARAM lParam) { - 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 , "--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; + wchar_t lTitleName[MAX_PATH]; + GetWindowTextW(hwnd, lTitleName, MAX_PATH); + /* wprintf(L"lTitleName %ls \n", lTitleName); */ + if (wcscmp(L"tinyfiledialogsTopWindow", lTitleName) == 0) + { + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + return 0; + } + return 1; } -static char const * selectFolderDialogWinConsole ( - char * const aoBuff , - char const * const aTitle , /* NULL or "" */ - char const * const aDefaultPath ) /* NULL or "" */ +static void hiddenConsoleW(wchar_t const * aString, wchar_t const * aDialogTitle, int aInFront) { - 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 , "--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 ) ; + STARTUPINFOW StartupInfo; + PROCESS_INFORMATION ProcessInfo; - 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; -} + if (!aString || !wcslen(aString) ) return; + memset(&StartupInfo, 0, sizeof(StartupInfo)); + StartupInfo.cb = sizeof(STARTUPINFOW); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; -/* returns 0 for cancel/no , 1 for ok/yes */ -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" */ - char const * const aIconType , /* "info" "warning" "error" "question" */ - int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ -{ - char lChar ; + if (!CreateProcessW(NULL, (LPWSTR)aString, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE, NULL, NULL, + &StartupInfo, &ProcessInfo)) + { + return; /* GetLastError(); */ + } -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) - && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) - { - return messageBoxWinGui( - aTitle,aMessage,aDialogType,aIconType,aDefaultButton); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - if ( dialogPresent() ) - { - return messageBoxWinConsole( - aTitle,aMessage,aDialogType,aIconType,aDefaultButton); - } - else - { - if ( !gWarningDisplayed ) - { - gWarningDisplayed = 1 ; - printf ("\n\n%s\n", gTitle); - printf ("%s\n\n\n", gMessageWin); - } - if ( aTitle && strlen(aTitle) ) - { - printf ("%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 ( aMessage && strlen(aMessage) ) - { - printf("%s\n\n",aMessage); - } - printf("press any key to continue "); - lChar = (char) _getch() ; - printf("\n\n"); - return 1 ; - } - } + WaitForInputIdle(ProcessInfo.hProcess, INFINITE); + if (aInFront) + { + while (EnumWindows(EnumThreadWndProc, (LPARAM)NULL)) {} + SetWindowTextW(GetForegroundWindow(), aDialogTitle); + } + WaitForSingleObject(ProcessInfo.hProcess, INFINITE); + CloseHandle(ProcessInfo.hThread); + CloseHandle(ProcessInfo.hProcess); } -/* 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 */ +int tinyfd_messageBoxW( + wchar_t const * aTitle, /* NULL or "" */ + wchar_t const * aMessage, /* NULL or "" may contain \n and \t */ + wchar_t const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ + wchar_t const * aIconType, /* "info" "warning" "error" "question" */ + int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { - static char lBuff [ MAX_PATH_OR_CMD ] ; - -#ifndef TINYFD_WIN_CONSOLE_ONLY - DWORD mode = 0; - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);; - if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) - && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) - { - lBuff[0]='\0'; - return inputBoxWinGui(lBuff,aTitle,aMessage,aDefaultInput); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - if ( dialogPresent() ) - { - lBuff[0]='\0'; - return inputBoxWinConsole(lBuff,aTitle,aMessage,aDefaultInput); - } - else - { - lBuff[0]='\0'; - if ( !gWarningDisplayed ) - { - gWarningDisplayed = 1 ; - printf ("\n\n%s\n", gTitle); - printf ("%s\n\n\n", gMessageWin); - } - if ( aTitle && strlen(aTitle) ) - { - printf ("%s\n\n", aTitle); - } - if ( aMessage && strlen(aMessage) ) - { - printf("%s\n",aMessage); - } - printf("(esc+enter to cancel): "); -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ! aDefaultInput ) - { - GetConsoleMode(hStdin,&mode); - SetConsoleMode(hStdin,mode & (~ENABLE_ECHO_INPUT) ); - } -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - fgets(lBuff, MAX_PATH_OR_CMD, stdin); -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ! aDefaultInput ) - { - SetConsoleMode(hStdin,mode); - printf ("\n"); - } -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - printf ("\n"); - if ( strchr(lBuff,27) ) - { - return NULL ; - } - if ( lBuff[ strlen ( lBuff ) -1 ] == '\n' ) - { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; - } - return lBuff ; - } -} +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + int lBoxReturnValue; + UINT aCode; + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 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 lString[MAX_PATH_OR_CMD] ; - char const * p ; - lBuff[0]='\0'; -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) - && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) - { - p = saveFileDialogWinGui(lBuff, - aTitle,aDefaultPathAndFile,aNumOfFilterPatterns,aFilterPatterns,aSingleFilterDescription); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - if ( dialogPresent() ) - { - p = saveFileDialogWinConsole(lBuff,aTitle,aDefaultPathAndFile); - } - else - { - p = tinyfd_inputBox(aTitle, "Save file",""); - } + if (quoteDetectedW(aTitle)) return tinyfd_messageBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); + if (quoteDetectedW(aMessage)) return tinyfd_messageBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); - 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 ; -} + 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; + } -/* 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*MAX_PATH_OR_CMD]; - char const * p ; -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) - && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) - { - p = openFileDialogWinGui(lBuff, - aTitle,aDefaultPathAndFile,aNumOfFilterPatterns, - aFilterPatterns,aSingleFilterDescription,aAllowMultipleSelects); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - if ( dialogPresent() ) - { - p = openFileDialogWinConsole(lBuff, - aTitle,aDefaultPathAndFile,aAllowMultipleSelects); - } - else - { - p = tinyfd_inputBox(aTitle, "Open file",""); - } + aCode += MB_TOPMOST; - 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 ; + lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode); + if (((aDialogType + && wcscmp(L"okcancel", aDialogType) + && wcscmp(L"yesno", aDialogType))) + || (lBoxReturnValue == IDOK) + || (lBoxReturnValue == IDYES)) + { + return 1; + } + else + { + return 0; + } } -char const * tinyfd_selectFolderDialog ( - char const * const aTitle , /* NULL or "" */ - char const * const aDefaultPath ) /* NULL or "" */ +/* return has only meaning for tinyfd_query */ +int tinyfd_notifyPopupW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ + wchar_t const * aIconType) /* L"info" L"warning" L"error" */ { - static char lBuff [ MAX_PATH_OR_CMD ] ; - char const * p ; -#ifndef TINYFD_WIN_CONSOLE_ONLY - if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) - && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) - { - p = selectFolderDialogWinGui(lBuff,aTitle,aDefaultPath); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - if ( dialogPresent() ) - { - p = selectFolderDialogWinConsole(lBuff,aTitle,aDefaultPath); - } - else - { - 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_WIN_CONSOLE_ONLY - if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) - && (!getenv("SSH_CLIENT") || getenv("DISPLAY")) ) - { - return colorChooserWinGui( - aTitle,aDefaultHexRGB,aDefaultRGB,aoResultRGB); - } - else -#endif /* TINYFD_WIN_CONSOLE_ONLY */ - 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 ( !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 defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t * lDialogString; + size_t lTitleLen; + size_t lMessageLen; + size_t lDialogStringLen; + + if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; } + + if (quoteDetectedW(aTitle)) return tinyfd_notifyPopupW(L"INVALID TITLE WITH QUOTES", aMessage, aIconType); + if (quoteDetectedW(aMessage)) return tinyfd_notifyPopupW(aTitle, L"INVALID MESSAGE WITH QUOTES", aIconType); + + lTitleLen = aTitle ? wcslen(aTitle) : 0; + lMessageLen = aMessage ? wcslen(aMessage) : 0; + lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; + lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); + if (!lDialogString) return 0; + + wcscpy(lDialogString, L"powershell.exe -command \"\ +function Show-BalloonTip {\ +[cmdletbinding()] \ +param( \ +[string]$Title = ' ', \ +[string]$Message = ' ', \ +[ValidateSet('info', 'warning', 'error')] \ +[string]$IconType = 'info');\ +[system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; \ +$balloon = New-Object System.Windows.Forms.NotifyIcon ; \ +$path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; \ +$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;"); + + wcscat(lDialogString, L"\ +$balloon.Icon = $icon ; \ +$balloon.BalloonTipIcon = $IconType ; \ +$balloon.BalloonTipText = $Message ; \ +$balloon.BalloonTipTitle = $Title ; \ +$balloon.Text = 'tinyfiledialogs' ; \ +$balloon.Visible = $true ; \ +$balloon.ShowBalloonTip(5000)};\ +Show-BalloonTip"); + + if (aTitle && wcslen(aTitle)) + { + wcscat(lDialogString, L" -Title '"); + wcscat(lDialogString, aTitle); + wcscat(lDialogString, L"'"); + } + if (aMessage && wcslen(aMessage)) + { + wcscat(lDialogString, L" -Message '"); + wcscat(lDialogString, aMessage); + wcscat(lDialogString, L"'"); + } + if (aMessage && wcslen(aIconType)) + { + wcscat(lDialogString, L" -IconType '"); + wcscat(lDialogString, aIconType); + wcscat(lDialogString, L"'"); + } + wcscat(lDialogString, L"\""); -#else /* unix */ + /* wprintf ( L"lDialogString: %ls\n" , lDialogString ) ; */ -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 ; + hiddenConsoleW(lDialogString, aTitle, 0); + free(lDialogString); + return 1; } -static int dirExists ( char const * const aDirPath ) +wchar_t * tinyfd_inputBoxW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aMessage, /* NULL or L"" (\n and \t have no effect) */ + wchar_t const * aDefaultInput) /* L"" , if NULL it's a passwordBox */ { - DIR * lDir ; - if ( ! aDirPath || ! strlen ( aDirPath ) ) - return 0 ; - lDir = opendir ( aDirPath ) ; - if ( ! lDir ) - { - return 0 ; - } - closedir ( lDir ) ; - return 1 ; -} +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + static wchar_t lBuff[MAX_PATH_OR_CMD]; + wchar_t * lDialogString; + FILE * lIn; + FILE * lFile; + int lResult; + size_t lTitleLen; + size_t lMessageLen; + size_t lDialogStringLen; + + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } + + if (quoteDetectedW(aTitle)) return tinyfd_inputBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); + if (quoteDetectedW(aMessage)) return tinyfd_inputBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDefaultInput); + if (quoteDetectedW(aDefaultInput)) return tinyfd_inputBoxW(aTitle, aMessage, L"INVALID DEFAULT_INPUT WITH QUOTES"); + + lTitleLen = aTitle ? wcslen(aTitle) : 0 ; + lMessageLen = aMessage ? wcslen(aMessage) : 0 ; + lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; + lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); + + if (aDefaultInput) + { + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP")); + } + else + { + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.hta", _wgetenv(L"TEMP")); + } + lIn = _wfopen(lDialogString, L"w"); + if (!lIn) + { + free(lDialogString); + return NULL; + } - -static int detectPresence ( char const * const aExecutable ) -{ - char lBuff [ MAX_PATH_OR_CMD ] ; - char lTestedString [ MAX_PATH_OR_CMD ] = "which " ; - FILE * lIn ; + if ( aDefaultInput ) + { + wcscpy(lDialogString, L"Dim result:result=InputBox(\""); + if (aMessage && wcslen(aMessage)) + { + wcscpy(lBuff, aMessage); + replaceWchar(lBuff, L'\n', L' '); + wcscat(lDialogString, lBuff); + } + wcscat(lDialogString, L"\",\"tinyfiledialogsTopWindow\",\""); + if (aDefaultInput && wcslen(aDefaultInput)) + { + wcscpy(lBuff, aDefaultInput); + replaceWchar(lBuff, L'\n', L' '); + wcscat(lDialogString, lBuff); + } + wcscat(lDialogString, L"\"):If IsEmpty(result) then:WScript.Echo 0"); + wcscat(lDialogString, L":Else: WScript.Echo \"1\" & result : End If"); + } + else + { + wcscpy(lDialogString, L"\n\ +\n\ +\n\ +"); - strcat ( lTestedString , aExecutable ) ; - lIn = popen ( lTestedString , "r" ) ; - if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) - && ( ! strchr ( lBuff , ':' ) ) ) - { /* present */ - pclose ( lIn ) ; - return 1 ; - } - else - { - pclose ( lIn ) ; - return 0 ; - } -} + wcscat(lDialogString, L"tinyfiledialogsTopWindow"); + wcscat(lDialogString, L"\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +\n\ +
\n"); - lIn = popen ( aCommand , "r" ) ; - if ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) == NULL ) - { /* present */ - pclose ( lIn ) ; - return 1 ; - } - else - { - pclose ( lIn ) ; - return 0 ; - } + wcscat(lDialogString, aMessage ? aMessage : L""); -} + wcscat(lDialogString, L"\n\ +\n\ +\n\ +\n\ +
\n\ +

\n\ +\n\ +
\n\ +
\n"); + wcscat(lDialogString, L"\n\ +\n\ +\n\ +\n\ +
\n\ +
\n\ +
\n\ +\n\ +\n\ +" ) ; + } + fputws(lDialogString, lIn); + fclose(lIn); -static char const * terminalName ( ) -{ - static char lTerminalName[64] = "*" ; - if ( lTerminalName[0] == '*' ) - { - if ( isDarwin() ) - { - if ( strcpy(lTerminalName , "/opt/X11/bin/xterm" ) - && detectPresence ( lTerminalName ) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else - { - strcpy(lTerminalName , "" ) ; - } - } - else if ( strcpy(lTerminalName,"gnome-terminal") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"konsole") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"xterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - //strcat(lTerminalName , " -e " ) ; - } - else if ( strcpy(lTerminalName,"lxterminal") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"xfce4-terminal") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"Terminal") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"rxvt") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"urxvt") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"mrxvt") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"wterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"eterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"aterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"terminology") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"multi-gnome-terminal") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"hpterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"winterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -c bash -c " ) ; - } - else if ( strcpy(lTerminalName,"roxterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"st") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"sakura") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"mlterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"vte") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"terminator") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"lilyterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -x bash -c " ) ; - } - else if ( strcpy(lTerminalName,"dtterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"nxterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"pterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"xgterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"evilvte") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"kterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"xiterm") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"termit") - && detectPresence(lTerminalName) ) - { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"xvt") - && detectPresence(lTerminalName) ) + if (aDefaultInput) + { + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.txt",_wgetenv(L"TEMP")); + +#ifdef TINYFD_NOCCSUNICODE + lFile = _wfopen(lDialogString, L"w"); + fputc(0xFF, lFile); + fputc(0xFE, lFile); +#else + lFile = _wfopen(lDialogString, L"wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/ +#endif + fclose(lFile); + + wcscpy(lDialogString, L"cmd.exe /c cscript.exe //U //Nologo "); + wcscat(lDialogString, L"\"%TEMP%\\tinyfd.vbs\" "); + wcscat(lDialogString, L">> \"%TEMP%\\tinyfd.txt\""); + } + else + { + wcscpy(lDialogString, + L"cmd.exe /c mshta.exe \"%TEMP%\\tinyfd.hta\""); + } + + /* wprintf ( "lDialogString: %ls\n" , lDialogString ) ; */ + + hiddenConsoleW(lDialogString, aTitle, 1); + + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.txt", _wgetenv(L"TEMP")); + /* wprintf(L"lDialogString: %ls\n", lDialogString); */ +#ifdef TINYFD_NOCCSUNICODE + if (!(lIn = _wfopen(lDialogString, L"r"))) +#else + if (!(lIn = _wfopen(lDialogString, L"rt, ccs=UNICODE"))) /*or ccs=UTF-16LE*/ +#endif { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"vala-terminal") - && detectPresence(lTerminalName) ) + _wremove(lDialogString); + free(lDialogString); + return NULL; + } + + memset(lBuff, 0, MAX_PATH_OR_CMD * sizeof(wchar_t) ); + +#ifdef TINYFD_NOCCSUNICODE + fgets((char *)lBuff, 2*MAX_PATH_OR_CMD, lIn); +#else + fgetws(lBuff, MAX_PATH_OR_CMD, lIn); +#endif + fclose(lIn); + wipefileW(lDialogString); + _wremove(lDialogString); + + if (aDefaultInput) { - strcat(lTerminalName , " -e bash -c " ) ; - } - else if ( strcpy(lTerminalName,"osso-xterm") - && detectPresence(lTerminalName) ) + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP")); + } + else + { + swprintf(lDialogString, +#if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) + lDialogStringLen, +#endif + L"%ls\\tinyfd.hta", _wgetenv(L"TEMP")); + } + _wremove(lDialogString); + free(lDialogString); + /* wprintf( L"lBuff: %ls\n" , lBuff ) ; */ +#ifdef TINYFD_NOCCSUNICODE + lResult = !wcsncmp(lBuff+1, L"1", 1); +#else + lResult = !wcsncmp(lBuff, L"1", 1); +#endif + + /* printf( "lResult: %d \n" , lResult ) ; */ + if (!lResult) + { + return NULL ; + } + + /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */ + +#ifdef TINYFD_NOCCSUNICODE + if (aDefaultInput) { - strcat(lTerminalName , " -e bash -c " ) ; + lDialogStringLen = wcslen(lBuff) ; + lBuff[lDialogStringLen - 1] = L'\0'; + lBuff[lDialogStringLen - 2] = L'\0'; } - else + return lBuff + 2; +#else + if (aDefaultInput) lBuff[wcslen(lBuff) - 1] = L'\0'; + return lBuff + 1; +#endif +} + + +wchar_t * tinyfd_saveFileDialogW( + wchar_t const * aTitle, /* NULL or "" */ + wchar_t const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * aSingleFilterDescription) /* NULL or "image files" */ +{ +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + 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; + wchar_t const * ldefExt = NULL; + int i; + HRESULT lHResult; + OPENFILENAMEW ofn = {0}; + + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } + + if (quoteDetectedW(aTitle)) return tinyfd_saveFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (quoteDetectedW(aDefaultPathAndFile)) return tinyfd_saveFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_saveFileDialogW(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES"); + for (i = 0; i < aNumOfFilterPatterns; i++) { - strcpy(lTerminalName , "" ) ; + if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_saveFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); } - } - if ( strlen(lTerminalName) ) - { - return lTerminalName ; - } - else - { - return NULL ; - } + + lHResult = CoInitializeEx(NULL, 0); + + getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); + getLastNameW(lBuff, aDefaultPathAndFile); + + if (aNumOfFilterPatterns > 0) + { + ldefExt = aFilterPatterns[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 = GetForegroundWindow(); + ofn.hInstance = 0; + ofn.lpstrFilter = 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_PATH_OR_CMD/2; + ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST ; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = ldefExt; + 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 * dialogName ( ) +wchar_t * tinyfd_openFileDialogW( + wchar_t const * aTitle, /* NULL or "" */ + wchar_t const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns, /* 0 */ + wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + wchar_t const * aSingleFilterDescription, /* NULL or "image files" */ + int aAllowMultipleSelects) /* 0 or 1 ; -1 to free allocated memory and return */ { - static char lDialogName[64] = "*" ; - if ( lDialogName[0] == '*' ) - { - if ( isDarwin() && strcpy(lDialogName , "/opt/local/bin/dialog" ) - && detectPresence ( lDialogName ) ) - {} - else if ( strcpy(lDialogName , "dialog" ) - && detectPresence ( lDialogName ) ) - {} +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + 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+1]; + wchar_t * p; + int i, j; + size_t lBuffLen; + DWORD lFullBuffLen; + HRESULT lHResult; + OPENFILENAMEW ofn = { 0 }; + static wchar_t * lBuff = NULL; + + free(lBuff); + lBuff = NULL; + if (aAllowMultipleSelects < 0) return (wchar_t *)0; + + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } + + if (quoteDetectedW(aTitle)) return tinyfd_openFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (quoteDetectedW(aDefaultPathAndFile)) return tinyfd_openFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_openFileDialogW(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_openFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); + } + + if (aAllowMultipleSelects) + { + lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; + lBuff = (wchar_t*)(malloc(lFullBuffLen * sizeof(wchar_t))); + if (!lBuff) + { + lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; + lBuff = (wchar_t*)( malloc( lFullBuffLen * sizeof(wchar_t))); + } + } else { - strcpy(lDialogName , "" ) ; + lFullBuffLen = MAX_PATH_OR_CMD + 1; + lBuff = (wchar_t*)(malloc(lFullBuffLen * sizeof(wchar_t))); } - } - if ( strlen(lDialogName) && ( isatty(1) || terminalName() ) ) - { - return lDialogName ; - } - else - { - return NULL ; - } -} + if (!lBuff) return NULL; + lHResult = CoInitializeEx(NULL, 0); -static int whiptailPresent ( ) -{ - static int lWhiptailPresent = -1 ; - if ( lWhiptailPresent < 0 ) - { - lWhiptailPresent = detectPresence ( "whiptail" ) ; - } - return lWhiptailPresent && ( isatty(1) || terminalName() ) ; -} + 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++; + } + } -static int graphicMode() -{ - return !( tinyfd_forceConsole && (isatty(1) || terminalName()) ) - && getenv ( "DISPLAY" ); -} + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = GetForegroundWindow(); + ofn.hInstance = 0; + ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = lBuff; + ofn.nMaxFile = lFullBuffLen; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; + ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; + ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; + ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + 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) + { + free(lBuff); + lBuff = NULL; + } + else + { + lBuffLen = wcslen(lBuff); + lPointers[0] = lBuff + lBuffLen + 1; + if (aAllowMultipleSelects && (lPointers[0][0] != L'\0')) + { + i = 0; + do + { + lLengths[i] = wcslen(lPointers[i]); + lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; + i++; + } while (lPointers[i][0] != L'\0' && i < MAX_MULTIPLE_FILES ); + if (i > MAX_MULTIPLE_FILES) + { + free(lBuff); + lBuff = NULL; + } + else + { + i--; + p = lBuff + lFullBuffLen - 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++; + wcscpy(lBuff, p); + lBuffLen = wcslen(lBuff); + } + } + if (lBuff) lBuff = (wchar_t*)(realloc(lBuff, (lBuffLen + 1) * sizeof(wchar_t))); + } -static int xmessagePresent ( ) -{ - static int lXmessagePresent = -1 ; - if ( lXmessagePresent < 0 ) - { - lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ - } - return lXmessagePresent && graphicMode ( ) ; + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lBuff; } -static int gxmessagePresent ( ) +BOOL CALLBACK BrowseCallbackProcW_enum(HWND hWndChild, LPARAM lParam) { - static int lGxmessagePresent = -1 ; - if ( lGxmessagePresent < 0 ) - { - lGxmessagePresent = detectPresence("gxmessage") ; + wchar_t buf[255]; + GetClassNameW(hWndChild, buf, sizeof(buf)); + if (wcscmp(buf, L"SysTreeView32") == 0) { + HTREEITEM hNode = TreeView_GetSelection(hWndChild); + TreeView_EnsureVisible(hWndChild, hNode); + return FALSE; } - return lGxmessagePresent && graphicMode ( ) ; + return TRUE; } -static int notifysendPresent ( ) +static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { - static int lNotifysendPresent = -1 ; - if ( lNotifysendPresent < 0 ) - { - lNotifysendPresent = detectPresence("notify-send") ; + switch (uMsg) { + case BFFM_INITIALIZED: + SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData); + break; + case BFFM_SELCHANGED: + EnumChildWindows(hwnd, BrowseCallbackProcW_enum, 0); } - return lNotifysendPresent && graphicMode ( ) ; + return 0; } - -static int xdialogPresent ( ) +wchar_t * tinyfd_selectFolderDialogW( + wchar_t const * aTitle, /* NULL or "" */ + wchar_t const * aDefaultPath) /* NULL or "" */ { - static int lXdialogPresent = -1 ; - if ( lXdialogPresent < 0 ) - { - lXdialogPresent = detectPresence("Xdialog") ; - } - return lXdialogPresent && graphicMode ( ) ; -} +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + static wchar_t lBuff[MAX_PATH_OR_CMD]; + wchar_t * lRetval; + BROWSEINFOW bInfo; + LPITEMIDLIST lpItem; + HRESULT lHResult; -static int gdialogPresent ( ) -{ - static int lGdialoglPresent = -1 ; - if ( lGdialoglPresent < 0 ) - { - lGdialoglPresent = detectPresence ( "gdialog" ) ; - } - return lGdialoglPresent && graphicMode ( ) ; -} + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } + if (quoteDetectedW(aTitle)) return tinyfd_selectFolderDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPath); + if (quoteDetectedW(aDefaultPath)) return tinyfd_selectFolderDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES"); -static int osascriptPresent ( ) -{ - static int lOsascriptPresent = -1 ; - if ( lOsascriptPresent < 0 ) - { - lOsascriptPresent = detectPresence ( "osascript" ) ; - } - return lOsascriptPresent && graphicMode ( ) ; -} + lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + bInfo.hwndOwner = GetForegroundWindow(); + 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; -static int kdialogPresent ( ) + lpItem = SHBrowseForFolderW(&bInfo); + if (!lpItem) + { + lRetval = NULL; + } + else + { + SHGetPathFromIDListW(lpItem, lBuff); + lRetval = lBuff ; + } + + if (lHResult == S_OK || lHResult == S_FALSE) + { + CoUninitialize(); + } + return lRetval; +} + + +wchar_t * tinyfd_colorChooserW( + wchar_t const * aTitle, /* NULL or "" */ + wchar_t const * aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { - static int lKdialogPresent = -1 ; - if ( lKdialogPresent < 0 ) +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + static wchar_t lResultHexRGB[8]; + CHOOSECOLORW cc; + COLORREF crCustColors[16]; + unsigned char lDefaultRGB[3]; + int lRet; + + HRESULT lHResult; + + if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } + + if (quoteDetectedW(aTitle)) return tinyfd_colorChooserW(L"INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); + if (quoteDetectedW(aDefaultHexRGB)) return tinyfd_colorChooserW(aTitle, L"INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); + + 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 = GetForegroundWindow(); + cc.hInstance = NULL; + cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); + cc.lpCustColors = crCustColors; + cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR ; + 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 int messageBoxWinGui( + char const * aTitle, /* NULL or "" */ + char const * aMessage, /* NULL or "" may contain \n and \t */ + char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * aIconType, /* "info" "warning" "error" "question" */ + int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + int lIntRetVal; + wchar_t lTitle[128] = L""; + wchar_t * lMessage = NULL; + wchar_t lDialogType[16] = L""; + wchar_t lIconType[16] = L""; + wchar_t * lTmpWChar; + + if (aTitle) { - lKdialogPresent = detectPresence("kdialog") ; + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); } - return lKdialogPresent && graphicMode ( ) ; + if (aMessage) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); + else lTmpWChar = tinyfd_mbcsTo16(aMessage); + lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); + if (lMessage) wcscpy(lMessage, lTmpWChar); + } + if (aDialogType) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDialogType); + else lTmpWChar = tinyfd_mbcsTo16(aDialogType); + wcscpy(lDialogType, lTmpWChar); + } + if (aIconType) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType); + else lTmpWChar = tinyfd_mbcsTo16(aIconType); + wcscpy(lIconType, lTmpWChar); + } + + lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, lDialogType, lIconType, aDefaultButton); + + free(lMessage); + + return lIntRetVal; } -static int zenityPresent ( ) +static int notifyWinGui( + char const * aTitle, /* NULL or "" */ + char const * aMessage, /* NULL or "" may NOT contain \n nor \t */ + char const * aIconType) { - static int lZenityPresent = -1 ; - if ( lZenityPresent < 0 ) +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t lTitle[128] = L""; + wchar_t * lMessage = NULL; + wchar_t lIconType[16] = L""; + wchar_t * lTmpWChar; + + if (aTitle) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); + } + if (aMessage) { - lZenityPresent = detectPresence("zenity") ; + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); + else lTmpWChar = tinyfd_mbcsTo16(aMessage); + lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); + if (lMessage) wcscpy(lMessage, lTmpWChar); } - return lZenityPresent && graphicMode ( ) ; + if (aIconType) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType); + else lTmpWChar = tinyfd_mbcsTo16(aIconType); + wcscpy(lIconType, lTmpWChar); + } + + tinyfd_notifyPopupW(lTitle, lMessage, lIconType); + + free(lMessage); + + return 1; } -static int zenity3Present ( ) +static int inputBoxWinGui( + char * aoBuff, + char const * aTitle, /* NULL or "" */ + char const * aMessage, /* NULL or "" may NOT contain \n nor \t */ + char const * aDefaultInput) /* "" , if NULL it's a passwordBox */ { - static int lZenity3Present = -1 ; - char lBuff [ MAX_PATH_OR_CMD ] ; - FILE * lIn ; - +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t lTitle[128] = L""; + wchar_t * lMessage = NULL; + wchar_t lDefaultInput[MAX_PATH_OR_CMD] = L""; + wchar_t * lTmpWChar; + char * lTmpChar; - if ( lZenity3Present < 0 ) + if (aTitle) { - if ( ! zenityPresent() ) - { - lZenity3Present = 0 ; - } - else - { - lIn = popen ( "zenity --version" , "r" ) ; - if ( ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) - && ( atoi(lBuff) >= 3 ) - && ( atoi(strtok(lBuff,".")+1) >= 0 ) ) - { - lZenity3Present = 1 ; - } - else - { - lZenity3Present = 0 ; - } - pclose ( lIn ) ; - } + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); + } + if (aMessage) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); + else lTmpWChar = tinyfd_mbcsTo16(aMessage); + lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); + if (lMessage) wcscpy(lMessage, lTmpWChar); + } + if (aDefaultInput) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultInput); + else lTmpWChar = tinyfd_mbcsTo16(aDefaultInput); + wcscpy(lDefaultInput, lTmpWChar); + lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, lDefaultInput); } - return lZenity3Present && graphicMode ( ) ; + else lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, NULL); + + free(lMessage); + + if (!lTmpWChar) + { + aoBuff[0] = '\0'; + return 0; + } + + if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); + else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); + + strcpy(aoBuff, lTmpChar); + + return 1; } -static int tkinter2Present ( ) +static char * saveFileDialogWinGui( + char * aoBuff, + char const * aTitle, /* NULL or "" */ + char const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns, /* 0 */ + char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * aSingleFilterDescription) /* NULL or "image files" */ { - static int lTkinter2Present = -1 ; - char lPythonCommand[256]; - char lPythonParams[256] = -"-c \"try:\n\timport Tkinter;\nexcept:\n\tprint(0);\""; +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t lTitle[128] = L""; + wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L""; + wchar_t lSingleFilterDescription[128] = L""; + wchar_t * * lFilterPatterns; + wchar_t * lTmpWChar; + char * lTmpChar; int i; - if ( lTkinter2Present < 0 ) - { - strcpy(gPython2Name , "python" ) ; - sprintf ( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; - lTkinter2Present = tryCommand(lPythonCommand); - if ( ! lTkinter2Present ) - { - 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 ("gPython2Name %s\n", gPython2Name) ; //*/ - return lTkinter2Present && graphicMode ( ) ; -} - - -/* returns 0 for cancel/no , 1 for ok/yes */ -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"*/ - char const * const aIconType , /* "info" "warning" "error" "question" */ - int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes */ -{ - char lBuff [ MAX_PATH_OR_CMD ] ; - char lDialogString [ MAX_PATH_OR_CMD ] ; - FILE * lIn ; - int lWasGraphicDialog = 0 ; - int lWasXterm = 0 ; - int lResult ; - char lChar ; - struct termios infoOri; - struct termios info; - lBuff[0]='\0'; - - if ( osascriptPresent ( ) ) - { - strcpy ( lDialogString , "osascript -e 'try' -e '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 ) ) + lFilterPatterns = (wchar_t **)malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]); + else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]); + lFilterPatterns[i] = (wchar_t *)malloc((wcslen(lTmpWChar) + 1) * sizeof(wchar_t *)); + if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar); + } + + if (aTitle) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); + } + if (aDefaultPathAndFile) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndFile); + else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndFile); + wcscpy(lDefaultPathAndFile, lTmpWChar); + } + if (aSingleFilterDescription) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription); + else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription); + wcscpy(lSingleFilterDescription, lTmpWChar); + } + + lTmpWChar = tinyfd_saveFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/ + lSingleFilterDescription); + + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) + { + return NULL; + } + + if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); + else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); + strcpy(aoBuff, lTmpChar); + if (tinyfd_winUtf8) (void)tinyfd_utf16to8(NULL); + else (void)tinyfd_utf16toMbcs(NULL); + + return aoBuff; +} + + +static char * openFileDialogWinGui( + char const * aTitle, /* NULL or "" */ + char const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns, /* 0 */ + char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * aSingleFilterDescription, /* NULL or "image files" */ + int aAllowMultipleSelects) /* 0 or 1 */ +{ +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t lTitle[128] = L""; + wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L""; + wchar_t lSingleFilterDescription[128] = L""; + wchar_t * * lFilterPatterns; + wchar_t * lTmpWChar; + char * lTmpChar; + int i; + + lFilterPatterns = (wchar_t * *)malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]); + else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]); + lFilterPatterns[i] = (wchar_t *)malloc((wcslen(lTmpWChar) + 1)*sizeof(wchar_t *)); + if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar); + } + + if (aTitle) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); + } + if (aDefaultPathAndFile) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndFile); + else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndFile); + wcscpy(lDefaultPathAndFile, lTmpWChar); + } + if (aSingleFilterDescription) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription); + else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription); + wcscpy(lSingleFilterDescription, lTmpWChar); + } + + lTmpWChar = tinyfd_openFileDialogW( + lTitle, + lDefaultPathAndFile, + aNumOfFilterPatterns, + (wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/ + lSingleFilterDescription, + aAllowMultipleSelects); + + for (i = 0; i < aNumOfFilterPatterns; i++) + { + free(lFilterPatterns[i]); + } + free(lFilterPatterns); + + if (!lTmpWChar) return NULL; + + if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); + else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); + (void)tinyfd_openFileDialogW(NULL, NULL, 0, NULL, NULL, -1); + + return lTmpChar; +} + + +static char * selectFolderDialogWinGui( + char * aoBuff, + char const * aTitle, /* NULL or "" */ + char const * aDefaultPath) /* NULL or "" */ +{ +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + wchar_t lTitle[128] = L""; + wchar_t lDefaultPath[MAX_PATH_OR_CMD] = L""; + wchar_t * lTmpWChar; + char * lTmpChar; + + if (aTitle) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); + } + if (aDefaultPath) + { + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPath); + else lTmpWChar = tinyfd_mbcsTo16(aDefaultPath); + wcscpy(lDefaultPath, lTmpWChar); + } + + lTmpWChar = tinyfd_selectFolderDialogW( + lTitle, + lDefaultPath); + + if (!lTmpWChar) + { + return NULL; + } + + if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); + else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); + strcpy(aoBuff, lTmpChar); + + return aoBuff; +} + + +static char * colorChooserWinGui( + char const * aTitle, /* NULL or "" */ + char const * aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ +#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); +#endif + static char lResultHexRGB[8]; + + wchar_t lTitle[128]; + wchar_t lDefaultHexRGB[16]; + wchar_t * lTmpWChar; + char * lTmpChar; + + if (aTitle) { - if ( ! aDefaultButton ) - { - strcat ( lDialogString ,"default button \"Cancel\" " ) ; - } + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); + else lTmpWChar = tinyfd_mbcsTo16(aTitle); + wcscpy(lTitle, lTmpWChar); } - else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + if (aDefaultHexRGB) { - strcat ( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ; - if (aDefaultButton) - { - strcat ( lDialogString ,"default button \"Yes\" " ) ; - } - else - { - strcat ( lDialogString ,"default button \"No\" " ) ; - } - strcat ( lDialogString ,"cancel button \"No\"" ) ; + if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultHexRGB); + else lTmpWChar = tinyfd_mbcsTo16(aDefaultHexRGB); + wcscpy(lDefaultHexRGB, lTmpWChar); } - else - { - strcat ( lDialogString ,"buttons {\"OK\"} " ) ; - strcat ( lDialogString ,"default button \"OK\" " ) ; - } - strcat(lDialogString, "' ") ; - strcat(lDialogString, "-e '1' " ); - strcat(lDialogString, "-e 'on error number -128' " ) ; - strcat(lDialogString, "-e '0' " ); - strcat(lDialogString, "-e 'end try'") ; - } - else if ( zenityPresent() ) - { - strcpy ( lDialogString , "zenity --" ) ; - if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + lTmpWChar = tinyfd_colorChooserW( + lTitle, + lDefaultHexRGB, + aDefaultRGB, + aoResultRGB ); + + if (!lTmpWChar) { - strcat ( lDialogString , - "question --ok-label=Ok --cancel-label=Cancel" ) ; + return NULL; } - else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + + if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); + else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); + strcpy(lResultHexRGB, lTmpChar); + + return lResultHexRGB; +} + + +static int dialogPresent(void) +{ + static int lDialogPresent = -1 ; + char lBuff[MAX_PATH_OR_CMD] ; + FILE * lIn ; + char const * lString = "dialog.exe"; + if (!tinyfd_allowCursesDialogs) return 0; + if (lDialogPresent < 0) { - strcat ( lDialogString , "question" ) ; - } - else if ( aIconType && ! strcmp( "error" , aIconType ) ) - { - strcat ( lDialogString , "error" ) ; + 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 ; + } } - else if ( aIconType && ! strcmp( "warning" , aIconType ) ) - { - strcat ( lDialogString , "warning" ) ; - } - else - { - strcat ( lDialogString , "info" ) ; + return lDialogPresent; +} + + +static int messageBoxWinConsole( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" may contain \n and \t */ + char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * aIconType , /* "info" "warning" "error" "question" */ + int 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 ( 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 ( ) ) - { - strcat ( lDialogString , " --icon-name=dialog-" ) ; - if ( aIconType && (! strcmp( "question" , aIconType ) - || ! strcmp( "error" , aIconType ) - || ! strcmp( "warning" , aIconType ) ) ) - { - strcat ( lDialogString , aIconType ) ; - } - else - { - strcat ( lDialogString , "info" ) ; - } - } - strcat ( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); - } - else if ( kdialogPresent() ) - { - strcpy ( lDialogString , "kdialog --" ) ; - if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType ) - || ! strcmp( "yesno" , aDialogType ) ) ) - { - if ( aIconType && ( ! strcmp( "warning" , aIconType ) - || ! strcmp( "error" , aIconType ) ) ) - { - strcat ( lDialogString , "warning" ) ; - } - 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, "\"") ; - } - strcat ( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); - } - else if ( ! xdialogPresent() && tkinter2Present ( ) ) - { - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && isDarwin ( ) ) + + if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) + || !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) ) { - strcat ( lDialogString , " -i" ) ; /* for osx without console */ + strcat(lDialogString, "--backtitle \"") ; + strcat(lDialogString, "tab: move focus") ; + strcat(lDialogString, "\" ") ; } - - 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," ) ; - } + if ( ! aDefaultButton ) + { + strcat( lDialogString , "--defaultno " ) ; + } + strcat( lDialogString , + "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { - strcat ( lDialogString , "askyesno(" ) ; - if ( aDefaultButton ) - { - strcat ( lDialogString , "default=tkMessageBox.YES," ) ; - } - else - { - strcat ( lDialogString , "default=tkMessageBox.NO," ) ; - } + 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 , "showinfo(" ) ; + strcat( lDialogString , "--msgbox " ) ; + } + + strcat( lDialogString , "\"" ) ; + if ( aMessage && strlen(aMessage) ) + { + tfd_replaceSubStr( aMessage , "\n" , "\\n" , lBuff ) ; + strcat(lDialogString, lBuff) ; + lBuff[0]='\0'; } - strcat ( lDialogString , "icon='" ) ; - if ( aIconType && (! strcmp( "question" , aIconType ) - || ! strcmp( "error" , aIconType ) - || ! strcmp( "warning" , aIconType ) ) ) + strcat(lDialogString, "\" "); + + if (aDialogType && !strcmp("yesnocancel", aDialogType)) { - strcat ( lDialogString , aIconType ) ; + strcat(lDialogString, "0 60 0 Yes \"\" No \"\""); + strcat(lDialogString, "2>>"); } else { - strcat ( lDialogString , "info" ) ; + strcat(lDialogString, "10 60"); + strcat(lDialogString, " && echo 1 > "); } - strcat(lDialogString, "',") ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "title='") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "',") ; - } - if ( aMessage && strlen(aMessage) ) - { - replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; - strcat(lDialogString, "message='") ; - strcat(lDialogString, lBuff) ; - strcat(lDialogString, "'") ; - lBuff[0]='\0'; - } - strcat(lDialogString, ");\n\ -if res==False :\n\tprint 0\n\ -else :\n\tprint 1\n\"" ) ; + + strcpy(lDialogFile, getenv("TEMP")); + strcat(lDialogFile, "\\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 int inputBoxWinConsole( + char * aoBuff , + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" may NOT contain \n nor \t */ + char 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("TEMP")); + strcat(lDialogFile, "\\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("TEMP")); + strcat(lDialogFile, "\\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); + aoBuff[0] = '\0'; + 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' ; } - else if (!xdialogPresent() && !gdialogPresent() && gxmessagePresent() ) + /* 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 ) { - strcpy ( lDialogString , "gxmessage"); - if ( aDialogType && ! strcmp("okcancel" , aDialogType) ) - { - strcpy ( lDialogString , " -buttons Ok:1,Cancel:0"); - } - else if ( aDialogType && ! strcmp("yesno" , aDialogType) ) - { - strcpy ( lDialogString , " -buttons Yes:1,No:0"); - } - - strcpy ( lDialogString , " -center \""); - if ( aMessage && strlen(aMessage) ) - { - strcat ( lDialogString , aMessage ) ; - } - strcat(lDialogString, "\"" ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat ( lDialogString , " -title \""); - strcat ( lDialogString , aTitle ) ; - strcat(lDialogString, "\"" ) ; - } + aoBuff[0] = '\0'; + return 0 ; } - else if (!xdialogPresent() && !gdialogPresent() && notifysendPresent() - && strcmp("okcancel" , aDialogType) - && strcmp("yesno" , aDialogType) ) + /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ + strcpy(aoBuff, aoBuff+3); + return 1; +} + + +static char * saveFileDialogWinConsole( + char * aoBuff , + char const * aTitle , /* NULL or "" */ + char 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("TEMP")); + strcat(lPathAndFile, "\\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 * openFileDialogWinConsole( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile ) /* NULL or "" */ +{ + char lFilterPatterns[MAX_PATH_OR_CMD] = ""; + char lDialogString[MAX_PATH_OR_CMD] ; + FILE * lIn; + + static char aoBuff[MAX_PATH_OR_CMD]; + + 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("TEMP")); + strcat(lFilterPatterns, "\\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 * selectFolderDialogWinConsole( + char * aoBuff , + char const * aTitle , /* NULL or "" */ + char 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("TEMP")); + strcat(lString, "\\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; +} + +static void writeUtf8( char const * aUtf8String ) +{ + unsigned long lNum; + void * lConsoleHandle; + wchar_t * lTmpWChar; + + lConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); + lTmpWChar = tinyfd_utf8to16(aUtf8String); + (void)WriteConsoleW(lConsoleHandle, lTmpWChar, (DWORD) wcslen(lTmpWChar), &lNum, NULL); +} + + +int tinyfd_messageBox( + char const * aTitle, /* NULL or "" */ + char const * aMessage, /* NULL or "" may contain \n and \t */ + char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * aIconType, /* "info" "warning" "error" "question" */ + int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ +{ + char lChar; + UINT lOriginalCP = 0; + UINT lOriginalOutputCP = 0; + + if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); + if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); + + if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { - strcpy ( lDialogString , "notify-send \"" ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, aTitle) ; - strcat ( lDialogString , " | " ) ; - } - if ( aMessage && strlen(aMessage) ) - { - strcat(lDialogString, aMessage) ; - } - strcat ( lDialogString , "\"" ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; } + return messageBoxWinGui(aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } - else if (!xdialogPresent() && !gdialogPresent() && xmessagePresent() - && strcmp("okcancel" , aDialogType) - && strcmp("yesno" , aDialogType) ) + else if (dialogPresent()) { - strcpy ( lDialogString , "xmessage -center \""); - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\n\n" ) ; - } - if ( aMessage && strlen(aMessage) ) - { - strcat(lDialogString, aMessage) ; - } - strcat(lDialogString, "\"" ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return 0; } + return messageBoxWinConsole( + aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } - else if ( xdialogPresent() || gdialogPresent() - || dialogName() || whiptailPresent() ) + else { - if ( xdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(Xdialog " ) ; - } - else if ( gdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(gdialog " ) ; - } - else if ( dialogName ( ) ) - { - if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(dialog " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(" ) ; - strcat ( lDialogString , dialogName() ) ; - strcat ( lDialogString , " " ) ; - } - } - else if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(whiptail " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(whiptail " ) ; - } - - if ( aTitle && strlen(aTitle) ) + if (!tinyfd_winUtf8) { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - 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 " ) ; + lOriginalCP = GetConsoleCP(); + lOriginalOutputCP = GetConsoleOutputCP(); + (void)SetConsoleCP(GetACP()); + (void)SetConsoleOutputCP(GetACP()); } - else - { - strcat ( lDialogString , "--msgbox " ) ; - } - strcat ( lDialogString , "\"" ) ; - if ( aMessage && strlen(aMessage) ) + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return 0; } + if (!gWarningDisplayed && !tinyfd_forceConsole) { - strcat(lDialogString, aMessage) ; + gWarningDisplayed = 1; + printf("\n\n%s\n", gTitle); + printf("%s\n\n", tinyfd_needs); } - if ( lWasGraphicDialog ) + if (aTitle && strlen(aTitle)) { - strcat(lDialogString, - "\" 10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); + printf("\n"); + if (tinyfd_winUtf8) writeUtf8(aTitle); + else printf("%s", aTitle); + printf("\n\n"); } - else + if (aDialogType && !strcmp("yesno", aDialogType)) { - 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 + do { - strcat(lDialogString, - "then echo 1;else echo 0;fi;clear >/dev/tty"); - } - } - } - else if ( ! isatty ( 1 ) && terminalName() ) - { - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'" ) ; - if ( !gWarningDisplayed ) - { - 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 raw -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 raw -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 - { - strcat(lDialogString , "echo -n \"press any key to continue \"; "); - strcat ( lDialogString , "stty raw -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 ( !gWarningDisplayed ) - { - gWarningDisplayed = 1 ; - printf ("\n\n%s\n", gTitle); - printf ("%s\n\n\n", gMessageUnix); - } - if ( aTitle && strlen(aTitle) ) - { - printf ("%s\n\n", aTitle); + if (aMessage && strlen(aMessage)) + { + if (tinyfd_winUtf8) writeUtf8(aMessage); + else printf("%s", aMessage); + printf("\n"); + } + printf("y/n: "); + lChar = (char)tolower(_getch()); + printf("\n\n"); + } while (lChar != 'y' && lChar != 'n'); + if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } + return lChar == 'y' ? 1 : 0; } - - 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) ) + else if (aDialogType && !strcmp("okcancel", aDialogType)) { do { - if ( aMessage && strlen(aMessage) ) + if (aMessage && strlen(aMessage)) { - printf("%s\n",aMessage); + if (tinyfd_winUtf8) writeUtf8(aMessage); + else printf("%s", aMessage); + printf("\n"); } - printf("y/n: "); - lChar = tolower ( getchar() ) ; + printf("[O]kay/[C]ancel: "); + lChar = (char)tolower(_getch()); printf("\n\n"); - } - while ( lChar != 'y' && lChar != 'n' ); - lResult = lChar == 'y' ? 1 : 0 ; + } while (lChar != 'o' && lChar != 'c'); + if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } + return lChar == 'o' ? 1 : 0; } - else if ( aDialogType && !strcmp("okcancel",aDialogType) ) + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { do { - if ( aMessage && strlen(aMessage) ) + if (aMessage && strlen(aMessage)) { - printf("%s\n",aMessage); + if (tinyfd_winUtf8) writeUtf8(aMessage); + else printf("%s", aMessage); + printf("\n"); } - printf("[O]kay/[C]ancel: "); - lChar = tolower ( getchar() ) ; + printf("[Y]es/[N]o/[C]ancel: "); + lChar = (char)tolower(_getch()); printf("\n\n"); - } - while ( lChar != 'o' && lChar != 'c' ); - lResult = lChar == 'o' ? 1 : 0 ; + } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); + if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } + return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0; } else { - if ( aMessage && strlen(aMessage) ) + if (aMessage && strlen(aMessage)) { - printf("%s\n\n",aMessage); + if (tinyfd_winUtf8) writeUtf8(aMessage); + else printf("%s", aMessage); + printf("\n\n"); } - printf("press any key to continue "); - getchar() ; + printf("press enter to continue "); + lChar = (char)_getch(); printf("\n\n"); - lResult = 1 ; + if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } + return 1; } - tcsetattr(0, TCSANOW, &infoOri); - return lResult ; } +} - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) - { - 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' ) + +/* return has only meaning for tinyfd_query */ +int tinyfd_notifyPopup( + char const * aTitle, /* NULL or "" */ + char const * aMessage , /* NULL or "" may contain \n \t */ + char const * aIconType ) /* "info" "warning" "error" */ +{ + if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType); + if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType); + + if ( powershellPresent() && (!tinyfd_forceConsole || !( + GetConsoleWindow() || + dialogPresent())) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return 1;} + return notifyWinGui(aTitle, aMessage, aIconType); } - /* printf ( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; //*/ - lResult = strcmp ( lBuff , "1" ) ? 0 : 1 ; - /* printf ( "lResult: %d\n" , lResult ) ; //*/ - return lResult ; + else + return tinyfd_messageBox(aTitle, aMessage, "ok" , aIconType, 0); } /* 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 */ +char * tinyfd_inputBox( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" (\n and \t have no effect) */ + char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */ { - static char lBuff[MAX_PATH_OR_CMD]; - char lDialogString[MAX_PATH_OR_CMD]; - FILE * lIn ; - int lResult ; - int lWasGdialog = 0 ; - int lWasGraphicDialog = 0 ; - int lWasXterm = 0 ; - int lWasBasicXterm = 0 ; - struct termios oldt ; - struct termios newt ; - lBuff[0]='\0'; - - if ( osascriptPresent ( ) ) + static char lBuff[MAX_PATH_OR_CMD] = ""; + char * lEOF; + + DWORD mode = 0; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + + unsigned long lNum; + void * lConsoleHandle; + char * lTmpChar; + wchar_t lBuffW[1024]; + + UINT lOriginalCP = 0; + UINT lOriginalOutputCP = 0; + + if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */ + + if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); + if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput); + if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES"); + + mode = 0; + hStdin = GetStdHandle(STD_INPUT_HANDLE); + + if ((!tinyfd_forceConsole || !( + GetConsoleWindow() || + dialogPresent())) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { - strcpy ( lDialogString , "osascript -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 (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} + lBuff[0]='\0'; + if (inputBoxWinGui(lBuff, aTitle, aMessage, aDefaultInput)) return lBuff; + else return NULL; } - else if ( zenityPresent() ) - { - strcpy ( lDialogString , "szAnswer=$(zenity --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 ) - { - if ( strlen(aDefaultInput) ) - { - strcat(lDialogString, " --entry-text=\"") ; - strcat(lDialogString, aDefaultInput) ; - strcat(lDialogString, "\"") ; - } + else if ( dialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} + lBuff[0]='\0'; + if (inputBoxWinConsole(lBuff, aTitle, aMessage, aDefaultInput) ) return lBuff; + else return NULL; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;} + lBuff[0]='\0'; + if (!gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + printf("\n\n%s\n", gTitle); + printf("%s\n\n", tinyfd_needs); + } + + if (!tinyfd_winUtf8) + { + lOriginalCP = GetConsoleCP(); + lOriginalOutputCP = GetConsoleOutputCP(); + (void)SetConsoleCP(GetACP()); + (void)SetConsoleOutputCP(GetACP()); + } + + if (aTitle && strlen(aTitle)) + { + printf("\n"); + if (tinyfd_winUtf8) writeUtf8(aTitle); + else printf("%s", aTitle); + printf("\n\n"); + } + if ( aMessage && strlen(aMessage) ) + { + if (tinyfd_winUtf8) writeUtf8(aMessage); + else printf("%s", aMessage); + printf("\n"); + } + printf("(ctrl-Z + enter to cancel): "); + if ( ! aDefaultInput ) + { + (void) GetConsoleMode(hStdin, &mode); + (void) SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); + } + if (tinyfd_winUtf8) + { + lConsoleHandle = GetStdHandle(STD_INPUT_HANDLE); + (void) ReadConsoleW(lConsoleHandle, lBuffW, MAX_PATH_OR_CMD, &lNum, NULL); + if (!aDefaultInput) + { + (void)SetConsoleMode(hStdin, mode); + printf("\n"); + } + lBuffW[lNum] = '\0'; + if (lBuffW[wcslen(lBuffW) - 1] == '\n') lBuffW[wcslen(lBuffW) - 1] = '\0'; + if (lBuffW[wcslen(lBuffW) - 1] == '\r') lBuffW[wcslen(lBuffW) - 1] = '\0'; + lTmpChar = tinyfd_utf16to8(lBuffW); + if (lTmpChar) + { + strcpy(lBuff, lTmpChar); + return lBuff; } else - { - strcat(lDialogString, " --hide-text") ; + return NULL; + } + else + { + lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); + if (!aDefaultInput) + { + (void)SetConsoleMode(hStdin, mode); + printf("\n"); + } + + if (!tinyfd_winUtf8) + { + (void)SetConsoleCP(lOriginalCP); + (void)SetConsoleOutputCP(lOriginalOutputCP); + } + + if (!lEOF) + { + return NULL; + } + printf("\n"); + if (strchr(lBuff, 27)) + { + return NULL; + } + if (lBuff[strlen(lBuff) - 1] == '\n') + { + lBuff[strlen(lBuff) - 1] = '\0'; + } + return lBuff; } - strcat ( lDialogString , - ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } - else if ( kdialogPresent() ) - { - strcpy ( lDialogString , "szAnswer=$(kdialog" ) ; - if ( ! aDefaultInput ) - { - strcat(lDialogString, " --password ") ; - } - else +} + + +char * tinyfd_saveFileDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile , /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 */ + char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * aSingleFilterDescription ) /* NULL or "image files" */ +{ + static char lBuff[MAX_PATH_OR_CMD] ; + char lString[MAX_PATH_OR_CMD] ; + char * p ; + char * lPointerInputBox; + int i; + + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES"); + for (i = 0; i < aNumOfFilterPatterns; i++) { - strcat(lDialogString, " --inputbox ") ; - + if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); } - strcat(lDialogString, "\"") ; - if ( aMessage && strlen(aMessage) ) + + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} + p = saveFileDialogWinGui(lBuff, + aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, (char const * const *)aFilterPatterns, aSingleFilterDescription); + } + else if (dialogPresent()) { - strcat(lDialogString, aMessage ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } + p = saveFileDialogWinConsole(lBuff, aTitle, aDefaultPathAndFile); } - strcat(lDialogString , "\" \"" ) ; - if ( aDefaultInput && strlen(aDefaultInput) ) + else { - 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 ( ! xdialogPresent() && tkinter2Present ( ) ) - { - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && 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) ) - { - replaceSubStr ( aMessage , "\n" , "\\n" , lBuff ) ; - strcat(lDialogString, "prompt='") ; - strcat(lDialogString, lBuff) ; - strcat(lDialogString, "',") ; - lBuff[0]='\0'; - } - if ( aDefaultInput ) - { - if ( strlen(aDefaultInput) ) - { - strcat(lDialogString, "initialvalue='") ; - strcat(lDialogString, aDefaultInput) ; - strcat(lDialogString, "',") ; - } - } - else - { - strcat(lDialogString, "show='*'") ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } + strcpy(lBuff, "Save file in "); + strcat(lBuff, getCurDir()); + + lPointerInputBox = tinyfd_inputBox(NULL,NULL,NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ + p = lBuff; } - strcat(lDialogString, ");\nif res is None :\n\tprint 0"); - strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ; - } - else if (!xdialogPresent() && !gdialogPresent() && gxmessagePresent() ) + + 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 * tinyfd_openFileDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 */ + char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ + char const * aSingleFilterDescription, /* NULL or "image files" */ + int aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lString[MAX_PATH_OR_CMD]; + char lBuff[MAX_PATH_OR_CMD]; + char * p; + char * lPointerInputBox; + int i; + + if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); + for (i = 0; i < aNumOfFilterPatterns; i++) { - strcpy ( lDialogString , "gxmessage -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, "\"" ) ; + if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); } - else if ( xdialogPresent() || gdialogPresent() - || dialogName() || whiptailPresent() ) - { - if ( xdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(Xdialog " ) ; - } - else if ( gdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - lWasGdialog = 1 ; - strcpy ( lDialogString , "(gdialog " ) ; - } - else if ( dialogName ( ) ) - { - if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(dialog " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(" ) ; - strcat ( lDialogString , dialogName() ) ; - strcat ( lDialogString , " " ) ; - } - } - else if ( isatty ( 1 ) ) + + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} + p = openFileDialogWinGui( aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, + (char const * const *)aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + } + else if (dialogPresent()) { - strcpy ( lDialogString , "(whiptail " ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } + p = openFileDialogWinConsole(aTitle, aDefaultPathAndFile); } else { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(whiptail " ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } + strcpy(lBuff, "Open file from "); + strcat(lBuff, getCurDir()); + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ + p = lBuff; } + if ( ! p || ! strlen( p ) ) + { + return NULL; + } + if ( aAllowMultipleSelects && strchr(p, '|') ) + { + p = ensureFilesExist( (char *) p , p ) ; + } + else if ( ! fileExists(p) ) + { + return NULL ; + } + /* printf( "lBuff3: %s\n" , p ) ; */ + return p ; +} + - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\" ") ; - } - if ( aDefaultInput || lWasGdialog ) - { - strcat ( lDialogString , "--inputbox" ) ; - } +char * tinyfd_selectFolderDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPath ) /* NULL or "" */ +{ + static char lBuff[MAX_PATH_OR_CMD]; + char * p; + char * lPointerInputBox; + char lString[MAX_PATH_OR_CMD]; + + if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath); + if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES"); + + if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} + p = selectFolderDialogWinGui(lBuff, aTitle, aDefaultPath); + } else + if (dialogPresent()) { - 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);\ - rm /tmp/tinyfd.txt;echo $tinyfdBool$tinyfdRes") ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } + p = selectFolderDialogWinConsole(lBuff, aTitle, aDefaultPath); } else { - strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\ - if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ - tinyfdRes=$(cat /tmp/tinyfd.txt);\ - rm /tmp/tinyfd.txt;echo $tinyfdBool$tinyfdRes") ; - if ( lWasXterm ) - { - strcat ( lDialogString , - " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt;rm /tmp/tinyfd0.txt"); - } - else - { - strcat(lDialogString, "; clear >/dev/tty") ; - } - } - } - else if ( ! isatty ( 1 ) && terminalName() ) - { - lWasBasicXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'" ) ; - if ( !gWarningDisplayed ) - { - 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;" ) ; - } - - strcat ( lDialogString , "echo \"" ) ; - if ( aMessage && strlen(aMessage) ) - { - strcat ( lDialogString, aMessage) ; - } - strcat ( lDialogString , "\";read " ) ; - if ( ! aDefaultInput ) - { - strcat ( lDialogString , "-s " ) ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } + strcpy(lBuff, "Select folder from "); + strcat(lBuff, getCurDir()); + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ + p = lBuff; } - 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;rm /tmp/tinyfd.txt"); - } - else if ( isatty ( 1 ) ) + + 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 * tinyfd_colorChooser( + char const * aTitle, /* NULL or "" */ + char const * aDefaultHexRGB, /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ +{ + static char lDefaultHexRGB[16]; + int i; + char * p ; + char * lPointerInputBox; + char lString[MAX_PATH_OR_CMD]; + + lDefaultHexRGB[0] = '\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); + if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); + + if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) + && (!getenv("SSH_CLIENT") || getenvDISPLAY())) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} + p = colorChooserWinGui(aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); + if (p) + { + strcpy(lDefaultHexRGB, p); + return lDefaultHexRGB; + } + return NULL; + } + else if (dialogPresent()) { - if ( !gWarningDisplayed ) - { - gWarningDisplayed = 1 ; - printf ("\n\n%s\n", gTitle); - printf ("%s\n\n\n", gMessageUnix); - } - if ( aTitle && strlen(aTitle) ) - { - printf ("%s\n\n", aTitle); - } - if ( aMessage && strlen(aMessage) ) - { - printf("%s\n",aMessage); - } - printf("(esc+enter to cancel): "); - if ( ! aDefaultInput ) - { - tcgetattr(STDIN_FILENO, & oldt) ; - newt = oldt ; - newt.c_lflag &= ~ECHO ; - tcsetattr(STDIN_FILENO, TCSANOW, & newt); - } - fgets(lBuff, MAX_PATH_OR_CMD, stdin); - if ( ! aDefaultInput ) - { - tcsetattr(STDIN_FILENO, TCSANOW, & oldt); - printf ("\n"); - } - printf ("\n"); - if ( strchr(lBuff,27) ) - { - return NULL ; - } - if ( lBuff[ strlen ( lBuff ) -1 ] == '\n' ) - { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; - } - return lBuff ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } } else { - if ( !gWarningDisplayed ) - { - gWarningDisplayed = 1 ; - tinyfd_messageBox ( gTitle , gMessageUnix , "ok", "error" , 1 ) ; - } - return NULL ; + if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) - { - return NULL ; - } - while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) - {} - pclose ( lIn ) ; - /* printf ( "len Buff: %lu\n" , strlen(lBuff) ) ; //*/ - /* printf ( "lBuff0: %s\n" , lBuff ) ; //*/ - if ( lBuff[ strlen ( lBuff ) -1 ] == '\n' ) + if (aDefaultHexRGB) { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; + strncpy(lDefaultHexRGB, aDefaultHexRGB,7); + lDefaultHexRGB[7]='\0'; } - /* printf ( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; //*/ - if ( lWasBasicXterm ) + else { - if ( strstr(lBuff,"^[") ) /* esc was pressed */ - { - return NULL ; - } + RGB2Hex(aDefaultRGB, lDefaultHexRGB); } - lResult = strncmp ( lBuff , "1" , 1) ? 0 : 1 ; - /* printf ( "lResult: %d \n" , lResult ) ; //*/ - if ( ! lResult ) + + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB); + + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) { - return NULL ; + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( (int) p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + + strcpy(lDefaultHexRGB, p); + + if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ + + return lDefaultHexRGB; +} + + +#else /* unix */ + +static char gPython2Name[16]; +static char gPython3Name[16]; +static char gPythonName[16]; + +int tfd_isDarwin(void) +{ + static int lsIsDarwin = -1 ; + struct utsname lUtsname ; + if ( lsIsDarwin < 0 ) + { + lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ; + } + return lsIsDarwin ; +} + + +static int dirExists( char 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 * aExecutable ) +{ + char lBuff[MAX_PATH_OR_CMD] ; + char lTestedString[MAX_PATH_OR_CMD] = "which " ; + FILE * lIn ; +#ifdef _GNU_SOURCE + char* lAllocatedCharString; + int lSubstringUndetected; +#endif + + 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 ) ; + +#ifdef _GNU_SOURCE /*to bypass this, just comment out "#define _GNU_SOURCE" at the top of the file*/ + if ( lBuff[strlen( lBuff ) -1] == '\n' ) lBuff[strlen( lBuff ) -1] = '\0' ; + lAllocatedCharString = realpath(lBuff,NULL); /*same as canonicalize_file_name*/ + lSubstringUndetected = ! strstr(lAllocatedCharString, aExecutable); + free(lAllocatedCharString); + if (lSubstringUndetected) + { + if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0); + return 0; + } +#endif /*_GNU_SOURCE*/ + + 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 * getVersion( char const * aExecutable ) /*version must be first numeral*/ +{ + 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 ) ; + + lTmp += strcspn(lTmp,"0123456789"); + /* printf("lTmp:%s\n", lTmp); */ + return lTmp ; +} + + +static int * getMajorMinorPatch( char const * aExecutable ) +{ + static int lArray[3] ; + char * lTmp ; + + lTmp = (char *) getVersion(aExecutable); + lArray[0] = atoi( strtok(lTmp," ,.-") ) ; + /* printf("lArray0 %d\n", lArray[0]); */ + lArray[1] = atoi( strtok(0," ,.-") ) ; + /* printf("lArray1 %d\n", lArray[1]); */ + lArray[2] = atoi( strtok(0," ,.-") ) ; + /* printf("lArray2 %d\n", lArray[2]); */ + + if ( !lArray[0] && !lArray[1] && !lArray[2] ) return NULL; + return lArray ; +} + + +static int tryCommand( char 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(void) +{ + static int lIsTerminalRunning = -1 ; + if ( lIsTerminalRunning < 0 ) + { + lIsTerminalRunning = isatty(1); + if (tinyfd_verbose) printf("isTerminalRunning %d\n", lIsTerminalRunning ); } - /* printf ( "lBuff+1: %s\n" , lBuff+1 ) ; //*/ - 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 ; - DIR * lDir ; - FILE * lIn ; - lBuff[0]='\0'; - - if ( osascriptPresent ( ) ) + return lIsTerminalRunning; +} + + +static char * dialogNameOnly(void) +{ + static char lDialogName[128] = "*" ; + if ( lDialogName[0] == '*' ) { - strcpy ( lDialogString , - "osascript -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) ) + if (!tinyfd_allowCursesDialogs) { - strcat(lDialogString, "default location \"") ; - strcat(lDialogString, lString ) ; - strcat(lDialogString , "\" " ) ; + strcpy(lDialogName , "" ); } - getLastName ( lString , aDefaultPathAndFile ) ; - if ( strlen(lString) ) + else if ( tfd_isDarwin() && * strcpy(lDialogName , "/opt/local/bin/dialog" ) + && detectPresence( lDialogName ) ) + {} + else if ( * strcpy(lDialogName , "dialog" ) + && detectPresence( lDialogName ) ) + {} + else { - strcat(lDialogString, "default name \"") ; - strcat(lDialogString, lString ) ; - strcat(lDialogString , "\" " ) ; + strcpy(lDialogName , "" ); } - strcat ( lDialogString , ")'" ) ; } - else if ( zenityPresent() ) - { - strcpy ( lDialogString , - "zenity --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() ) - { - strcpy ( lDialogString , "kdialog --getsavefilename" ) ; - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + return lDialogName ; +} + + +int isDialogVersionBetter09b(void) +{ + 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 ( ! strlen(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(void) +{ + static int lWhiptailPresent = -1 ; + if (!tinyfd_allowCursesDialogs) return 0; + if ( lWhiptailPresent < 0 ) { - 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 ( ) ) - { + lWhiptailPresent = detectPresence( "whiptail" ) ; + } + return lWhiptailPresent ; +} + + +static char * terminalName(void) +{ + static char lTerminalName[128] = "*" ; + char lShellName[64] = "*" ; + int * lArray; + + if ( lTerminalName[0] == '*' ) + { + if ( detectPresence( "bash" ) ) + { + strcpy(lShellName , "bash -c " ) ; /*good for basic input*/ + } + else if ( strlen(dialogNameOnly()) || whiptailPresentOnly() ) + { + strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/ + } + else + { + strcpy(lTerminalName , "" ) ; + return NULL ; + } + + if ( tfd_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,"tilix") /*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 if ( * strcpy(lTerminalName,"gnome-terminal") + && detectPresence(lTerminalName) && (lArray = getMajorMinorPatch(lTerminalName)) + && ((lArray[0]<3) || (lArray[0]==3 && lArray[1]<=6)) ) + { + strcat(lTerminalName , " --disable-factory -x " ) ; + strcat(lTerminalName , lShellName ) ; + } + else + { + strcpy(lTerminalName , "" ) ; + } + /* 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 * dialogName(void) +{ + char * lDialogName ; + lDialogName = dialogNameOnly( ) ; + if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) ) + { + return lDialogName ; + } + else + { + return NULL ; + } +} + + +static int whiptailPresent(void) +{ + int lWhiptailPresent ; + lWhiptailPresent = whiptailPresentOnly( ) ; + if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) ) + { + return lWhiptailPresent ; + } + else + { + return 0 ; + } +} + + + +static int graphicMode(void) +{ + return !( tinyfd_forceConsole && (isTerminalRunning() || terminalName()) ) + && ( getenvDISPLAY() + || (tfd_isDarwin() && (!getenv("SSH_TTY") || getenvDISPLAY() ) ) ) ; +} + + +static int pactlPresent(void) +{ + static int lPactlPresent = -1 ; + if ( lPactlPresent < 0 ) + { + lPactlPresent = detectPresence("pactl") ; + } + return lPactlPresent ; +} + + +static int speakertestPresent(void) +{ + static int lSpeakertestPresent = -1 ; + if ( lSpeakertestPresent < 0 ) + { + lSpeakertestPresent = detectPresence("speaker-test") ; + } + return lSpeakertestPresent ; +} + + +static int playPresent() +{ + static int lPlayPresent = -1; + if (lPlayPresent < 0) + { + lPlayPresent = detectPresence("sox"); /*if sox is present, play is ready*/ + } + return lPlayPresent; +} + + +static int beepexePresent() +{ + static int lBeepexePresent = -1; + if (lBeepexePresent < 0) + { + lBeepexePresent = detectPresence("beep.exe"); + } + return lBeepexePresent; +} + + +static int beepPresent(void) +{ + static int lBeepPresent = -1 ; + if ( lBeepPresent < 0 ) + { + lBeepPresent = detectPresence("beep") ; + } + return lBeepPresent ; +} + + +static int xmessagePresent(void) +{ + static int lXmessagePresent = -1 ; + if ( lXmessagePresent < 0 ) + { + lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ + } + return lXmessagePresent && graphicMode( ) ; +} + + +static int gxmessagePresent(void) +{ + static int lGxmessagePresent = -1 ; + if ( lGxmessagePresent < 0 ) + { + lGxmessagePresent = detectPresence("gxmessage") ; + } + return lGxmessagePresent && graphicMode( ) ; +} + + +static int gmessagePresent(void) +{ + static int lGmessagePresent = -1 ; + if ( lGmessagePresent < 0 ) + { + lGmessagePresent = detectPresence("gmessage") ; + } + return lGmessagePresent && graphicMode( ) ; +} + + +static int notifysendPresent(void) +{ + static int lNotifysendPresent = -1 ; + if ( lNotifysendPresent < 0 ) + { + lNotifysendPresent = detectPresence("notify-send") ; + } + return lNotifysendPresent && graphicMode( ) ; +} + + +static int perlPresent(void) +{ + static int lPerlPresent = -1 ; + char lBuff[MAX_PATH_OR_CMD] ; + FILE * lIn ; + + if ( lPerlPresent < 0 ) + { + lPerlPresent = detectPresence("perl") ; + if (lPerlPresent) + { + lIn = popen("perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1", "r"); + if (fgets(lBuff, sizeof(lBuff), lIn) == NULL) + { + lPerlPresent = 2; + } + pclose(lIn); + if (tinyfd_verbose) printf("perl-dbus %d\n", lPerlPresent); + } + } + return graphicMode() ? lPerlPresent : 0 ; +} + + +static int afplayPresent(void) +{ + static int lAfplayPresent = -1 ; + char lBuff[MAX_PATH_OR_CMD] ; + FILE * lIn ; + + if ( lAfplayPresent < 0 ) + { + lAfplayPresent = detectPresence("afplay") ; + if ( lAfplayPresent ) + { + lIn = popen( "test -e /System/Library/Sounds/Ping.aiff || echo Ping" , "r" ) ; + if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) + { + lAfplayPresent = 2 ; + } + pclose( lIn ) ; + if (tinyfd_verbose) printf("afplay %d\n", lAfplayPresent); + } + } + return graphicMode() ? lAfplayPresent : 0 ; +} + + +static int xdialogPresent(void) +{ + static int lXdialogPresent = -1 ; + if ( lXdialogPresent < 0 ) + { + lXdialogPresent = detectPresence("Xdialog") ; + } + return lXdialogPresent && graphicMode( ) ; +} + + +static int gdialogPresent(void) +{ + static int lGdialoglPresent = -1 ; + if ( lGdialoglPresent < 0 ) + { + lGdialoglPresent = detectPresence( "gdialog" ) ; + } + return lGdialoglPresent && graphicMode( ) ; +} + + +static int osascriptPresent(void) +{ + static int lOsascriptPresent = -1 ; + if ( lOsascriptPresent < 0 ) + { + gWarningDisplayed |= !!getenv("SSH_TTY"); + lOsascriptPresent = detectPresence( "osascript" ) ; + } + return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ; +} + + +int tfd_qarmaPresent(void) +{ + static int lQarmaPresent = -1 ; + if ( lQarmaPresent < 0 ) + { + lQarmaPresent = detectPresence("qarma") ; + } + return lQarmaPresent && graphicMode( ) ; +} + + +int tfd_matedialogPresent(void) +{ + static int lMatedialogPresent = -1 ; + if ( lMatedialogPresent < 0 ) + { + lMatedialogPresent = detectPresence("matedialog") ; + } + return lMatedialogPresent && graphicMode( ) ; +} + + +int tfd_shellementaryPresent(void) +{ + static int lShellementaryPresent = -1 ; + if ( lShellementaryPresent < 0 ) + { + lShellementaryPresent = 0 ; /*detectPresence("shellementary"); shellementary is not ready yet */ + } + return lShellementaryPresent && graphicMode( ) ; +} + + +int tfd_xpropPresent(void) +{ + static int lXpropPresent = -1 ; + if ( lXpropPresent < 0 ) + { + lXpropPresent = detectPresence("xprop") ; + } + return lXpropPresent && graphicMode( ) ; +} + + +int tfd_zenityPresent(void) +{ + static int lZenityPresent = -1 ; + if ( lZenityPresent < 0 ) + { + lZenityPresent = detectPresence("zenity") ; + } + return lZenityPresent && graphicMode( ) ; +} + + +int tfd_yadPresent(void) +{ + static int lYadPresent = -1; + if (lYadPresent < 0) + { + lYadPresent = detectPresence("yad"); + } + return lYadPresent && graphicMode(); +} + + +int tfd_zenity3Present(void) +{ + static int lZenity3Present = -1 ; + char lBuff[MAX_PATH_OR_CMD] ; + FILE * lIn ; + int lIntTmp ; + + if ( lZenity3Present < 0 ) + { + lZenity3Present = 0 ; + if ( tfd_zenityPresent() ) + { + lIn = popen( "zenity --version" , "r" ) ; + if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) + { + if ( atoi(lBuff) >= 3 ) + { + lZenity3Present = 3 ; + lIntTmp = atoi(strtok(lBuff,".")+2 ) ; + if ( lIntTmp >= 18 ) + { + lZenity3Present = 5 ; + } + else if ( lIntTmp >= 10 ) + { + lZenity3Present = 4 ; + } + } + else if ( ( atoi(lBuff) == 2 ) && ( atoi(strtok(lBuff,".")+2 ) >= 32 ) ) + { + lZenity3Present = 2 ; + } + if (tinyfd_verbose) printf("zenity type %d\n", lZenity3Present); + } + pclose( lIn ) ; + } + } + return graphicMode() ? lZenity3Present : 0 ; +} + + +int tfd_kdialogPresent(void) +{ + static int lKdialogPresent = -1 ; + char lBuff[MAX_PATH_OR_CMD] ; + FILE * lIn ; + char * lDesktop; + + if ( lKdialogPresent < 0 ) + { + if ( tfd_zenityPresent() ) + { + lDesktop = getenv("XDG_SESSION_DESKTOP"); + if ( !lDesktop || ( strcmp(lDesktop, "KDE") && strcmp(lDesktop, "lxqt") ) ) + { + lKdialogPresent = 0 ; + return lKdialogPresent ; + } + } + + lKdialogPresent = detectPresence("kdialog") ; + if ( lKdialogPresent && !getenv("SSH_TTY") ) + { + lIn = popen( "kdialog --attach 2>&1" , "r" ) ; + if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) + { + if ( ! strstr( "Unknown" , lBuff ) ) + { + lKdialogPresent = 2 ; + if (tinyfd_verbose) printf("kdialog-attach %d\n", lKdialogPresent); + } + } + pclose( lIn ) ; + + if (lKdialogPresent == 2) + { + lKdialogPresent = 1 ; + lIn = popen( "kdialog --passivepopup 2>&1" , "r" ) ; + if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) + { + if ( ! strstr( "Unknown" , lBuff ) ) + { + lKdialogPresent = 2 ; + if (tinyfd_verbose) printf("kdialog-popup %d\n", lKdialogPresent); + } + } + pclose( lIn ) ; + } + } + } + return graphicMode() ? lKdialogPresent : 0 ; +} + + +static int osx9orBetter(void) +{ + 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 ) ; + if (tinyfd_verbose) printf("Osx10 = %d, %d = %s\n", lOsx9orBetter, V, lBuff) ; + } + return lOsx9orBetter ; +} + + +static int python3Present(void) +{ + static int lPython3Present = -1 ; + int i; + + if ( lPython3Present < 0 ) + { + lPython3Present = 0 ; + strcpy(gPython3Name , "python3" ) ; + if ( detectPresence(gPython3Name) ) lPython3Present = 1; + else + { + for ( i = 9 ; i >= 0 ; i -- ) + { + sprintf( gPython3Name , "python3.%d" , i ) ; + if ( detectPresence(gPython3Name) ) + { + lPython3Present = 1; + break; + } + } + } + if (tinyfd_verbose) printf("lPython3Present %d\n", lPython3Present) ; + if (tinyfd_verbose) printf("gPython3Name %s\n", gPython3Name) ; + } + return lPython3Present ; +} + + +static int python2Present(void) +{ + static int lPython2Present = -1 ; + int i; + + if ( lPython2Present < 0 ) + { + lPython2Present = 0 ; + strcpy(gPython2Name , "python2" ) ; + if ( detectPresence(gPython2Name) ) lPython2Present = 1; + else + { + for ( i = 9 ; i >= 0 ; i -- ) + { + sprintf( gPython2Name , "python2.%d" , i ) ; + if ( detectPresence(gPython2Name) ) + { + lPython2Present = 1; + break; + } + } + } + if (tinyfd_verbose) printf("lPython2Present %d\n", lPython2Present) ; + if (tinyfd_verbose) printf("gPython2Name %s\n", gPython2Name) ; + } + return lPython2Present ; +} + + +static int tkinter3Present(void) +{ + static int lTkinter3Present = -1 ; + char lPythonCommand[256]; + char lPythonParams[128] = + "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\""; + + if ( lTkinter3Present < 0 ) + { + lTkinter3Present = 0 ; + if ( python3Present() ) + { + sprintf( lPythonCommand , "%s %s" , gPython3Name , lPythonParams ) ; + lTkinter3Present = tryCommand(lPythonCommand) ; + } + if (tinyfd_verbose) printf("lTkinter3Present %d\n", lTkinter3Present) ; + } + return lTkinter3Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); +} + + +static int tkinter2Present(void) +{ + static int lTkinter2Present = -1 ; + char lPythonCommand[256]; + char lPythonParams[128] = + "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\""; + + if ( lTkinter2Present < 0 ) + { + lTkinter2Present = 0 ; + if ( python2Present() ) + { + sprintf( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; + lTkinter2Present = tryCommand(lPythonCommand) ; + } + if (tinyfd_verbose) printf("lTkinter2Present %d\n", lTkinter2Present) ; + } + return lTkinter2Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); +} + + +static int pythonDbusPresent(void) +{ + static int lPythonDbusPresent = -1 ; + char lPythonCommand[384]; + char lPythonParams[256] = +"-c \"try:\n\timport dbus;bus=dbus.SessionBus();\ +notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');\ +notify=dbus.Interface(notif,'org.freedesktop.Notifications');\nexcept:\n\tprint(0);\""; + + if (lPythonDbusPresent < 0 ) + { + lPythonDbusPresent = 0 ; + if ( python2Present() ) + { + strcpy(gPythonName , gPython2Name ) ; + sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; + lPythonDbusPresent = tryCommand(lPythonCommand) ; + } + + if ( !lPythonDbusPresent && python3Present() ) + { + strcpy(gPythonName , gPython3Name ) ; + sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; + lPythonDbusPresent = tryCommand(lPythonCommand) ; + } + + if (tinyfd_verbose) printf("lPythonDbusPresent %d\n", lPythonDbusPresent) ; + if (tinyfd_verbose) printf("gPythonName %s\n", gPythonName) ; + } + return lPythonDbusPresent && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); +} + + +static void sigHandler(int signum) +{ + FILE * lIn ; + if ( ( lIn = popen( "pactl unload-module module-sine" , "r" ) ) ) + { + pclose( lIn ) ; + } + if (tinyfd_verbose) printf("tinyfiledialogs caught signal %d\n", signum); +} + +void tinyfd_beep(void) +{ + char lDialogString[256] ; + FILE * lIn ; + + if ( osascriptPresent() ) + { + if ( afplayPresent() >= 2 ) + { + strcpy( lDialogString , "afplay /System/Library/Sounds/Ping.aiff") ; + } + else + { + strcpy( lDialogString , "osascript -e 'tell application \"System Events\" to beep'") ; + } + } + else if ( pactlPresent() ) + { + signal(SIGINT, sigHandler); + /*strcpy( lDialogString , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/ + strcpy( lDialogString , "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum" ) ; + } + else if ( speakertestPresent() ) + { + /*strcpy( lDialogString , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/ + strcpy( lDialogString , "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .4; kill -9 $pid" ) ; /*.3 was too short for mac g3*/ + } + else if (beepexePresent()) + { + strcpy(lDialogString, "beep.exe 440 300"); + } + else if (playPresent()) /* play is part of sox */ + { + strcpy(lDialogString, "play -q -n synth .3 sine 440"); + } + else if ( beepPresent() ) + { + strcpy( lDialogString , "beep -f 440 -l 300" ) ; + } + else + { + strcpy( lDialogString , "printf '\a' > /dev/tty" ) ; + } + + if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; + + if ( ( lIn = popen( lDialogString , "r" ) ) ) + { + pclose( lIn ) ; + } + + if ( pactlPresent() ) + { + signal(SIGINT, SIG_DFL); + } +} + + +int tinyfd_messageBox( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" may contain \n and \t */ + char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * aIconType , /* "info" "warning" "error" "question" */ + int 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; + size_t lTitleLen ; + size_t lMessageLen ; + + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); + if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); + + 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 \"OK\" 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 ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} + + strcpy( lDialogString , "kdialog" ) ; + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + + strcat( lDialogString , " --" ) ; + 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 ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) + { + if ( tfd_zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} + strcpy( lDialogString , "szAnswer=$(zenity" ) ; + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} + strcpy( lDialogString , "szAnswer=$(matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;} + strcpy( lDialogString , "szAnswer=$(shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;} + strcpy( lDialogString , "szAnswer=$(qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat(lDialogString, " --"); + + 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" ) ; + } + + strcat(lDialogString, " --title=\""); + if ( aTitle && strlen(aTitle) ) strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\""); + + if (strcmp("yesnocancel", aDialogType)) strcat(lDialogString, " --no-wrap"); + + strcat(lDialogString, " --text=\"") ; + if (aMessage && strlen(aMessage)) strcat(lDialogString, aMessage) ; + strcat(lDialogString, "\"") ; + + if ( (tfd_zenity3Present() >= 3) || (!tfd_zenityPresent() && (tfd_shellementaryPresent() || tfd_qarmaPresent()) ) ) + { + strcat( lDialogString , " --icon-name=dialog-" ) ; + if ( aIconType && (! strcmp( "question" , aIconType ) + || ! strcmp( "error" , aIconType ) + || ! strcmp( "warning" , aIconType ) ) ) + { + strcat( lDialogString , aIconType ) ; + } + else + { + strcat( lDialogString , "information" ) ; + } + } + + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + + 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 (tfd_yadPresent()) + { + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return 1; } + strcpy(lDialogString, "szAnswer=$(yad --"); + if (aDialogType && !strcmp("ok", aDialogType)) + { + strcat(lDialogString,"button=Ok:1"); + } + else if (aDialogType && !strcmp("okcancel", aDialogType)) + { + strcat(lDialogString,"button=Ok:1 --button=Cancel:0"); + } + else if (aDialogType && !strcmp("yesno", aDialogType)) + { + strcat(lDialogString, "button=Yes:1 --button=No:0"); + } + else if (aDialogType && !strcmp("yesnocancel", aDialogType)) + { + strcat(lDialogString, "button=Yes:1 --button=No:2 --button=Cancel:0"); + } + 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, "\""); + } + + strcat(lDialogString, " --icon-name=dialog-"); + if (aIconType && (!strcmp("question", aIconType) + || !strcmp("error", aIconType) + || !strcmp("warning", aIconType))) + { + strcat(lDialogString, aIconType); + } + else + { + strcat(lDialogString, "information"); + } + + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + strcat(lDialogString,");echo $?"); + } + + else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return 1;} + + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();"); + + strcat( lDialogString ,"res=messagebox." ) ; + if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) + { + strcat( lDialogString , "askokcancel(" ) ; + if ( aDefaultButton ) + { + strcat( lDialogString , "default=messagebox.OK," ) ; + } + else + { + strcat( lDialogString , "default=messagebox.CANCEL," ) ; + } + } + else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) + { + strcat( lDialogString , "askyesno(" ) ; + if ( aDefaultButton ) + { + strcat( lDialogString , "default=messagebox.YES," ) ; + } + else + { + strcat( lDialogString , "default=messagebox.NO," ) ; + } + } + else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) + { + strcat( lDialogString , "askyesnocancel(" ) ; + switch ( aDefaultButton ) + { + case 1: strcat( lDialogString , "default=messagebox.YES," ); break; + case 2: strcat( lDialogString , "default=messagebox.NO," ); break; + case 0: strcat( lDialogString , "default=messagebox.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); + tfd_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() && tkinter2Present() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return 1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat( lDialogString , +" -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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); + tfd_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, tinyfd_needs) ; + 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() && pythonDbusPresent() && !strcmp("ok" , aDialogType) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} + strcpy( lDialogString , gPythonName ) ; + strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); + strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; + strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; + strcat( lDialogString ,"notify.Notify('',0,'" ) ; + if ( aIconType && strlen(aIconType) ) + { + strcat( lDialogString , aIconType ) ; + } + strcat(lDialogString, "','") ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + } + strcat(lDialogString, "','") ; + if ( aMessage && strlen(aMessage) ) + { + lpDialogString = lDialogString + strlen(lDialogString); + tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; + } + strcat(lDialogString, "','','',5000)\"") ; + } + else if ( !isTerminalRunning() && (perlPresent() >= 2) && !strcmp("ok" , aDialogType) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} + + strcpy( lDialogString , "perl -e \"use Net::DBus;\ +my \\$sessionBus = Net::DBus->session;\ +my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ +my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ +'org.freedesktop.Notifications');"); + + sprintf( lDialogString + strlen(lDialogString), +"my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", + aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; + } + else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) ) + { + + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} + strcpy( lDialogString , "notify-send" ) ; + if ( aIconType && strlen(aIconType) ) + { + strcat( lDialogString , " -i '" ) ; + strcat( lDialogString , aIconType ) ; + strcat( lDialogString , "'" ) ; + } + strcat( lDialogString , " \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + strcat( lDialogString , " | " ) ; + } + if ( aMessage && strlen(aMessage) ) + { + tfd_replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; + tfd_replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; + tfd_replaceSubStr( aMessage , "\t" , " " , lBuff ) ; + strcat(lDialogString, lBuff) ; + } + 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", tinyfd_needs); + } + 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 ; +} + + +/* return has only meaning for tinyfd_query */ +int tinyfd_notifyPopup( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" may contain \n and \t */ + char const * aIconType ) /* "info" "warning" "error" */ +{ + char lBuff[MAX_PATH_OR_CMD]; + char * lDialogString = NULL ; + char * lpDialogString ; + FILE * lIn ; + size_t lTitleLen ; + size_t lMessageLen ; + + if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType); + if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType); + + if ( getenv("SSH_TTY") ) + { + return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 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 'display notification \"") ; + if ( aMessage && strlen(aMessage) ) + { + strcat(lDialogString, aMessage) ; + } + strcat(lDialogString, " \" ") ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, "with title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\" ") ; + } + + strcat( lDialogString, "' -e 'end try'") ; + if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; + } + else if ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} + strcpy( lDialogString , "kdialog" ) ; + + if ( aIconType && strlen(aIconType) ) + { + strcat( lDialogString , " --icon '" ) ; + strcat( lDialogString , aIconType ) ; + strcat( lDialogString , "'" ) ; + } + if ( aTitle && strlen(aTitle) ) + { + strcat( lDialogString , " --title \"" ) ; + strcat( lDialogString , aTitle ) ; + strcat( lDialogString , "\"" ) ; + } + + strcat( lDialogString , " --passivepopup" ) ; + strcat( lDialogString , " \"" ) ; + if ( aMessage ) + { + strcat( lDialogString , aMessage ) ; + } + strcat( lDialogString , " \" 5" ) ; + } + else if ( (tfd_zenity3Present()>=5) ) + { + /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */ + /* zenity 3.8 show the notification as an alert ok cancel box */ + if ( tfd_zenity3Present()>=5 ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} + strcpy( lDialogString , "zenity" ) ; + } + + strcat( lDialogString , " --notification"); + + if ( aIconType && strlen( aIconType ) ) + { + strcat( lDialogString , " --window-icon '"); + strcat( lDialogString , aIconType ) ; + strcat( lDialogString , "'" ) ; + } + + strcat( lDialogString , " --text \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\n") ; + } + if ( aMessage && strlen( aMessage ) ) + { + strcat( lDialogString , aMessage ) ; + } + strcat( lDialogString , " \"" ) ; + } + else if ( perlPresent() >= 2 ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} + + strcpy( lDialogString , "perl -e \"use Net::DBus;\ +my \\$sessionBus = Net::DBus->session;\ +my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ +my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ +'org.freedesktop.Notifications');"); + + sprintf( lDialogString + strlen(lDialogString) , +"my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", +aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; + } + else if ( pythonDbusPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} + strcpy( lDialogString , gPythonName ) ; + strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); + strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; + strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; + strcat( lDialogString ,"notify.Notify('',0,'" ) ; + if ( aIconType && strlen(aIconType) ) + { + strcat( lDialogString , aIconType ) ; + } + strcat(lDialogString, "','") ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + } + strcat(lDialogString, "','") ; + if ( aMessage && strlen(aMessage) ) + { + lpDialogString = lDialogString + strlen(lDialogString); + tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; + } + strcat(lDialogString, "','','',5000)\"") ; + } + else if ( notifysendPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} + strcpy( lDialogString , "notify-send" ) ; + if ( aIconType && strlen(aIconType) ) + { + strcat( lDialogString , " -i '" ) ; + strcat( lDialogString , aIconType ) ; + strcat( lDialogString , "'" ) ; + } + strcat( lDialogString , " \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + strcat( lDialogString , " | " ) ; + } + if ( aMessage && strlen(aMessage) ) + { + tfd_replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; + tfd_replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; + tfd_replaceSubStr( aMessage , "\t" , " " , lBuff ) ; + strcat(lDialogString, lBuff) ; + } + strcat( lDialogString , "\"" ) ; + } + else + { + return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0); + } - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && isDarwin ( )) - { - strcat ( lDialogString , " -i" ) ; /* for osx without console */ - } - strcat ( lDialogString , -" -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; - if ( isDarwin ( ) ) - { - strcat ( lDialogString , -"import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set\ - frontmost of process \\\"Python\\\" to true' ''');"); - } + if ( ! ( lIn = popen( lDialogString , "r" ) ) ) + { + free(lDialogString); + return 0 ; + } - 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) ) + pclose( lIn ) ; + free(lDialogString); + return 1; +} + + +/* returns NULL on cancel */ +char * tinyfd_inputBox( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" (\n and \t have no effect) */ + char 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; + size_t lTitleLen ; + size_t lMessageLen ; + + if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */ + + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); + if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput); + if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES"); + + 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 *)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 ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} + strcpy( lDialogString , "szAnswer=$(kdialog" ) ; + + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + + 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 ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) + { + if ( tfd_zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} + strcpy( lDialogString , "szAnswer=$(zenity" ) ; + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} + strcpy( lDialogString , "szAnswer=$(matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} + strcpy( lDialogString , "szAnswer=$(shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} + strcpy( lDialogString , "szAnswer=$(qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat( lDialogString ," --entry" ) ; + + strcat(lDialogString, " --title=\"") ; + if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + + strcat(lDialogString, " --text=\"") ; + if (aMessage && strlen(aMessage)) strcat(lDialogString, aMessage) ; + strcat(lDialogString, "\"") ; + + if ( aDefaultInput && strlen(aDefaultInput) ) + { + strcat(lDialogString, " --entry-text=\"") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "\"") ; + } + else + { + strcat(lDialogString, " --hide-text") ; + } + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + strcat( lDialogString , + ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); + } + else if (tfd_yadPresent()) + { + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } + strcpy(lDialogString, "szAnswer=$(yad --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"); + } + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + 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 *)1;} + strcpy( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \""); + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char *)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() && !xdialogPresent() && tkinter3Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();"); + strcat( lDialogString ,"res=simpledialog.askstring(" ) ; + if ( aTitle && strlen(aTitle) ) { - strcat(lDialogString, "initialdir='") ; - strcat(lDialogString, lString ) ; - strcat(lDialogString , "'," ) ; + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; } - getLastName ( lString , aDefaultPathAndFile ) ; - if ( strlen(lString) ) + if ( aMessage && strlen(aMessage) ) { - strcat(lDialogString, "initialfile='") ; - strcat(lDialogString, lString ) ; - strcat(lDialogString , "'," ) ; + + strcat(lDialogString, "prompt='") ; + lpDialogString = lDialogString + strlen(lDialogString); + tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; + 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) ) + if ( aDefaultInput ) { - strcat ( lDialogString , aSingleFilterDescription ) ; + if ( strlen(aDefaultInput) ) + { + strcat(lDialogString, "initialvalue='") ; + strcat(lDialogString, aDefaultInput) ; + strcat(lDialogString, "',") ; + } } - strcat ( lDialogString , "',(" ) ; - for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + else { - strcat ( lDialogString , "'" ) ; - strcat ( lDialogString , aFilterPatterns [ i ] ) ; - strcat ( lDialogString , "'," ) ; + strcat(lDialogString, "show='*'") ; } - strcat ( lDialogString , "))," ) ; - strcat ( lDialogString , "('All files','*'))" ) ; - } - strcat ( lDialogString , ")\"" ) ; - } - else if ( xdialogPresent() || dialogName() ) - { - if ( xdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(Xdialog " ) ; - } - else if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(dialog " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(" ) ; - strcat ( lDialogString , dialogName() ) ; - strcat ( lDialogString , " " ) ; + strcat(lDialogString, ");\nif res is None :\n\tprint(0)"); + strcat(lDialogString, "\nelse :\n\tprint('1'+res)\n\"" ) ; } + else if ( !gdialogPresent() && !xdialogPresent() && tkinter2Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat( lDialogString , + " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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); + tfd_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 *)1;} + lWasGraphicDialog = 1 ; + lWasGdialog = 1 ; + strcpy( lDialogString , "(gdialog " ) ; + } + else if ( xdialogPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} + lWasGraphicDialog = 1 ; + strcpy( lDialogString , "(Xdialog " ) ; + } + else if ( dialogName( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)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 *)0;} + strcpy( lDialogString , "(whiptail " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char *)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 *)0;} + lWasBasicXterm = 1 ; + strcpy( lDialogString , terminalName() ) ; + strcat( lDialogString , "'" ) ; + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); + } + 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 ( !gWarningDisplayed && ! isTerminalRunning( ) && ! terminalName() ) { + gWarningDisplayed = 1 ; + tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"no_solution");return (char *)0;} + free(lDialogString); + return NULL; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;} + if ( !gWarningDisplayed && !tinyfd_forceConsole) + { + gWarningDisplayed = 1 ; + tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); + } + 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 ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\" ") ; - } - strcat ( lDialogString , "--fselect \"" ) ; - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) - { - if ( ! strchr(aDefaultPathAndFile, '/') ) + 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 * tinyfd_saveFileDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile , /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 */ + char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char 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 * p ; + char * lPointerInputBox ; + FILE * lIn ; + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); + if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES"); + for (i = 0; i < aNumOfFilterPatterns; i++) + { + if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); + } + + if ( osascriptPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)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 ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} + + strcpy( lDialogString , "kdialog" ) ; + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + strcat( lDialogString , " --getsavefilename " ) ; + + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( aDefaultPathAndFile[0] != '/' ) + { + strcat(lDialogString, "$PWD/") ; + } + strcat(lDialogString, "\"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, "$PWD/") ; + } + + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + strcat( lDialogString , aFilterPatterns[0] ) ; + for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat( lDialogString , " " ) ; + strcat( lDialogString , aFilterPatterns[i] ) ; + } + 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 ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) + { + if ( tfd_zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} + strcpy( lDialogString , "zenity" ) ; + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} + strcpy( lDialogString , "matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} + strcpy( lDialogString , "shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} + strcpy( lDialogString , "qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ; + + strcat(lDialogString, " --title=\"") ; + if (aTitle && strlen(aTitle)) 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 , " " ) ; + strcat( lDialogString , aFilterPatterns[i] ) ; + } + strcat( lDialogString , "' --file-filter='All files | *'" ) ; + } + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + } + else if (tfd_yadPresent()) + { + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } + strcpy(lDialogString, "yad --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, " "); + strcat(lDialogString, aFilterPatterns[i]); + } + strcat(lDialogString, "' --file-filter='All files | *'"); + } + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + } + else if ( !xdialogPresent() && tkinter3Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); + strcat( lDialogString , "res=filedialog.asksaveasfilename("); + if ( aTitle && strlen(aTitle) ) { - strcat(lDialogString, "./") ; + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; } - strcat(lDialogString, aDefaultPathAndFile) ; - } - else if ( ! isatty ( 1 ) && !lWasGraphicDialog ) - { - strcat(lDialogString, getenv("HOME")) ; - strcat(lDialogString, "/") ; - } - - if ( lWasGraphicDialog ) - { - strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; - } - else - { - strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; - if ( lWasXterm ) + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { - strcat ( lDialogString , - "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + 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 , "'," ) ; + } } - else + if ( ( aNumOfFilterPatterns > 1 ) + || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ + && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { - strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + 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(res, tuple):\n\tprint(res)\n\"" ) ; } - } - else - { - p = tinyfd_inputBox ( aTitle , "Save file" , "" ) ; - getPathWithoutFinalSlash ( lString , p ) ; - if ( strlen ( lString ) && ! dirExists ( lString ) ) - { - return NULL ; - } - getLastName(lString,p); - if ( ! strlen(lString) ) - { - return NULL; + else if ( !xdialogPresent() && tkinter2Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( )) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat( lDialogString , +" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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=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, ");\nif not isinstance(res, tuple):\n\tprint res \n\"" ) ; } - return p ; - } + else if ( xdialogPresent() || dialogName() ) + { + if ( xdialogPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} + lWasGraphicDialog = 1 ; + strcpy( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} + strcpy( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)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);} + strcpy(lBuff, "Save file in "); + strcat(lBuff, getCurDir()); + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ + p = lBuff; + + getPathWithoutFinalSlash( lString , p ) ; + if ( strlen( lString ) && ! dirExists( lString ) ) + { + return NULL ; + } + getLastName(lString,p); + if ( ! strlen(lString) ) + { + return NULL; + } + return p ; + } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } - while ( fgets ( lBuff , sizeof ( lBuff ) , lIn ) != NULL ) + while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} - pclose ( lIn ) ; - if ( lBuff[ strlen ( lBuff ) -1 ] == '\n' ) + pclose( lIn ) ; + if ( lBuff[strlen( lBuff ) -1] == '\n' ) { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; + lBuff[strlen( lBuff ) -1] = '\0' ; } - /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ - if ( ! strlen(lBuff) ) - { - return NULL; - } - getPathWithoutFinalSlash ( lString , lBuff ) ; - if ( strlen ( lString ) && ! dirExists ( lString ) ) + /* 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; - } + 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*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 ( ) ) - { - strcpy ( lDialogString , "osascript -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 , ")'" ) ; - } - } - else if ( zenityPresent() ) - { - strcpy ( lDialogString ,"zenity --file-selection" ) ; - if ( aAllowMultipleSelects ) - { - strcat ( lDialogString , " --multiple" ) ; - } - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, " --title=\"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\"") ; - } - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) +char * tinyfd_openFileDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile , /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 */ + char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ + char const * aSingleFilterDescription , /* NULL or "image files" */ + int aAllowMultipleSelects ) /* 0 or 1 */ +{ + char lDialogString[MAX_PATH_OR_CMD] ; + char lString[MAX_PATH_OR_CMD] ; + int i ; + FILE * lIn ; + char * p ; + char * lPointerInputBox ; + int lWasKdialog = 0 ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + size_t lFullBuffLen ; + static char * lBuff = NULL; + + if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); + if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); + for (i = 0; i < aNumOfFilterPatterns; i++) { - strcat(lDialogString, " --filename=\"") ; - strcat(lDialogString, aDefaultPathAndFile) ; - strcat(lDialogString, "\"") ; + if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); } - 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() ) - { - lWasKdialog = 1 ; - strcpy ( lDialogString , "kdialog --getopenfilename" ) ; - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) - { - strcat(lDialogString, " \"") ; - strcat(lDialogString, aDefaultPathAndFile ) ; - strcat(lDialogString , "\"" ) ; - } - else + free(lBuff); + if (aTitle&&!strcmp(aTitle,"tinyfd_query")) { - strcat(lDialogString, " :" ) ; + lBuff = NULL; } - if ( aNumOfFilterPatterns > 0 ) + else { - strcat(lDialogString , " \"" ) ; - for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + if (aAllowMultipleSelects) { - strcat ( lDialogString , aFilterPatterns [ i ] ) ; - strcat ( lDialogString , " " ) ; + lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; + lBuff = (char *)(malloc(lFullBuffLen * sizeof(char))); + if (!lBuff) + { + lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; + lBuff = (char *)( malloc( lFullBuffLen * sizeof(char))); + } } - if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) + else { - strcat ( lDialogString , " | " ) ; - strcat ( lDialogString , aSingleFilterDescription ) ; + lFullBuffLen = MAX_PATH_OR_CMD + 1; + lBuff = (char *)(malloc(lFullBuffLen * sizeof(char))); } - strcat ( lDialogString , "\"" ) ; - } - if ( aAllowMultipleSelects ) - { - strcat ( lDialogString , " --multiple --separate-output" ) ; - } - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, " --title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\"") ; + if (!lBuff) return NULL; + lBuff[0]='\0'; } - } - else if ( ! xdialogPresent() && tkinter2Present ( ) ) + + if ( osascriptPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} + strcpy( lDialogString , "osascript "); + if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); + strcat( lDialogString , " -e 'try' -e '" ); + if ( ! aAllowMultipleSelects ) { - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && 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 , "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 ( tfd_kdialogPresent() ) { - strcat ( lDialogString , "multiple=1," ) ; + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} + lWasKdialog = 1 ; + + strcpy( lDialogString , "kdialog" ) ; + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + strcat( lDialogString , " --getopenfilename " ) ; + + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + { + if ( aDefaultPathAndFile[0] != '/' ) + { + strcat(lDialogString, "$PWD/") ; + } + strcat(lDialogString, "\"") ; + strcat(lDialogString, aDefaultPathAndFile ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, "$PWD/") ; + } + + if ( aNumOfFilterPatterns > 0 ) + { + strcat(lDialogString , " \"" ) ; + strcat( lDialogString , aFilterPatterns[0] ) ; + for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) + { + strcat( lDialogString , " " ) ; + strcat( lDialogString , aFilterPatterns[i] ) ; + } + 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, "\"") ; + } } - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "title='") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "',") ; - } - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) + else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { - 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] != '*' ) ) ) + if ( tfd_zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} + strcpy( lDialogString , "zenity" ) ; + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} + strcpy( lDialogString , "matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} + strcpy( lDialogString , "shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} + strcpy( lDialogString , "qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat( lDialogString , " --file-selection" ) ; + + if ( aAllowMultipleSelects ) + { + strcat( lDialogString , " --multiple" ) ; + } + + strcat(lDialogString, " --title=\"") ; + if (aTitle && strlen(aTitle)) 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 , " " ) ; + strcat( lDialogString , aFilterPatterns[i] ) ; + } + strcat( lDialogString , "' --file-filter='All files | *'" ) ; + } + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + } + else if (tfd_yadPresent()) { - strcat(lDialogString , "filetypes=(" ) ; - strcat ( lDialogString , "('" ) ; - if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) - { - strcat ( lDialogString , aSingleFilterDescription ) ; - } - strcat ( lDialogString , "',(" ) ; - for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } + strcpy(lDialogString, "yad --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, " "); + strcat(lDialogString, aFilterPatterns[i]); + } + strcat(lDialogString, "' --file-filter='All files | *'"); + } + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + } + else if ( tkinter3Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); + strcat( lDialogString , "lFiles=filedialog.askopenfilename("); + if ( aAllowMultipleSelects ) { - strcat ( lDialogString , "'" ) ; - strcat ( lDialogString , aFilterPatterns [ i ] ) ; - strcat ( lDialogString , "'," ) ; + strcat( lDialogString , "multiple=1," ) ; } - 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 ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(Xdialog " ) ; - } - else if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(dialog " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(" ) ; - strcat ( lDialogString , dialogName() ) ; - strcat ( lDialogString , " " ) ; - } - - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\" ") ; - } - strcat ( lDialogString , "--fselect \"" ) ; - if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) - { - if ( ! strchr(aDefaultPathAndFile, '/') ) + if ( aTitle && strlen(aTitle) ) { - strcat(lDialogString, "./") ; + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; } - strcat(lDialogString, aDefaultPathAndFile) ; - } - else if ( ! isatty ( 1 ) && !lWasGraphicDialog ) - { - strcat(lDialogString, getenv("HOME")) ; - strcat(lDialogString, "/"); - } - - if ( lWasGraphicDialog ) - { - strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; - } - else - { - strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; - if ( lWasXterm ) + if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { - strcat ( lDialogString , - "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + 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 , "'," ) ; + } } - else + if ( ( aNumOfFilterPatterns > 1 ) + || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ + && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { - strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + 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 - { - p2 = tinyfd_inputBox(aTitle, "Open file",""); - if ( ! fileExists (p2) ) - { - return NULL ; - } - return p2 ; - } + else if ( tkinter2Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat( lDialogString , +" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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 *)1;} + lWasGraphicDialog = 1 ; + strcpy( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} + strcpy( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)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);} + strcpy(lBuff, "Open file from "); + strcat(lBuff, getCurDir()); + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if ( p ) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ + if ( ! fileExists(lBuff) ) + { + free(lBuff); + lBuff = NULL; + } + else + { + lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char))); + } + return lBuff ; + } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; + if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { - return NULL ; + free(lBuff); + lBuff = NULL; + 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[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' ; + 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 ) ; //*/ + /* printf( "lBuff: %s\n" , lBuff ) ; */ + if ( lWasKdialog && aAllowMultipleSelects ) + { + p = lBuff ; + while ( ( p = strchr( p , '\n' ) ) ) + * p = '|' ; + } + /* printf( "lBuff2: %s\n" , lBuff ) ; */ + if ( ! strlen( lBuff ) ) + { + free(lBuff); + lBuff = NULL; + return NULL; + } + if ( aAllowMultipleSelects && strchr(lBuff, '|') ) + { + if( ! ensureFilesExist( lBuff , lBuff ) ) + { + free(lBuff); + lBuff = NULL; + return NULL; + } + } + else if ( !fileExists(lBuff) ) + { + free(lBuff); + lBuff = NULL; + return NULL; + } - return p2 ; -} + lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char))); + /*printf( "lBuff3: %s\n" , lBuff ) ; */ + return lBuff ; +} -char const * tinyfd_selectFolderDialog ( - char const * const aTitle , /* "" */ - char const * const aDefaultPath ) /* "" */ +char * tinyfd_selectFolderDialog( + char const * aTitle , /* "" */ + char const * aDefaultPath ) /* "" */ { - static char lBuff [ MAX_PATH_OR_CMD ] ; - char lDialogString [ MAX_PATH_OR_CMD ] ; - DIR * lDir ; - FILE * lIn ; - char const * p ; - int lWasGraphicDialog = 0 ; - int lWasXterm = 0 ; - lBuff[0]='\0'; - - if ( osascriptPresent ( )) - { - strcpy(lDialogString ,"osascript -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 , ")'" ) ; - } - else if ( zenityPresent() ) - { - strcpy ( lDialogString , "zenity --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() ) - { - 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 ( ) ) - { - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && 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) ) + static char lBuff[MAX_PATH_OR_CMD] ; + char lDialogString[MAX_PATH_OR_CMD] ; + FILE * lIn ; + char * p ; + char * lPointerInputBox ; + int lWasGraphicDialog = 0 ; + int lWasXterm = 0 ; + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath); + if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES"); + + if ( osascriptPresent( )) { - strcat(lDialogString, "initialdir='") ; - strcat(lDialogString, aDefaultPath ) ; - strcat(lDialogString , "'" ) ; - } - strcat ( lDialogString , ")\"" ) ; - } - else if ( xdialogPresent() || dialogName() ) - { - if ( xdialogPresent ( ) ) - { - lWasGraphicDialog = 1 ; - strcpy ( lDialogString , "(Xdialog " ) ; - } - else if ( isatty ( 1 ) ) - { - strcpy ( lDialogString , "(dialog " ) ; - } - else - { - lWasXterm = 1 ; - strcpy ( lDialogString , terminalName() ) ; - strcat ( lDialogString , "'(" ) ; - strcat ( lDialogString , dialogName() ) ; - strcat ( lDialogString , " " ) ; - } - - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, "--title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\" ") ; - } - strcat ( lDialogString , "--dselect \"" ) ; - if ( aDefaultPath && strlen(aDefaultPath) ) - { - strcat(lDialogString, aDefaultPath) ; - ensureFinalSlash(lDialogString); - } - else if ( ! isatty ( 1 ) && !lWasGraphicDialog ) - { - strcat(lDialogString, getenv("HOME")) ; - strcat(lDialogString, "/"); - } - - if ( lWasGraphicDialog ) - { - strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; - } - else - { - strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; - if ( lWasXterm ) + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)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 ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} + strcpy( lDialogString , "kdialog" ) ; + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + strcat( lDialogString , " --getexistingdirectory " ) ; + + if ( aDefaultPath && strlen(aDefaultPath) ) + { + if ( aDefaultPath[0] != '/' ) + { + strcat(lDialogString, "$PWD/") ; + } + strcat(lDialogString, "\"") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "\"" ) ; + } + else + { + strcat(lDialogString, "$PWD/") ; + } + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) + { + if ( tfd_zenityPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} + strcpy( lDialogString , "zenity" ) ; + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} + strcpy( lDialogString , "matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} + strcpy( lDialogString , "shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} + strcpy( lDialogString , "qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat( lDialogString , " --file-selection --directory" ) ; + + strcat(lDialogString, " --title=\"") ; + if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + + if ( aDefaultPath && strlen(aDefaultPath) ) + { + strcat(lDialogString, " --filename=\"") ; + strcat(lDialogString, aDefaultPath) ; + strcat(lDialogString, "\"") ; + } + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + } + else if (tfd_yadPresent()) + { + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } + strcpy(lDialogString, "yad --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, "\""); + } + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + } + else if ( !xdialogPresent() && tkinter3Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); + strcat( lDialogString , "res=filedialog.askdirectory("); + if ( aTitle && strlen(aTitle) ) { - strcat ( lDialogString , - "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); + strcat(lDialogString, "title='") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "',") ; } - else + if ( aDefaultPath && strlen(aDefaultPath) ) { - strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; + strcat(lDialogString, "initialdir='") ; + strcat(lDialogString, aDefaultPath ) ; + strcat(lDialogString , "'" ) ; } + strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint(res)\n\"" ) ; } - } - else - { - p = tinyfd_inputBox(aTitle, "Select folder",""); - if ( !p || ! strlen ( p ) || ! dirExists ( p ) ) - { - return NULL ; - } - return p ; - } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) + else if ( !xdialogPresent() && tkinter2Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + strcat( lDialogString , +" -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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 *)1;} + lWasGraphicDialog = 1 ; + strcpy( lDialogString , "(Xdialog " ) ; + } + else if ( isTerminalRunning( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} + strcpy( lDialogString , "(dialog " ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)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);} + strcpy(lBuff, "Select folder from "); + strcat(lBuff, getCurDir()); + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, lBuff, ""); + if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; + if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ + p = lBuff; + + 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' ) + while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) + {} + pclose( lIn ) ; + if ( lBuff[strlen( lBuff ) -1] == '\n' ) { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; + lBuff[strlen( lBuff ) -1] = '\0' ; } - /* printf ( "lBuff: %s\n" , lBuff ) ; //*/ - if ( ! strlen ( lBuff ) || ! dirExists ( lBuff ) ) - { - return NULL ; - } - return lBuff ; + /* printf( "lBuff: %s\n" , lBuff ) ; */ + if ( ! strlen( lBuff ) || ! dirExists( lBuff ) ) + { + return NULL ; + } + return lBuff ; } @@ -3875,278 +7291,498 @@ frontmost of process \\\"Python\\\" to true' ''');"); /* 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 [ 16 ] ; - char lTmp [ 16 ] ; - 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 ( ) ) - { - lWasOsascript = 1 ; - strcpy ( lDialogString , "osascript -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)/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)/256 as integer) as string' " ); - strcat ( lDialogString , "-e 'end repeat' " ); - strcat ( lDialogString , "-e 'mystring'"); - } - else if ( zenity3Present() ) - { - lWasZenity3 = 1 ; - sprintf ( lDialogString , -"zenity --color-selection --show-palette --color=%s" , lpDefaultHexRGB ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, " --title=\"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\"") ; - } - } - else if ( kdialogPresent() ) - { - sprintf ( lDialogString , -"kdialog --getcolor --default '%s'" , lpDefaultHexRGB ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, " --title \"") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "\"") ; - } - } - else if ( xdialogPresent() ) - { - lWasXdialog = 1 ; - strcpy ( lDialogString , "Xdialog --colorsel \"" ) ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, aTitle) ; +char * tinyfd_colorChooser( + char const * aTitle , /* NULL or "" */ + char const * aDefaultHexRGB , /* NULL or "#FF0000"*/ + unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ + unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */ +{ + static char lDefaultHexRGB[16]; + char lBuff[128] ; + + char lTmp[128] ; +#if !((defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)) + char * lTmp2 ; +#endif + char lDialogString[MAX_PATH_OR_CMD] ; + unsigned char lDefaultRGB[3]; + char * p; + char * lPointerInputBox; + FILE * lIn ; + int i ; + int lWasZenity3 = 0 ; + int lWasOsascript = 0 ; + int lWasXdialog = 0 ; + lBuff[0]='\0'; + + if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); + if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); + + if (aDefaultHexRGB) + { + Hex2RGB(aDefaultHexRGB, lDefaultRGB); + strcpy(lDefaultHexRGB, aDefaultHexRGB); } - 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 ( ) ) - { - strcpy ( lDialogString , gPython2Name ) ; - if ( ! isatty ( 1 ) && isDarwin ( ) ) + else { - strcat ( lDialogString , " -i" ) ; /* for osx without console */ + lDefaultRGB[0] = aDefaultRGB[0]; + lDefaultRGB[1] = aDefaultRGB[1]; + lDefaultRGB[2] = aDefaultRGB[2]; + RGB2Hex(aDefaultRGB, lDefaultHexRGB); } - - strcat ( lDialogString , -" -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); - if ( isDarwin ( ) ) - { - strcat ( lDialogString , + if ( osascriptPresent( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)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 ( tfd_kdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} + strcpy( lDialogString , "kdialog" ) ; + if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + sprintf( lDialogString + strlen(lDialogString) , " --getcolor --default '%s'" , lDefaultHexRGB ) ; + + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, " --title \"") ; + strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + } + } + else if ( tfd_zenity3Present() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) + { + lWasZenity3 = 1 ; + if ( tfd_zenity3Present() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char *)1;} + strcpy( lDialogString , "zenity" ); + if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + else if ( tfd_matedialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} + strcpy( lDialogString , "matedialog" ) ; + } + else if ( tfd_shellementaryPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} + strcpy( lDialogString , "shellementary" ) ; + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} + strcpy( lDialogString , "qarma" ) ; + if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) + { + strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ + } + } + strcat( lDialogString , " --color-selection --show-palette" ) ; + sprintf( lDialogString + strlen(lDialogString), " --color=%s" , lDefaultHexRGB ) ; + + strcat(lDialogString, " --title=\"") ; + if (aTitle && strlen(aTitle)) strcat(lDialogString, aTitle) ; + strcat(lDialogString, "\"") ; + + if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); + } + else if (tfd_yadPresent()) + { + if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } + strcpy(lDialogString, "yad --color"); + sprintf(lDialogString + strlen(lDialogString), " --init-color=%s", lDefaultHexRGB); + if (aTitle && strlen(aTitle)) + { + strcat(lDialogString, " --title=\""); + strcat(lDialogString, aTitle); + strcat(lDialogString, "\""); + } + if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); + } + else if ( xdialogPresent() ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} + lWasXdialog = 1 ; + strcpy( lDialogString , "Xdialog --colorsel \"" ) ; + if ( aTitle && strlen(aTitle) ) + { + strcat(lDialogString, aTitle) ; + } + strcat(lDialogString, "\" 0 60 ") ; +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); +#else + sprintf(lTmp,"%hu %hu %hu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); +#endif + strcat(lDialogString, lTmp) ; + strcat(lDialogString, " 2>&1"); + } + else if ( tkinter3Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} + strcpy( lDialogString , gPython3Name ) ; + strcat( lDialogString , + " -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();"); + strcat( lDialogString , "res=colorchooser.askcolor(color='" ) ; + strcat(lDialogString, lDefaultHexRGB ) ; + 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 ( tkinter2Present( ) ) + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} + strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; + strcat( lDialogString , gPython2Name ) ; + if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) + { + strcat( lDialogString , " -i" ) ; /* for osx without console */ + } + + strcat( lDialogString , +" -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); + + if ( tfd_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, "'") ; + strcat( lDialogString , "res=tkColorChooser.askcolor(color='" ) ; + strcat(lDialogString, lDefaultHexRGB ) ; + strcat(lDialogString, "'") ; - if ( aTitle && strlen(aTitle) ) - { - strcat(lDialogString, ",title='") ; - strcat(lDialogString, aTitle) ; - strcat(lDialogString, "'") ; - } - 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 - { - 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 ; - } + } + else + { + if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} + lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ + if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ + p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB); + + if ( !p || (strlen(p) != 7) || (p[0] != '#') ) + { + return NULL ; + } + for ( i = 1 ; i < 7 ; i ++ ) + { + if ( ! isxdigit( (int) p[i] ) ) + { + return NULL ; + } + } + Hex2RGB(p,aoResultRGB); + strcpy(lDefaultHexRGB, p); + if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ + return lDefaultHexRGB; + } - /* printf ( "lDialogString: %s\n" , lDialogString ) ; //*/ - if ( ! ( lIn = popen ( lDialogString , "r" ) ) ) - { - 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 ( ! strlen ( lBuff ) ) + 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' ) + /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */ + /* printf( "lBuff0: %s\n" , lBuff ) ; */ + if ( lBuff[strlen( lBuff ) -1] == '\n' ) { - lBuff[ strlen ( lBuff ) -1 ] = '\0' ; + lBuff[strlen( lBuff ) -1] = '\0' ; } - if ( lWasZenity3 ) + + if ( lWasZenity3 ) { - lBuff[3]=lBuff[5]; - lBuff[4]=lBuff[6]; - lBuff[5]=lBuff[9]; - lBuff[6]=lBuff[10]; - lBuff[7]='\0'; - Hex2RGB(lBuff,aoResultRGB); + if ( lBuff[0] == '#' ) + { + if ( strlen(lBuff)>7 ) + { + 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] == '(' ) { +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + sscanf(lBuff,"rgb(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); +#else + aoResultRGB[0] = strtol(lBuff+4, & lTmp2, 10 ); + aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); + aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); +#endif + RGB2Hex(aoResultRGB,lBuff); + } + else if ( lBuff[4] == '(' ) { +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + sscanf(lBuff,"rgba(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); +#else + aoResultRGB[0] = strtol(lBuff+5, & lTmp2, 10 ); + aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); + aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); +#endif + RGB2Hex(aoResultRGB,lBuff); + } } else if ( lWasOsascript || lWasXdialog ) { - sscanf(lBuff,"%hhu %hhu %hhu", - & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); - RGB2Hex(aoResultRGB,lBuff); + /* printf( "lBuff: %s\n" , lBuff ) ; */ +#if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) + sscanf(lBuff,"%hhu %hhu %hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); +#else + aoResultRGB[0] = strtol(lBuff, & lTmp2, 10 ); + aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); + aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); +#endif + 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 ; -} + Hex2RGB(lBuff,aoResultRGB); + } + /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */ + /* printf( "lBuff: %s\n" , lBuff ) ; */ + strcpy(lDefaultHexRGB,lBuff); + return lDefaultHexRGB ; +} #endif /* _WIN32 */ -/*// hello.c -int main() +/* +int main( int argc , char * argv[] ) +{ +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 lString[1024]; +char const * lFilterPatterns[2] = { "*.txt", "*.text" }; + +tinyfd_verbose = argc - 1; +tinyfd_silent = 1; + +lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL); + +strcpy(lBuffer, "v"); +strcat(lBuffer, tinyfd_version); +if (lWillBeGraphicMode) +{ + strcat(lBuffer, "\ngraphic mode: "); +} +else +{ + strcat(lBuffer, "\nconsole mode: "); +} +strcat(lBuffer, tinyfd_response); +strcat(lBuffer, "\n"); +strcat(lBuffer, tinyfd_needs+78); +strcpy(lString, "tinyfiledialogs"); +tinyfd_messageBox(lString, lBuffer, "ok", "info", 0); + +tinyfd_notifyPopup("the title", "the message\n\tfrom outer-space", "info"); + +if (lWillBeGraphicMode && !tinyfd_forceConsole) { - char const * lThePassword; - char const * lTheSaveFileName; - char const * lTheOpenFileName; - FILE * lIn; - char lBuffer[1024]; + tinyfd_forceConsole = ! tinyfd_messageBox("Hello World", + "graphic dialogs [yes] / console mode [no]?", + "yesno", "question", 1); +} - tinyfd_forceConsole = tinyfd_messageBox("Hello World", - "force dialogs into console mode?\ - \n\t(it's better if dialog is installed)", - "yesno", "question", 0); +lTmp = tinyfd_inputBox( + "a password box", "your password will be revealed", NULL); - lThePassword = tinyfd_inputBox( - "a password box","your password will be revealed",NULL); +if (!lTmp) return 1; - lTheSaveFileName = tinyfd_saveFileDialog ( - "let's save this password", - "passwordFile.txt", - 0, - NULL, - NULL ); +strcpy(lString, lTmp); - lIn = fopen(lTheSaveFileName, "w"); - if (!lIn) - { - tinyfd_messageBox( - "Error", - "Can not open this file in writting mode", - "ok", - "error", - 1 ); - return(1); - } - fputs(lThePassword, lIn); - fclose(lIn); - - lTheOpenFileName = tinyfd_openFileDialog ( - "let's read this password", - "", - 0, - NULL, - NULL, - 0); - - lIn = fopen(lTheOpenFileName, "r"); - if (!lIn) - { - tinyfd_messageBox( - "Error", - "Can not open this file in reading mode", - "ok", - "error", - 1 ); - return(1); - } - fgets(lBuffer, sizeof(lBuffer), lIn); - fclose(lIn); +lTheSaveFileName = tinyfd_saveFileDialog( + "let us save this password", + "passwordFile.txt", + 2, + lFilterPatterns, + NULL); - if ( lBuffer ) - tinyfd_messageBox("your password is", lBuffer, "ok", "info", 1); +if (!lTheSaveFileName) +{ + tinyfd_messageBox( + "Error", + "Save file name is NULL", + "ok", + "error", + 1); + return 1; } -//*/ -#pragma warning(default:4996) +lIn = fopen(lTheSaveFileName, "w"); +if (!lIn) +{ + tinyfd_messageBox( + "Error", + "Can not open this file in write mode", + "ok", + "error", + 1); + return 1; +} +fputs(lString, 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; +} -/* -OSX : -$ gcc -o main.app main.c tinyfiledialogs.c - -UNIX : -$ gcc -o main main.c tinyfiledialogs.c - -MinGW : -> gcc -o main.exe main.c tinyfiledialogs.c -LC:/mingw/lib -lcomdlg32 - -VisualStudio : - Create a console application project, it links against Comdlg32.lib. - Right click on your Project, select Properties. - Configuration Properties/General Character Set to Multi-Byte. +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); + + tinyfd_beep(); + + return 0; +} */ + +#ifdef _MSC_VER +#pragma warning(default:4996) +#pragma warning(default:4100) +#pragma warning(default:4706) +#endif diff --git a/libtinyfiledialogs/tinyfiledialogs.h b/libtinyfiledialogs/tinyfiledialogs.h index 8765abe..9c8a852 100644 --- a/libtinyfiledialogs/tinyfiledialogs.h +++ b/libtinyfiledialogs/tinyfiledialogs.h @@ -1,65 +1,42 @@ -/* -tinyfiledialogs.h -unique header file of "tiny file dialogs" created [November 9, 2014] -Copyright (c) 2014 - 2016 Guillaume Vareille http://ysengrin.com -http://tinyfiledialogs.sourceforge.net - - Let me know here mailto:tinfyfiledialogs@ysengrin.com - - 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 not listed here hardware / OS / compiler. - - and please, 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 -v2.3.1 [January 12, 2016] zlib licence. - -A single C file (add it to your C or C++ project) with 6 modal function calls: -- message box & question box -- input box & password box -- save file dialog -- open file dialog & multiple files -- select folder dialog -- color picker. - -Complement to OpenGL GLFW GLUT GLUI VTK SDL Ogre3D Unity3D -or any GUI-less program, there is NO INIT & NO MAIN LOOP. -The dialogs can be forced into console mode. - -On Windows: -- native code & some vbs create the graphic dialogs -- enhanced console mode can use dialog.exe from - http://andrear.altervista.org/home/cdialog.php -- basic console input. - -On Unix (command line call attempts): -- applescript -- zenity -- kdialog -- Xdialog -- python2 tkinter -- dialog (opens a console if needed) -- whiptail, gdialog, gxmessage -- basic console input. -The same executable can run across desktops & distributions. - -tested with C & C++ compilers -on Windows Visual Studio MinGW Mac OSX LINUX FREEBSD ILLUMOS SOLARIS -using Gnome Kde Enlightenment Mate Cinnamon Unity -Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox - -- License - +/* If you are using a C++ compiler to compile tinyfiledialogs.c (maybe renamed with an extension ".cpp") +then comment out << extern "C" >> bellow in this header file) */ + +/*_________ + / \ tinyfiledialogs.h v3.8.8 [Apr 22, 2021] zlib licence + |tiny file| Unique header file created [November 9, 2014] + | dialogs | Copyright (c) 2014 - 2021 Guillaume Vareille http://ysengrin.com + \____ ___/ http://tinyfiledialogs.sourceforge.net + \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd + ____________________________________________ +| | +| email: tinyfiledialogs at ysengrin.com | +|____________________________________________| + ________________________________________________________________________________ +| ____________________________________________________________________________ | +| | | | +| | on windows: | | +| | - for UTF-16, use the wchar_t functions at the bottom of the header file | | +| | - _wfopen() requires wchar_t | | +| | | | +| | - in tinyfiledialogs, char is UTF-8 by default (since v3.6) | | +| | - but fopen() expects MBCS (not UTF-8) | | +| | - if you want char to be MBCS: set tinyfd_winUtf8 to 0 | | +| | | | +| | - alternatively, tinyfiledialogs provides | | +| | functions to convert between UTF-8, UTF-16 and MBCS | | +| |____________________________________________________________________________| | +|________________________________________________________________________________| + +If you like tinyfiledialogs, please upvote my stackoverflow answer +https://stackoverflow.com/a/47651444 +- 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 @@ -72,209 +49,255 @@ misrepresented as being the original software. #ifndef TINYFILEDIALOGS_H #define TINYFILEDIALOGS_H -/* -if tinydialogs.c is compiled with a C++ compiler -rather than with a C compiler, you need to comment out: -extern "C" { -and the corresponding closing bracket: -} -*/ - #ifdef __cplusplus +/* if tinydialogs.c is compiled as C++ code rather than C code, you may need to comment this out + and the corresponding closing bracket near the end of this file. */ extern "C" { -#endif /* __cplusplus */ - - -int tinyfd_messageBox ( - char const * const aTitle , /* "" */ - char const * const aMessage , /* "" may contain \n \t */ - char const * const aDialogType , /* "ok" "okcancel" "yesno" */ - char 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 */ - -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 */ - -extern int tinyfd_forceConsole ; /* 0 (default) or 1 -can be modified at run time. -for unix & windows: 0 (graphic mode) or 1 (console mode). +#endif + +/******************************************************************************************************/ +/**************************************** UTF-8 on Windows ********************************************/ +/******************************************************************************************************/ +#ifdef _WIN32 +/* On windows, if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of this file ) +Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */ +extern int tinyfd_winUtf8; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */ +/* for MBCS change this to 0, in tinyfiledialogs.c or in your code */ + +/* Here are some functions to help you convert between UTF-16 UTF-8 MBSC */ +char * tinyfd_utf8toMbcs(char const * aUtf8string); +char * tinyfd_utf16toMbcs(wchar_t const * aUtf16string); +wchar_t * tinyfd_mbcsTo16(char const * aMbcsString); +char * tinyfd_mbcsTo8(char const * aMbcsString); +wchar_t * tinyfd_utf8to16(char const * aUtf8string); +char * tinyfd_utf16to8(wchar_t const * aUtf16string); +#endif +/******************************************************************************************************/ +/******************************************************************************************************/ +/******************************************************************************************************/ + +/************* 3 funtions for C# (you don't need this in C or C++) : */ +char const * tinyfd_getGlobalChar(char const * aCharVariableName); /* returns NULL on error */ +int tinyfd_getGlobalInt(char const * aIntVariableName); /* returns -1 on error */ +int tinyfd_setGlobalInt(char const * aIntVariableName, int aValue); /* returns -1 on error */ +/* aCharVariableName: "tinyfd_version" "tinyfd_needs" "tinyfd_response" + aIntVariableName : "tinyfd_verbose" "tinyfd_silent" "tinyfd_allowCursesDialogs" + "tinyfd_forceConsole" "tinyfd_assumeGraphicDisplay" "tinyfd_winUtf8" +**************/ + + +extern char tinyfd_version[8]; /* contains tinyfd current version number */ +extern char tinyfd_needs[]; /* info about requirements */ +extern int tinyfd_verbose; /* 0 (default) or 1 : on unix, prints the command line calls */ +extern int tinyfd_silent; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */ + +/* Curses dialogs are difficult to use, on windows they are only ascii and uses the unix backslah */ +extern int tinyfd_allowCursesDialogs; /* 0 (default) or 1 */ + +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 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 */ - -/* #define TINYFD_WIN_CONSOLE_ONLY //*/ -/* On windows, Define this if you don't want to include the code -creating the GUI dialogs. Then you don't need link against Comdlg32.lib */ - +1: forces all dialogs into console mode even when an X server is present, + it can use the package dialog or dialog.exe. + on windows it only make sense for console applications */ + +extern int tinyfd_assumeGraphicDisplay; /* 0 (default) or 1 */ +/* some systems don't set the environment variable DISPLAY even when a graphic display is present. +set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */ + +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 graphic mode: + windows_wchar windows applescript kdialog zenity zenity3 matedialog + shellementary qarma yad python2-tkinter python3-tkinter python-dbus + perl-dbus gxmessage gmessage xmessage xdialog gdialog +for console mode: + dialog whiptail basicinput no_solution */ + +void tinyfd_beep(void); + +int tinyfd_notifyPopup( + char const * aTitle, /* NULL or "" */ + char const * aMessage, /* NULL or "" may contain \n \t */ + char const * aIconType); /* "info" "warning" "error" */ + /* return has only meaning for tinyfd_query */ + +int tinyfd_messageBox( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" may contain \n \t */ + char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ + char const * aIconType , /* "info" "warning" "error" "question" */ + int aDefaultButton ) ; + /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ + +char * tinyfd_inputBox( + char const * aTitle , /* NULL or "" */ + char const * aMessage , /* NULL or "" (\n and \t have no effect) */ + char const * aDefaultInput ) ; /* NULL passwordBox, "" inputbox */ + /* returns NULL on cancel */ + +char * tinyfd_saveFileDialog( + char const * aTitle , /* NULL or "" */ + char const * aDefaultPathAndFile , /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 (1 in the following example) */ + char const * const * aFilterPatterns , /* NULL or char const * lFilterPatterns[1]={"*.txt"} */ + char const * aSingleFilterDescription ) ; /* NULL or "text files" */ + /* returns NULL on cancel */ + +char * tinyfd_openFileDialog( + char const * aTitle, /* NULL or "" */ + char const * aDefaultPathAndFile, /* NULL or "" */ + int aNumOfFilterPatterns , /* 0 (2 in the following example) */ + char const * const * aFilterPatterns, /* NULL or char const * lFilterPatterns[2]={"*.png","*.jpg"}; */ + char const * aSingleFilterDescription, /* NULL or "image files" */ + int aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +char * tinyfd_selectFolderDialog( + char const * aTitle, /* NULL or "" */ + char const * aDefaultPath); /* NULL or "" */ + /* returns NULL on cancel */ + +char * tinyfd_colorChooser( + char const * aTitle, /* NULL or "" */ + char const * aDefaultHexRGB, /* NULL or "#FF0000" */ + unsigned char const aDefaultRGB[3] , /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */ + unsigned char aoResultRGB[3] ) ; /* unsigned char lResultRGB[3]; */ + /* 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 */ + + +/************ WINDOWS ONLY SECTION ************************/ +#ifdef _WIN32 + +/* windows only - utf-16 version */ +int tinyfd_notifyPopupW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ + wchar_t const * aIconType); /* L"info" L"warning" L"error" */ + +/* windows only - utf-16 version */ +int tinyfd_messageBoxW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ + wchar_t const * aDialogType, /* L"ok" L"okcancel" L"yesno" */ + wchar_t const * aIconType, /* L"info" L"warning" L"error" L"question" */ + int 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 * tinyfd_inputBoxW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aMessage, /* NULL or L"" (\n nor \t not respected) */ + wchar_t const * aDefaultInput); /* NULL passwordBox, L"" inputbox */ + +/* windows only - utf-16 version */ +wchar_t * tinyfd_saveFileDialogW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aDefaultPathAndFile, /* NULL or L"" */ + int aNumOfFilterPatterns, /* 0 (1 in the following example) */ + wchar_t const * const * aFilterPatterns, /* NULL or wchar_t const * lFilterPatterns[1]={L"*.txt"} */ + wchar_t const * aSingleFilterDescription); /* NULL or L"text files" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t * tinyfd_openFileDialogW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aDefaultPathAndFile, /* NULL or L"" */ + int aNumOfFilterPatterns , /* 0 (2 in the following example) */ + wchar_t const * const * aFilterPatterns, /* NULL or wchar_t const * lFilterPatterns[2]={L"*.png","*.jpg"} */ + wchar_t const * aSingleFilterDescription, /* NULL or L"image files" */ + int aAllowMultipleSelects ) ; /* 0 or 1 */ + /* in case of multiple files, the separator is | */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t * tinyfd_selectFolderDialogW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aDefaultPath); /* NULL or L"" */ + /* returns NULL on cancel */ + +/* windows only - utf-16 version */ +wchar_t * tinyfd_colorChooserW( + wchar_t const * aTitle, /* NULL or L"" */ + wchar_t const * aDefaultHexRGB, /* NULL or L"#FF0000" */ + unsigned char const aDefaultRGB[3], /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */ + unsigned char aoResultRGB[3]); /* unsigned char lResultRGB[3]; */ + /* returns the hexcolor as a string L"#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 /*_WIN32 */ #ifdef __cplusplus -} -#endif /* __cplusplus */ +} /*extern "C"*/ +#endif #endif /* TINYFILEDIALOGS_H */ - /* -- This is not for android nor ios. -- The code is pure C, perfectly compatible with C++. + ________________________________________________________________________________ +| ____________________________________________________________________________ | +| | | | +| | on windows: | | +| | - for UTF-16, use the wchar_t functions at the bottom of the header file | | +| | - _wfopen() requires wchar_t | | +| | | | +| | - in tinyfiledialogs, char is UTF-8 by default (since v3.6) | | +| | - but fopen() expects MBCS (not UTF-8) | | +| | - if you want char to be MBCS: set tinyfd_winUtf8 to 0 | | +| | | | +| | - alternatively, tinyfiledialogs provides | | +| | functions to convert between UTF-8, UTF-16 and MBCS | | +| |____________________________________________________________________________| | +|________________________________________________________________________________| + +- This is not for ios nor android (it works in termux though). +- The files can be renamed with extension ".cpp" as the code is 100% compatible C C++ + (just comment out << extern "C" >> 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 latest (maybe even older versions) +- Do not use " and ' as the dialogs will be displayed with a warning + instead of the title, message, etc... - 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, inputbox and passwordbox are not as smooth as they should be: - they open a console window for a few seconds. -- On visual studio: - set Configuration Properties/General Character Set to Multi-Byte. -- On windows link against Comdlg32.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, kdialog, Xdialog, python2/tkinter - or dialog (will open a terminal if running without console); +- On windows link against Comdlg32.lib and Ole32.lib + (on windows the no linking claim is a lie) +- On unix: it tries command line calls, so no such need (NO LINKING). +- On unix you need one of the following: + applescript, kdialog, zenity, matedialog, shellementary, qarma, yad, + python (2 or 3)/tkinter/python-dbus (optional), Xdialog + or curses dialogs (opens 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. -- Avoid using " and ' in titles and messages. -- Use windows separators on windows and unix separators on unix. + with a textinputbox. If nothing is found, it switches to basic console input, + it opens a console if needed (requires xterm + bash). +- for curses dialogs you must set tinyfd_allowCursesDialogs=1 +- You can query the type of dialog that will be used (pass "tinyfd_query" as aTitle) - 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. +- File and path names are tested before return, they should be valid. - 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. +- On windows, console mode is not implemented for wchar_T UTF-16. +- Mutiple selects are not possible in console mode. +- The package dialog must be installed to run in curses dialogs in 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, +- On osx, the package dialog can be installed via + http://macappstore.org/dialog or http://macports.org +- On windows, for curses dialogs 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. - -- Here is the Hello World (and a bit more): - 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 "tinyfiledialogs.h" -#pragma warning(disable:4996) // allows usage of strncpy, strcpy, strcat, sprintf, fopen -int main() -{ - char const * lThePassword; - char const * lTheSaveFileName; - char const * lTheOpenFileName; - FILE * lIn; - char lBuffer[1024]; - - tinyfd_forceConsole = tinyfd_messageBox("Hello World", - "force dialogs into console mode?\ - \n\t(it's better if dialog is installed)", - "yesno", "question", 0); - - lThePassword = tinyfd_inputBox( - "a password box","your password will be revealed",NULL); - - lTheSaveFileName = tinyfd_saveFileDialog ( - "let's save this password", - "passwordFile.txt", - 0, - NULL, - NULL ); - - lIn = fopen(lTheSaveFileName, "w"); - if (!lIn) - { - tinyfd_messageBox( - "Error", - "Can not open this file in writting mode", - "ok", - "error", - 1 ); - return(1); - } - fputs(lThePassword, lIn); - fclose(lIn); - - lTheOpenFileName = tinyfd_openFileDialog ( - "let's read this password", - "", - 0, - NULL, - NULL, - 0); - - lIn = fopen(lTheOpenFileName, "r"); - if (!lIn) - { - tinyfd_messageBox( - "Error", - "Can not open this file in reading mode", - "ok", - "error", - 1 ); - return(1); - } - fgets(lBuffer, sizeof(lBuffer), lIn); - fclose(lIn); - - if ( lBuffer ) - tinyfd_messageBox("your password is", lBuffer, "ok", "info", 1); -} -#pragma warning(default:4996) - -OSX : -$ gcc -o hello.app hello.c tinyfiledialogs.c - -UNIX : -$ gcc -o hello hello.c tinyfiledialogs.c - -MinGW : -> gcc -o hello.exe hello.c tinyfiledialogs.c -LC:/mingw/lib -lcomdlg32 - -VisualStudio : - Create a console application project, it links against Comdlg32.lib. - Right click on your Project, select Properties. - Configuration Properties/General Character Set to Multi-Byte. */ diff --git a/makefile.cargo b/makefile.cargo deleted file mode 100644 index 4d91725..0000000 --- a/makefile.cargo +++ /dev/null @@ -1,16 +0,0 @@ -ifneq ($(HOST),$(TARGET)) -CC ?= $(TARGET)-gcc -AR ?= $(TARGET)-ar -else -CC ?= gcc -AR ?= ar -endif - -.PHONY: all -all: $(OUT_DIR)/libtinyfiledialogs.a - -$(OUT_DIR)/tinyfiledialogs.o: libtinyfiledialogs/tinyfiledialogs.c $(CURDIR)/makefile.cargo - mkdir -p `dirname $@` && $(CC) $< -o $@ -c $(CXXFLAGS) -fPIC - -$(OUT_DIR)/libtinyfiledialogs.a: $(OUT_DIR)/tinyfiledialogs.o - $(AR) rcs $@ $(OUT_DIR)/tinyfiledialogs.o diff --git a/patches/0002_WindowsHidpiVisualStyle.patch b/patches/0002_WindowsHidpiVisualStyle.patch new file mode 100644 index 0000000..2c15ddc --- /dev/null +++ b/patches/0002_WindowsHidpiVisualStyle.patch @@ -0,0 +1,167 @@ +diff --git a/libtinyfiledialogs/tinyfiledialogs.c b/libtinyfiledialogs/tinyfiledialogs.c +index 30865fe..b7662fd 100644 +--- a/libtinyfiledialogs/tinyfiledialogs.c ++++ b/libtinyfiledialogs/tinyfiledialogs.c +@@ -182,6 +182,14 @@ char tinyfd_needs[] = "\ + #pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ + #endif + ++#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) ++#include ++ ++#pragma comment(linker,"\"/manifestdependency:type='win32' \ ++name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ ++processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") ++#endif // USE_WINDOWS_HIDPI ++ + static int getenvDISPLAY(void) + { + return tinyfd_assumeGraphicDisplay || getenv("DISPLAY"); +@@ -1060,6 +1068,9 @@ int tinyfd_messageBoxW( + wchar_t const * aIconType, /* "info" "warning" "error" "question" */ + int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ + { ++#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) ++ SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); ++#endif + int lBoxReturnValue; + UINT aCode; + +@@ -1130,6 +1141,9 @@ int tinyfd_notifyPopupW( + wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ + wchar_t const * aIconType) /* L"info" L"warning" L"error" */ + { ++#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) ++ SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); ++#endif + wchar_t * lDialogString; + size_t lTitleLen; + size_t lMessageLen; +@@ -1202,6 +1216,9 @@ wchar_t * tinyfd_inputBoxW( + wchar_t const * aMessage, /* NULL or L"" (\n and \t have no effect) */ + wchar_t const * aDefaultInput) /* L"" , if NULL it's a passwordBox */ + { ++#if defined(USE_WINDOWS_HIDPI) && defined(_WIN64) ++ SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); ++#endif + static wchar_t lBuff[MAX_PATH_OR_CMD]; + wchar_t * lDialogString; + FILE * lIn; +@@ -1273,6 +1290,7 @@ wchar_t * tinyfd_inputBoxW( + + wcscat(lDialogString, L"tinyfiledialogsTopWindow"); + wcscat(lDialogString, L"\n\ ++\n\ + c_int; - pub fn tinyfd_inputBox ( + fn tinyfd_inputBox ( aTitle: *const c_char , aMessage: *const c_char, aDefaultInput: *const c_char) -> *const c_char; - pub fn tinyfd_saveFileDialog ( + fn tinyfd_saveFileDialog ( aTitle: *const c_char, aDefaultPathAndFile: *const c_char, aNumOfFilterPatterns: c_int, - aFilterPatterns: *const c_char, + aFilterPatterns: *const *const c_char, aSingleFilterDescription: *const c_char) -> *const c_char; - pub fn tinyfd_openFileDialog ( + fn tinyfd_openFileDialog ( aTitle: *const c_char, aDefaultPathAndFile: *const c_char, aNumOfFilterPatterns: c_int, - aFilterPatterns: *const c_char, + aFilterPatterns: *const *const c_char, aSingleFilterDescription: *const c_char, aAllowMultipleSelects: c_int) -> *const c_char; - pub fn tinyfd_selectFolderDialog ( + fn tinyfd_selectFolderDialog ( aTitle: *const c_char, aDefaultPath: *const c_char) -> *const c_char; - pub fn tinyfd_colorChooser ( + fn tinyfd_colorChooser ( aTitle: *const c_char, aDefaultHexRGB: *const c_char, - aDefaultRGB: &[c_uchar ; 3], - aoResultRGB: &[c_uchar; 3]) -> *const c_char; + aDefaultRGB: *const c_uchar, + aoResultRGB: *mut c_uchar) -> *const c_char; +} + +fn message_box(title: &str, message: &str, box_type: &str, icon: &str, button: i32) -> i32 { + let message_box_title = CString::new(title).unwrap(); + let message_box_message = CString::new(message).unwrap(); + let message_box_type = CString::new(box_type).unwrap(); + let message_box_icon = CString::new(icon).unwrap(); + + unsafe { + tinyfd_messageBox( + message_box_title.as_ptr(), + message_box_message.as_ptr(), + message_box_type.as_ptr(), + message_box_icon.as_ptr(), + button) + } +} + +pub enum MessageBoxIcon { + Info, + Warning, + Error, + Question, +} + +impl MessageBoxIcon { + fn to_str(&self) -> &'static str { + match *self { + MessageBoxIcon::Info => "info", + MessageBoxIcon::Warning => "warning", + MessageBoxIcon::Error => "error", + MessageBoxIcon::Question => "question", + } + } +} + +pub fn message_box_ok(title: &str, message: &str, icon: MessageBoxIcon) { + let _ = message_box(title, message, "ok", icon.to_str(), 0); +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum OkCancel { + Cancel = 0, + Ok, +} + +pub fn message_box_ok_cancel(title: &str, message: &str, icon: MessageBoxIcon, default: OkCancel) -> OkCancel { + match message_box(title, message, "okcancel", icon.to_str(), default as i32) { + 0 => OkCancel::Cancel, + 1 => OkCancel::Ok, + _ => unreachable!(), + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum YesNo { + No = 0, + Yes, +} + +pub fn message_box_yes_no(title: &str, message: &str, icon: MessageBoxIcon, default: YesNo) -> YesNo { + match message_box(title, message, "yesno", icon.to_str(), default as i32) { + 0 => YesNo::No, + 1 => YesNo::Yes, + _ => unreachable!(), + } +} + +fn input_box_impl(title: &str, message: &str, default: Option<&str>) -> Option { + let input_box_title = CString::new(title).unwrap(); + let input_box_message = CString::new(message).unwrap(); + let input_box_default = default.map(|default| CString::new(default).unwrap()); + + let c_input = unsafe { + tinyfd_inputBox(input_box_title.as_ptr(), + input_box_message.as_ptr(), + input_box_default.as_ref().map(|d| d.as_ptr()).unwrap_or(ptr::null())) + }; + + if !c_input.is_null() { + unsafe { + Some(CStr::from_ptr(c_input).to_string_lossy().into_owned()) + } + } else { + None + } +} + +pub fn input_box(title: &str, message: &str, default: &str) -> Option { + input_box_impl(title, message, Some(default)) +} + +pub fn password_box(title: &str, message: &str) -> Option { + input_box_impl(title, message, None) +} + +fn save_file_dialog_impl(title: &str, path: &str, filter: Option<(&[&str], &str)>) -> Option { + let save_dialog_title = CString::new(title).unwrap(); + let save_dialog_path = CString::new(path).unwrap(); + let save_dialog_des = CString::new(filter.map_or("", |f| f.1)).unwrap(); + + let filter_patterns = + filter.map_or(vec![], |f| f.0.iter().map(|s| CString::new(*s).unwrap()).collect()); + let ptr_filter_patterns = filter_patterns.iter().map(|c| c.as_ptr()).collect::>(); + + let c_file_name = unsafe { + tinyfd_saveFileDialog( + save_dialog_title.as_ptr(), + save_dialog_path.as_ptr(), + ptr_filter_patterns.len() as c_int, + ptr_filter_patterns.as_ptr(), + save_dialog_des.as_ptr()) + }; + + if !c_file_name.is_null() { + unsafe { + Some(CStr::from_ptr(c_file_name).to_string_lossy().into_owned()) + } + } else { + None + } +} + +pub fn save_file_dialog_with_filter(title: &str, + path: &str, + filter_patterns: &[&str], + description: &str) -> Option { + save_file_dialog_impl(title, path, Some((filter_patterns, description))) +} + +pub fn save_file_dialog(title: &str, path: &str) -> Option { + save_file_dialog_impl(title, path, None) +} + +fn open_file_dialog_impl(title: &str, + path: &str, + filter: Option<(&[&str], &str)>, + multi: bool) -> Option> { + let open_dialog_title = CString::new(title).unwrap(); + let open_dialog_path = CString::new(path).unwrap(); + let open_dialog_des = CString::new(filter.map_or("", |f| f.1)).unwrap(); + + let filter_patterns = + filter.map_or(vec![], |f| f.0.iter().map(|s| CString::new(*s).unwrap()).collect()); + let ptr_filter_patterns = + filter_patterns.iter().map(|c| c.as_ptr()).collect::>(); + + let c_file_name = unsafe { + tinyfd_openFileDialog( + open_dialog_title.as_ptr(), + open_dialog_path.as_ptr(), + ptr_filter_patterns.len() as c_int, + ptr_filter_patterns.as_ptr(), + open_dialog_des.as_ptr(), + multi as c_int) + }; + + if !c_file_name.is_null() { + let result = unsafe { + CStr::from_ptr(c_file_name).to_string_lossy().into_owned() + }; + Some(if multi { + result.split('|').map(|s| s.to_owned()).collect() + } else { + vec![result] + }) + } else { + None + } +} + +pub fn open_file_dialog(title: &str, + path: &str, + filter: Option<(&[&str], &str)>) -> Option { + open_file_dialog_impl(title, path, filter, false).and_then(|v| v.into_iter().next()) +} + +pub fn open_file_dialog_multi(title: &str, + path: &str, + filter: Option<(&[&str], &str)>) -> Option> { + open_file_dialog_impl(title, path, filter, true) +} + +pub fn select_folder_dialog(title: &str, path: &str) -> Option { + let select_folder_title = CString::new(title).unwrap(); + let select_folder_path = CString::new(path).unwrap(); + + let folder = unsafe { + tinyfd_selectFolderDialog(select_folder_title.as_ptr(), select_folder_path.as_ptr()) + }; + + if !folder.is_null() { + unsafe { + Some(CStr::from_ptr(folder).to_string_lossy().into_owned()) + } + } else { + None + } +} + +pub enum DefaultColorValue<'a> { + Hex(&'a str), + RGB(&'a [u8; 3]), +} + +pub fn color_chooser_dialog(title: &str, default: DefaultColorValue) + -> Option<(String, [u8; 3])> { + let color_title = CString::new(title).unwrap(); + let rubbish = [0, 0, 0]; + let (color_default_hex, color_default_rgb) = match default { + DefaultColorValue::Hex(hex) => (Some(CString::new(hex).unwrap()), &rubbish), + DefaultColorValue::RGB(rgb) => (None, rgb), + }; + let mut color_result_rgb = [0, 0, 0]; + + let result = unsafe { + tinyfd_colorChooser(color_title.as_ptr(), + color_default_hex.as_ref().map_or(ptr::null(), |h| h.as_ptr()), + color_default_rgb.as_ptr(), + color_result_rgb.as_mut_ptr()) + }; + + if !result.is_null() { + unsafe { + Some((CStr::from_ptr(result).to_string_lossy().into_owned(), color_result_rgb)) + } + } else { + None + } } diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..3949429 --- /dev/null +++ b/update.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eux + +for ext in h c +do + wget "https://sourceforge.net/projects/tinyfiledialogs/files/tinyfiledialogs.${ext}/download" -O - | tr -d '\r' > libtinyfiledialogs/tinyfiledialogs.${ext} +done + +for p in $(ls patches -v) +do + git apply patches/${p} +done