├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── dub.sdl
└── tinyfiledialogs.d
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 | max_line_length = 120
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.obj
3 | *.so
4 | *.dylib
5 | *.dll
6 | *.a
7 | *.lib
8 | *.exe
9 |
10 | .dub/
11 | dub.selections.json
12 | *.~*
13 | *.*~
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | zlib License
2 |
3 | (C) 2014-2019 Guillaume Vareille
4 | (C) 2019 dayllenger
5 |
6 | This software is provided 'as-is', without any express or implied
7 | warranty. In no event will the authors be held liable for any damages
8 | arising from the use of this software.
9 |
10 | Permission is granted to anyone to use this software for any purpose,
11 | including commercial applications, and to alter it and redistribute it
12 | freely, subject to the following restrictions:
13 |
14 | 1. The origin of this software must not be misrepresented; you must not
15 | claim that you wrote the original software. If you use this software
16 | in a product, an acknowledgment in the product documentation would be
17 | appreciated but is not required.
18 | 2. Altered source versions must be plainly marked as such, and must not be
19 | misrepresented as being the original software.
20 | 3. This notice may not be removed or altered from any source distribution.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## tinyfiledialogs
2 |
3 | Native dialog library for Windows, macOS, GTK+, Qt, console & more.
4 | SSH supported via automatic switch to console mode or X11 forwarding.
5 |
6 | *NOTE: this package may not reflect the latest version of [the original library by its author](https://sourceforge.net/projects/tinyfiledialogs/).*
7 |
8 | *NOTE: the main purpose of this package is to implement native file and color dialogs in [beamui](https://github.com/dayllenger/beamui), so don't expect much support and development.*
9 |
10 |
11 |
12 | Originally pure C89. To build in betterC mode, enable `BC` subconfiguration.
13 |
14 | For examples and info take a look at [the documentation](https://tinyfiledialogs.dpldocs.info/tinyfiledialogs.html).
15 |
16 | Short example of how to open a save dialog and show the name of chosen file in a message box:
17 | ```D
18 | import tinyfiledialogs;
19 |
20 | // pairs of pattern list and optional description
21 | const TFD_Filter[] filters = [
22 | { ["*.png" ], "PNG image" },
23 | { ["*.jpg", "*.jpeg" ], "JPEG image" },
24 | { ["*.tif", "*.tiff" ], "TIFF image" },
25 | { ["*.tga" ], "TarGA image" },
26 | ];
27 | // it blocks until the dialog is closed
28 | const char* filename = tinyfd_saveFileDialog("Save as...", "Untitled.png", filters);
29 | if (filename)
30 | {
31 | // now we can save our file physically
32 | tinyfd_messageBox("The filename is", filename, "ok", "info", 1);
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/dub.sdl:
--------------------------------------------------------------------------------
1 | name "tinyfiledialogs"
2 | description "Cross-platform single-file library for native dialogs."
3 | authors "Guillaume Vareille" "dayllenger"
4 | license "Zlib"
5 |
6 | importPaths "."
7 | sourceFiles "tinyfiledialogs.d"
8 |
9 | configuration "default" {
10 | targetType "staticLibrary"
11 | }
12 | configuration "BC" {
13 | buildOptions "betterC"
14 | targetType "staticLibrary"
15 | }
16 |
--------------------------------------------------------------------------------
/tinyfiledialogs.d:
--------------------------------------------------------------------------------
1 | /*________
2 | / \ This is a custom D port of tinyfiledialogs v3.3.9 [Apr 14, 2019]
3 | |tiny file| http://tinyfiledialogs.sourceforge.net
4 | | dialogs | Copyright (c) 2014-2019 Guillaume Vareille
5 | \____ ___/ Copyright (c) 2019 dayllenger
6 | \|
7 | */
8 | /**
9 | Native dialog library for Windows, macOS, GTK+, Qt, console & more.
10 | SSH supported via automatic switch to console mode or X11 forwarding.
11 |
12 | 8 functions:
13 | $(LIST
14 | * beep
15 | * notify popup (tray)
16 | * message & question
17 | * input & password
18 | * save file
19 | * open file(s)
20 | * select folder
21 | * color picker
22 | )
23 | Each function is documented with examples.
24 | The dialogs can be forced into console mode.
25 | Supports UTF-8 (except Windows console).
26 |
27 | Windows XP to 10:
28 | $(LIST
29 | * native code & vbs create the graphic dialogs
30 | * enhanced console mode can use dialog.exe from
31 | `http://andrear.altervista.org/home/cdialog.php`
32 | * basic console input
33 | )
34 | Unix (using command line calls):
35 | $(LIST
36 | * applescript, kdialog, zenity
37 | * python (2 or 3) + tkinter + python-dbus (optional)
38 | * whiptail, dialog (opens a console if needed)
39 | * basic console input
40 | )
41 | The same executable can run across desktops & distributions.
42 |
43 | Notes
44 | =====
45 | $(LIST
46 | * This is $(I not) for Android nor iOS.
47 | * The code is betterC compatible, originally pure C89.
48 | * Windows is fully supported from XP to 10 (maybe even older versions).
49 | * OSX supported from 10.4 to latest (maybe even older versions).
50 | * On Windows, it links against comdlg32.lib, ole32.lib, and user32.lib.
51 | * Set TINYFD_NOLIB version if you don't want to include the code creating
52 | graphic dialogs. Then you won't need to link against those libs.
53 | * On Unix, it tries command line calls.
54 | They are already available on most (if not all) desktops.
55 | * In the absence of those it will use gdialog, gxmessage or whiptail
56 | with a textinputbox.
57 | * If nothing is found, it switches to basic console input,
58 | and opens a console if needed (requires xterm + bash).
59 | * On OSX, the package dialog can be installed via
60 | `http://macappstore.org/dialog` or `https://www.macports.org/`
61 | * On Windows, for enhanced console mode,
62 | dialog.exe should be copied somewhere on your executable path.
63 | It can be found at the bottom of the following page:
64 | `http://andrear.altervista.org/home/cdialog.php`
65 | * If dialog is missing, it will switch to basic console input.
66 | * Mutiple selects are not allowed in console mode.
67 | )
68 | $(LIST
69 | * Avoid using " and ' in titles and messages.
70 | * String memory is preallocated statically for all the returned values.
71 | * Use platform-specific path separators: \ on Windows, / on Unix.
72 | * If you pass only a path instead of path + filename,
73 | make sure it ends with a separator.
74 | * File and path names are tested before return, they are valid.
75 | * You can query the type of dialog that will be used, see [tinyfd_response].
76 | )
77 | Thanks for contributions, bug corrections & thorough testing to:
78 | $(LIST
79 | * Don Heyse http://ldglite.sf.net for bug corrections & thorough testing!
80 | * Paul Rouget
81 | )
82 | */
83 | /*
84 | zlib License
85 |
86 | This software is provided 'as-is', without any express or implied
87 | warranty. In no event will the authors be held liable for any damages
88 | arising from the use of this software.
89 |
90 | Permission is granted to anyone to use this software for any purpose,
91 | including commercial applications, and to alter it and redistribute it
92 | freely, subject to the following restrictions:
93 |
94 | 1. The origin of this software must not be misrepresented; you must not
95 | claim that you wrote the original software. If you use this software
96 | in a product, an acknowledgment in the product documentation would be
97 | appreciated but is not required.
98 | 2. Altered source versions must be plainly marked as such, and must not be
99 | misrepresented as being the original software.
100 | 3. This notice may not be removed or altered from any source distribution.
101 | */
102 | module tinyfiledialogs;
103 |
104 | // version = TINYFD_NOLIB;
105 | // version = TINYFD_NOSELECTFOLDERWIN;
106 | version = TINYFD_NOCCSUNICODE;
107 |
108 | extern (C) nothrow @nogc:
109 |
110 | alias c_str = const(char*);
111 |
112 | /// No params, no return value, just beep
113 | void tinyfd_beep()
114 | {
115 | _beep();
116 | }
117 |
118 | /** Params:
119 | title = C-string or null
120 | message = C-string or null, can be multiline
121 | iconType = "info" "warning" "error"
122 |
123 | Returns:
124 | Return has only meaning for "tinyfd_query".
125 |
126 | Example:
127 | ---
128 | tinyfd_notifyPopup("the title", "the message from outer-space", "info");
129 | ---
130 | */
131 | int tinyfd_notifyPopup(c_str title, c_str message, c_str iconType)
132 | {
133 | return _notifyPopup(title, message, iconType);
134 | }
135 |
136 | /** Params:
137 | title = C-string or null
138 | message = C-string or null, can be multiline
139 | dialogType = "ok" "okcancel" "yesno" "yesnocancel"
140 | iconType = "info" "warning" "error" "question"
141 | defaultButton = 0 - cancel/no, 1 - ok/yes, 2 - no in yesnocancel
142 |
143 | Returns:
144 | 0 - cancel/no, 1 - ok/yes, 2 - no in yesnocancel.
145 |
146 | Example:
147 | ---
148 | const int ret = tinyfd_messageBox("Hello World",
149 | "graphic dialogs [yes] / console mode [no]?",
150 | "yesno", "question", 1);
151 | tinyfd_forceConsole = (ret == 0);
152 | ---
153 | */
154 | int tinyfd_messageBox(
155 | c_str title,
156 | c_str message,
157 | c_str dialogType,
158 | c_str iconType,
159 | int defaultButton,
160 | )
161 | {
162 | return _messageBox(title, message, dialogType, iconType, defaultButton);
163 | }
164 |
165 | /** Params:
166 | title = C-string or null
167 | message = C-string or null; some platforms (Windows, GTK-based) can't display multiline text
168 | defaultInput = C-string, if null it's a password box
169 |
170 | Returns:
171 | Entered text, `null` on cancel.
172 |
173 | Example:
174 | ---
175 | c_str passwd = tinyfd_inputBox("A password box", "Your password will be revealed", null);
176 | if (passwd)
177 | tinyfd_notifyPopup("Your password is:", passwd, "warning");
178 | ---
179 | */
180 | c_str tinyfd_inputBox(c_str title, c_str message, c_str defaultInput)
181 | {
182 | return _inputBox(title, message, defaultInput);
183 | }
184 |
185 | /// Single filter, used in open/save file dialogs
186 | struct TFD_Filter
187 | {
188 | /// Patterns like `["*.jpg", "*.png"]` or MIME types (kdialog only) `["audio/mp3"]`
189 | c_str[] patterns;
190 | /// Description like "Image files". If none, the list of patterns becomes the description
191 | c_str description;
192 | }
193 |
194 | /** Params:
195 | title = C-string or null
196 | defaultPathAndFile = C-string or null
197 | filters = list of file type patterns
198 |
199 | Returns:
200 | Selected file full name, `null` on cancel.
201 |
202 | Example:
203 | ---
204 | const TFD_Filter[] filters = [
205 | { ["*.css" ], "CSS" },
206 | { ["*.sass", "*.scss" ], "SASS" },
207 | ];
208 | c_str filename = tinyfd_saveFileDialog("Save as...", "style.css", filters);
209 | if (filename)
210 | tinyfd_messageBox("Chosen file is", filename, "ok", "info", 1);
211 | ---
212 | */
213 | c_str tinyfd_saveFileDialog(
214 | c_str title,
215 | c_str defaultPathAndFile,
216 | const TFD_Filter[] filters,
217 | )
218 | {
219 | return _saveFileDialog(title, defaultPathAndFile, filters);
220 | }
221 |
222 | /** Params:
223 | title = C-string or null
224 | defaultPathAndFile = C-string or null
225 | filters = list of file type patterns
226 | allowMultipleSelects = does not work on console
227 |
228 | Returns:
229 | Selected file full name, `null` on cancel. In case of multiple files, the separator is |.
230 |
231 | Example:
232 | ---
233 | const TFD_Filter[] filters = [
234 | { ["*.c" ], "C source code" },
235 | { ["*.cpp", "*.cc", "*.C", "*.cxx", "*.c++"], "C++ source code" }
236 | ];
237 | c_str filename = tinyfd_openFileDialog("Open a C/C++ File", null, filters, false);
238 | if (filename)
239 | tinyfd_messageBox("Chosen file is", filename, "ok", "info", 1);
240 | ---
241 | */
242 | c_str tinyfd_openFileDialog(
243 | c_str title,
244 | c_str defaultPathAndFile,
245 | const TFD_Filter[] filters,
246 | bool allowMultipleSelects,
247 | )
248 | {
249 | return _openFileDialog(title, defaultPathAndFile, filters, allowMultipleSelects);
250 | }
251 |
252 | /** Params:
253 | title = C-string or null
254 | defaultPath = C-string or null
255 |
256 | Returns:
257 | Selected folder path, `null` on cancel
258 |
259 | Example:
260 | ---
261 | c_str name = tinyfd_selectFolderDialog("Let us just select a directory", null);
262 | if (name)
263 | tinyfd_messageBox("The selected folder is", name, "ok", "info", 1);
264 | ---
265 | */
266 | c_str tinyfd_selectFolderDialog(c_str title, c_str defaultPath)
267 | {
268 | return _selectFolderDialog(title, defaultPath);
269 | }
270 |
271 | /** Params:
272 | title = C-string or null
273 | defaultHexRGB = default color, C-string or null
274 | defaultRGB = used only if the previous parameter is null
275 | resultRGB = contains the decoded result
276 |
277 | Returns:
278 | The hexcolor as a string like "#FF0000" or `null` on cancel.
279 |
280 | Example:
281 | ---
282 | ubyte[3] rgb;
283 | c_str hexColor = tinyfd_colorChooser("Choose a nice color", "#FF0077", rgb, rgb);
284 | if (hexColor)
285 | tinyfd_messageBox("The selected hexcolor is", hexColor, "ok", "info", 1);
286 | ---
287 | */
288 | c_str tinyfd_colorChooser(
289 | c_str title,
290 | c_str defaultHexRGB,
291 | ref const ubyte[3] defaultRGB,
292 | ref ubyte[3] resultRGB,
293 | )
294 | {
295 | return _colorChooser(title, defaultHexRGB, defaultRGB, resultRGB);
296 | }
297 |
298 | /**** Constants and variables ****/
299 | __gshared:
300 |
301 | /// Contains tinyfd current version number
302 | immutable char[8] tinyfd_version = "3.3.9";
303 |
304 | /// Info about requirements for a platform
305 | version (Windows)
306 | immutable char[] tinyfd_needs =
307 | ` ___________
308 | / \
309 | | tiny file |
310 | | dialogs |
311 | \_____ ____/
312 | \|
313 | tiny file dialogs on Windows needs:
314 | a graphic display
315 | or dialog.exe (enhanced console mode)
316 | or a console for basic input`;
317 | else
318 | immutable char[] tinyfd_needs =
319 | ` ___________
320 | / \
321 | | tiny file |
322 | | dialogs |
323 | \_____ ____/
324 | \|
325 | tiny file dialogs on UNIX needs:
326 | applescript
327 | or kdialog
328 | or zenity (or matedialog or qarma)
329 | or python (2 or 3)
330 | + tkinter + python-dbus (optional)
331 | or dialog (opens console if needed)
332 | or xterm + bash
333 | (opens console for basic input)
334 | or existing console for basic input`;
335 |
336 | /// On unix, prints the command line calls; default is `false`
337 | bool tinyfd_verbose;
338 | /// On unix, hide errors and warnings from called dialog; default is `true`
339 | bool tinyfd_silent = true;
340 |
341 | /** `false` (default) - try to use a graphic solution, if it fails use console mode.
342 | `true` - force all dialogs into console mode even when an X server is present,
343 | if the package `whiptail` or `dialog` and a console are present in a system,
344 | or `dialog.exe` is installed.
345 | On Windows it only make sense for console applications.
346 | */
347 | version (Windows)
348 | {
349 | version (TINYFD_NOLIB)
350 | bool tinyfd_forceConsole = true;
351 | else
352 | bool tinyfd_forceConsole;
353 | }
354 | else
355 | bool tinyfd_forceConsole;
356 |
357 | /** If you pass "tinyfd_query" as `title`, the functions will not display
358 | the dialogs but will return 0 for console mode, 1 for graphic mode.
359 | `tinyfd_response` is then filled with the retain solution.
360 | possible values for `tinyfd_response` are (all lowercase),
361 | for graphic mode: `windows`
362 | `applescript kdialog zenity zenity3 matedialog qarma`
363 | `python2-tkinter python3-tkinter python-dbus perl-dbus`
364 | `gxmessage gmessage xmessage xdialog gdialog`,
365 | for console mode: `dialog whiptail basicinput no_solution`
366 |
367 | Example:
368 | ---
369 | import core.stdc.string;
370 |
371 | char[1024] buf = '\0';
372 | c_str mode = tinyfd_inputBox("tinyfd_query", null, null);
373 |
374 | strcpy(buf.ptr, "v");
375 | strcat(buf.ptr, tinyfd_version.ptr);
376 | strcat(buf.ptr, "\n");
377 | if (mode)
378 | strcat(buf.ptr, "graphic mode: ");
379 | else
380 | strcat(buf.ptr, "console mode: ");
381 | strcat(buf.ptr, tinyfd_response.ptr);
382 | strcat(buf.ptr, "\n");
383 | strcat(buf.ptr, tinyfd_needs.ptr + 78);
384 | tinyfd_messageBox("Hello", buf.ptr, "ok", "info", 0);
385 | ---
386 | */
387 | char[1024] tinyfd_response = '\0';
388 |
389 | /**************************** IMPLEMENTATION ****************************/
390 |
391 | private:
392 | import core.stdc.ctype;
393 | import core.stdc.stdlib;
394 | import core.stdc.string;
395 |
396 | version (Windows)
397 | {
398 | import core.stdc.stdio;
399 | import core.stdc.wchar_;
400 | import core.sys.windows.commdlg;
401 | import core.sys.windows.w32api;
402 | import core.sys.windows.winbase;
403 | import core.sys.windows.wincon : GetConsoleMode, SetConsoleMode, GetConsoleWindow, ENABLE_ECHO_INPUT;
404 | import core.sys.windows.windef;
405 | import core.sys.windows.winnls;
406 |
407 | static assert(_WIN32_WINNT >= 0x0500);
408 |
409 | enum SLASH = "\\";
410 |
411 | int _getch();
412 | FILE* _popen(const char* command, const char* mode);
413 | int _pclose(FILE* stream);
414 |
415 | version (TINYFD_NOLIB)
416 | {
417 | bool gWarningDisplayed = true;
418 | }
419 | else
420 | {
421 | version = TINYFD_LIB;
422 | import core.sys.windows.com : COINIT_APARTMENTTHREADED;
423 | import core.sys.windows.wingdi : RGB, GetRValue, GetGValue, GetBValue;
424 | import core.sys.windows.winuser;
425 |
426 | pragma(lib, "comdlg32.lib");
427 | pragma(lib, "ole32.lib");
428 | pragma(lib, "user32.lib");
429 |
430 | FILE* _wfopen(const wchar* filename, const wchar* mode);
431 | int _wremove(const wchar* path);
432 | wchar* _wgetenv(const wchar* varname);
433 | // header versions are not nothrow @nogc
434 | extern(Windows) HRESULT CoInitializeEx(LPVOID, DWORD);
435 | extern(Windows) void CoUninitialize();
436 |
437 | bool gWarningDisplayed;
438 | }
439 |
440 | version (TINYFD_NOSELECTFOLDERWIN) {}
441 | else
442 | {
443 | version = TINYFD_SELECTFOLDERWIN;
444 | import core.sys.windows.shlobj : BFFM_INITIALIZED, BFFM_SETSELECTIONW, BIF_USENEWUI,
445 | BROWSEINFOW, LPCITEMIDLIST, LPITEMIDLIST, PBROWSEINFOW;
446 |
447 | extern(Windows) LPITEMIDLIST SHBrowseForFolderW(PBROWSEINFOW);
448 | extern(Windows) BOOL SHGetPathFromIDListW(LPCITEMIDLIST, LPWSTR);
449 | }
450 | }
451 | else
452 | {
453 | import core.stdc.limits;
454 | import core.sys.posix.dirent;
455 | import core.sys.posix.signal;
456 | import core.sys.posix.stdio;
457 | import core.sys.posix.sys.stat;
458 | import core.sys.posix.sys.utsname;
459 | import core.sys.posix.termios;
460 | import core.sys.posix.unistd;
461 |
462 | enum SLASH = "/";
463 | bool gWarningDisplayed;
464 | }
465 |
466 | enum int MAX_PATH_OR_CMD = 1024; /* _MAX_PATH or MAX_PATH */
467 | enum int MAX_MULTIPLE_FILES = 32;
468 | enum int MAX_PATTERNS = 8;
469 |
470 | immutable char[] gTitle = "missing software! (we will try basic console input)";
471 |
472 | bool some(const char* str)
473 | {
474 | return str && str[0] != '\0';
475 | }
476 |
477 | bool wsome(const wchar* str)
478 | {
479 | return str && str[0] != '\0';
480 | }
481 |
482 | bool eq(const char* s1, const char* s2)
483 | {
484 | if (s1 is s2)
485 | return true;
486 | if (s1 is null || s2 is null)
487 | return false;
488 | return strcmp(s1, s2) == 0;
489 | }
490 |
491 | char lastch(const char* str)
492 | {
493 | if (str)
494 | {
495 | size_t len = strlen(str);
496 | if (len > 0)
497 | return str[len - 1];
498 | }
499 | return '\0';
500 | }
501 |
502 | void removeLastNL(char* str)
503 | {
504 | size_t len = strlen(str);
505 | if (len > 0 && str[len - 1] == '\n')
506 | {
507 | str[len - 1] = '\0';
508 | }
509 | }
510 |
511 | void response(const char* message)
512 | {
513 | strcpy(tinyfd_response.ptr, message);
514 | }
515 |
516 | char* getPathWithoutFinalSlash(
517 | char* aoDestination, /* make sure it is allocated, use _MAX_PATH */
518 | const char* aSource) /* aoDestination and aSource can be the same */
519 | {
520 | const(char)* lTmp;
521 | if (aSource)
522 | {
523 | lTmp = strrchr(aSource, '/');
524 | if (!lTmp)
525 | {
526 | lTmp = strrchr(aSource, '\\');
527 | }
528 | if (lTmp)
529 | {
530 | strncpy(aoDestination, aSource, lTmp - aSource);
531 | aoDestination[lTmp - aSource] = '\0';
532 | }
533 | else
534 | {
535 | *aoDestination = '\0';
536 | }
537 | }
538 | else
539 | {
540 | *aoDestination = '\0';
541 | }
542 | return aoDestination;
543 | }
544 |
545 | char* getLastName(
546 | char* aoDestination, /* make sure it is allocated */
547 | const char* aSource)
548 | {
549 | /* copy the last name after '/' or '\' */
550 | const(char)* lTmp;
551 | if (aSource)
552 | {
553 | lTmp = strrchr(aSource, '/');
554 | if (!lTmp)
555 | {
556 | lTmp = strrchr(aSource, '\\');
557 | }
558 | if (lTmp)
559 | {
560 | strcpy(aoDestination, lTmp + 1);
561 | }
562 | else
563 | {
564 | strcpy(aoDestination, aSource);
565 | }
566 | }
567 | else
568 | {
569 | *aoDestination = '\0';
570 | }
571 | return aoDestination;
572 | }
573 |
574 | void ensureFinalSlash(char* aioString)
575 | {
576 | if (some(aioString))
577 | {
578 | char* lastcar = aioString + strlen(aioString) - 1;
579 | if (strncmp(lastcar, SLASH, 1))
580 | {
581 | strcat(lastcar, SLASH);
582 | }
583 | }
584 | }
585 |
586 | uint getValidPatterns(ref const TFD_Filter filter, out bool isMime, out const(char)*[MAX_PATTERNS] output)
587 | {
588 | uint len;
589 | foreach (pt; filter.patterns)
590 | {
591 | if (some(pt))
592 | {
593 | output[len] = pt;
594 | len++;
595 | if (len == MAX_PATTERNS)
596 | break;
597 | }
598 | }
599 | if (len == 0)
600 | return 0;
601 |
602 | uint mime;
603 | foreach (pt; output[0 .. len])
604 | {
605 | if (strchr(pt, '/'))
606 | mime++;
607 | else
608 | break;
609 | }
610 | if (mime > 0)
611 | {
612 | // forbid mixing of MIME and usual patterns
613 | if (mime != len)
614 | {
615 | isMime = false;
616 | output[0 .. len] = null;
617 | return 0;
618 | }
619 | isMime = true;
620 | }
621 | return len;
622 | }
623 |
624 | void Hex2RGB(const char* aHexRGB, ref ubyte[3] aoResultRGB)
625 | {
626 | char[8] lColorChannel = '\0';
627 | if (aHexRGB)
628 | {
629 | strcpy(lColorChannel.ptr, aHexRGB);
630 | aoResultRGB[2] = cast(ubyte)strtoul(lColorChannel.ptr + 5, null, 16);
631 | lColorChannel[5] = '\0';
632 | aoResultRGB[1] = cast(ubyte)strtoul(lColorChannel.ptr + 3, null, 16);
633 | lColorChannel[3] = '\0';
634 | aoResultRGB[0] = cast(ubyte)strtoul(lColorChannel.ptr + 1, null, 16);
635 | /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */
636 | }
637 | else
638 | {
639 | aoResultRGB[0] = 0;
640 | aoResultRGB[1] = 0;
641 | aoResultRGB[2] = 0;
642 | }
643 | }
644 |
645 | void RGB2Hex(const ubyte[3] aRGB, char* aoResultHexRGB)
646 | {
647 | if (aoResultHexRGB)
648 | {
649 | // NOTE: here was compiler ifdef
650 | sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]);
651 | /* printf("aoResultHexRGB %s\n", aoResultHexRGB); */
652 | }
653 | }
654 |
655 | void replaceSubStr(const char* aSource,
656 | const char* aOldSubStr,
657 | const char* aNewSubStr,
658 | char* aoDestination)
659 | {
660 | const(char)* pOccurence;
661 | const(char)* p;
662 | const(char)* lNewSubStr = "";
663 | size_t lOldSubLen = strlen(aOldSubStr);
664 |
665 | if (!aSource)
666 | {
667 | *aoDestination = '\0';
668 | return;
669 | }
670 | if (!aOldSubStr)
671 | {
672 | strcpy(aoDestination, aSource);
673 | return;
674 | }
675 | if (aNewSubStr)
676 | {
677 | lNewSubStr = aNewSubStr;
678 | }
679 | p = aSource;
680 | *aoDestination = '\0';
681 | while ((pOccurence = strstr(p, aOldSubStr)) !is null)
682 | {
683 | strncat(aoDestination, p, pOccurence - p);
684 | strcat(aoDestination, lNewSubStr);
685 | p = pOccurence + lOldSubLen;
686 | }
687 | strcat(aoDestination, p);
688 | }
689 |
690 | bool filenameValid(const char* aFileNameWithoutPath)
691 | {
692 | return some(aFileNameWithoutPath) && !strpbrk(aFileNameWithoutPath, "\\/:*?\"<>|");
693 | }
694 |
695 | void wipefile(const char* aFilename)
696 | {
697 | // file must exist beforehand
698 | FILE* lIn = fopen(aFilename, "w");
699 | if (lIn)
700 | {
701 | fseek(lIn, 0, SEEK_END);
702 | const size = ftell(lIn);
703 | rewind(lIn);
704 | foreach (i; 0 .. size)
705 | {
706 | fputc('A', lIn);
707 | }
708 | fclose(lIn);
709 | }
710 | }
711 |
712 | /* source and destination can be the same or ovelap*/
713 | const(char)* ensureFilesExist(char* aDestination, const char* aSourcePathsAndNames)
714 | {
715 | if (!some(aSourcePathsAndNames))
716 | return null;
717 |
718 | char* lDestination = aDestination;
719 | const(char)* p;
720 | const(char)* p2;
721 | size_t lLen = strlen(aSourcePathsAndNames);
722 |
723 | p = aSourcePathsAndNames;
724 | while ((p2 = strchr(p, '|')) !is null)
725 | {
726 | lLen = p2 - p;
727 | memmove(lDestination, p, lLen);
728 | lDestination[lLen] = '\0';
729 | if (fileExists(lDestination))
730 | {
731 | lDestination += lLen;
732 | *lDestination = '|';
733 | lDestination++;
734 | }
735 | p = p2 + 1;
736 | }
737 | if (fileExists(p))
738 | {
739 | lLen = strlen(p);
740 | memmove(lDestination, p, lLen);
741 | lDestination[lLen] = '\0';
742 | }
743 | else
744 | {
745 | *(lDestination - 1) = '\0';
746 | }
747 | return aDestination;
748 | }
749 |
750 | version (Windows)
751 | {
752 | bool fileExists(const char* aFilePathAndName)
753 | {
754 | if (!some(aFilePathAndName))
755 | return false;
756 |
757 | wchar* lTmpWChar = utf8to16(aFilePathAndName);
758 | DWORD attribs = GetFileAttributes(lTmpWChar);
759 | free(lTmpWChar);
760 | return !(attribs & FILE_ATTRIBUTE_DEVICE) && !(attribs & FILE_ATTRIBUTE_DIRECTORY);
761 | }
762 | }
763 | else // unix
764 | {
765 | bool fileExists(const char* aFilePathAndName)
766 | {
767 | if (!some(aFilePathAndName))
768 | return false;
769 |
770 | FILE* lIn = fopen(aFilePathAndName, "r");
771 | if (!lIn)
772 | return false;
773 | fclose(lIn);
774 | return true;
775 | }
776 | }
777 |
778 | version (Windows) {
779 |
780 | bool replaceChr(char* aString, const char aOldChr, const char aNewChr)
781 | {
782 | if (!aString)
783 | return false;
784 | if (aOldChr == aNewChr)
785 | return false;
786 |
787 | bool ret;
788 | char* p = aString;
789 | while ((p = strchr(p, aOldChr)) !is null)
790 | {
791 | *p = aNewChr;
792 | p++;
793 | ret = true;
794 | }
795 | return ret;
796 | }
797 |
798 | int sizeUtf16(const char* aUtf8string)
799 | {
800 | return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
801 | aUtf8string, -1, null, 0);
802 | }
803 |
804 | wchar* utf8to16(const char* str)
805 | {
806 | if (!some(str))
807 | return null;
808 |
809 | int lSize = sizeUtf16(str);
810 | wchar* ret = cast(wchar*)malloc(lSize * wchar.sizeof);
811 | lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
812 | str, -1, ret, lSize);
813 | if (lSize == 0)
814 | {
815 | free(ret);
816 | return null;
817 | }
818 | return ret;
819 | }
820 |
821 | bool dirExists(const char* aDirPath)
822 | {
823 | if (!some(aDirPath))
824 | return false;
825 | size_t lDirLen = strlen(aDirPath);
826 | if (lDirLen == 2 && aDirPath[1] == ':')
827 | return true;
828 |
829 | wchar* lTmpWChar = utf8to16(aDirPath);
830 | DWORD attribs = GetFileAttributes(lTmpWChar);
831 | free(lTmpWChar);
832 | return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
833 | }
834 |
835 | version (TINYFD_NOLIB) {
836 |
837 | void _beep()
838 | {
839 | printf("\a");
840 | }
841 |
842 | } else { // TINYFD_NOLIB
843 |
844 | void _beep()
845 | {
846 | Beep(440, 300);
847 | }
848 |
849 | void wipefileW(const wchar* aFilename)
850 | {
851 | // file must exist beforehand
852 | FILE* lIn = _wfopen(aFilename, "w");
853 | if (lIn)
854 | {
855 | fseek(lIn, 0, SEEK_END);
856 | const size = ftell(lIn);
857 | rewind(lIn);
858 | foreach (i; 0 .. size)
859 | {
860 | fputc('A', lIn);
861 | }
862 | fclose(lIn);
863 | }
864 | }
865 |
866 | wchar* getPathWithoutFinalSlashW(
867 | wchar* aoDestination, /* make sure it is allocated, use _MAX_PATH */
868 | const wchar* aSource) /* aoDestination and aSource can be the same */
869 | {
870 | const(wchar)* lTmp;
871 | if (aSource)
872 | {
873 | lTmp = wcsrchr(aSource, '/');
874 | if (!lTmp)
875 | {
876 | lTmp = wcsrchr(aSource, '\\');
877 | }
878 | if (lTmp)
879 | {
880 | wcsncpy(aoDestination, aSource, lTmp - aSource);
881 | aoDestination[lTmp - aSource] = '\0';
882 | }
883 | else
884 | {
885 | *aoDestination = '\0';
886 | }
887 | }
888 | else
889 | {
890 | *aoDestination = '\0';
891 | }
892 | return aoDestination;
893 | }
894 |
895 | wchar* getLastNameW(
896 | wchar* aoDestination, /* make sure it is allocated */
897 | const wchar* aSource)
898 | {
899 | /* copy the last name after '/' or '\' */
900 | const(wchar)* lTmp;
901 | if (aSource)
902 | {
903 | lTmp = wcsrchr(aSource, '/');
904 | if (!lTmp)
905 | {
906 | lTmp = wcsrchr(aSource, '\\');
907 | }
908 | if (lTmp)
909 | {
910 | wcscpy(aoDestination, lTmp + 1);
911 | }
912 | else
913 | {
914 | wcscpy(aoDestination, aSource);
915 | }
916 | }
917 | else
918 | {
919 | *aoDestination = '\0';
920 | }
921 | return aoDestination;
922 | }
923 |
924 | static if (!is(WC_ERR_INVALID_CHARS))
925 | /* undefined prior to Vista, so not yet in MINGW header file */
926 | enum DWORD WC_ERR_INVALID_CHARS = 0x00000080;
927 |
928 | int sizeUtf8(const wchar* aUtf16string)
929 | {
930 | return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
931 | aUtf16string, -1, null, 0, null, null);
932 | }
933 |
934 | char* utf16to8(const wchar* str)
935 | {
936 | if (!wsome(str))
937 | return null;
938 |
939 | int lSize = sizeUtf8(str);
940 | char* ret = cast(char*)malloc(lSize);
941 | lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
942 | str, -1, ret, lSize, null, null);
943 | if (lSize == 0)
944 | {
945 | free(ret);
946 | return null;
947 | }
948 | return ret;
949 | }
950 |
951 | bool replaceWchar(wchar* aString, const wchar aOldChr, const wchar aNewChr)
952 | {
953 | if (!aString)
954 | return false;
955 | if (aOldChr == aNewChr)
956 | return false;
957 |
958 | bool ret;
959 | wchar* p = aString;
960 | while ((p = wcsrchr(p, aOldChr)) !is null)
961 | {
962 | *p = aNewChr;
963 | version (TINYFD_NOCCSUNICODE)
964 | p++;
965 | p++;
966 | ret = true;
967 | }
968 | return ret;
969 | }
970 |
971 | bool willBeGui()
972 | {
973 | return (!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) &&
974 | (!getenv("SSH_CLIENT") || getenv("DISPLAY"));
975 | }
976 |
977 | extern (Windows) int EnumThreadWndProc(HWND hwnd, LPARAM lParam)
978 | {
979 | wchar[MAX_PATH] lTitleName = '\0';
980 | GetWindowTextW(hwnd, lTitleName.ptr, MAX_PATH);
981 | /* wprintf("lTitleName %ls \n", lTitleName.ptr); */
982 | if (wcscmp("tinyfiledialogsTopWindow", lTitleName.ptr) == 0)
983 | {
984 | SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
985 | return 0;
986 | }
987 | return 1;
988 | }
989 |
990 | void hiddenConsoleW(const wchar* aString, const wchar* aDialogTitle, const int aInFront)
991 | {
992 | if (!wsome(aString))
993 | return;
994 |
995 | STARTUPINFOW StartupInfo;
996 | PROCESS_INFORMATION ProcessInfo;
997 |
998 | StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
999 | StartupInfo.wShowWindow = SW_HIDE;
1000 |
1001 | if (!CreateProcessW(null, cast(LPWSTR)aString, null, null, FALSE,
1002 | CREATE_NEW_CONSOLE, null, null,
1003 | &StartupInfo, &ProcessInfo))
1004 | {
1005 | return; /* GetLastError(); */
1006 | }
1007 |
1008 | WaitForInputIdle(ProcessInfo.hProcess, INFINITE);
1009 | if (aInFront)
1010 | {
1011 | while (EnumWindows(&EnumThreadWndProc, 0))
1012 | {
1013 | }
1014 | SetWindowTextW(GetForegroundWindow(), aDialogTitle);
1015 | }
1016 | WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
1017 | CloseHandle(ProcessInfo.hThread);
1018 | CloseHandle(ProcessInfo.hProcess);
1019 | }
1020 |
1021 | int messageBoxWinGui(
1022 | const char* aTitle,
1023 | const char* aMessage,
1024 | const char* aDialogType,
1025 | const char* aIconType,
1026 | const int aDefaultButton)
1027 | {
1028 | wchar* lTitle = utf8to16(aTitle);
1029 | wchar* lMessage = utf8to16(aMessage);
1030 |
1031 | UINT aCode;
1032 |
1033 | if (eq("warning", aIconType))
1034 | {
1035 | aCode = MB_ICONWARNING;
1036 | }
1037 | else if (eq("error", aIconType))
1038 | {
1039 | aCode = MB_ICONERROR;
1040 | }
1041 | else if (eq("question", aIconType))
1042 | {
1043 | aCode = MB_ICONQUESTION;
1044 | }
1045 | else
1046 | {
1047 | aCode = MB_ICONINFORMATION;
1048 | }
1049 |
1050 | if (eq("okcancel", aDialogType))
1051 | {
1052 | aCode += MB_OKCANCEL;
1053 | if (!aDefaultButton)
1054 | {
1055 | aCode += MB_DEFBUTTON2;
1056 | }
1057 | }
1058 | else if (eq("yesno", aDialogType))
1059 | {
1060 | aCode += MB_YESNO;
1061 | if (!aDefaultButton)
1062 | {
1063 | aCode += MB_DEFBUTTON2;
1064 | }
1065 | }
1066 | else
1067 | {
1068 | aCode += MB_OK;
1069 | }
1070 |
1071 | aCode += MB_TOPMOST;
1072 |
1073 | int lBoxReturnValue = MessageBoxW(GetForegroundWindow(), lMessage, lTitle, aCode);
1074 | free(lTitle);
1075 | free(lMessage);
1076 |
1077 | if (eq("ok", aDialogType) || lBoxReturnValue == IDOK || lBoxReturnValue == IDYES)
1078 | return 1;
1079 | else
1080 | return 0;
1081 | }
1082 |
1083 | int notifyWinGui(
1084 | const char* aTitle,
1085 | const char* aMessage,
1086 | const char* aIconType)
1087 | {
1088 | wchar* lTitle = utf8to16(aTitle);
1089 | wchar* lMessage = utf8to16(aMessage);
1090 | wchar* lIconType = utf8to16(aIconType);
1091 |
1092 | size_t lTitleLen = lTitle ? wcslen(lTitle) : 0;
1093 | size_t lMessageLen = lMessage ? wcslen(lMessage) : 0;
1094 | size_t lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;
1095 | wchar* str = cast(wchar*)malloc(lDialogStringLen * wchar.sizeof);
1096 |
1097 | wcscpy(str, "powershell.exe -command \"" ~
1098 | "function Show-BalloonTip {" ~
1099 | "[cmdletbinding()] " ~
1100 | "param( " ~
1101 | "[string]$Title = ' ', " ~
1102 | "[string]$Message = ' ', " ~
1103 | "[ValidateSet('info', 'warning', 'error')] " ~
1104 | "[string]$IconType = 'info');" ~
1105 | "[system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; " ~
1106 | "$balloon = New-Object System.Windows.Forms.NotifyIcon ; " ~
1107 | "$path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; " ~
1108 | "$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;");
1109 |
1110 | wcscat(str,
1111 | "$balloon.Icon = $icon ; " ~
1112 | "$balloon.BalloonTipIcon = $IconType ; " ~
1113 | "$balloon.BalloonTipText = $Message ; " ~
1114 | "$balloon.BalloonTipTitle = $Title ; " ~
1115 | "$balloon.Text = 'lalala' ; " ~
1116 | "$balloon.Visible = $true ; " ~
1117 | "$balloon.ShowBalloonTip(5000)};" ~
1118 | "Show-BalloonTip");
1119 |
1120 | if (wsome(lTitle))
1121 | {
1122 | wcscat(str, " -Title '");
1123 | wcscat(str, lTitle);
1124 | wcscat(str, "'");
1125 | }
1126 | if (wsome(lMessage))
1127 | {
1128 | wcscat(str, " -Message '");
1129 | wcscat(str, lMessage);
1130 | wcscat(str, "'");
1131 | }
1132 | if (wsome(lIconType))
1133 | {
1134 | wcscat(str, " -IconType '");
1135 | wcscat(str, lIconType);
1136 | wcscat(str, "'");
1137 | }
1138 | wcscat(str, "\"");
1139 |
1140 | /* wprintf ( "str: %ls\n" , str ) ; */
1141 |
1142 | hiddenConsoleW(str, lTitle, 0);
1143 |
1144 | free(str);
1145 | free(lTitle);
1146 | free(lMessage);
1147 | free(lIconType);
1148 | return 1;
1149 | }
1150 |
1151 | const(wchar)* _inputBoxW(
1152 | const wchar* aTitle,
1153 | const wchar* aMessage,
1154 | const wchar* aDefaultInput)
1155 | {
1156 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0';
1157 |
1158 | size_t lTitleLen = aTitle ? wcslen(aTitle) : 0;
1159 | size_t lMessageLen = aMessage ? wcslen(aMessage) : 0;
1160 | size_t lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen;
1161 | wchar* str = cast(wchar*)malloc(lDialogStringLen * wchar.sizeof);
1162 |
1163 | wcscpy(str, _wgetenv("USERPROFILE"));
1164 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.");
1165 | wcscat(str, aDefaultInput ? "vbs" : "hta");
1166 |
1167 | FILE* lIn = _wfopen(str, "w");
1168 | if (!lIn)
1169 | {
1170 | free(str);
1171 | return null;
1172 | }
1173 |
1174 | if (aDefaultInput)
1175 | {
1176 | wcscpy(str, "Dim result:result=InputBox(\"");
1177 | if (wsome(aMessage))
1178 | {
1179 | wcscpy(lBuff.ptr, aMessage);
1180 | replaceWchar(lBuff.ptr, '\n', ' ');
1181 | wcscat(str, lBuff.ptr);
1182 | }
1183 | wcscat(str, "\",\"tinyfiledialogsTopWindow\",\"");
1184 | if (wsome(aDefaultInput))
1185 | {
1186 | wcscpy(lBuff.ptr, aDefaultInput);
1187 | replaceWchar(lBuff.ptr, '\n', ' ');
1188 | wcscat(str, lBuff.ptr);
1189 | }
1190 | wcscat(str, "\"):If IsEmpty(result) then:WScript.Echo 0");
1191 | wcscat(str, ":Else: WScript.Echo \"1\" & result : End If");
1192 | }
1193 | else
1194 | {
1195 | wcscpy(str, `
1196 |
1197 |
1198 | `);
1199 |
1200 | wcscat(str, "tinyfiledialogsTopWindow");
1201 | wcscat(str, `
1202 |
1211 |
1212 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 | `);
1264 |
1265 | wcscat(str, aMessage ? aMessage : "");
1266 |
1267 | wcscat(str, `
1268 | |
1269 |
1270 |
1276 | |
1277 |
1278 |
1279 | `);
1280 |
1281 | wcscat(str, `
1289 |
1290 |
1291 | `);
1292 | }
1293 | fputws(str, lIn);
1294 | fclose(lIn);
1295 |
1296 | if (aDefaultInput)
1297 | {
1298 | wcscpy(str, _wgetenv("USERPROFILE"));
1299 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.txt");
1300 |
1301 | version (TINYFD_NOCCSUNICODE) {
1302 | FILE* lFile = _wfopen(str, "w");
1303 | fputc(0xFF, lFile);
1304 | fputc(0xFE, lFile);
1305 | } else {
1306 | FILE* lFile = _wfopen(str, "wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/
1307 | }
1308 | fclose(lFile);
1309 |
1310 | wcscpy(str, "cmd.exe /c cscript.exe //U //Nologo ");
1311 | wcscat(str, "\"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs\" ");
1312 | wcscat(str, ">> \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.txt\"");
1313 | }
1314 | else
1315 | {
1316 | wcscpy(str, "cmd.exe /c mshta.exe \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta\"");
1317 | }
1318 |
1319 | /* wprintf ( "str: %ls\n" , str ) ; */
1320 |
1321 | hiddenConsoleW(str, aTitle, 1);
1322 |
1323 | wcscpy(str, _wgetenv("USERPROFILE"));
1324 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.txt");
1325 | /* wprintf("str: %ls\n", str); */
1326 | version (TINYFD_NOCCSUNICODE)
1327 | const wchar* mode = "r";
1328 | else
1329 | const wchar* mode = "rt, ccs=UNICODE"; /*or ccs=UTF-16LE*/
1330 | lIn = _wfopen(str, mode);
1331 | if (!lIn)
1332 | {
1333 | _wremove(str);
1334 | free(str);
1335 | return null;
1336 | }
1337 |
1338 | lBuff = '\0';
1339 |
1340 | version (TINYFD_NOCCSUNICODE) {
1341 | fgets(cast(char*)lBuff.ptr, 2 * MAX_PATH_OR_CMD, lIn);
1342 | } else {
1343 | fgetws(lBuff.ptr, MAX_PATH_OR_CMD, lIn);
1344 | }
1345 | fclose(lIn);
1346 | wipefileW(str);
1347 | _wremove(str);
1348 |
1349 | wcscpy(str, _wgetenv("USERPROFILE"));
1350 | wcscat(str, "\\AppData\\Local\\Temp\\tinyfd.");
1351 | wcscat(str, aDefaultInput ? "vbs" : "hta");
1352 | _wremove(str);
1353 | free(str);
1354 | /* wprintf( "lBuff: %ls\n" , lBuff ) ; */
1355 | version (TINYFD_NOCCSUNICODE) {
1356 | int lResult = !wcsncmp(lBuff.ptr + 1, "1", 1);
1357 | } else {
1358 | int lResult = !wcsncmp(lBuff.ptr, "1", 1);
1359 | }
1360 |
1361 | /* printf( "lResult: %d \n" , lResult ) ; */
1362 | if (!lResult)
1363 | return null;
1364 |
1365 | /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */
1366 |
1367 | version (TINYFD_NOCCSUNICODE) {
1368 |
1369 | if (aDefaultInput)
1370 | {
1371 | lDialogStringLen = wcslen(lBuff.ptr);
1372 | assert(lDialogStringLen >= 2);
1373 | lBuff[lDialogStringLen - 1] = '\0';
1374 | lBuff[lDialogStringLen - 2] = '\0';
1375 | }
1376 | return lBuff.ptr + 2;
1377 |
1378 | } else {
1379 |
1380 | if (aDefaultInput)
1381 | {
1382 | lDialogStringLen = wcslen(lBuff.ptr);
1383 | assert(lDialogStringLen > 0);
1384 | lBuff[lDialogStringLen - 1] = '\0';
1385 | }
1386 | return lBuff.ptr + 1;
1387 | }
1388 | }
1389 |
1390 | const(char)* inputBoxWinGui(
1391 | char* aoBuff,
1392 | const char* aTitle,
1393 | const char* aMessage,
1394 | const char* aDefaultInput)
1395 | {
1396 | wchar* lTitle = utf8to16(aTitle);
1397 | wchar* lMessage = utf8to16(aMessage);
1398 | wchar* lDefaultInput = utf8to16(aDefaultInput);
1399 |
1400 | const(wchar)* lTmpWChar = _inputBoxW(lTitle, lMessage, lDefaultInput);
1401 |
1402 | free(lTitle);
1403 | free(lMessage);
1404 | free(lDefaultInput);
1405 |
1406 | if (!lTmpWChar)
1407 | return null;
1408 |
1409 | char* lTmpChar = utf16to8(lTmpWChar);
1410 | strcpy(aoBuff, lTmpChar);
1411 | free(lTmpChar);
1412 |
1413 | return aoBuff;
1414 | }
1415 |
1416 | wchar* buildFilterString(const TFD_Filter[] filters)
1417 | {
1418 | bool isMime;
1419 | const(char)*[MAX_PATTERNS] patterns;
1420 | char[MAX_PATH_OR_CMD] str = '\0';
1421 | char[MAX_PATH_OR_CMD] patternsStr = '\0';
1422 |
1423 | foreach (filter; filters)
1424 | {
1425 | const plen = getValidPatterns(filter, isMime, patterns);
1426 | if (plen == 0 || isMime)
1427 | continue;
1428 |
1429 | patternsStr[0] = '\0';
1430 | foreach (i, pt; patterns[0 .. plen])
1431 | {
1432 | if (i != 0)
1433 | strcat(patternsStr.ptr, ";");
1434 | strcat(patternsStr.ptr, pt);
1435 | }
1436 |
1437 | strcat(str.ptr, some(filter.description) ? filter.description : patternsStr.ptr);
1438 | strcat(str.ptr, "\n");
1439 | strcat(str.ptr, patternsStr.ptr);
1440 | strcat(str.ptr, "\n");
1441 | }
1442 | strcat(str.ptr, "All Files\n*.*\n");
1443 |
1444 | wchar* ret = utf8to16(str.ptr);
1445 | if (!ret)
1446 | return null;
1447 |
1448 | wchar* p = ret;
1449 | while ((p = wcschr(p, '\n')) !is null)
1450 | {
1451 | *p = '\0';
1452 | p++;
1453 | }
1454 | return ret;
1455 | }
1456 |
1457 | const(char)* saveFileDialogWinGui(
1458 | char* aoBuff,
1459 | const char* aTitle,
1460 | const char* aDefaultPathAndFile,
1461 | const TFD_Filter[] aFilters)
1462 | {
1463 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0';
1464 | wchar[MAX_PATH_OR_CMD] lDirname = '\0';
1465 |
1466 | wchar* lTitle = utf8to16(aTitle);
1467 | wchar* lDefaultPathAndFile = utf8to16(aDefaultPathAndFile);
1468 | wchar* lFilterStr = buildFilterString(aFilters);
1469 |
1470 | getPathWithoutFinalSlashW(lDirname.ptr, lDefaultPathAndFile);
1471 | getLastNameW(lBuff.ptr, lDefaultPathAndFile);
1472 |
1473 | HRESULT lHResult = CoInitializeEx(null, 0);
1474 |
1475 | OPENFILENAMEW ofn = {0};
1476 | ofn.lStructSize = OPENFILENAMEW.sizeof;
1477 | ofn.hwndOwner = GetForegroundWindow();
1478 | ofn.hInstance = null;
1479 | ofn.lpstrFilter = lFilterStr;
1480 | ofn.lpstrCustomFilter = null;
1481 | ofn.nMaxCustFilter = 0;
1482 | ofn.nFilterIndex = 1;
1483 | ofn.lpstrFile = lBuff.ptr;
1484 |
1485 | ofn.nMaxFile = MAX_PATH_OR_CMD;
1486 | ofn.lpstrFileTitle = null;
1487 | ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;
1488 | ofn.lpstrInitialDir = wsome(lDirname.ptr) ? lDirname.ptr : null;
1489 | ofn.lpstrTitle = wsome(lTitle) ? lTitle : null;
1490 | ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
1491 | ofn.nFileOffset = 0;
1492 | ofn.nFileExtension = 0;
1493 | ofn.lpstrDefExt = null;
1494 | ofn.lCustData = 0;
1495 | ofn.lpfnHook = null;
1496 | ofn.lpTemplateName = null;
1497 |
1498 | wchar* lTmpWChar;
1499 | if (GetSaveFileNameW(&ofn) == 0)
1500 | {
1501 | lTmpWChar = null;
1502 | }
1503 | else
1504 | {
1505 | lTmpWChar = lBuff.ptr;
1506 | }
1507 |
1508 | if (lHResult == S_OK || lHResult == S_FALSE)
1509 | {
1510 | CoUninitialize();
1511 | }
1512 |
1513 | free(lTitle);
1514 | free(lDefaultPathAndFile);
1515 | free(lFilterStr);
1516 |
1517 | if (!lTmpWChar)
1518 | return null;
1519 |
1520 | char* lTmpChar = utf16to8(lTmpWChar);
1521 | strcpy(aoBuff, lTmpChar);
1522 | free(lTmpChar);
1523 |
1524 | return aoBuff;
1525 | }
1526 |
1527 | const(char)* openFileDialogWinGui(
1528 | char* aoBuff,
1529 | const char* aTitle,
1530 | const char* aDefaultPathAndFile,
1531 | const TFD_Filter[] aFilters,
1532 | const bool aAllowMultipleSelects)
1533 | {
1534 | static wchar[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0';
1535 | wchar[MAX_PATH_OR_CMD] lDirname = '\0';
1536 |
1537 | wchar* lTitle = utf8to16(aTitle);
1538 | wchar* lDefaultPathAndFile = utf8to16(aDefaultPathAndFile);
1539 | wchar* lFilterStr = buildFilterString(aFilters);
1540 |
1541 | getPathWithoutFinalSlashW(lDirname.ptr, lDefaultPathAndFile);
1542 | getLastNameW(lBuff.ptr, lDefaultPathAndFile);
1543 |
1544 | HRESULT lHResult = CoInitializeEx(null, 0);
1545 |
1546 | OPENFILENAMEW ofn = {0};
1547 | ofn.lStructSize = OPENFILENAME.sizeof;
1548 | ofn.hwndOwner = GetForegroundWindow();
1549 | ofn.hInstance = null;
1550 | ofn.lpstrFilter = lFilterStr;
1551 | ofn.lpstrCustomFilter = null;
1552 | ofn.nMaxCustFilter = 0;
1553 | ofn.nFilterIndex = 1;
1554 | ofn.lpstrFile = lBuff.ptr;
1555 | ofn.nMaxFile = MAX_PATH_OR_CMD;
1556 | ofn.lpstrFileTitle = null;
1557 | ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2;
1558 | ofn.lpstrInitialDir = wsome(lDirname.ptr) ? lDirname.ptr : null;
1559 | ofn.lpstrTitle = wsome(lTitle) ? lTitle : null;
1560 | ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
1561 | ofn.nFileOffset = 0;
1562 | ofn.nFileExtension = 0;
1563 | ofn.lpstrDefExt = null;
1564 | ofn.lCustData = 0;
1565 | ofn.lpfnHook = null;
1566 | ofn.lpTemplateName = null;
1567 |
1568 | if (aAllowMultipleSelects)
1569 | {
1570 | ofn.Flags |= OFN_ALLOWMULTISELECT;
1571 | }
1572 |
1573 | size_t[MAX_MULTIPLE_FILES] lLengths;
1574 | wchar*[MAX_MULTIPLE_FILES] lPointers;
1575 | wchar* lTmpWChar;
1576 | if (GetOpenFileNameW(&ofn) == 0)
1577 | {
1578 | lTmpWChar = null;
1579 | }
1580 | else
1581 | {
1582 | size_t lBuffLen = wcslen(lBuff.ptr);
1583 | lPointers[0] = lBuff.ptr + lBuffLen + 1;
1584 | if (!aAllowMultipleSelects || (lPointers[0][0] == '\0'))
1585 | {
1586 | lTmpWChar = lBuff.ptr;
1587 | }
1588 | else
1589 | {
1590 | int i;
1591 | do
1592 | {
1593 | lLengths[i] = wcslen(lPointers[i]);
1594 | lPointers[i + 1] = lPointers[i] + lLengths[i] + 1;
1595 | i++;
1596 | } while (lPointers[i][0] != '\0');
1597 | i--;
1598 | wchar* p = lBuff.ptr + MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD - 1;
1599 | *p = '\0';
1600 | for (int j = i; j >= 0; j--)
1601 | {
1602 | p -= lLengths[j];
1603 | memmove(p, lPointers[j], lLengths[j] * wchar.sizeof);
1604 | p--;
1605 | *p = '\\';
1606 | p -= lBuffLen;
1607 | memmove(p, lBuff.ptr, lBuffLen * wchar.sizeof);
1608 | p--;
1609 | *p = '|';
1610 | }
1611 | p++;
1612 | lTmpWChar = p;
1613 | }
1614 | }
1615 |
1616 | if (lHResult == S_OK || lHResult == S_FALSE)
1617 | {
1618 | CoUninitialize();
1619 | }
1620 |
1621 | free(lTitle);
1622 | free(lDefaultPathAndFile);
1623 | free(lFilterStr);
1624 |
1625 | if (!lTmpWChar)
1626 | return null;
1627 |
1628 | char* lTmpChar = utf16to8(lTmpWChar);
1629 | strcpy(aoBuff, lTmpChar);
1630 | free(lTmpChar);
1631 |
1632 | return aoBuff;
1633 | }
1634 |
1635 | version (TINYFD_SELECTFOLDERWIN) {
1636 |
1637 | extern (Windows) int BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
1638 | {
1639 | if (uMsg == BFFM_INITIALIZED)
1640 | {
1641 | SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, cast(LPARAM)pData);
1642 | }
1643 | return 0;
1644 | }
1645 |
1646 | const(char)* selectFolderDialogWinGui(
1647 | char* aoBuff,
1648 | const char* aTitle,
1649 | const char* aDefaultPath)
1650 | {
1651 | static wchar[MAX_PATH_OR_CMD] lBuff = '\0';
1652 |
1653 | wchar* lTitle = utf8to16(aTitle);
1654 | wchar* lDefaultPath = utf8to16(aDefaultPath);
1655 |
1656 | HRESULT lHResult = CoInitializeEx(null, COINIT_APARTMENTTHREADED);
1657 |
1658 | BROWSEINFOW bInfo;
1659 | bInfo.hwndOwner = GetForegroundWindow();
1660 | bInfo.pidlRoot = null;
1661 | bInfo.pszDisplayName = lBuff.ptr;
1662 | bInfo.lpszTitle = wsome(lTitle) ? lTitle : null;
1663 | if (lHResult == S_OK || lHResult == S_FALSE)
1664 | {
1665 | bInfo.ulFlags = BIF_USENEWUI;
1666 | }
1667 | bInfo.lpfn = &BrowseCallbackProcW;
1668 | bInfo.lParam = cast(LPARAM)lDefaultPath;
1669 | bInfo.iImage = -1;
1670 |
1671 | LPITEMIDLIST lpItem = SHBrowseForFolderW(&bInfo);
1672 | if (lpItem)
1673 | {
1674 | SHGetPathFromIDListW(lpItem, lBuff.ptr);
1675 | }
1676 |
1677 | if (lHResult == S_OK || lHResult == S_FALSE)
1678 | {
1679 | CoUninitialize();
1680 | }
1681 |
1682 | char* lTmpChar = utf16to8(lBuff.ptr);
1683 | if (lTmpChar)
1684 | {
1685 | strcpy(aoBuff, lTmpChar);
1686 | free(lTmpChar);
1687 | }
1688 | free(lTitle);
1689 | free(lDefaultPath);
1690 | return aoBuff;
1691 | }
1692 |
1693 | } // TINYFD_SELECTFOLDERWIN
1694 |
1695 | const(char)* colorChooserWinGui(
1696 | const char* aTitle,
1697 | const char* aDefaultHexRGB,
1698 | ref const ubyte[3] aDefaultRGB,
1699 | ref ubyte[3] aoResultRGB)
1700 | {
1701 | static char[8] lResultHexRGB = '\0';
1702 | ubyte[3] lDefaultRGB = aDefaultRGB;
1703 |
1704 | HRESULT lHResult = CoInitializeEx(null, 0);
1705 |
1706 | if (aDefaultHexRGB)
1707 | {
1708 | Hex2RGB(aDefaultHexRGB, lDefaultRGB);
1709 | }
1710 |
1711 | CHOOSECOLORW cc;
1712 | COLORREF[16] crCustColors;
1713 | /* we can't use aTitle */
1714 | cc.lStructSize = CHOOSECOLOR.sizeof;
1715 | cc.hwndOwner = GetForegroundWindow();
1716 | cc.hInstance = null;
1717 | cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
1718 | cc.lpCustColors = crCustColors.ptr;
1719 | cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR;
1720 | cc.lCustData = 0;
1721 | cc.lpfnHook = null;
1722 | cc.lpTemplateName = null;
1723 |
1724 | const(char)* ret;
1725 | if (ChooseColorW(&cc))
1726 | {
1727 | aoResultRGB[0] = GetRValue(cc.rgbResult);
1728 | aoResultRGB[1] = GetGValue(cc.rgbResult);
1729 | aoResultRGB[2] = GetBValue(cc.rgbResult);
1730 | RGB2Hex(aoResultRGB, lResultHexRGB.ptr);
1731 | ret = lResultHexRGB.ptr;
1732 | }
1733 |
1734 | if (lHResult == S_OK || lHResult == S_FALSE)
1735 | {
1736 | CoUninitialize();
1737 | }
1738 | return ret;
1739 | }
1740 |
1741 | } // TINYFD_NOLIB
1742 |
1743 | int dialogPresent()
1744 | {
1745 | static int ret = -1;
1746 | if (ret < 0)
1747 | {
1748 | FILE* lIn = _popen("where dialog.exe", "r");
1749 | if (!lIn)
1750 | {
1751 | ret = 0;
1752 | return 0;
1753 | }
1754 |
1755 | char[MAX_PATH_OR_CMD] lBuff = '\0';
1756 | const char* lString = "dialog.exe";
1757 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
1758 | {
1759 | }
1760 | _pclose(lIn);
1761 | removeLastNL(lBuff.ptr);
1762 | if (strcmp(lBuff.ptr + strlen(lBuff.ptr) - strlen(lString), lString))
1763 | ret = 0;
1764 | else
1765 | ret = 1;
1766 | }
1767 | return ret;
1768 | }
1769 |
1770 | int messageBoxWinConsole(
1771 | const char* aTitle,
1772 | const char* aMessage,
1773 | const char* aDialogType,
1774 | const char* aIconType,
1775 | const int aDefaultButton)
1776 | {
1777 | char[MAX_PATH_OR_CMD] str_buf = '\0';
1778 | char[MAX_PATH_OR_CMD] lDialogFile_buf = '\0';
1779 | char* str = str_buf.ptr;
1780 | char* lDialogFile = lDialogFile_buf.ptr;
1781 | FILE* lIn;
1782 | char[MAX_PATH_OR_CMD] lBuff = '\0';
1783 |
1784 | strcpy(str, "dialog ");
1785 | if (some(aTitle))
1786 | {
1787 | strcat(str, "--title \"");
1788 | strcat(str, aTitle);
1789 | strcat(str, "\" ");
1790 | }
1791 |
1792 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType))
1793 | {
1794 | strcat(str, "--backtitle \"");
1795 | strcat(str, "tab: move focus");
1796 | strcat(str, "\" ");
1797 | }
1798 |
1799 | if (eq("okcancel", aDialogType))
1800 | {
1801 | if (!aDefaultButton)
1802 | {
1803 | strcat(str, "--defaultno ");
1804 | }
1805 | strcat(str,
1806 | "--yes-label \"Ok\" --no-label \"Cancel\" --yesno ");
1807 | }
1808 | else if (eq("yesno", aDialogType))
1809 | {
1810 | if (!aDefaultButton)
1811 | {
1812 | strcat(str, "--defaultno ");
1813 | }
1814 | strcat(str, "--yesno ");
1815 | }
1816 | else if (eq("yesnocancel", aDialogType))
1817 | {
1818 | if (!aDefaultButton)
1819 | {
1820 | strcat(str, "--defaultno ");
1821 | }
1822 | strcat(str, "--menu ");
1823 | }
1824 | else
1825 | {
1826 | strcat(str, "--msgbox ");
1827 | }
1828 |
1829 | strcat(str, "\"");
1830 | if (some(aMessage))
1831 | {
1832 | replaceSubStr(aMessage, "\n", "\\n", lBuff.ptr);
1833 | strcat(str, lBuff.ptr);
1834 | lBuff[0] = '\0';
1835 | }
1836 | strcat(str, "\" ");
1837 |
1838 | if (eq("yesnocancel", aDialogType))
1839 | {
1840 | strcat(str, "0 60 0 Yes \"\" No \"\"");
1841 | strcat(str, "2>>");
1842 | }
1843 | else
1844 | {
1845 | strcat(str, "10 60");
1846 | strcat(str, " && echo 1 > ");
1847 | }
1848 |
1849 | strcpy(lDialogFile, getenv("USERPROFILE"));
1850 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
1851 | strcat(str, lDialogFile);
1852 |
1853 | /*if (tinyfd_verbose) printf( "str: %s\n" , str ) ;*/
1854 | system(str);
1855 |
1856 | lIn = fopen(lDialogFile, "r");
1857 | if (!lIn)
1858 | {
1859 | remove(lDialogFile);
1860 | return 0;
1861 | }
1862 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
1863 | {
1864 | }
1865 | fclose(lIn);
1866 | remove(lDialogFile);
1867 | removeLastNL(lBuff.ptr);
1868 |
1869 | /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff.ptr); */
1870 | if (!some(lBuff.ptr))
1871 | {
1872 | return 0;
1873 | }
1874 |
1875 | if (eq("yesnocancel", aDialogType))
1876 | {
1877 | if (lBuff[0] == 'Y')
1878 | return 1;
1879 | else
1880 | return 2;
1881 | }
1882 |
1883 | return 1;
1884 | }
1885 |
1886 | const(char)* inputBoxWinConsole(
1887 | char* aoBuff,
1888 | const char* aTitle,
1889 | const char* aMessage,
1890 | const char* aDefaultInput)
1891 | {
1892 | char[MAX_PATH_OR_CMD] str_buf = '\0';
1893 | char[MAX_PATH_OR_CMD] lDialogFile_buf = '\0';
1894 | char* str = str_buf.ptr;
1895 | char* lDialogFile = lDialogFile_buf.ptr;
1896 | FILE* lIn;
1897 | int lResult;
1898 |
1899 | strcpy(lDialogFile, getenv("USERPROFILE"));
1900 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
1901 | strcpy(str, "echo|set /p=1 >");
1902 | strcat(str, lDialogFile);
1903 | strcat(str, " & ");
1904 |
1905 | strcat(str, "dialog ");
1906 | if (some(aTitle))
1907 | {
1908 | strcat(str, "--title \"");
1909 | strcat(str, aTitle);
1910 | strcat(str, "\" ");
1911 | }
1912 |
1913 | strcat(str, "--backtitle \"");
1914 | strcat(str, "tab: move focus");
1915 | if (!aDefaultInput)
1916 | {
1917 | strcat(str, " (sometimes nothing, no blink nor star, is shown in text field)");
1918 | }
1919 |
1920 | strcat(str, "\" ");
1921 |
1922 | if (!aDefaultInput)
1923 | {
1924 | strcat(str, "--insecure --passwordbox");
1925 | }
1926 | else
1927 | {
1928 | strcat(str, "--inputbox");
1929 | }
1930 | strcat(str, " \"");
1931 | if (some(aMessage))
1932 | {
1933 | strcat(str, aMessage);
1934 | }
1935 | strcat(str, "\" 10 60 ");
1936 | if (some(aDefaultInput))
1937 | {
1938 | strcat(str, "\"");
1939 | strcat(str, aDefaultInput);
1940 | strcat(str, "\" ");
1941 | }
1942 |
1943 | strcat(str, "2>>");
1944 | strcpy(lDialogFile, getenv("USERPROFILE"));
1945 | strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
1946 | strcat(str, lDialogFile);
1947 | strcat(str, " || echo 0 > ");
1948 | strcat(str, lDialogFile);
1949 |
1950 | /* printf( "str: %s\n" , str ) ; */
1951 | system(str);
1952 |
1953 | lIn = fopen(lDialogFile, "r");
1954 | if (!lIn)
1955 | {
1956 | remove(lDialogFile);
1957 | return null;
1958 | }
1959 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null)
1960 | {
1961 | }
1962 | fclose(lIn);
1963 |
1964 | wipefile(lDialogFile);
1965 | remove(lDialogFile);
1966 | removeLastNL(aoBuff);
1967 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */
1968 |
1969 | /* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */
1970 | lResult = strncmp(aoBuff, "1", 1) ? 0 : 1;
1971 | /* printf( "lResult: %d \n" , lResult ) ; */
1972 | if (!lResult)
1973 | return null;
1974 | /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */
1975 | return aoBuff + 3;
1976 | }
1977 |
1978 | const(char)* saveFileDialogWinConsole(
1979 | char* aoBuff,
1980 | const char* aTitle,
1981 | const char* aDefaultPathAndFile)
1982 | {
1983 | char[MAX_PATH_OR_CMD] str_buf = '\0';
1984 | char[MAX_PATH_OR_CMD] lPathAndFile_buf = '\0';
1985 | char* str = str_buf.ptr;
1986 | char* lPathAndFile = lPathAndFile_buf.ptr;
1987 | FILE* lIn;
1988 |
1989 | strcpy(str, "dialog ");
1990 | if (some(aTitle))
1991 | {
1992 | strcat(str, "--title \"");
1993 | strcat(str, aTitle);
1994 | strcat(str, "\" ");
1995 | }
1996 |
1997 | strcat(str, "--backtitle \"");
1998 | strcat(str,
1999 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
2000 | strcat(str, "\" ");
2001 |
2002 | strcat(str, "--fselect \"");
2003 | if (some(aDefaultPathAndFile))
2004 | {
2005 | /* dialog.exe uses unix separators even on windows */
2006 | strcpy(lPathAndFile, aDefaultPathAndFile);
2007 | replaceChr(lPathAndFile, '\\', '/');
2008 | }
2009 |
2010 | /* dialog.exe needs at least one separator */
2011 | if (!strchr(lPathAndFile, '/'))
2012 | {
2013 | strcat(str, "./");
2014 | }
2015 | strcat(str, lPathAndFile);
2016 | strcat(str, "\" 0 60 2>");
2017 | strcpy(lPathAndFile, getenv("USERPROFILE"));
2018 | strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt");
2019 | strcat(str, lPathAndFile);
2020 |
2021 | /* printf( "str: %s\n" , str ) ; */
2022 | system(str);
2023 |
2024 | lIn = fopen(lPathAndFile, "r");
2025 | if (!lIn)
2026 | {
2027 | remove(lPathAndFile);
2028 | return null;
2029 | }
2030 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null)
2031 | {
2032 | }
2033 | fclose(lIn);
2034 | remove(lPathAndFile);
2035 | replaceChr(aoBuff, '/', '\\');
2036 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2037 | getLastName(str, aoBuff);
2038 | if (!some(str))
2039 | return null;
2040 | return aoBuff;
2041 | }
2042 |
2043 | const(char)* openFileDialogWinConsole(
2044 | char* aoBuff,
2045 | const char* aTitle,
2046 | const char* aDefaultPathAndFile,
2047 | const bool aAllowMultipleSelects)
2048 | {
2049 | char[MAX_PATH_OR_CMD] lFilterPatterns = '\0';
2050 | char[MAX_PATH_OR_CMD] str_buf = '\0';
2051 | char* str = str_buf.ptr;
2052 | FILE* lIn;
2053 |
2054 | strcpy(str, "dialog ");
2055 | if (some(aTitle))
2056 | {
2057 | strcat(str, "--title \"");
2058 | strcat(str, aTitle);
2059 | strcat(str, "\" ");
2060 | }
2061 |
2062 | strcat(str, "--backtitle \"");
2063 | strcat(str,
2064 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
2065 | strcat(str, "\" ");
2066 |
2067 | strcat(str, "--fselect \"");
2068 | if (some(aDefaultPathAndFile))
2069 | {
2070 | /* dialog.exe uses unix separators even on windows */
2071 | strcpy(lFilterPatterns.ptr, aDefaultPathAndFile);
2072 | replaceChr(lFilterPatterns.ptr, '\\', '/');
2073 | }
2074 |
2075 | /* dialog.exe needs at least one separator */
2076 | if (!strchr(lFilterPatterns.ptr, '/'))
2077 | {
2078 | strcat(str, "./");
2079 | }
2080 | strcat(str, lFilterPatterns.ptr);
2081 | strcat(str, "\" 0 60 2>");
2082 | strcpy(lFilterPatterns.ptr, getenv("USERPROFILE"));
2083 | strcat(lFilterPatterns.ptr, "\\AppData\\Local\\Temp\\tinyfd.txt");
2084 | strcat(str, lFilterPatterns.ptr);
2085 |
2086 | /* printf( "str: %s\n" , str ) ; */
2087 | system(str);
2088 |
2089 | lIn = fopen(lFilterPatterns.ptr, "r");
2090 | if (!lIn)
2091 | {
2092 | remove(lFilterPatterns.ptr);
2093 | return null;
2094 | }
2095 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null)
2096 | {
2097 | }
2098 | fclose(lIn);
2099 | remove(lFilterPatterns.ptr);
2100 | replaceChr(aoBuff, '/', '\\');
2101 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2102 | return aoBuff;
2103 | }
2104 |
2105 | const(char)* selectFolderDialogWinConsole(
2106 | char* aoBuff,
2107 | const char* aTitle,
2108 | const char* aDefaultPath)
2109 | {
2110 | char[MAX_PATH_OR_CMD] str_buf = '\0';
2111 | char[MAX_PATH_OR_CMD] lString_buf = '\0';
2112 | char* str = str_buf.ptr;
2113 | char* lString = lString_buf.ptr;
2114 | FILE* lIn;
2115 |
2116 | strcpy(str, "dialog ");
2117 | if (some(aTitle))
2118 | {
2119 | strcat(str, "--title \"");
2120 | strcat(str, aTitle);
2121 | strcat(str, "\" ");
2122 | }
2123 |
2124 | strcat(str, "--backtitle \"");
2125 | strcat(str,
2126 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
2127 | strcat(str, "\" ");
2128 |
2129 | strcat(str, "--dselect \"");
2130 | if (some(aDefaultPath))
2131 | {
2132 | /* dialog.exe uses unix separators even on windows */
2133 | strcpy(lString, aDefaultPath);
2134 | ensureFinalSlash(lString);
2135 | replaceChr(lString, '\\', '/');
2136 | strcat(str, lString);
2137 | }
2138 | else
2139 | {
2140 | /* dialog.exe needs at least one separator */
2141 | strcat(str, "./");
2142 | }
2143 | strcat(str, "\" 0 60 2>");
2144 | strcpy(lString, getenv("USERPROFILE"));
2145 | strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt");
2146 | strcat(str, lString);
2147 |
2148 | /* printf( "str: %s\n" , str ) ; */
2149 | system(str);
2150 |
2151 | lIn = fopen(lString, "r");
2152 | if (!lIn)
2153 | {
2154 | remove(lString);
2155 | return null;
2156 | }
2157 | while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) !is null)
2158 | {
2159 | }
2160 | fclose(lIn);
2161 | remove(lString);
2162 | replaceChr(aoBuff, '/', '\\');
2163 | /* printf( "aoBuff: %s\n" , aoBuff ) ; */
2164 | return aoBuff;
2165 | }
2166 |
2167 | int _messageBox(
2168 | const char* aTitle,
2169 | const char* aMessage,
2170 | const char* aDialogType,
2171 | const char* aIconType,
2172 | int aDefaultButton)
2173 | {
2174 | char lChar;
2175 | const bool lQuery = eq(aTitle, "tinyfd_query");
2176 |
2177 | version (TINYFD_LIB) {
2178 |
2179 | if (willBeGui())
2180 | {
2181 | if (lQuery)
2182 | {
2183 | response("windows");
2184 | return 1;
2185 | }
2186 | return messageBoxWinGui(
2187 | aTitle, aMessage, aDialogType, aIconType, aDefaultButton);
2188 | }
2189 | }
2190 | if (dialogPresent())
2191 | {
2192 | if (lQuery)
2193 | {
2194 | response("dialog");
2195 | return 0;
2196 | }
2197 | return messageBoxWinConsole(
2198 | aTitle, aMessage, aDialogType, aIconType, aDefaultButton);
2199 | }
2200 | else
2201 | {
2202 | if (lQuery)
2203 | {
2204 | response("basicinput");
2205 | return 0;
2206 | }
2207 | if (!gWarningDisplayed && !tinyfd_forceConsole)
2208 | {
2209 | gWarningDisplayed = true;
2210 | printf("\n\n%s\n", gTitle.ptr);
2211 | printf("%s\n\n", tinyfd_needs.ptr);
2212 | }
2213 | if (some(aTitle))
2214 | {
2215 | printf("\n%s\n\n", aTitle);
2216 | }
2217 | if (eq("yesno", aDialogType))
2218 | {
2219 | do
2220 | {
2221 | if (some(aMessage))
2222 | {
2223 | printf("%s\n", aMessage);
2224 | }
2225 | printf("y/n: ");
2226 | lChar = cast(char)tolower(_getch());
2227 | printf("\n\n");
2228 | } while (lChar != 'y' && lChar != 'n');
2229 | return lChar == 'y' ? 1 : 0;
2230 | }
2231 | else if (eq("okcancel", aDialogType))
2232 | {
2233 | do
2234 | {
2235 | if (some(aMessage))
2236 | {
2237 | printf("%s\n", aMessage);
2238 | }
2239 | printf("[O]kay/[C]ancel: ");
2240 | lChar = cast(char)tolower(_getch());
2241 | printf("\n\n");
2242 | } while (lChar != 'o' && lChar != 'c');
2243 | return lChar == 'o' ? 1 : 0;
2244 | }
2245 | else if (eq("yesnocancel", aDialogType))
2246 | {
2247 | do
2248 | {
2249 | if (some(aMessage))
2250 | {
2251 | printf("%s\n", aMessage);
2252 | }
2253 | printf("[Y]es/[N]o/[C]ancel: ");
2254 | lChar = cast(char)tolower(_getch());
2255 | printf("\n\n");
2256 | } while (lChar != 'y' && lChar != 'n' && lChar != 'c');
2257 | return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0;
2258 | }
2259 | else
2260 | {
2261 | if (some(aMessage))
2262 | {
2263 | printf("%s\n\n", aMessage);
2264 | }
2265 | printf("press enter to continue ");
2266 | lChar = cast(char)_getch();
2267 | printf("\n\n");
2268 | return 1;
2269 | }
2270 | }
2271 | }
2272 |
2273 | int _notifyPopup(
2274 | const char* aTitle,
2275 | const char* aMessage,
2276 | const char* aIconType)
2277 | {
2278 | const bool lQuery = eq(aTitle, "tinyfd_query");
2279 |
2280 | version (TINYFD_LIB) {
2281 |
2282 | if (willBeGui())
2283 | {
2284 | if (lQuery)
2285 | {
2286 | response("windows");
2287 | return 1;
2288 | }
2289 | return notifyWinGui(aTitle, aMessage, aIconType);
2290 | }
2291 | }
2292 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0);
2293 | }
2294 |
2295 | const(char*) _inputBox(
2296 | const char* aTitle,
2297 | const char* aMessage,
2298 | const char* aDefaultInput)
2299 | {
2300 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
2301 | char* lEOF;
2302 | const bool lQuery = eq(aTitle, "tinyfd_query");
2303 |
2304 | version (TINYFD_LIB) {
2305 |
2306 | DWORD mode = 0;
2307 | HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
2308 |
2309 | if (willBeGui())
2310 | {
2311 | if (lQuery)
2312 | {
2313 | response("windows");
2314 | return cast(const(char)*)1;
2315 | }
2316 | lBuff[0] = '\0';
2317 | return inputBoxWinGui(lBuff.ptr, aTitle, aMessage, aDefaultInput);
2318 | }
2319 | }
2320 | if (dialogPresent())
2321 | {
2322 | if (lQuery)
2323 | {
2324 | response("dialog");
2325 | return cast(const(char)*)0;
2326 | }
2327 | lBuff[0] = '\0';
2328 | return inputBoxWinConsole(lBuff.ptr, aTitle, aMessage, aDefaultInput);
2329 | }
2330 | else
2331 | {
2332 | if (lQuery)
2333 | {
2334 | response("basicinput");
2335 | return cast(const(char)*)0;
2336 | }
2337 | lBuff[0] = '\0';
2338 | if (!gWarningDisplayed && !tinyfd_forceConsole)
2339 | {
2340 | gWarningDisplayed = true;
2341 | printf("\n\n%s\n", gTitle.ptr);
2342 | printf("%s\n\n", tinyfd_needs.ptr);
2343 | }
2344 | if (some(aTitle))
2345 | {
2346 | printf("\n%s\n\n", aTitle);
2347 | }
2348 | if (some(aMessage))
2349 | {
2350 | printf("%s\n", aMessage);
2351 | }
2352 | printf("(ctrl-Z + enter to cancel): ");
2353 | version (TINYFD_LIB)
2354 | {
2355 | if (!aDefaultInput)
2356 | {
2357 | GetConsoleMode(hStdin, &mode);
2358 | SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
2359 | }
2360 | }
2361 |
2362 | lEOF = fgets(lBuff.ptr, lBuff.sizeof, stdin);
2363 | if (!lEOF)
2364 | return null;
2365 |
2366 | version (TINYFD_LIB)
2367 | {
2368 | if (!aDefaultInput)
2369 | {
2370 | SetConsoleMode(hStdin, mode);
2371 | printf("\n");
2372 | }
2373 | }
2374 | printf("\n");
2375 | if (strchr(lBuff.ptr, 27))
2376 | return null;
2377 | removeLastNL(lBuff.ptr);
2378 | return lBuff.ptr;
2379 | }
2380 | }
2381 |
2382 | const(char*) _saveFileDialog(
2383 | const char* aTitle,
2384 | const char* aDefaultPathAndFile,
2385 | const TFD_Filter[] aFilters)
2386 | {
2387 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
2388 | const bool lQuery = eq(aTitle, "tinyfd_query");
2389 | char[MAX_PATH_OR_CMD] lString = '\0';
2390 | const(char)* p;
2391 | lBuff[0] = '\0';
2392 |
2393 | version (TINYFD_LIB) {
2394 |
2395 | if (willBeGui())
2396 | {
2397 | if (lQuery)
2398 | {
2399 | response("windows");
2400 | return cast(const(char)*)1;
2401 | }
2402 | p = saveFileDialogWinGui(lBuff.ptr, aTitle, aDefaultPathAndFile, aFilters);
2403 | goto end;
2404 | }
2405 | }
2406 | if (dialogPresent())
2407 | {
2408 | if (lQuery)
2409 | {
2410 | response("dialog");
2411 | return cast(const(char)*)0;
2412 | }
2413 | p = saveFileDialogWinConsole(lBuff.ptr, aTitle, aDefaultPathAndFile);
2414 | }
2415 | else
2416 | {
2417 | if (lQuery)
2418 | {
2419 | response("basicinput");
2420 | return cast(const(char)*)0;
2421 | }
2422 | p = _inputBox(aTitle, "Save file", "");
2423 | }
2424 |
2425 | end:
2426 |
2427 | if (!some(p))
2428 | return null;
2429 | getPathWithoutFinalSlash(lString.ptr, p);
2430 | if (!dirExists(lString.ptr))
2431 | return null;
2432 | getLastName(lString.ptr, p);
2433 | if (!filenameValid(lString.ptr))
2434 | return null;
2435 | return p;
2436 | }
2437 |
2438 | const(char*) _openFileDialog(
2439 | const char* aTitle,
2440 | const char* aDefaultPathAndFile,
2441 | const TFD_Filter[] aFilters,
2442 | const bool aAllowMultipleSelects)
2443 | {
2444 | static char[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0';
2445 | const bool lQuery = eq(aTitle, "tinyfd_query");
2446 | const(char)* p;
2447 |
2448 | version (TINYFD_LIB) {
2449 |
2450 | if (willBeGui())
2451 | {
2452 | if (lQuery)
2453 | {
2454 | response("windows");
2455 | return cast(const(char)*)1;
2456 | }
2457 | p = openFileDialogWinGui(lBuff.ptr, aTitle, aDefaultPathAndFile, aFilters, aAllowMultipleSelects);
2458 | goto end;
2459 | }
2460 | }
2461 | if (dialogPresent())
2462 | {
2463 | if (lQuery)
2464 | {
2465 | response("dialog");
2466 | return cast(const(char)*)0;
2467 | }
2468 | p = openFileDialogWinConsole(lBuff.ptr,
2469 | aTitle, aDefaultPathAndFile, aAllowMultipleSelects);
2470 | }
2471 | else
2472 | {
2473 | if (lQuery)
2474 | {
2475 | response("basicinput");
2476 | return cast(const(char)*)0;
2477 | }
2478 | p = _inputBox(aTitle, "Open file", "");
2479 | }
2480 |
2481 | end:
2482 |
2483 | if (!some(p))
2484 | return null;
2485 | if (aAllowMultipleSelects && strchr(p, '|'))
2486 | p = ensureFilesExist(lBuff.ptr, p);
2487 | else if (!fileExists(p))
2488 | return null;
2489 | return p;
2490 | }
2491 |
2492 | const(char*) _selectFolderDialog(const char* aTitle, const char* aDefaultPath)
2493 | {
2494 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
2495 | const bool lQuery = eq(aTitle, "tinyfd_query");
2496 | const(char)* p;
2497 |
2498 | version (TINYFD_LIB) {
2499 |
2500 | if (willBeGui())
2501 | {
2502 | if (lQuery)
2503 | {
2504 | response("windows");
2505 | return cast(const(char)*)1;
2506 | }
2507 | version (TINYFD_SELECTFOLDERWIN)
2508 | {
2509 | p = selectFolderDialogWinGui(lBuff.ptr, aTitle, aDefaultPath);
2510 | return dirExists(p) ? p : null;
2511 | }
2512 | }
2513 | }
2514 | if (dialogPresent())
2515 | {
2516 | if (lQuery)
2517 | {
2518 | response("dialog");
2519 | return cast(const(char)*)0;
2520 | }
2521 | p = selectFolderDialogWinConsole(lBuff.ptr, aTitle, aDefaultPath);
2522 | }
2523 | else
2524 | {
2525 | if (lQuery)
2526 | {
2527 | response("basicinput");
2528 | return cast(const(char)*)0;
2529 | }
2530 | p = _inputBox(aTitle, "Select folder", "");
2531 | }
2532 | return dirExists(p) ? p : null;
2533 | }
2534 |
2535 | const(char*) _colorChooser(
2536 | const char* aTitle,
2537 | const char* aDefaultHexRGB,
2538 | ref const ubyte[3] aDefaultRGB,
2539 | ref ubyte[3] aoResultRGB)
2540 | {
2541 | const bool lQuery = eq(aTitle, "tinyfd_query");
2542 | char[8] lDefaultHexRGB = '\0';
2543 | char* lpDefaultHexRGB;
2544 | const(char)* p;
2545 |
2546 | version (TINYFD_LIB) {
2547 |
2548 | if (willBeGui())
2549 | {
2550 | if (lQuery)
2551 | {
2552 | response("windows");
2553 | return cast(const(char)*)1;
2554 | }
2555 | return colorChooserWinGui(aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB);
2556 | }
2557 | }
2558 | if (aDefaultHexRGB)
2559 | {
2560 | lpDefaultHexRGB = cast(char*)aDefaultHexRGB;
2561 | }
2562 | else
2563 | {
2564 | RGB2Hex(aDefaultRGB, lDefaultHexRGB.ptr);
2565 | lpDefaultHexRGB = cast(char*)lDefaultHexRGB;
2566 | }
2567 | p = _inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lpDefaultHexRGB);
2568 | if (lQuery)
2569 | return p;
2570 |
2571 | if (!p || strlen(p) != 7 || p[0] != '#')
2572 | return null;
2573 |
2574 | foreach (i; 1 .. 7)
2575 | {
2576 | if (!isxdigit(p[i]))
2577 | return null;
2578 | }
2579 | Hex2RGB(p, aoResultRGB);
2580 | return p;
2581 | }
2582 |
2583 | } else { // unix
2584 |
2585 | char[16] gPython2Name = '\0';
2586 | char[16] gPython3Name = '\0';
2587 | char[16] gPythonName = '\0';
2588 |
2589 | int isDarwin()
2590 | {
2591 | static int ret = -1;
2592 | utsname lUtsname;
2593 | if (ret < 0)
2594 | {
2595 | ret = !uname(&lUtsname) && eq(lUtsname.sysname.ptr, "Darwin");
2596 | }
2597 | return ret;
2598 | }
2599 |
2600 | bool dirExists(const char* aDirPath)
2601 | {
2602 | if (!some(aDirPath))
2603 | return false;
2604 | DIR* lDir = opendir(aDirPath);
2605 | if (!lDir)
2606 | return false;
2607 | closedir(lDir);
2608 | return true;
2609 | }
2610 |
2611 | bool detectPresence(const char* aExecutable)
2612 | {
2613 | char[MAX_PATH_OR_CMD] lBuff = '\0';
2614 | char[MAX_PATH_OR_CMD] lTestedString = "which ";
2615 |
2616 | strcat(lTestedString.ptr, aExecutable);
2617 | strcat(lTestedString.ptr, " 2>/dev/null ");
2618 | FILE* lIn = popen(lTestedString.ptr, "r");
2619 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null && !strchr(lBuff.ptr, ':') && strncmp(lBuff.ptr, "no ", 3))
2620 | {
2621 | // present
2622 | pclose(lIn);
2623 | if (tinyfd_verbose)
2624 | printf("detectPresence %s %d\n", aExecutable, 1);
2625 | return true;
2626 | }
2627 | else
2628 | {
2629 | pclose(lIn);
2630 | if (tinyfd_verbose)
2631 | printf("detectPresence %s %d\n", aExecutable, 0);
2632 | return false;
2633 | }
2634 | }
2635 |
2636 | const(char)* getVersion(const char* aExecutable) /*version must be first numeral*/
2637 | {
2638 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
2639 | char[MAX_PATH_OR_CMD] lTestedString = '\0';
2640 | FILE* lIn;
2641 | char* lTmp;
2642 |
2643 | strcpy(lTestedString.ptr, aExecutable);
2644 | strcat(lTestedString.ptr, " --version");
2645 |
2646 | lIn = popen(lTestedString.ptr, "r");
2647 | lTmp = fgets(lBuff.ptr, lBuff.sizeof, lIn);
2648 | pclose(lIn);
2649 |
2650 | lTmp += strcspn(lTmp, "0123456789");
2651 | /* printf("lTmp:%s\n", lTmp); */
2652 | return lTmp;
2653 | }
2654 |
2655 | const(int)* getMajorMinorPatch(const char* aExecutable)
2656 | {
2657 | static int[3] lArray;
2658 | char* lTmp;
2659 |
2660 | lTmp = cast(char*)getVersion(aExecutable);
2661 | lArray[0] = atoi(strtok(lTmp, " ,.-"));
2662 | /* printf("lArray0 %d\n", lArray[0]); */
2663 | lArray[1] = atoi(strtok(null, " ,.-"));
2664 | /* printf("lArray1 %d\n", lArray[1]); */
2665 | lArray[2] = atoi(strtok(null, " ,.-"));
2666 | /* printf("lArray2 %d\n", lArray[2]); */
2667 |
2668 | if (!lArray[0] && !lArray[1] && !lArray[2])
2669 | return null;
2670 | return lArray.ptr;
2671 | }
2672 |
2673 | bool tryCommand(const char* aCommand)
2674 | {
2675 | char[MAX_PATH_OR_CMD] lBuff = '\0';
2676 | FILE* lIn = popen(aCommand, "r");
2677 | const bool present = fgets(lBuff.ptr, lBuff.sizeof, lIn) is null;
2678 | pclose(lIn);
2679 | return present;
2680 | }
2681 |
2682 | int isTerminalRunning()
2683 | {
2684 | static int ret = -1;
2685 | if (ret < 0)
2686 | {
2687 | ret = isatty(1);
2688 | if (tinyfd_verbose)
2689 | printf("isTerminalRunning %d\n", ret);
2690 | }
2691 | return ret;
2692 | }
2693 |
2694 | const(char)* dialogNameOnly()
2695 | {
2696 | static char[128] ret = "*";
2697 | if (ret[0] == '*')
2698 | {
2699 | if (isDarwin() && strcpy(ret.ptr, "/opt/local/bin/dialog") && detectPresence(ret.ptr))
2700 | {
2701 | }
2702 | else if (strcpy(ret.ptr, "dialog") && detectPresence(ret.ptr))
2703 | {
2704 | }
2705 | else
2706 | {
2707 | strcpy(ret.ptr, "");
2708 | }
2709 | }
2710 | return ret.ptr;
2711 | }
2712 |
2713 | bool isDialogVersionBetter09b()
2714 | {
2715 | const(char)* lDialogName;
2716 | char* lVersion;
2717 | int lMajor;
2718 | int lMinor;
2719 | int lDate;
2720 | int lResult;
2721 | char* lMinorP;
2722 | char* lLetter;
2723 | char[128] lBuff = '\0';
2724 |
2725 | /*char[128] lTest = " 0.9b-20031126" ;*/
2726 |
2727 | lDialogName = dialogNameOnly();
2728 | lVersion = cast(char*)getVersion(lDialogName);
2729 | if (!some(lDialogName) || !lVersion)
2730 | return false;
2731 | /*lVersion = lTest ;*/
2732 | /*printf("lVersion %s\n", lVersion);*/
2733 | strcpy(lBuff.ptr, lVersion);
2734 | lMajor = atoi(strtok(lVersion, " ,.-"));
2735 | /*printf("lMajor %d\n", lMajor);*/
2736 | lMinorP = strtok(null, " ,.-abcdefghijklmnopqrstuvxyz");
2737 | lMinor = atoi(lMinorP);
2738 | /*printf("lMinor %d\n", lMinor );*/
2739 | lDate = atoi(strtok(null, " ,.-"));
2740 | if (lDate < 0)
2741 | lDate = -lDate;
2742 | /*printf("lDate %d\n", lDate);*/
2743 | lLetter = lMinorP + strlen(lMinorP);
2744 | strcpy(lVersion, lBuff.ptr);
2745 | strtok(lLetter, " ,.-");
2746 | /*printf("lLetter %s\n", lLetter);*/
2747 | return lMajor > 0 || (lMinor == 9 && *lLetter == 'b' && lDate >= 20031126);
2748 | }
2749 |
2750 | int whiptailPresentOnly()
2751 | {
2752 | static int ret = -1;
2753 | if (ret < 0)
2754 | {
2755 | ret = detectPresence("whiptail");
2756 | }
2757 | return ret;
2758 | }
2759 |
2760 | const(char)* terminalName()
2761 | {
2762 | static char[128] ret_buf = "*";
2763 | char[64] shellName_buf = "*";
2764 | char* ret = ret_buf.ptr;
2765 | char* lShellName = shellName_buf.ptr;
2766 | const(int)* lArray;
2767 |
2768 | if (ret[0] == '*')
2769 | {
2770 | if (detectPresence("bash"))
2771 | {
2772 | strcpy(lShellName, "bash -c "); /*good for basic input*/
2773 | }
2774 | else if (some(dialogNameOnly()) || whiptailPresentOnly())
2775 | {
2776 | strcpy(lShellName, "sh -c "); /*good enough for dialog & whiptail*/
2777 | }
2778 | else
2779 | {
2780 | strcpy(ret, "");
2781 | return null;
2782 | }
2783 |
2784 | if (isDarwin())
2785 | {
2786 | if (strcpy(ret, "/opt/X11/bin/xterm") && detectPresence(ret))
2787 | {
2788 | strcat(ret, " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e ");
2789 | strcat(ret, lShellName);
2790 | }
2791 | else
2792 | {
2793 | strcpy(ret, "");
2794 | }
2795 | }
2796 | else if (strcpy(ret, "xterm") /*good (small without parameters)*/
2797 | && detectPresence(ret))
2798 | {
2799 | strcat(ret, " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e ");
2800 | strcat(ret, lShellName);
2801 | }
2802 | else if (strcpy(ret, "terminator") /*good*/
2803 | && detectPresence(ret))
2804 | {
2805 | strcat(ret, " -x ");
2806 | strcat(ret, lShellName);
2807 | }
2808 | else if (strcpy(ret, "lxterminal") /*good*/
2809 | && detectPresence(ret))
2810 | {
2811 | strcat(ret, " -e ");
2812 | strcat(ret, lShellName);
2813 | }
2814 | else if (strcpy(ret, "konsole") /*good*/
2815 | && detectPresence(ret))
2816 | {
2817 | strcat(ret, " -e ");
2818 | strcat(ret, lShellName);
2819 | }
2820 | else if (strcpy(ret, "kterm") /*good*/
2821 | && detectPresence(ret))
2822 | {
2823 | strcat(ret, " -e ");
2824 | strcat(ret, lShellName);
2825 | }
2826 | else if (strcpy(ret, "tilix") /*good*/
2827 | && detectPresence(ret))
2828 | {
2829 | strcat(ret, " -e ");
2830 | strcat(ret, lShellName);
2831 | }
2832 | else if (strcpy(ret, "xfce4-terminal") /*good*/
2833 | && detectPresence(ret))
2834 | {
2835 | strcat(ret, " -x ");
2836 | strcat(ret, lShellName);
2837 | }
2838 | else if (strcpy(ret, "mate-terminal") /*good*/
2839 | && detectPresence(ret))
2840 | {
2841 | strcat(ret, " -x ");
2842 | strcat(ret, lShellName);
2843 | }
2844 | else if (strcpy(ret, "Eterm") /*good*/
2845 | && detectPresence(ret))
2846 | {
2847 | strcat(ret, " -e ");
2848 | strcat(ret, lShellName);
2849 | }
2850 | else if (strcpy(ret, "evilvte") /*good*/
2851 | && detectPresence(ret))
2852 | {
2853 | strcat(ret, " -e ");
2854 | strcat(ret, lShellName);
2855 | }
2856 | else if (strcpy(ret, "pterm") /*good (only letters)*/
2857 | && detectPresence(ret))
2858 | {
2859 | strcat(ret, " -e ");
2860 | strcat(ret, lShellName);
2861 | }
2862 | else if (strcpy(ret, "gnome-terminal") && detectPresence(ret))
2863 | {
2864 | lArray = getMajorMinorPatch(ret);
2865 | if (lArray[0] < 3 || lArray[0] == 3 && lArray[1] <= 6)
2866 | {
2867 | strcat(ret, " --disable-factory -x ");
2868 | strcat(ret, lShellName);
2869 | }
2870 | else
2871 | {
2872 | strcpy(ret, "");
2873 | }
2874 | }
2875 | else
2876 | {
2877 | strcpy(ret, "");
2878 | }
2879 | /* bad: koi rxterm guake tilda vala-terminal qterminal
2880 | aterm Terminal terminology sakura lilyterm weston-terminal
2881 | roxterm termit xvt rxvt mrxvt urxvt */
2882 | }
2883 | return some(ret) ? ret : null;
2884 | }
2885 |
2886 | const(char)* dialogName()
2887 | {
2888 | const(char)* ret;
2889 | ret = dialogNameOnly();
2890 | if (some(ret) && (isTerminalRunning() || terminalName()))
2891 | {
2892 | return ret;
2893 | }
2894 | else
2895 | return null;
2896 | }
2897 |
2898 | int whiptailPresent()
2899 | {
2900 | int ret;
2901 | ret = whiptailPresentOnly();
2902 | if (ret && (isTerminalRunning() || terminalName()))
2903 | {
2904 | return ret;
2905 | }
2906 | else
2907 | return 0;
2908 | }
2909 |
2910 | int graphicMode()
2911 | {
2912 | return !(tinyfd_forceConsole && (isTerminalRunning() || terminalName())) && (getenv("DISPLAY") || (isDarwin() && (!getenv("SSH_TTY") || getenv("DISPLAY"))));
2913 | }
2914 |
2915 | int pactlPresent()
2916 | {
2917 | static int ret = -1;
2918 | if (ret < 0)
2919 | {
2920 | ret = detectPresence("pactl");
2921 | }
2922 | return ret;
2923 | }
2924 |
2925 | int speakertestPresent()
2926 | {
2927 | static int ret = -1;
2928 | if (ret < 0)
2929 | {
2930 | ret = detectPresence("speaker-test");
2931 | }
2932 | return ret;
2933 | }
2934 |
2935 | int beepexePresent()
2936 | {
2937 | static int ret = -1;
2938 | if (ret < 0)
2939 | {
2940 | ret = detectPresence("beep.exe");
2941 | }
2942 | return ret;
2943 | }
2944 |
2945 | int xmessagePresent()
2946 | {
2947 | static int ret = -1;
2948 | if (ret < 0)
2949 | {
2950 | ret = detectPresence("xmessage"); /*if not tty,not on osxpath*/
2951 | }
2952 | return ret && graphicMode();
2953 | }
2954 |
2955 | int gxmessagePresent()
2956 | {
2957 | static int ret = -1;
2958 | if (ret < 0)
2959 | {
2960 | ret = detectPresence("gxmessage");
2961 | }
2962 | return ret && graphicMode();
2963 | }
2964 |
2965 | int gmessagePresent()
2966 | {
2967 | static int ret = -1;
2968 | if (ret < 0)
2969 | {
2970 | ret = detectPresence("gmessage");
2971 | }
2972 | return ret && graphicMode();
2973 | }
2974 |
2975 | int notifysendPresent()
2976 | {
2977 | static int ret = -1;
2978 | if (ret < 0)
2979 | {
2980 | ret = detectPresence("notify-send");
2981 | }
2982 | return ret && graphicMode();
2983 | }
2984 |
2985 | int perlPresent()
2986 | {
2987 | static int ret = -1;
2988 | char[MAX_PATH_OR_CMD] lBuff = 0;
2989 | FILE* lIn;
2990 |
2991 | if (ret < 0)
2992 | {
2993 | ret = detectPresence("perl");
2994 | if (ret)
2995 | {
2996 | lIn = popen("perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1", "r");
2997 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) is null)
2998 | {
2999 | ret = 2;
3000 | }
3001 | pclose(lIn);
3002 | if (tinyfd_verbose)
3003 | printf("perl-dbus %d\n", ret);
3004 | }
3005 | }
3006 | return graphicMode() ? ret : 0;
3007 | }
3008 |
3009 | int afplayPresent()
3010 | {
3011 | static int ret = -1;
3012 | char[MAX_PATH_OR_CMD] lBuff = '\0';
3013 | FILE* lIn;
3014 |
3015 | if (ret < 0)
3016 | {
3017 | ret = detectPresence("afplay");
3018 | if (ret)
3019 | {
3020 | lIn = popen("test -e /System/Library/Sounds/Ping.aiff || echo Ping", "r");
3021 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) is null)
3022 | {
3023 | ret = 2;
3024 | }
3025 | pclose(lIn);
3026 | if (tinyfd_verbose)
3027 | printf("afplay %d\n", ret);
3028 | }
3029 | }
3030 | return graphicMode() ? ret : 0;
3031 | }
3032 |
3033 | int xdialogPresent()
3034 | {
3035 | static int ret = -1;
3036 | if (ret < 0)
3037 | {
3038 | ret = detectPresence("Xdialog");
3039 | }
3040 | return ret && graphicMode();
3041 | }
3042 |
3043 | int gdialogPresent()
3044 | {
3045 | static int ret = -1;
3046 | if (ret < 0)
3047 | {
3048 | ret = detectPresence("gdialog");
3049 | }
3050 | return ret && graphicMode();
3051 | }
3052 |
3053 | int osascriptPresent()
3054 | {
3055 | static int ret = -1;
3056 | if (ret < 0)
3057 | {
3058 | gWarningDisplayed |= !!getenv("SSH_TTY");
3059 | ret = detectPresence("osascript");
3060 | }
3061 | return ret && graphicMode() && !getenv("SSH_TTY");
3062 | }
3063 |
3064 | int qarmaPresent()
3065 | {
3066 | static int ret = -1;
3067 | if (ret < 0)
3068 | {
3069 | ret = detectPresence("qarma");
3070 | }
3071 | return ret && graphicMode();
3072 | }
3073 |
3074 | int matedialogPresent()
3075 | {
3076 | static int ret = -1;
3077 | if (ret < 0)
3078 | {
3079 | ret = detectPresence("matedialog");
3080 | }
3081 | return ret && graphicMode();
3082 | }
3083 |
3084 | int shellementaryPresent()
3085 | {
3086 | static int ret = -1;
3087 | if (ret < 0)
3088 | {
3089 | ret = 0; /*detectPresence("shellementary"); shellementary is not ready yet */
3090 | }
3091 | return ret && graphicMode();
3092 | }
3093 |
3094 | int zenityPresent()
3095 | {
3096 | static int ret = -1;
3097 | if (ret < 0)
3098 | {
3099 | ret = detectPresence("zenity");
3100 | }
3101 | return ret && graphicMode();
3102 | }
3103 |
3104 | int zenity3Present()
3105 | {
3106 | static int ret = -1;
3107 | char[MAX_PATH_OR_CMD] lBuff = '\0';
3108 | FILE* lIn;
3109 | int lIntTmp;
3110 |
3111 | if (ret < 0)
3112 | {
3113 | ret = 0;
3114 | if (zenityPresent())
3115 | {
3116 | lIn = popen("zenity --version", "r");
3117 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
3118 | {
3119 | if (atoi(lBuff.ptr) >= 3)
3120 | {
3121 | ret = 3;
3122 | lIntTmp = atoi(strtok(lBuff.ptr, ".") + 2);
3123 | if (lIntTmp >= 18)
3124 | {
3125 | ret = 5;
3126 | }
3127 | else if (lIntTmp >= 10)
3128 | {
3129 | ret = 4;
3130 | }
3131 | }
3132 | else if ((atoi(lBuff.ptr) == 2) && (atoi(strtok(lBuff.ptr, ".") + 2) >= 32))
3133 | {
3134 | ret = 2;
3135 | }
3136 | if (tinyfd_verbose)
3137 | printf("zenity %d\n", ret);
3138 | }
3139 | pclose(lIn);
3140 | }
3141 | }
3142 | return graphicMode() ? ret : 0;
3143 | }
3144 |
3145 | int kdialogPresent()
3146 | {
3147 | static int ret = -1;
3148 | char[MAX_PATH_OR_CMD] lBuff = '\0';
3149 | FILE* lIn;
3150 | char* lDesktop;
3151 |
3152 | if (ret < 0)
3153 | {
3154 | if (zenityPresent())
3155 | {
3156 | lDesktop = getenv("XDG_SESSION_DESKTOP");
3157 | if (!eq(lDesktop, "KDE") && !eq(lDesktop, "lxqt"))
3158 | {
3159 | ret = 0;
3160 | return ret;
3161 | }
3162 | }
3163 |
3164 | ret = detectPresence("kdialog");
3165 | if (ret && !getenv("SSH_TTY"))
3166 | {
3167 | lIn = popen("kdialog --attach 2>&1", "r");
3168 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
3169 | {
3170 | if (!strstr("Unknown", lBuff.ptr))
3171 | {
3172 | ret = 2;
3173 | if (tinyfd_verbose)
3174 | printf("kdialog-attach %d\n", ret);
3175 | }
3176 | }
3177 | pclose(lIn);
3178 |
3179 | if (ret == 2)
3180 | {
3181 | ret = 1;
3182 | lIn = popen("kdialog --passivepopup 2>&1", "r");
3183 | if (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
3184 | {
3185 | if (!strstr("Unknown", lBuff.ptr))
3186 | {
3187 | ret = 2;
3188 | if (tinyfd_verbose)
3189 | printf("kdialog-popup %d\n", ret);
3190 | }
3191 | }
3192 | pclose(lIn);
3193 | }
3194 | }
3195 | }
3196 | return graphicMode() ? ret : 0;
3197 | }
3198 |
3199 | int osx9orBetter()
3200 | {
3201 | static int ret = -1;
3202 | char[MAX_PATH_OR_CMD] lBuff = '\0';
3203 | FILE* lIn;
3204 | int V, v;
3205 |
3206 | if (ret < 0)
3207 | {
3208 | ret = 0;
3209 | lIn = popen("osascript -e 'set osver to system version of (system info)'", "r");
3210 | if ((fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null) && (2 == sscanf(lBuff.ptr, "%d.%d", &V, &v)))
3211 | {
3212 | V = V * 100 + v;
3213 | if (V >= 1009)
3214 | {
3215 | ret = 1;
3216 | }
3217 | }
3218 | pclose(lIn);
3219 | if (tinyfd_verbose)
3220 | printf("Osx10 = %d, %d = %s\n", ret, V, lBuff.ptr);
3221 | }
3222 | return ret;
3223 | }
3224 |
3225 | int python2Present()
3226 | {
3227 | static int ret = -1;
3228 |
3229 | if (ret < 0)
3230 | {
3231 | ret = 0;
3232 | strcpy(gPython2Name.ptr, "python2");
3233 | if (detectPresence(gPython2Name.ptr))
3234 | ret = 1;
3235 | else
3236 | {
3237 | for (int i = 9; i >= 0; i--)
3238 | {
3239 | sprintf(gPython2Name.ptr, "python2.%d", i);
3240 | if (detectPresence(gPython2Name.ptr))
3241 | {
3242 | ret = 1;
3243 | break;
3244 | }
3245 | }
3246 | /*if ( ! ret )
3247 | {
3248 | strcpy(gPython2Name , "python" ) ;
3249 | if ( detectPresence(gPython2Name.ptr) ) ret = 1;
3250 | }*/
3251 | }
3252 | if (tinyfd_verbose)
3253 | printf("python2Present %d\n", ret);
3254 | if (tinyfd_verbose)
3255 | printf("gPython2Name %s\n", gPython2Name.ptr);
3256 | }
3257 | return ret;
3258 | }
3259 |
3260 | int python3Present()
3261 | {
3262 | static int ret = -1;
3263 |
3264 | if (ret < 0)
3265 | {
3266 | ret = 0;
3267 | strcpy(gPython3Name.ptr, "python3");
3268 | if (detectPresence(gPython3Name.ptr))
3269 | ret = 1;
3270 | else
3271 | {
3272 | for (int i = 9; i >= 0; i--)
3273 | {
3274 | sprintf(gPython3Name.ptr, "python3.%d", i);
3275 | if (detectPresence(gPython3Name.ptr))
3276 | {
3277 | ret = 1;
3278 | break;
3279 | }
3280 | }
3281 | /*if ( ! ret )
3282 | {
3283 | strcpy(gPython3Name , "python" ) ;
3284 | if ( detectPresence(gPython3Name.ptr) ) ret = 1;
3285 | }*/
3286 | }
3287 | if (tinyfd_verbose)
3288 | printf("python3Present %d\n", ret);
3289 | if (tinyfd_verbose)
3290 | printf("gPython3Name %s\n", gPython3Name.ptr);
3291 | }
3292 | return ret;
3293 | }
3294 |
3295 | int tkinter2Present()
3296 | {
3297 | static int ret = -1;
3298 | char[256] lPythonCommand = '\0';
3299 | char[256] lPythonParams =
3300 | "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\"";
3301 |
3302 | if (ret < 0)
3303 | {
3304 | ret = 0;
3305 | if (python2Present())
3306 | {
3307 | sprintf(lPythonCommand.ptr, "%s %s", gPython2Name.ptr, lPythonParams.ptr);
3308 | ret = tryCommand(lPythonCommand.ptr);
3309 | }
3310 | if (tinyfd_verbose)
3311 | printf("tkinter2Present %d\n", ret);
3312 | }
3313 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY"));
3314 | }
3315 |
3316 | int tkinter3Present()
3317 | {
3318 | static int ret = -1;
3319 | char[256] lPythonCommand = '\0';
3320 | char[256] lPythonParams =
3321 | "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\"";
3322 |
3323 | if (ret < 0)
3324 | {
3325 | ret = 0;
3326 | if (python3Present())
3327 | {
3328 | sprintf(lPythonCommand.ptr, "%s %s", gPython3Name.ptr, lPythonParams.ptr);
3329 | ret = tryCommand(lPythonCommand.ptr);
3330 | }
3331 | if (tinyfd_verbose)
3332 | printf("tkinter3Present %d\n", ret);
3333 | }
3334 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY"));
3335 | }
3336 |
3337 | int pythonDbusPresent()
3338 | {
3339 | static int ret = -1;
3340 | char[256] lPythonCommand = '\0';
3341 | char[256] lPythonParams =
3342 | `-c "try:
3343 | import dbus
3344 | bus=dbus.SessionBus()
3345 | notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications')
3346 | notify=dbus.Interface(notif,'org.freedesktop.Notifications')
3347 | except:
3348 | print(0)"`;
3349 |
3350 | if (ret < 0)
3351 | {
3352 | ret = 0;
3353 | if (python2Present())
3354 | {
3355 | strcpy(gPythonName.ptr, gPython2Name.ptr);
3356 | sprintf(lPythonCommand.ptr, "%s %s", gPythonName.ptr, lPythonParams.ptr);
3357 | ret = tryCommand(lPythonCommand.ptr);
3358 | }
3359 |
3360 | if (!ret && python3Present())
3361 | {
3362 | strcpy(gPythonName.ptr, gPython3Name.ptr);
3363 | sprintf(lPythonCommand.ptr, "%s %s", gPythonName.ptr, lPythonParams.ptr);
3364 | ret = tryCommand(lPythonCommand.ptr);
3365 | }
3366 |
3367 | if (tinyfd_verbose)
3368 | printf("dbusPresent %d\n", ret);
3369 | if (tinyfd_verbose)
3370 | printf("gPythonName %s\n", gPythonName.ptr);
3371 | }
3372 | return ret && graphicMode() && !(isDarwin() && getenv("SSH_TTY"));
3373 | }
3374 |
3375 | void sigHandler(int sig)
3376 | {
3377 | FILE* lIn = popen("pactl unload-module module-sine", "r");
3378 | if (lIn)
3379 | {
3380 | pclose(lIn);
3381 | }
3382 | }
3383 |
3384 | void _beep()
3385 | {
3386 | char[256] str_buf = '\0';
3387 | char* str = str_buf.ptr;
3388 | FILE* lIn;
3389 |
3390 | if (osascriptPresent())
3391 | {
3392 | if (afplayPresent() >= 2)
3393 | {
3394 | strcpy(str, "afplay /System/Library/Sounds/Ping.aiff");
3395 | }
3396 | else
3397 | {
3398 | strcpy(str, "osascript -e 'tell application \"System Events\" to beep'");
3399 | }
3400 | }
3401 | else if (pactlPresent())
3402 | {
3403 | signal(SIGINT, &sigHandler);
3404 | /*strcpy( str , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/
3405 | strcpy(str, "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum");
3406 | }
3407 | else if (speakertestPresent())
3408 | {
3409 | /*strcpy( str , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/
3410 | strcpy(str, "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .3; kill -9 $pid");
3411 | }
3412 | else if (beepexePresent())
3413 | {
3414 | strcpy(str, "beep.exe 440 300");
3415 | }
3416 | else
3417 | {
3418 | strcpy(str, "printf '\a' > /dev/tty");
3419 | }
3420 |
3421 | if (tinyfd_verbose)
3422 | printf("str: %s\n", str);
3423 |
3424 | lIn = popen(str, "r");
3425 | if (lIn)
3426 | {
3427 | pclose(lIn);
3428 | }
3429 |
3430 | if (pactlPresent())
3431 | {
3432 | signal(SIGINT, SIG_DFL);
3433 | }
3434 | }
3435 |
3436 | int _messageBox(
3437 | const char* aTitle,
3438 | const char* aMessage,
3439 | const char* aDialogType,
3440 | const char* aIconType,
3441 | int aDefaultButton)
3442 | {
3443 | char[MAX_PATH_OR_CMD] lBuff = '\0';
3444 | const bool lQuery = eq(aTitle, "tinyfd_query");
3445 | char* str;
3446 | char* lpDialogString;
3447 | FILE* lIn;
3448 | bool lWasGraphicDialog;
3449 | bool lWasXterm;
3450 | int lResult;
3451 | char lChar;
3452 | termios infoOri;
3453 | termios info;
3454 | size_t lTitleLen;
3455 | size_t lMessageLen;
3456 |
3457 | lBuff[0] = '\0';
3458 |
3459 | lTitleLen = aTitle ? strlen(aTitle) : 0;
3460 | lMessageLen = aMessage ? strlen(aMessage) : 0;
3461 | if (!aTitle || !lQuery)
3462 | {
3463 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen);
3464 | }
3465 |
3466 | if (osascriptPresent())
3467 | {
3468 | if (lQuery)
3469 | {
3470 | response("applescript");
3471 | return 1;
3472 | }
3473 |
3474 | strcpy(str, "osascript ");
3475 | if (!osx9orBetter())
3476 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
3477 | strcat(str, " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"");
3478 | if (some(aMessage))
3479 | {
3480 | strcat(str, aMessage);
3481 | }
3482 | strcat(str, "\" ");
3483 | if (some(aTitle))
3484 | {
3485 | strcat(str, "with title \"");
3486 | strcat(str, aTitle);
3487 | strcat(str, "\" ");
3488 | }
3489 | strcat(str, "with icon ");
3490 | if (eq("error", aIconType))
3491 | {
3492 | strcat(str, "stop ");
3493 | }
3494 | else if (eq("warning", aIconType))
3495 | {
3496 | strcat(str, "caution ");
3497 | }
3498 | else /* question or info */
3499 | {
3500 | strcat(str, "note ");
3501 | }
3502 | if (eq("okcancel", aDialogType))
3503 | {
3504 | if (!aDefaultButton)
3505 | {
3506 | strcat(str, "default button \"Cancel\" ");
3507 | }
3508 | }
3509 | else if (eq("yesno", aDialogType))
3510 | {
3511 | strcat(str, "buttons {\"No\", \"Yes\"} ");
3512 | if (aDefaultButton)
3513 | {
3514 | strcat(str, "default button \"Yes\" ");
3515 | }
3516 | else
3517 | {
3518 | strcat(str, "default button \"No\" ");
3519 | }
3520 | strcat(str, "cancel button \"No\"");
3521 | }
3522 | else if (eq("yesnocancel", aDialogType))
3523 | {
3524 | strcat(str, "buttons {\"No\", \"Yes\", \"Cancel\"} ");
3525 | switch (aDefaultButton)
3526 | {
3527 | case 1:
3528 | strcat(str, "default button \"Yes\" ");
3529 | break;
3530 | case 2:
3531 | strcat(str, "default button \"No\" ");
3532 | break;
3533 | case 0:
3534 | strcat(str, "default button \"Cancel\" ");
3535 | break;
3536 | default:
3537 | break;
3538 | }
3539 | strcat(str, "cancel button \"Cancel\"");
3540 | }
3541 | else
3542 | {
3543 | strcat(str, "buttons {\"OK\"} ");
3544 | strcat(str, "default button \"OK\" ");
3545 | }
3546 | strcat(str, ")' ");
3547 |
3548 | strcat(str,
3549 | " -e 'if vButton is \"Yes\" then' -e 'return 1'" ~
3550 | " -e 'else if vButton is \"OK\" then' -e 'return 1'" ~
3551 | " -e 'else if vButton is \"No\" then' -e 'return 2'" ~
3552 | " -e 'else' -e 'return 0' -e 'end if' ");
3553 |
3554 | strcat(str, "-e 'on error number -128' ");
3555 | strcat(str, "-e '0' ");
3556 |
3557 | strcat(str, "-e 'end try'");
3558 | if (!osx9orBetter())
3559 | strcat(str, " -e 'end tell'");
3560 | }
3561 | else if (kdialogPresent())
3562 | {
3563 | if (lQuery)
3564 | {
3565 | response("kdialog");
3566 | return 1;
3567 | }
3568 |
3569 | strcpy(str, "kdialog");
3570 | if (kdialogPresent() == 2)
3571 | {
3572 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
3573 | }
3574 |
3575 | strcat(str, " --");
3576 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType))
3577 | {
3578 | if (eq("warning", aIconType) || eq("error", aIconType))
3579 | {
3580 | strcat(str, "warning");
3581 | }
3582 | if (eq("yesnocancel", aDialogType))
3583 | {
3584 | strcat(str, "yesnocancel");
3585 | }
3586 | else
3587 | {
3588 | strcat(str, "yesno");
3589 | }
3590 | }
3591 | else if (eq("error", aIconType))
3592 | {
3593 | strcat(str, "error");
3594 | }
3595 | else if (eq("warning", aIconType))
3596 | {
3597 | strcat(str, "sorry");
3598 | }
3599 | else
3600 | {
3601 | strcat(str, "msgbox");
3602 | }
3603 | strcat(str, " \"");
3604 | if (aMessage)
3605 | {
3606 | strcat(str, aMessage);
3607 | }
3608 | strcat(str, "\"");
3609 | if (eq("okcancel", aDialogType))
3610 | {
3611 | strcat(str,
3612 | " --yes-label Ok --no-label Cancel");
3613 | }
3614 | if (some(aTitle))
3615 | {
3616 | strcat(str, " --title \"");
3617 | strcat(str, aTitle);
3618 | strcat(str, "\"");
3619 | }
3620 |
3621 | if (eq("yesnocancel", aDialogType))
3622 | {
3623 | strcat(str, "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi");
3624 | }
3625 | else
3626 | {
3627 | strcat(str, ";if [ $? = 0 ];then echo 1;else echo 0;fi");
3628 | }
3629 | }
3630 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
3631 | {
3632 | if (zenityPresent())
3633 | {
3634 | if (lQuery)
3635 | {
3636 | response("zenity");
3637 | return 1;
3638 | }
3639 | strcpy(str, "szAnswer=$(zenity");
3640 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
3641 | {
3642 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
3643 | }
3644 | }
3645 | else if (matedialogPresent())
3646 | {
3647 | if (lQuery)
3648 | {
3649 | response("matedialog");
3650 | return 1;
3651 | }
3652 | strcpy(str, "szAnswer=$(matedialog");
3653 | }
3654 | else if (shellementaryPresent())
3655 | {
3656 | if (lQuery)
3657 | {
3658 | response("shellementary");
3659 | return 1;
3660 | }
3661 | strcpy(str, "szAnswer=$(shellementary");
3662 | }
3663 | else
3664 | {
3665 | if (lQuery)
3666 | {
3667 | response("qarma");
3668 | return 1;
3669 | }
3670 | strcpy(str, "szAnswer=$(qarma");
3671 | if (!getenv("SSH_TTY"))
3672 | {
3673 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
3674 | }
3675 | }
3676 | strcat(str, " --");
3677 |
3678 | if (eq("okcancel", aDialogType))
3679 | {
3680 | strcat(str, "question --ok-label=Ok --cancel-label=Cancel");
3681 | }
3682 | else if (eq("yesno", aDialogType))
3683 | {
3684 | strcat(str, "question");
3685 | }
3686 | else if (eq("yesnocancel", aDialogType))
3687 | {
3688 | strcat(str, "list --column \"\" --hide-header \"Yes\" \"No\"");
3689 | }
3690 | else if (eq("error", aIconType))
3691 | {
3692 | strcat(str, "error");
3693 | }
3694 | else if (eq("warning", aIconType))
3695 | {
3696 | strcat(str, "warning");
3697 | }
3698 | else
3699 | {
3700 | strcat(str, "info");
3701 | }
3702 | if (some(aTitle))
3703 | {
3704 | strcat(str, " --title=\"");
3705 | strcat(str, aTitle);
3706 | strcat(str, "\"");
3707 | }
3708 | if (some(aMessage))
3709 | {
3710 | strcat(str, " --no-wrap --text=\"");
3711 | strcat(str, aMessage);
3712 | strcat(str, "\"");
3713 | }
3714 | if ((zenity3Present() >= 3) || (!zenityPresent() && (shellementaryPresent() || qarmaPresent())))
3715 | {
3716 | strcat(str, " --icon-name=dialog-");
3717 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType))
3718 | {
3719 | strcat(str, aIconType);
3720 | }
3721 | else
3722 | {
3723 | strcat(str, "information");
3724 | }
3725 | }
3726 |
3727 | if (tinyfd_silent)
3728 | strcat(str, " 2>/dev/null ");
3729 |
3730 | if (eq("yesnocancel", aDialogType))
3731 | {
3732 | strcat(str,
3733 | ");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi");
3734 | }
3735 | else
3736 | {
3737 | strcat(str, ");if [ $? = 0 ];then echo 1;else echo 0;fi");
3738 | }
3739 | }
3740 | else if (!gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present())
3741 | {
3742 | if (lQuery)
3743 | {
3744 | response("python2-tkinter");
3745 | return 1;
3746 | }
3747 |
3748 | strcpy(str, gPython2Name.ptr);
3749 | if (!isTerminalRunning() && isDarwin())
3750 | {
3751 | strcat(str, " -i"); /* for osx without console */
3752 | }
3753 |
3754 | strcat(str,
3755 | " -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();");
3756 |
3757 | if (isDarwin())
3758 | {
3759 | strcat(str,
3760 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~
3761 | "frontmost of process \\\"Python\\\" to true' ''');");
3762 | }
3763 |
3764 | strcat(str, "res=tkMessageBox.");
3765 | if (eq("okcancel", aDialogType))
3766 | {
3767 | strcat(str, "askokcancel(");
3768 | if (aDefaultButton)
3769 | {
3770 | strcat(str, "default=tkMessageBox.OK,");
3771 | }
3772 | else
3773 | {
3774 | strcat(str, "default=tkMessageBox.CANCEL,");
3775 | }
3776 | }
3777 | else if (eq("yesno", aDialogType))
3778 | {
3779 | strcat(str, "askyesno(");
3780 | if (aDefaultButton)
3781 | {
3782 | strcat(str, "default=tkMessageBox.YES,");
3783 | }
3784 | else
3785 | {
3786 | strcat(str, "default=tkMessageBox.NO,");
3787 | }
3788 | }
3789 | else if (eq("yesnocancel", aDialogType))
3790 | {
3791 | strcat(str, "askyesnocancel(");
3792 | switch (aDefaultButton)
3793 | {
3794 | case 1:
3795 | strcat(str, "default=tkMessageBox.YES,");
3796 | break;
3797 | case 2:
3798 | strcat(str, "default=tkMessageBox.NO,");
3799 | break;
3800 | case 0:
3801 | strcat(str, "default=tkMessageBox.CANCEL,");
3802 | break;
3803 | default:
3804 | break;
3805 | }
3806 | }
3807 | else
3808 | {
3809 | strcat(str, "showinfo(");
3810 | }
3811 |
3812 | strcat(str, "icon='");
3813 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType))
3814 | {
3815 | strcat(str, aIconType);
3816 | }
3817 | else
3818 | {
3819 | strcat(str, "info");
3820 | }
3821 |
3822 | strcat(str, "',");
3823 | if (some(aTitle))
3824 | {
3825 | strcat(str, "title='");
3826 | strcat(str, aTitle);
3827 | strcat(str, "',");
3828 | }
3829 | if (some(aMessage))
3830 | {
3831 | strcat(str, "message='");
3832 | lpDialogString = str + strlen(str);
3833 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
3834 | strcat(str, "'");
3835 | }
3836 |
3837 | if (eq("yesnocancel", aDialogType))
3838 | {
3839 | strcat(str, `);
3840 | if res is None:
3841 | print 0
3842 | elif res is False:
3843 | print 2
3844 | else:
3845 | print 1
3846 | "`);
3847 | }
3848 | else
3849 | {
3850 | strcat(str, `);
3851 | if res is False:
3852 | print 0
3853 | else:
3854 | print 1
3855 | "`);
3856 | }
3857 | }
3858 | else if (!gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present())
3859 | {
3860 | if (lQuery)
3861 | {
3862 | response("python3-tkinter");
3863 | return 1;
3864 | }
3865 |
3866 | strcpy(str, gPython3Name.ptr);
3867 | strcat(str,
3868 | " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();");
3869 |
3870 | strcat(str, "res=messagebox.");
3871 | if (eq("okcancel", aDialogType))
3872 | {
3873 | strcat(str, "askokcancel(");
3874 | if (aDefaultButton)
3875 | {
3876 | strcat(str, "default=messagebox.OK,");
3877 | }
3878 | else
3879 | {
3880 | strcat(str, "default=messagebox.CANCEL,");
3881 | }
3882 | }
3883 | else if (eq("yesno", aDialogType))
3884 | {
3885 | strcat(str, "askyesno(");
3886 | if (aDefaultButton)
3887 | {
3888 | strcat(str, "default=messagebox.YES,");
3889 | }
3890 | else
3891 | {
3892 | strcat(str, "default=messagebox.NO,");
3893 | }
3894 | }
3895 | else if (eq("yesnocancel", aDialogType))
3896 | {
3897 | strcat(str, "askyesnocancel(");
3898 | switch (aDefaultButton)
3899 | {
3900 | case 1:
3901 | strcat(str, "default=messagebox.YES,");
3902 | break;
3903 | case 2:
3904 | strcat(str, "default=messagebox.NO,");
3905 | break;
3906 | case 0:
3907 | strcat(str, "default=messagebox.CANCEL,");
3908 | break;
3909 | default:
3910 | break;
3911 | }
3912 | }
3913 | else
3914 | {
3915 | strcat(str, "showinfo(");
3916 | }
3917 |
3918 | strcat(str, "icon='");
3919 | if (eq("question", aIconType) || eq("error", aIconType) || eq("warning", aIconType))
3920 | {
3921 | strcat(str, aIconType);
3922 | }
3923 | else
3924 | {
3925 | strcat(str, "info");
3926 | }
3927 |
3928 | strcat(str, "',");
3929 | if (some(aTitle))
3930 | {
3931 | strcat(str, "title='");
3932 | strcat(str, aTitle);
3933 | strcat(str, "',");
3934 | }
3935 | if (some(aMessage))
3936 | {
3937 | strcat(str, "message='");
3938 | lpDialogString = str + strlen(str);
3939 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
3940 | strcat(str, "'");
3941 | }
3942 |
3943 | if (eq("yesnocancel", aDialogType))
3944 | {
3945 | strcat(str, `);
3946 | if res is None:
3947 | print(0)
3948 | elif res is False:
3949 | print(2)
3950 | else:
3951 | print(1)
3952 | "`);
3953 | }
3954 | else
3955 | {
3956 | strcat(str, `);
3957 | if res is False:
3958 | print(0)
3959 | else:
3960 | print(1)
3961 | "`);
3962 | }
3963 | }
3964 | else if (gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()))
3965 | {
3966 | if (gxmessagePresent())
3967 | {
3968 | if (lQuery)
3969 | {
3970 | response("gxmessage");
3971 | return 1;
3972 | }
3973 | strcpy(str, "gxmessage");
3974 | }
3975 | else if (gmessagePresent())
3976 | {
3977 | if (lQuery)
3978 | {
3979 | response("gmessage");
3980 | return 1;
3981 | }
3982 | strcpy(str, "gmessage");
3983 | }
3984 | else
3985 | {
3986 | if (lQuery)
3987 | {
3988 | response("xmessage");
3989 | return 1;
3990 | }
3991 | strcpy(str, "xmessage");
3992 | }
3993 |
3994 | if (eq("okcancel", aDialogType))
3995 | {
3996 | strcat(str, " -buttons Ok:1,Cancel:0");
3997 | switch (aDefaultButton)
3998 | {
3999 | case 1:
4000 | strcat(str, " -default Ok");
4001 | break;
4002 | case 0:
4003 | strcat(str, " -default Cancel");
4004 | break;
4005 | default:
4006 | break;
4007 | }
4008 | }
4009 | else if (eq("yesno", aDialogType))
4010 | {
4011 | strcat(str, " -buttons Yes:1,No:0");
4012 | switch (aDefaultButton)
4013 | {
4014 | case 1:
4015 | strcat(str, " -default Yes");
4016 | break;
4017 | case 0:
4018 | strcat(str, " -default No");
4019 | break;
4020 | default:
4021 | break;
4022 | }
4023 | }
4024 | else if (eq("yesnocancel", aDialogType))
4025 | {
4026 | strcat(str, " -buttons Yes:1,No:2,Cancel:0");
4027 | switch (aDefaultButton)
4028 | {
4029 | case 1:
4030 | strcat(str, " -default Yes");
4031 | break;
4032 | case 2:
4033 | strcat(str, " -default No");
4034 | break;
4035 | case 0:
4036 | strcat(str, " -default Cancel");
4037 | break;
4038 | default:
4039 | break;
4040 | }
4041 | }
4042 | else
4043 | {
4044 | strcat(str, " -buttons Ok:1");
4045 | strcat(str, " -default Ok");
4046 | }
4047 |
4048 | strcat(str, " -center \"");
4049 | if (some(aMessage))
4050 | {
4051 | strcat(str, aMessage);
4052 | }
4053 | strcat(str, "\"");
4054 | if (some(aTitle))
4055 | {
4056 | strcat(str, " -title \"");
4057 | strcat(str, aTitle);
4058 | strcat(str, "\"");
4059 | }
4060 | strcat(str, " ; echo $? ");
4061 | }
4062 | else if (xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent())
4063 | {
4064 | if (gdialogPresent())
4065 | {
4066 | if (lQuery)
4067 | {
4068 | response("gdialog");
4069 | return 1;
4070 | }
4071 | lWasGraphicDialog = true;
4072 | strcpy(str, "(gdialog ");
4073 | }
4074 | else if (xdialogPresent())
4075 | {
4076 | if (lQuery)
4077 | {
4078 | response("xdialog");
4079 | return 1;
4080 | }
4081 | lWasGraphicDialog = true;
4082 | strcpy(str, "(Xdialog ");
4083 | }
4084 | else if (dialogName())
4085 | {
4086 | if (lQuery)
4087 | {
4088 | response("dialog");
4089 | return 0;
4090 | }
4091 | if (isTerminalRunning())
4092 | {
4093 | strcpy(str, "(dialog ");
4094 | }
4095 | else
4096 | {
4097 | lWasXterm = true;
4098 | strcpy(str, terminalName());
4099 | strcat(str, "'(");
4100 | strcat(str, dialogName());
4101 | strcat(str, " ");
4102 | }
4103 | }
4104 | else if (isTerminalRunning())
4105 | {
4106 | if (lQuery)
4107 | {
4108 | response("whiptail");
4109 | return 0;
4110 | }
4111 | strcpy(str, "(whiptail ");
4112 | }
4113 | else
4114 | {
4115 | if (lQuery)
4116 | {
4117 | response("whiptail");
4118 | return 0;
4119 | }
4120 | lWasXterm = true;
4121 | strcpy(str, terminalName());
4122 | strcat(str, "'(whiptail ");
4123 | }
4124 |
4125 | if (some(aTitle))
4126 | {
4127 | strcat(str, "--title \"");
4128 | strcat(str, aTitle);
4129 | strcat(str, "\" ");
4130 | }
4131 |
4132 | if (!xdialogPresent() && !gdialogPresent())
4133 | {
4134 | if (eq("okcancel", aDialogType) || eq("yesno", aDialogType) || eq("yesnocancel", aDialogType))
4135 | {
4136 | strcat(str, "--backtitle \"");
4137 | strcat(str, "tab: move focus");
4138 | strcat(str, "\" ");
4139 | }
4140 | }
4141 |
4142 | if (eq("okcancel", aDialogType))
4143 | {
4144 | if (!aDefaultButton)
4145 | {
4146 | strcat(str, "--defaultno ");
4147 | }
4148 | strcat(str,
4149 | "--yes-label \"Ok\" --no-label \"Cancel\" --yesno ");
4150 | }
4151 | else if (eq("yesno", aDialogType))
4152 | {
4153 | if (!aDefaultButton)
4154 | {
4155 | strcat(str, "--defaultno ");
4156 | }
4157 | strcat(str, "--yesno ");
4158 | }
4159 | else if (eq("yesnocancel", aDialogType))
4160 | {
4161 | if (!aDefaultButton)
4162 | {
4163 | strcat(str, "--defaultno ");
4164 | }
4165 | strcat(str, "--menu ");
4166 | }
4167 | else
4168 | {
4169 | strcat(str, "--msgbox ");
4170 | }
4171 | strcat(str, "\"");
4172 | if (some(aMessage))
4173 | {
4174 | strcat(str, aMessage);
4175 | }
4176 | strcat(str, "\" ");
4177 |
4178 | if (lWasGraphicDialog)
4179 | {
4180 | if (eq("yesnocancel", aDialogType))
4181 | {
4182 | strcat(str, "0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;" ~
4183 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~
4184 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes");
4185 | }
4186 | else
4187 | {
4188 | strcat(str, "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi");
4189 | }
4190 | }
4191 | else
4192 | {
4193 | if (eq("yesnocancel", aDialogType))
4194 | {
4195 | strcat(str, "0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;" ~
4196 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~
4197 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes");
4198 |
4199 | if (lWasXterm)
4200 | {
4201 | strcat(str, " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");
4202 | }
4203 | else
4204 | {
4205 | strcat(str, "; clear >/dev/tty");
4206 | }
4207 | }
4208 | else
4209 | {
4210 | strcat(str, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];");
4211 | if (lWasXterm)
4212 | {
4213 | strcat(str,
4214 | "then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
4215 | }
4216 | else
4217 | {
4218 | strcat(str,
4219 | "then echo 1;else echo 0;fi;clear >/dev/tty");
4220 | }
4221 | }
4222 | }
4223 | }
4224 | else if (!isTerminalRunning() && terminalName())
4225 | {
4226 | if (lQuery)
4227 | {
4228 | response("basicinput");
4229 | return 0;
4230 | }
4231 | strcpy(str, terminalName());
4232 | strcat(str, "'");
4233 | if (!gWarningDisplayed && !tinyfd_forceConsole)
4234 | {
4235 | gWarningDisplayed = true;
4236 | strcat(str, "echo \"");
4237 | strcat(str, gTitle.ptr);
4238 | strcat(str, "\";");
4239 | strcat(str, "echo \"");
4240 | strcat(str, tinyfd_needs.ptr);
4241 | strcat(str, "\";echo;echo;");
4242 | }
4243 | if (some(aTitle))
4244 | {
4245 | strcat(str, "echo \"");
4246 | strcat(str, aTitle);
4247 | strcat(str, "\";echo;");
4248 | }
4249 | if (some(aMessage))
4250 | {
4251 | strcat(str, "echo \"");
4252 | strcat(str, aMessage);
4253 | strcat(str, "\"; ");
4254 | }
4255 | if (eq("yesno", aDialogType))
4256 | {
4257 | strcat(str, "echo -n \"y/n: \"; ");
4258 | strcat(str, "stty sane -echo;");
4259 | strcat(str,
4260 | "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);");
4261 | strcat(str,
4262 | "if echo \"$answer\" | grep -iq \"^y\";then\n");
4263 | strcat(str, "\techo 1\nelse\n\techo 0\nfi");
4264 | }
4265 | else if (eq("okcancel", aDialogType))
4266 | {
4267 | strcat(str, "echo -n \"[O]kay/[C]ancel: \"; ");
4268 | strcat(str, "stty sane -echo;");
4269 | strcat(str,
4270 | "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);");
4271 | strcat(str,
4272 | "if echo \"$answer\" | grep -iq \"^o\";then\n");
4273 | strcat(str, "\techo 1\nelse\n\techo 0\nfi");
4274 | }
4275 | else if (eq("yesnocancel", aDialogType))
4276 | {
4277 | strcat(str, "echo -n \"[Y]es/[N]o/[C]ancel: \"; ");
4278 | strcat(str, "stty sane -echo;");
4279 | strcat(str,
4280 | "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);");
4281 | strcat(str,
4282 | "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n");
4283 | strcat(str, "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n");
4284 | strcat(str, "else\n\techo 0\nfi");
4285 | }
4286 | else
4287 | {
4288 | strcat(str, "echo -n \"press enter to continue \"; ");
4289 | strcat(str, "stty sane -echo;");
4290 | strcat(str,
4291 | "answer=$( while ! head -c 1;do true ;done);echo 1");
4292 | }
4293 | strcat(str,
4294 | " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
4295 | }
4296 | else if (!isTerminalRunning() && pythonDbusPresent() && eq("ok", aDialogType))
4297 | {
4298 | if (lQuery)
4299 | {
4300 | response("python-dbus");
4301 | return 1;
4302 | }
4303 | strcpy(str, gPythonName.ptr);
4304 | strcat(str, " -c \"import dbus;bus=dbus.SessionBus();");
4305 | strcat(str, "notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');");
4306 | strcat(str, "notify=dbus.Interface(notif,'org.freedesktop.Notifications');");
4307 | strcat(str, "notify.Notify('',0,'");
4308 | if (some(aIconType))
4309 | {
4310 | strcat(str, aIconType);
4311 | }
4312 | strcat(str, "','");
4313 | if (some(aTitle))
4314 | {
4315 | strcat(str, aTitle);
4316 | }
4317 | strcat(str, "','");
4318 | if (some(aMessage))
4319 | {
4320 | lpDialogString = str + strlen(str);
4321 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
4322 | }
4323 | strcat(str, "','','',5000)\"");
4324 | }
4325 | else if (!isTerminalRunning() && (perlPresent() >= 2) && eq("ok", aDialogType))
4326 | {
4327 | if (lQuery)
4328 | {
4329 | response("perl-dbus");
4330 | return 1;
4331 | }
4332 | sprintf(str, `perl -e "use Net::DBus;
4333 | my \$sessionBus = Net::DBus->session;
4334 | my \$notificationsService = \$sessionBus->get_service('org.freedesktop.Notifications');
4335 | my \$notificationsObject = \$notificationsService->get_object('/org/freedesktop/Notifications',
4336 | 'org.freedesktop.Notifications');
4337 | my \$notificationId;\$notificationId = \$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);"`,
4338 | aIconType ? aIconType : "", aTitle ? aTitle : "", aMessage ? aMessage : "");
4339 | }
4340 | else if (!isTerminalRunning() && notifysendPresent() && eq("ok", aDialogType))
4341 | {
4342 |
4343 | if (lQuery)
4344 | {
4345 | response("notifysend");
4346 | return 1;
4347 | }
4348 | strcpy(str, "notify-send");
4349 | if (some(aIconType))
4350 | {
4351 | strcat(str, " -i '");
4352 | strcat(str, aIconType);
4353 | strcat(str, "'");
4354 | }
4355 | strcat(str, " \"");
4356 | if (some(aTitle))
4357 | {
4358 | strcat(str, aTitle);
4359 | strcat(str, " | ");
4360 | }
4361 | if (some(aMessage))
4362 | {
4363 | replaceSubStr(aMessage, "\n\t", " | ", lBuff.ptr);
4364 | replaceSubStr(aMessage, "\n", " | ", lBuff.ptr);
4365 | replaceSubStr(aMessage, "\t", " ", lBuff.ptr);
4366 | strcat(str, lBuff.ptr);
4367 | }
4368 | strcat(str, "\"");
4369 | }
4370 | else
4371 | {
4372 | if (lQuery)
4373 | {
4374 | response("basicinput");
4375 | return 0;
4376 | }
4377 | if (!gWarningDisplayed && !tinyfd_forceConsole)
4378 | {
4379 | gWarningDisplayed = true;
4380 | printf("\n\n%s\n", gTitle.ptr);
4381 | printf("%s\n\n", tinyfd_needs.ptr);
4382 | }
4383 | if (some(aTitle))
4384 | {
4385 | printf("\n%s\n", aTitle);
4386 | }
4387 |
4388 | tcgetattr(0, &infoOri);
4389 | tcgetattr(0, &info);
4390 | info.c_lflag &= ~ICANON;
4391 | info.c_cc[VMIN] = 1;
4392 | info.c_cc[VTIME] = 0;
4393 | tcsetattr(0, TCSANOW, &info);
4394 | if (eq("yesno", aDialogType))
4395 | {
4396 | do
4397 | {
4398 | if (some(aMessage))
4399 | {
4400 | printf("\n%s\n", aMessage);
4401 | }
4402 | printf("y/n: ");
4403 | fflush(stdout);
4404 | lChar = cast(char)tolower(getchar());
4405 | printf("\n\n");
4406 | } while (lChar != 'y' && lChar != 'n');
4407 | lResult = lChar == 'y' ? 1 : 0;
4408 | }
4409 | else if (eq("okcancel", aDialogType))
4410 | {
4411 | do
4412 | {
4413 | if (some(aMessage))
4414 | {
4415 | printf("\n%s\n", aMessage);
4416 | }
4417 | printf("[O]kay/[C]ancel: ");
4418 | fflush(stdout);
4419 | lChar = cast(char)tolower(getchar());
4420 | printf("\n\n");
4421 | } while (lChar != 'o' && lChar != 'c');
4422 | lResult = lChar == 'o' ? 1 : 0;
4423 | }
4424 | else if (eq("yesnocancel", aDialogType))
4425 | {
4426 | do
4427 | {
4428 | if (some(aMessage))
4429 | {
4430 | printf("\n%s\n", aMessage);
4431 | }
4432 | printf("[Y]es/[N]o/[C]ancel: ");
4433 | fflush(stdout);
4434 | lChar = cast(char)tolower(getchar());
4435 | printf("\n\n");
4436 | } while (lChar != 'y' && lChar != 'n' && lChar != 'c');
4437 | lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0;
4438 | }
4439 | else
4440 | {
4441 | if (some(aMessage))
4442 | {
4443 | printf("\n%s\n\n", aMessage);
4444 | }
4445 | printf("press enter to continue ");
4446 | fflush(stdout);
4447 | getchar();
4448 | printf("\n\n");
4449 | lResult = 1;
4450 | }
4451 | tcsetattr(0, TCSANOW, &infoOri);
4452 | free(str);
4453 | return lResult;
4454 | }
4455 |
4456 | if (tinyfd_verbose)
4457 | printf("str: %s\n", str);
4458 |
4459 | lIn = popen(str, "r");
4460 | if (!lIn)
4461 | {
4462 | free(str);
4463 | return 0;
4464 | }
4465 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
4466 | {
4467 | }
4468 |
4469 | pclose(lIn);
4470 |
4471 | /* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */
4472 | removeLastNL(lBuff.ptr);
4473 | /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */
4474 |
4475 | if (eq("yesnocancel", aDialogType))
4476 | {
4477 | if (lBuff[0] == '1')
4478 | {
4479 | if (eq(lBuff.ptr + 1, "Yes"))
4480 | strcpy(lBuff.ptr, "1");
4481 | else if (eq(lBuff.ptr + 1, "No"))
4482 | strcpy(lBuff.ptr, "2");
4483 | }
4484 | }
4485 | /* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */
4486 |
4487 | lResult = eq(lBuff.ptr, "2") ? 2 : eq(lBuff.ptr, "1") ? 1 : 0;
4488 |
4489 | /* printf( "lResult: %d\n" , lResult ) ; */
4490 | free(str);
4491 | return lResult;
4492 | }
4493 |
4494 | int _notifyPopup(
4495 | const char* aTitle,
4496 | const char* aMessage,
4497 | const char* aIconType)
4498 | {
4499 | char[MAX_PATH_OR_CMD] lBuff = '\0';
4500 | char* str;
4501 | char* lpDialogString;
4502 | FILE* lIn;
4503 | size_t lTitleLen;
4504 | size_t lMessageLen;
4505 | bool lQuery;
4506 |
4507 | if (getenv("SSH_TTY"))
4508 | {
4509 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0);
4510 | }
4511 |
4512 | lQuery = eq(aTitle, "tinyfd_query");
4513 | lTitleLen = aTitle ? strlen(aTitle) : 0;
4514 | lMessageLen = aMessage ? strlen(aMessage) : 0;
4515 | if (!aTitle || !lQuery)
4516 | {
4517 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen);
4518 | }
4519 |
4520 | if (osascriptPresent())
4521 | {
4522 | if (lQuery)
4523 | {
4524 | response("applescript");
4525 | return 1;
4526 | }
4527 |
4528 | strcpy(str, "osascript ");
4529 | if (!osx9orBetter())
4530 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
4531 | strcat(str, " -e 'try' -e 'display notification \"");
4532 | if (some(aMessage))
4533 | {
4534 | strcat(str, aMessage);
4535 | }
4536 | strcat(str, " \" ");
4537 | if (some(aTitle))
4538 | {
4539 | strcat(str, "with title \"");
4540 | strcat(str, aTitle);
4541 | strcat(str, "\" ");
4542 | }
4543 |
4544 | strcat(str, "' -e 'end try'");
4545 | if (!osx9orBetter())
4546 | strcat(str, " -e 'end tell'");
4547 | }
4548 | else if (kdialogPresent())
4549 | {
4550 | if (lQuery)
4551 | {
4552 | response("kdialog");
4553 | return 1;
4554 | }
4555 | strcpy(str, "kdialog");
4556 |
4557 | if (some(aIconType))
4558 | {
4559 | strcat(str, " --icon '");
4560 | strcat(str, aIconType);
4561 | strcat(str, "'");
4562 | }
4563 | if (some(aTitle))
4564 | {
4565 | strcat(str, " --title \"");
4566 | strcat(str, aTitle);
4567 | strcat(str, "\"");
4568 | }
4569 |
4570 | strcat(str, " --passivepopup");
4571 | strcat(str, " \"");
4572 | if (aMessage)
4573 | {
4574 | strcat(str, aMessage);
4575 | }
4576 | strcat(str, " \" 5");
4577 | }
4578 | else if ((zenity3Present() >= 5) || matedialogPresent() || shellementaryPresent() || qarmaPresent())
4579 | {
4580 | /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */
4581 | /* zenity 3.8 show the notification as an alert ok cancel box */
4582 | if (zenity3Present() >= 5)
4583 | {
4584 | if (lQuery)
4585 | {
4586 | response("zenity");
4587 | return 1;
4588 | }
4589 | strcpy(str, "zenity");
4590 | }
4591 | else if (matedialogPresent())
4592 | {
4593 | if (lQuery)
4594 | {
4595 | response("matedialog");
4596 | return 1;
4597 | }
4598 | strcpy(str, "matedialog");
4599 | }
4600 | else if (shellementaryPresent())
4601 | {
4602 | if (lQuery)
4603 | {
4604 | response("shellementary");
4605 | return 1;
4606 | }
4607 | strcpy(str, "shellementary");
4608 | }
4609 | else
4610 | {
4611 | if (lQuery)
4612 | {
4613 | response("qarma");
4614 | return 1;
4615 | }
4616 | strcpy(str, "qarma");
4617 | }
4618 |
4619 | strcat(str, " --notification");
4620 |
4621 | if (some(aIconType))
4622 | {
4623 | strcat(str, " --window-icon '");
4624 | strcat(str, aIconType);
4625 | strcat(str, "'");
4626 | }
4627 |
4628 | strcat(str, " --text \"");
4629 | if (some(aTitle))
4630 | {
4631 | strcat(str, aTitle);
4632 | strcat(str, "\n");
4633 | }
4634 | if (some(aMessage))
4635 | {
4636 | strcat(str, aMessage);
4637 | }
4638 | strcat(str, " \"");
4639 | }
4640 | else if (perlPresent() >= 2)
4641 | {
4642 | if (lQuery)
4643 | {
4644 | response("perl-dbus");
4645 | return 1;
4646 | }
4647 | sprintf(str, `perl -e "use Net::DBus;
4648 | my \$sessionBus = Net::DBus->session;
4649 | my \$notificationsService = \$sessionBus->get_service('org.freedesktop.Notifications');
4650 | my \$notificationsObject = \$notificationsService->get_object('/org/freedesktop/Notifications',
4651 | 'org.freedesktop.Notifications');
4652 | my \$notificationId;\$notificationId = \$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);"`,
4653 | aIconType ? aIconType : "", aTitle ? aTitle : "", aMessage ? aMessage : "");
4654 | }
4655 | else if (pythonDbusPresent())
4656 | {
4657 | if (lQuery)
4658 | {
4659 | response("python-dbus");
4660 | return 1;
4661 | }
4662 | strcpy(str, gPythonName.ptr);
4663 | strcat(str, " -c \"import dbus;bus=dbus.SessionBus();");
4664 | strcat(str, "notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');");
4665 | strcat(str, "notify=dbus.Interface(notif,'org.freedesktop.Notifications');");
4666 | strcat(str, "notify.Notify('',0,'");
4667 | if (some(aIconType))
4668 | {
4669 | strcat(str, aIconType);
4670 | }
4671 | strcat(str, "','");
4672 | if (some(aTitle))
4673 | {
4674 | strcat(str, aTitle);
4675 | }
4676 | strcat(str, "','");
4677 | if (some(aMessage))
4678 | {
4679 | lpDialogString = str + strlen(str);
4680 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
4681 | }
4682 | strcat(str, "','','',5000)\"");
4683 | }
4684 | else if (notifysendPresent())
4685 | {
4686 | if (lQuery)
4687 | {
4688 | response("notifysend");
4689 | return 1;
4690 | }
4691 | strcpy(str, "notify-send");
4692 | if (some(aIconType))
4693 | {
4694 | strcat(str, " -i '");
4695 | strcat(str, aIconType);
4696 | strcat(str, "'");
4697 | }
4698 | strcat(str, " \"");
4699 | if (some(aTitle))
4700 | {
4701 | strcat(str, aTitle);
4702 | strcat(str, " | ");
4703 | }
4704 | if (some(aMessage))
4705 | {
4706 | replaceSubStr(aMessage, "\n\t", " | ", lBuff.ptr);
4707 | replaceSubStr(aMessage, "\n", " | ", lBuff.ptr);
4708 | replaceSubStr(aMessage, "\t", " ", lBuff.ptr);
4709 | strcat(str, lBuff.ptr);
4710 | }
4711 | strcat(str, "\"");
4712 | }
4713 | else
4714 | {
4715 | return _messageBox(aTitle, aMessage, "ok", aIconType, 0);
4716 | }
4717 |
4718 | if (tinyfd_verbose)
4719 | printf("str: %s\n", str);
4720 |
4721 | lIn = popen(str, "r");
4722 | if (!lIn)
4723 | {
4724 | free(str);
4725 | return 0;
4726 | }
4727 |
4728 | pclose(lIn);
4729 | free(str);
4730 | return 1;
4731 | }
4732 |
4733 | const(char*) _inputBox(
4734 | const char* aTitle,
4735 | const char* aMessage,
4736 | const char* aDefaultInput)
4737 | {
4738 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
4739 | const bool lQuery = eq(aTitle, "tinyfd_query");
4740 | char* str;
4741 | char* lpDialogString;
4742 | FILE* lIn;
4743 | int lResult;
4744 | bool lWasGdialog;
4745 | bool lWasGraphicDialog;
4746 | bool lWasXterm;
4747 | bool lWasBasicXterm;
4748 | termios oldt;
4749 | termios newt;
4750 | char* lEOF;
4751 | size_t lTitleLen;
4752 | size_t lMessageLen;
4753 |
4754 | lBuff[0] = '\0';
4755 |
4756 | lTitleLen = aTitle ? strlen(aTitle) : 0;
4757 | lMessageLen = aMessage ? strlen(aMessage) : 0;
4758 | if (!aTitle || !lQuery)
4759 | {
4760 | str = cast(char*)malloc(MAX_PATH_OR_CMD + lTitleLen + lMessageLen);
4761 | }
4762 |
4763 | if (osascriptPresent())
4764 | {
4765 | if (lQuery)
4766 | {
4767 | response("applescript");
4768 | return cast(const(char)*)1;
4769 | }
4770 | strcpy(str, "osascript ");
4771 | if (!osx9orBetter())
4772 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
4773 | strcat(str, " -e 'try' -e 'display dialog \"");
4774 | if (some(aMessage))
4775 | {
4776 | strcat(str, aMessage);
4777 | }
4778 | strcat(str, "\" ");
4779 | strcat(str, "default answer \"");
4780 | if (some(aDefaultInput))
4781 | {
4782 | strcat(str, aDefaultInput);
4783 | }
4784 | strcat(str, "\" ");
4785 | if (!aDefaultInput)
4786 | {
4787 | strcat(str, "hidden answer true ");
4788 | }
4789 | if (some(aTitle))
4790 | {
4791 | strcat(str, "with title \"");
4792 | strcat(str, aTitle);
4793 | strcat(str, "\" ");
4794 | }
4795 | strcat(str, "with icon note' ");
4796 | strcat(str, "-e '\"1\" & text returned of result' ");
4797 | strcat(str, "-e 'on error number -128' ");
4798 | strcat(str, "-e '0' ");
4799 | strcat(str, "-e 'end try'");
4800 | if (!osx9orBetter())
4801 | strcat(str, " -e 'end tell'");
4802 | }
4803 | else if (kdialogPresent())
4804 | {
4805 | if (lQuery)
4806 | {
4807 | response("kdialog");
4808 | return cast(const(char)*)1;
4809 | }
4810 | strcpy(str, "szAnswer=$(kdialog");
4811 |
4812 | if (kdialogPresent() == 2)
4813 | {
4814 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4815 | }
4816 |
4817 | if (!aDefaultInput)
4818 | {
4819 | strcat(str, " --password ");
4820 | }
4821 | else
4822 | {
4823 | strcat(str, " --inputbox ");
4824 | }
4825 | strcat(str, "\"");
4826 | if (some(aMessage))
4827 | {
4828 | strcat(str, aMessage);
4829 | }
4830 | strcat(str, "\" \"");
4831 | if (some(aDefaultInput))
4832 | {
4833 | strcat(str, aDefaultInput);
4834 | }
4835 | strcat(str, "\"");
4836 | if (some(aTitle))
4837 | {
4838 | strcat(str, " --title \"");
4839 | strcat(str, aTitle);
4840 | strcat(str, "\"");
4841 | }
4842 | strcat(str, ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");
4843 | }
4844 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
4845 | {
4846 | if (zenityPresent())
4847 | {
4848 | if (lQuery)
4849 | {
4850 | response("zenity");
4851 | return cast(const(char)*)1;
4852 | }
4853 | strcpy(str, "szAnswer=$(zenity");
4854 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
4855 | {
4856 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4857 | }
4858 | }
4859 | else if (matedialogPresent())
4860 | {
4861 | if (lQuery)
4862 | {
4863 | response("matedialog");
4864 | return cast(const(char)*)1;
4865 | }
4866 | strcpy(str, "szAnswer=$(matedialog");
4867 | }
4868 | else if (shellementaryPresent())
4869 | {
4870 | if (lQuery)
4871 | {
4872 | response("shellementary");
4873 | return cast(const(char)*)1;
4874 | }
4875 | strcpy(str, "szAnswer=$(shellementary");
4876 | }
4877 | else
4878 | {
4879 | if (lQuery)
4880 | {
4881 | response("qarma");
4882 | return cast(const(char)*)1;
4883 | }
4884 | strcpy(str, "szAnswer=$(qarma");
4885 | if (!getenv("SSH_TTY"))
4886 | {
4887 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
4888 | }
4889 | }
4890 | strcat(str, " --entry");
4891 |
4892 | if (some(aTitle))
4893 | {
4894 | strcat(str, " --title=\"");
4895 | strcat(str, aTitle);
4896 | strcat(str, "\"");
4897 | }
4898 | if (some(aMessage))
4899 | {
4900 | strcat(str, " --text=\"");
4901 | strcat(str, aMessage);
4902 | strcat(str, "\"");
4903 | }
4904 | if (some(aDefaultInput))
4905 | {
4906 | strcat(str, " --entry-text=\"");
4907 | strcat(str, aDefaultInput);
4908 | strcat(str, "\"");
4909 | }
4910 | else
4911 | {
4912 | strcat(str, " --hide-text");
4913 | }
4914 | if (tinyfd_silent)
4915 | strcat(str, " 2>/dev/null ");
4916 | strcat(str, ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi");
4917 | }
4918 | else if (gxmessagePresent() || gmessagePresent())
4919 | {
4920 | if (gxmessagePresent())
4921 | {
4922 | if (lQuery)
4923 | {
4924 | response("gxmessage");
4925 | return cast(const(char)*)1;
4926 | }
4927 | strcpy(str, "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \"");
4928 | }
4929 | else
4930 | {
4931 | if (lQuery)
4932 | {
4933 | response("gmessage");
4934 | return cast(const(char)*)1;
4935 | }
4936 | strcpy(str, "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \"");
4937 | }
4938 |
4939 | if (some(aMessage))
4940 | {
4941 | strcat(str, aMessage);
4942 | }
4943 | strcat(str, "\"");
4944 | if (some(aTitle))
4945 | {
4946 | strcat(str, " -title \"");
4947 | strcat(str, aTitle);
4948 | strcat(str, "\" ");
4949 | }
4950 | strcat(str, " -entrytext \"");
4951 | if (some(aDefaultInput))
4952 | {
4953 | strcat(str, aDefaultInput);
4954 | }
4955 | strcat(str, "\"");
4956 | strcat(str, ");echo $?$szAnswer");
4957 | }
4958 | else if (!gdialogPresent() && !xdialogPresent() && tkinter2Present())
4959 | {
4960 | if (lQuery)
4961 | {
4962 | response("python2-tkinter");
4963 | return cast(const(char)*)1;
4964 | }
4965 | strcpy(str, gPython2Name.ptr);
4966 | if (!isTerminalRunning() && isDarwin())
4967 | {
4968 | strcat(str, " -i"); /* for osx without console */
4969 | }
4970 |
4971 | strcat(str,
4972 | " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();");
4973 |
4974 | if (isDarwin())
4975 | {
4976 | strcat(str,
4977 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~
4978 | "frontmost of process \\\"Python\\\" to true' ''');");
4979 | }
4980 |
4981 | strcat(str, "res=tkSimpleDialog.askstring(");
4982 | if (some(aTitle))
4983 | {
4984 | strcat(str, "title='");
4985 | strcat(str, aTitle);
4986 | strcat(str, "',");
4987 | }
4988 | if (some(aMessage))
4989 | {
4990 | strcat(str, "prompt='");
4991 | lpDialogString = str + strlen(str);
4992 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
4993 | strcat(str, "',");
4994 | }
4995 | if (aDefaultInput)
4996 | {
4997 | if (some(aDefaultInput))
4998 | {
4999 | strcat(str, "initialvalue='");
5000 | strcat(str, aDefaultInput);
5001 | strcat(str, "',");
5002 | }
5003 | }
5004 | else
5005 | {
5006 | strcat(str, "show='*'");
5007 | }
5008 | strcat(str, ");\nif res is None :\n\tprint 0");
5009 | strcat(str, "\nelse :\n\tprint '1'+res\n\"");
5010 | }
5011 | else if (!gdialogPresent() && !xdialogPresent() && tkinter3Present())
5012 | {
5013 | if (lQuery)
5014 | {
5015 | response("python3-tkinter");
5016 | return cast(const(char)*)1;
5017 | }
5018 | strcpy(str, gPython3Name.ptr);
5019 | strcat(str,
5020 | " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();");
5021 | strcat(str, "res=simpledialog.askstring(");
5022 | if (some(aTitle))
5023 | {
5024 | strcat(str, "title='");
5025 | strcat(str, aTitle);
5026 | strcat(str, "',");
5027 | }
5028 | if (some(aMessage))
5029 | {
5030 | strcat(str, "prompt='");
5031 | lpDialogString = str + strlen(str);
5032 | replaceSubStr(aMessage, "\n", "\\n", lpDialogString);
5033 | strcat(str, "',");
5034 | }
5035 | if (aDefaultInput)
5036 | {
5037 | if (some(aDefaultInput))
5038 | {
5039 | strcat(str, "initialvalue='");
5040 | strcat(str, aDefaultInput);
5041 | strcat(str, "',");
5042 | }
5043 | }
5044 | else
5045 | {
5046 | strcat(str, "show='*'");
5047 | }
5048 | strcat(str, ");\nif res is None :\n\tprint(0)");
5049 | strcat(str, "\nelse :\n\tprint('1'+res)\n\"");
5050 | }
5051 | else if (gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent())
5052 | {
5053 | if (gdialogPresent())
5054 | {
5055 | if (lQuery)
5056 | {
5057 | response("gdialog");
5058 | return cast(const(char)*)1;
5059 | }
5060 | lWasGraphicDialog = true;
5061 | lWasGdialog = true;
5062 | strcpy(str, "(gdialog ");
5063 | }
5064 | else if (xdialogPresent())
5065 | {
5066 | if (lQuery)
5067 | {
5068 | response("xdialog");
5069 | return cast(const(char)*)1;
5070 | }
5071 | lWasGraphicDialog = true;
5072 | strcpy(str, "(Xdialog ");
5073 | }
5074 | else if (dialogName())
5075 | {
5076 | if (lQuery)
5077 | {
5078 | response("dialog");
5079 | return cast(const(char)*)0;
5080 | }
5081 | if (isTerminalRunning())
5082 | {
5083 | strcpy(str, "(dialog ");
5084 | }
5085 | else
5086 | {
5087 | lWasXterm = true;
5088 | strcpy(str, terminalName());
5089 | strcat(str, "'(");
5090 | strcat(str, dialogName());
5091 | strcat(str, " ");
5092 | }
5093 | }
5094 | else if (isTerminalRunning())
5095 | {
5096 | if (lQuery)
5097 | {
5098 | response("whiptail");
5099 | return cast(const(char)*)0;
5100 | }
5101 | strcpy(str, "(whiptail ");
5102 | }
5103 | else
5104 | {
5105 | if (lQuery)
5106 | {
5107 | response("whiptail");
5108 | return cast(const(char)*)0;
5109 | }
5110 | lWasXterm = true;
5111 | strcpy(str, terminalName());
5112 | strcat(str, "'(whiptail ");
5113 | }
5114 |
5115 | if (some(aTitle))
5116 | {
5117 | strcat(str, "--title \"");
5118 | strcat(str, aTitle);
5119 | strcat(str, "\" ");
5120 | }
5121 |
5122 | if (!xdialogPresent() && !gdialogPresent())
5123 | {
5124 | strcat(str, "--backtitle \"");
5125 | strcat(str, "tab: move focus");
5126 | if (!aDefaultInput && !lWasGdialog)
5127 | {
5128 | strcat(str, " (sometimes nothing, no blink nor star, is shown in text field)");
5129 | }
5130 | strcat(str, "\" ");
5131 | }
5132 |
5133 | if (aDefaultInput || lWasGdialog)
5134 | {
5135 | strcat(str, "--inputbox");
5136 | }
5137 | else
5138 | {
5139 | if (!lWasGraphicDialog && dialogName() && isDialogVersionBetter09b())
5140 | {
5141 | strcat(str, "--insecure ");
5142 | }
5143 | strcat(str, "--passwordbox");
5144 | }
5145 | strcat(str, " \"");
5146 | if (some(aMessage))
5147 | {
5148 | strcat(str, aMessage);
5149 | }
5150 | strcat(str, "\" 10 60 ");
5151 | if (some(aDefaultInput))
5152 | {
5153 | strcat(str, "\"");
5154 | strcat(str, aDefaultInput);
5155 | strcat(str, "\" ");
5156 | }
5157 | if (lWasGraphicDialog)
5158 | {
5159 | strcat(str, ") 2>/tmp/tinyfd.txt;" ~
5160 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~
5161 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes");
5162 | }
5163 | else
5164 | {
5165 | strcat(str, ">/dev/tty ) 2>/tmp/tinyfd.txt;" ~
5166 | "if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;" ~
5167 | "tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes");
5168 |
5169 | if (lWasXterm)
5170 | {
5171 | strcat(str, " >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt");
5172 | }
5173 | else
5174 | {
5175 | strcat(str, "; clear >/dev/tty");
5176 | }
5177 | }
5178 | }
5179 | else if (!isTerminalRunning() && terminalName())
5180 | {
5181 | if (lQuery)
5182 | {
5183 | response("basicinput");
5184 | return cast(const(char)*)0;
5185 | }
5186 | lWasBasicXterm = true;
5187 | strcpy(str, terminalName());
5188 | strcat(str, "'");
5189 | if (!gWarningDisplayed && !tinyfd_forceConsole)
5190 | {
5191 | gWarningDisplayed = true;
5192 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0);
5193 | }
5194 | if (some(aTitle) && !tinyfd_forceConsole)
5195 | {
5196 | strcat(str, "echo \"");
5197 | strcat(str, aTitle);
5198 | strcat(str, "\";echo;");
5199 | }
5200 |
5201 | strcat(str, "echo \"");
5202 | if (some(aMessage))
5203 | {
5204 | strcat(str, aMessage);
5205 | }
5206 | strcat(str, "\";read ");
5207 | if (!aDefaultInput)
5208 | {
5209 | strcat(str, "-s ");
5210 | }
5211 | strcat(str, "-p \"");
5212 | strcat(str, "(esc+enter to cancel): \" ANSWER ");
5213 | strcat(str, ";echo 1$ANSWER >/tmp/tinyfd.txt';");
5214 | strcat(str, "cat -v /tmp/tinyfd.txt");
5215 | }
5216 | else if (!gWarningDisplayed && !isTerminalRunning() && !terminalName())
5217 | {
5218 | gWarningDisplayed = true;
5219 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0);
5220 | if (lQuery)
5221 | {
5222 | response("no_solution");
5223 | return cast(const(char)*)0;
5224 | }
5225 | return null;
5226 | }
5227 | else
5228 | {
5229 | if (lQuery)
5230 | {
5231 | response("basicinput");
5232 | return cast(const(char)*)0;
5233 | }
5234 | if (!gWarningDisplayed && !tinyfd_forceConsole)
5235 | {
5236 | gWarningDisplayed = true;
5237 | _messageBox(gTitle.ptr, tinyfd_needs.ptr, "ok", "warning", 0);
5238 | }
5239 | if (some(aTitle))
5240 | {
5241 | printf("\n%s\n", aTitle);
5242 | }
5243 | if (some(aMessage))
5244 | {
5245 | printf("\n%s\n", aMessage);
5246 | }
5247 | printf("(esc+enter to cancel): ");
5248 | fflush(stdout);
5249 | if (!aDefaultInput)
5250 | {
5251 | tcgetattr(STDIN_FILENO, &oldt);
5252 | newt = oldt;
5253 | newt.c_lflag &= ~ECHO;
5254 | tcsetattr(STDIN_FILENO, TCSANOW, &newt);
5255 | }
5256 |
5257 | lEOF = fgets(lBuff.ptr, MAX_PATH_OR_CMD, stdin);
5258 | /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */
5259 | if (!lEOF || (lBuff[0] == '\0'))
5260 | {
5261 | free(str);
5262 | return null;
5263 | }
5264 |
5265 | if (lBuff[0] == '\n')
5266 | {
5267 | lEOF = fgets(lBuff.ptr, MAX_PATH_OR_CMD, stdin);
5268 | /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */
5269 | if (!lEOF || (lBuff[0] == '\0'))
5270 | {
5271 | free(str);
5272 | return null;
5273 | }
5274 | }
5275 |
5276 | if (!aDefaultInput)
5277 | {
5278 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
5279 | printf("\n");
5280 | }
5281 | printf("\n");
5282 | if (strchr(lBuff.ptr, 27))
5283 | {
5284 | free(str);
5285 | return null;
5286 | }
5287 | removeLastNL(lBuff.ptr);
5288 | free(str);
5289 | return lBuff.ptr;
5290 | }
5291 |
5292 | if (tinyfd_verbose)
5293 | printf("str: %s\n", str);
5294 | lIn = popen(str, "r");
5295 | if (!lIn)
5296 | {
5297 | if (fileExists("/tmp/tinyfd.txt"))
5298 | {
5299 | wipefile("/tmp/tinyfd.txt");
5300 | remove("/tmp/tinyfd.txt");
5301 | }
5302 | if (fileExists("/tmp/tinyfd0.txt"))
5303 | {
5304 | wipefile("/tmp/tinyfd0.txt");
5305 | remove("/tmp/tinyfd0.txt");
5306 | }
5307 | free(str);
5308 | return null;
5309 | }
5310 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
5311 | {
5312 | }
5313 |
5314 | pclose(lIn);
5315 |
5316 | if (fileExists("/tmp/tinyfd.txt"))
5317 | {
5318 | wipefile("/tmp/tinyfd.txt");
5319 | remove("/tmp/tinyfd.txt");
5320 | }
5321 | if (fileExists("/tmp/tinyfd0.txt"))
5322 | {
5323 | wipefile("/tmp/tinyfd0.txt");
5324 | remove("/tmp/tinyfd0.txt");
5325 | }
5326 |
5327 | /* printf( "len Buff: %lu\n" , strlen(lBuff.ptr) ) ; */
5328 | /* printf( "lBuff0: %s\n" , lBuff ) ; */
5329 | removeLastNL(lBuff.ptr);
5330 | /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff.ptr) ) ; */
5331 | if (lWasBasicXterm)
5332 | {
5333 | if (strstr(lBuff.ptr, "^[")) /* esc was pressed */
5334 | {
5335 | free(str);
5336 | return null;
5337 | }
5338 | }
5339 |
5340 | lResult = strncmp(lBuff.ptr, "1", 1) ? 0 : 1;
5341 | /* printf( "lResult: %d \n" , lResult ) ; */
5342 | if (!lResult)
5343 | {
5344 | free(str);
5345 | return null;
5346 | }
5347 | /* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */
5348 | free(str);
5349 |
5350 | return lBuff.ptr + 1;
5351 | }
5352 |
5353 | void osascriptAppendFilters(const TFD_Filter[] filters, char* output)
5354 | {
5355 | assert(output);
5356 |
5357 | if (!filters.length)
5358 | return;
5359 |
5360 | bool isMime;
5361 | const(char)*[MAX_PATTERNS] patterns;
5362 |
5363 | foreach (filter; filters)
5364 | {
5365 | const plen = getValidPatterns(filter, isMime, patterns);
5366 | if (plen == 0 || isMime)
5367 | continue;
5368 |
5369 | strcat(output, "of type {\"");
5370 | foreach (i, pt; patterns[0 .. plen])
5371 | {
5372 | if (strstr(pt, "*."))
5373 | pt += 2;
5374 | if (*pt == '\0')
5375 | continue;
5376 |
5377 | if (i != 0)
5378 | strcat(output, "\",\"");
5379 | strcat(output, pt);
5380 | }
5381 | strcat(output, "\"} ");
5382 | break; // TODO: can multiple?
5383 | }
5384 | }
5385 |
5386 | void kdialogAppendFilters(const TFD_Filter[] filters, char* output)
5387 | {
5388 | assert(output);
5389 |
5390 | if (!filters.length)
5391 | return;
5392 |
5393 | bool isMime;
5394 | const(char)*[MAX_PATTERNS] patterns;
5395 | char[MAX_PATH_OR_CMD] patternsStr = void;
5396 | int j;
5397 |
5398 | // "Description(p1 p2) | p1(p1) | mime1 mime2"
5399 | strcat(output, " \"");
5400 | foreach (filter; filters)
5401 | {
5402 | const plen = getValidPatterns(filter, isMime, patterns);
5403 | if (plen == 0)
5404 | continue;
5405 |
5406 | patternsStr[0] = '\0';
5407 | foreach (i, pt; patterns[0 .. plen])
5408 | {
5409 | if (i != 0)
5410 | strcat(patternsStr.ptr, " ");
5411 | strcat(patternsStr.ptr, pt);
5412 | }
5413 |
5414 | if (j != 0)
5415 | strcat(output, " | ");
5416 | j++;
5417 |
5418 | if (!isMime)
5419 | {
5420 | strcat(output, some(filter.description) ? filter.description : patternsStr.ptr);
5421 | strcat(output, "(");
5422 | }
5423 | strcat(output, patternsStr.ptr);
5424 | if (!isMime)
5425 | {
5426 | strcat(output, ")");
5427 | }
5428 | }
5429 | strcat(output, "\"");
5430 | // no need to add "All files"
5431 | }
5432 |
5433 | void zenityAppendFilters(const TFD_Filter[] filters, char* output)
5434 | {
5435 | assert(output);
5436 |
5437 | if (!filters.length)
5438 | return;
5439 |
5440 | bool isMime;
5441 | const(char)*[MAX_PATTERNS] patterns;
5442 |
5443 | foreach (filter; filters)
5444 | {
5445 | const plen = getValidPatterns(filter, isMime, patterns);
5446 | if (plen == 0 || isMime)
5447 | continue;
5448 |
5449 | strcat(output, " --file-filter='");
5450 | if (some(filter.description))
5451 | {
5452 | strcat(output, filter.description);
5453 | strcat(output, " | ");
5454 | }
5455 | foreach (pt; patterns[0 .. plen])
5456 | {
5457 | strcat(output, pt);
5458 | strcat(output, " ");
5459 | }
5460 | strcat(output, "'");
5461 | }
5462 | strcat(output, " --file-filter='All files | *'");
5463 | }
5464 |
5465 | void tkinterAppendFilters(const TFD_Filter[] filters, char* output)
5466 | {
5467 | assert(output);
5468 |
5469 | if (!filters.length)
5470 | return;
5471 |
5472 | bool isMime;
5473 | const(char)*[MAX_PATTERNS] patterns;
5474 | char placeholder = 'a';
5475 |
5476 | // filetypes=[('Description', ['p1','p2',]), ('a', 'p3'), ('All files', '*')]
5477 | strcat(output, "filetypes=[");
5478 | foreach (filter; filters)
5479 | {
5480 | const plen = getValidPatterns(filter, isMime, patterns);
5481 | if (plen == 0 || isMime)
5482 | continue;
5483 | if (plen == 1 && lastch(patterns[0]) == '*')
5484 | continue; // poor osx behaviour
5485 |
5486 | strcat(output, "('");
5487 | if (some(filter.description))
5488 | {
5489 | strcat(output, filter.description);
5490 | }
5491 | else
5492 | {
5493 | const len = strlen(output);
5494 | output[len] = placeholder;
5495 | output[len + 1] = '\0';
5496 | placeholder++;
5497 | }
5498 | strcat(output, "', ");
5499 | if (plen > 1)
5500 | {
5501 | strcat(output, "[");
5502 | foreach (pt; patterns[0 .. plen])
5503 | {
5504 | strcat(output, "'");
5505 | strcat(output, pt);
5506 | strcat(output, "',");
5507 | }
5508 | strcat(output, "]");
5509 | }
5510 | else
5511 | {
5512 | strcat(output, "'");
5513 | strcat(output, patterns[0]);
5514 | strcat(output, "'");
5515 | }
5516 | strcat(output, "), ");
5517 | }
5518 | strcat(output, "('All files', '*')]");
5519 | }
5520 |
5521 | const(char*) _saveFileDialog(
5522 | const char* aTitle,
5523 | const char* aDefaultPathAndFile,
5524 | const TFD_Filter[] aFilters)
5525 | {
5526 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
5527 | const bool lQuery = eq(aTitle, "tinyfd_query");
5528 | char[MAX_PATH_OR_CMD] str_buf1 = '\0';
5529 | char[MAX_PATH_OR_CMD] str_buf2 = '\0';
5530 | char* str = str_buf1.ptr;
5531 | char* lString = str_buf2.ptr;
5532 | bool lWasGraphicDialog;
5533 | bool lWasXterm;
5534 | const(char)* p;
5535 | FILE* lIn;
5536 | lBuff[0] = '\0';
5537 |
5538 | if (osascriptPresent())
5539 | {
5540 | if (lQuery)
5541 | {
5542 | response("applescript");
5543 | return cast(const(char)*)1;
5544 | }
5545 | strcpy(str, "osascript ");
5546 | if (!osx9orBetter())
5547 | strcat(str, " -e 'tell application \"Finder\"' -e 'Activate'");
5548 | strcat(str, " -e 'try' -e 'POSIX path of ( choose file name ");
5549 | if (some(aTitle))
5550 | {
5551 | strcat(str, "with prompt \"");
5552 | strcat(str, aTitle);
5553 | strcat(str, "\" ");
5554 | }
5555 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
5556 | if (some(lString))
5557 | {
5558 | strcat(str, "default location \"");
5559 | strcat(str, lString);
5560 | strcat(str, "\" ");
5561 | }
5562 | getLastName(lString, aDefaultPathAndFile);
5563 | if (some(lString))
5564 | {
5565 | strcat(str, "default name \"");
5566 | strcat(str, lString);
5567 | strcat(str, "\" ");
5568 | }
5569 | strcat(str, ")' ");
5570 | strcat(str, "-e 'on error number -128' ");
5571 | strcat(str, "-e 'end try'");
5572 | if (!osx9orBetter())
5573 | strcat(str, " -e 'end tell'");
5574 | }
5575 | else if (kdialogPresent())
5576 | {
5577 | if (lQuery)
5578 | {
5579 | response("kdialog");
5580 | return cast(const(char)*)1;
5581 | }
5582 |
5583 | strcpy(str, "kdialog");
5584 | if (kdialogPresent() == 2)
5585 | {
5586 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5587 | }
5588 | strcat(str, " --getsavefilename ");
5589 |
5590 | if (some(aDefaultPathAndFile))
5591 | {
5592 | if (aDefaultPathAndFile[0] != '/')
5593 | {
5594 | strcat(str, "$PWD/");
5595 | }
5596 | strcat(str, "\"");
5597 | strcat(str, aDefaultPathAndFile);
5598 | strcat(str, "\"");
5599 | }
5600 | else
5601 | {
5602 | strcat(str, "$PWD/");
5603 | }
5604 |
5605 | kdialogAppendFilters(aFilters, str);
5606 |
5607 | if (some(aTitle))
5608 | {
5609 | strcat(str, " --title \"");
5610 | strcat(str, aTitle);
5611 | strcat(str, "\"");
5612 | }
5613 | }
5614 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
5615 | {
5616 | if (zenityPresent())
5617 | {
5618 | if (lQuery)
5619 | {
5620 | response("zenity");
5621 | return cast(const(char)*)1;
5622 | }
5623 | strcpy(str, "zenity");
5624 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
5625 | {
5626 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5627 | }
5628 | }
5629 | else if (matedialogPresent())
5630 | {
5631 | if (lQuery)
5632 | {
5633 | response("matedialog");
5634 | return cast(const(char)*)1;
5635 | }
5636 | strcpy(str, "matedialog");
5637 | }
5638 | else if (shellementaryPresent())
5639 | {
5640 | if (lQuery)
5641 | {
5642 | response("shellementary");
5643 | return cast(const(char)*)1;
5644 | }
5645 | strcpy(str, "shellementary");
5646 | }
5647 | else
5648 | {
5649 | if (lQuery)
5650 | {
5651 | response("qarma");
5652 | return cast(const(char)*)1;
5653 | }
5654 | strcpy(str, "qarma");
5655 | if (!getenv("SSH_TTY"))
5656 | {
5657 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5658 | }
5659 | }
5660 | strcat(str, " --file-selection --save --confirm-overwrite");
5661 |
5662 | if (some(aTitle))
5663 | {
5664 | strcat(str, " --title=\"");
5665 | strcat(str, aTitle);
5666 | strcat(str, "\"");
5667 | }
5668 | if (some(aDefaultPathAndFile))
5669 | {
5670 | strcat(str, " --filename=\"");
5671 | strcat(str, aDefaultPathAndFile);
5672 | strcat(str, "\"");
5673 | }
5674 |
5675 | zenityAppendFilters(aFilters, str);
5676 |
5677 | if (tinyfd_silent)
5678 | strcat(str, " 2>/dev/null ");
5679 | }
5680 | else if (!xdialogPresent() && tkinter2Present())
5681 | {
5682 | if (lQuery)
5683 | {
5684 | response("python2-tkinter");
5685 | return cast(const(char)*)1;
5686 | }
5687 | strcpy(str, gPython2Name.ptr);
5688 | if (!isTerminalRunning() && isDarwin())
5689 | {
5690 | strcat(str, " -i"); /* for osx without console */
5691 | }
5692 | strcat(str,
5693 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");
5694 |
5695 | if (isDarwin())
5696 | {
5697 | strcat(str,
5698 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~
5699 | "frontmost of process \\\"Python\\\" to true' ''');");
5700 | }
5701 |
5702 | strcat(str, "print tkFileDialog.asksaveasfilename(");
5703 | if (some(aTitle))
5704 | {
5705 | strcat(str, "title='");
5706 | strcat(str, aTitle);
5707 | strcat(str, "',");
5708 | }
5709 | if (some(aDefaultPathAndFile))
5710 | {
5711 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
5712 | if (some(lString))
5713 | {
5714 | strcat(str, "initialdir='");
5715 | strcat(str, lString);
5716 | strcat(str, "',");
5717 | }
5718 | getLastName(lString, aDefaultPathAndFile);
5719 | if (some(lString))
5720 | {
5721 | strcat(str, "initialfile='");
5722 | strcat(str, lString);
5723 | strcat(str, "',");
5724 | }
5725 | }
5726 |
5727 | tkinterAppendFilters(aFilters, str);
5728 |
5729 | strcat(str, ")\"");
5730 | }
5731 | else if (!xdialogPresent() && tkinter3Present())
5732 | {
5733 | if (lQuery)
5734 | {
5735 | response("python3-tkinter");
5736 | return cast(const(char)*)1;
5737 | }
5738 | strcpy(str, gPython3Name.ptr);
5739 | strcat(str,
5740 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");
5741 | strcat(str, "print( filedialog.asksaveasfilename(");
5742 | if (some(aTitle))
5743 | {
5744 | strcat(str, "title='");
5745 | strcat(str, aTitle);
5746 | strcat(str, "',");
5747 | }
5748 | if (some(aDefaultPathAndFile))
5749 | {
5750 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
5751 | if (some(lString))
5752 | {
5753 | strcat(str, "initialdir='");
5754 | strcat(str, lString);
5755 | strcat(str, "',");
5756 | }
5757 | getLastName(lString, aDefaultPathAndFile);
5758 | if (some(lString))
5759 | {
5760 | strcat(str, "initialfile='");
5761 | strcat(str, lString);
5762 | strcat(str, "',");
5763 | }
5764 | }
5765 |
5766 | tkinterAppendFilters(aFilters, str);
5767 |
5768 | strcat(str, "))\"");
5769 | }
5770 | else if (xdialogPresent() || dialogName())
5771 | {
5772 | if (xdialogPresent())
5773 | {
5774 | if (lQuery)
5775 | {
5776 | response("xdialog");
5777 | return cast(const(char)*)1;
5778 | }
5779 | lWasGraphicDialog = true;
5780 | strcpy(str, "(Xdialog ");
5781 | }
5782 | else if (isTerminalRunning())
5783 | {
5784 | if (lQuery)
5785 | {
5786 | response("dialog");
5787 | return cast(const(char)*)0;
5788 | }
5789 | strcpy(str, "(dialog ");
5790 | }
5791 | else
5792 | {
5793 | if (lQuery)
5794 | {
5795 | response("dialog");
5796 | return cast(const(char)*)0;
5797 | }
5798 | lWasXterm = true;
5799 | strcpy(str, terminalName());
5800 | strcat(str, "'(");
5801 | strcat(str, dialogName());
5802 | strcat(str, " ");
5803 | }
5804 |
5805 | if (some(aTitle))
5806 | {
5807 | strcat(str, "--title \"");
5808 | strcat(str, aTitle);
5809 | strcat(str, "\" ");
5810 | }
5811 |
5812 | if (!xdialogPresent() && !gdialogPresent())
5813 | {
5814 | strcat(str, "--backtitle \"");
5815 | strcat(str,
5816 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
5817 | strcat(str, "\" ");
5818 | }
5819 |
5820 | strcat(str, "--fselect \"");
5821 | if (some(aDefaultPathAndFile))
5822 | {
5823 | if (!strchr(aDefaultPathAndFile, '/'))
5824 | {
5825 | strcat(str, "./");
5826 | }
5827 | strcat(str, aDefaultPathAndFile);
5828 | }
5829 | else if (!isTerminalRunning() && !lWasGraphicDialog)
5830 | {
5831 | strcat(str, getenv("HOME"));
5832 | strcat(str, "/");
5833 | }
5834 | else
5835 | {
5836 | strcat(str, "./");
5837 | }
5838 |
5839 | if (lWasGraphicDialog)
5840 | {
5841 | strcat(str, "\" 0 60 ) 2>&1 ");
5842 | }
5843 | else
5844 | {
5845 | strcat(str, "\" 0 60 >/dev/tty) ");
5846 | if (lWasXterm)
5847 | {
5848 | strcat(str,
5849 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
5850 | }
5851 | else
5852 | {
5853 | strcat(str, "2>&1 ; clear >/dev/tty");
5854 | }
5855 | }
5856 | }
5857 | else
5858 | {
5859 | if (lQuery)
5860 | {
5861 | return _inputBox(aTitle, null, null);
5862 | }
5863 | p = _inputBox(aTitle, "Save file", "");
5864 | getPathWithoutFinalSlash(lString, p);
5865 | if (!dirExists(lString))
5866 | return null;
5867 | getLastName(lString, p);
5868 | if (!some(lString))
5869 | return null;
5870 | return p;
5871 | }
5872 |
5873 | if (tinyfd_verbose)
5874 | printf("str: %s\n", str);
5875 | lIn = popen(str, "r");
5876 | if (!lIn)
5877 | return null;
5878 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
5879 | {
5880 | }
5881 | pclose(lIn);
5882 | removeLastNL(lBuff.ptr);
5883 | /* printf( "lBuff: %s\n" , lBuff ) ; */
5884 | if (!some(lBuff.ptr))
5885 | return null;
5886 | getPathWithoutFinalSlash(lString, lBuff.ptr);
5887 | if (!dirExists(lString))
5888 | return null;
5889 | getLastName(lString, lBuff.ptr);
5890 | if (!filenameValid(lString))
5891 | return null;
5892 | return lBuff.ptr;
5893 | }
5894 |
5895 | const(char*) _openFileDialog(
5896 | const char* aTitle,
5897 | const char* aDefaultPathAndFile,
5898 | const TFD_Filter[] aFilters,
5899 | const bool aAllowMultipleSelects)
5900 | {
5901 | static char[MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD] lBuff = '\0';
5902 | const bool lQuery = eq(aTitle, "tinyfd_query");
5903 | char[MAX_PATH_OR_CMD] str_buf1 = '\0';
5904 | char[MAX_PATH_OR_CMD] str_buf2 = '\0';
5905 | char* str = str_buf1.ptr;
5906 | char* lString = str_buf2.ptr;
5907 | FILE* lIn;
5908 | char* p;
5909 | const(char)* p2;
5910 | bool lWasKdialog;
5911 | bool lWasGraphicDialog;
5912 | bool lWasXterm;
5913 | lBuff[0] = '\0';
5914 |
5915 | if (osascriptPresent())
5916 | {
5917 | if (lQuery)
5918 | {
5919 | response("applescript");
5920 | return cast(const(char)*)1;
5921 | }
5922 | strcpy(str, "osascript ");
5923 | if (!osx9orBetter())
5924 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
5925 | strcat(str, " -e 'try' -e '");
5926 | if (!aAllowMultipleSelects)
5927 | {
5928 |
5929 | strcat(str, "POSIX path of ( ");
5930 | }
5931 | else
5932 | {
5933 | strcat(str, "set mylist to ");
5934 | }
5935 | strcat(str, "choose file ");
5936 | if (some(aTitle))
5937 | {
5938 | strcat(str, "with prompt \"");
5939 | strcat(str, aTitle);
5940 | strcat(str, "\" ");
5941 | }
5942 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
5943 | if (some(lString))
5944 | {
5945 | strcat(str, "default location \"");
5946 | strcat(str, lString);
5947 | strcat(str, "\" ");
5948 | }
5949 |
5950 | osascriptAppendFilters(aFilters, str);
5951 |
5952 | if (aAllowMultipleSelects)
5953 | {
5954 | strcat(str, "multiple selections allowed true ' ");
5955 | strcat(str,
5956 | "-e 'set mystring to POSIX path of item 1 of mylist' ");
5957 | strcat(str,
5958 | "-e 'repeat with i from 2 to the count of mylist' ");
5959 | strcat(str, "-e 'set mystring to mystring & \"|\"' ");
5960 | strcat(str,
5961 | "-e 'set mystring to mystring & POSIX path of item i of mylist' ");
5962 | strcat(str, "-e 'end repeat' ");
5963 | strcat(str, "-e 'mystring' ");
5964 | }
5965 | else
5966 | {
5967 | strcat(str, ")' ");
5968 | }
5969 | strcat(str, "-e 'on error number -128' ");
5970 | strcat(str, "-e 'end try'");
5971 | if (!osx9orBetter())
5972 | strcat(str, " -e 'end tell'");
5973 | }
5974 | else if (kdialogPresent())
5975 | {
5976 | if (lQuery)
5977 | {
5978 | response("kdialog");
5979 | return cast(const(char)*)1;
5980 | }
5981 | lWasKdialog = true;
5982 |
5983 | strcpy(str, "kdialog");
5984 | if (kdialogPresent() == 2)
5985 | {
5986 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
5987 | }
5988 | strcat(str, " --getopenfilename ");
5989 |
5990 | if (some(aDefaultPathAndFile))
5991 | {
5992 | if (aDefaultPathAndFile[0] != '/')
5993 | {
5994 | strcat(str, "$PWD/");
5995 | }
5996 | strcat(str, "\"");
5997 | strcat(str, aDefaultPathAndFile);
5998 | strcat(str, "\"");
5999 | }
6000 | else
6001 | {
6002 | strcat(str, "$PWD/");
6003 | }
6004 |
6005 | kdialogAppendFilters(aFilters, str);
6006 |
6007 | if (aAllowMultipleSelects)
6008 | {
6009 | strcat(str, " --multiple --separate-output");
6010 | }
6011 | if (some(aTitle))
6012 | {
6013 | strcat(str, " --title \"");
6014 | strcat(str, aTitle);
6015 | strcat(str, "\"");
6016 | }
6017 | }
6018 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
6019 | {
6020 | if (zenityPresent())
6021 | {
6022 | if (lQuery)
6023 | {
6024 | response("zenity");
6025 | return cast(const(char)*)1;
6026 | }
6027 | strcpy(str, "zenity");
6028 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
6029 | {
6030 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6031 | }
6032 | }
6033 | else if (matedialogPresent())
6034 | {
6035 | if (lQuery)
6036 | {
6037 | response("matedialog");
6038 | return cast(const(char)*)1;
6039 | }
6040 | strcpy(str, "matedialog");
6041 | }
6042 | else if (shellementaryPresent())
6043 | {
6044 | if (lQuery)
6045 | {
6046 | response("shellementary");
6047 | return cast(const(char)*)1;
6048 | }
6049 | strcpy(str, "shellementary");
6050 | }
6051 | else
6052 | {
6053 | if (lQuery)
6054 | {
6055 | response("qarma");
6056 | return cast(const(char)*)1;
6057 | }
6058 | strcpy(str, "qarma");
6059 | if (!getenv("SSH_TTY"))
6060 | {
6061 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6062 | }
6063 | }
6064 | strcat(str, " --file-selection");
6065 |
6066 | if (aAllowMultipleSelects)
6067 | {
6068 | strcat(str, " --multiple");
6069 | }
6070 | if (some(aTitle))
6071 | {
6072 | strcat(str, " --title=\"");
6073 | strcat(str, aTitle);
6074 | strcat(str, "\"");
6075 | }
6076 | if (some(aDefaultPathAndFile))
6077 | {
6078 | strcat(str, " --filename=\"");
6079 | strcat(str, aDefaultPathAndFile);
6080 | strcat(str, "\"");
6081 | }
6082 |
6083 | zenityAppendFilters(aFilters, str);
6084 |
6085 | if (tinyfd_silent)
6086 | strcat(str, " 2>/dev/null ");
6087 | }
6088 | else if (tkinter2Present())
6089 | {
6090 | if (lQuery)
6091 | {
6092 | response("python2-tkinter");
6093 | return cast(const(char)*)1;
6094 | }
6095 | strcpy(str, gPython2Name.ptr);
6096 | if (!isTerminalRunning() && isDarwin())
6097 | {
6098 | strcat(str, " -i"); /* for osx without console */
6099 | }
6100 | strcat(str,
6101 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");
6102 |
6103 | if (isDarwin())
6104 | {
6105 | strcat(str,
6106 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~
6107 | "frontmost of process \\\"Python\\\" to true' ''');");
6108 | }
6109 | strcat(str, "lFiles=tkFileDialog.askopenfilename(");
6110 | if (aAllowMultipleSelects)
6111 | {
6112 | strcat(str, "multiple=1,");
6113 | }
6114 | if (some(aTitle))
6115 | {
6116 | strcat(str, "title='");
6117 | strcat(str, aTitle);
6118 | strcat(str, "',");
6119 | }
6120 | if (some(aDefaultPathAndFile))
6121 | {
6122 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
6123 | if (some(lString))
6124 | {
6125 | strcat(str, "initialdir='");
6126 | strcat(str, lString);
6127 | strcat(str, "',");
6128 | }
6129 | getLastName(lString, aDefaultPathAndFile);
6130 | if (some(lString))
6131 | {
6132 | strcat(str, "initialfile='");
6133 | strcat(str, lString);
6134 | strcat(str, "',");
6135 | }
6136 | }
6137 |
6138 | tkinterAppendFilters(aFilters, str);
6139 |
6140 | strcat(str, `);
6141 | if not isinstance(lFiles, tuple):
6142 | print lFiles
6143 | else:
6144 | lFilesString=''
6145 | for lFile in lFiles:
6146 | lFilesString += str(lFile) + '|'
6147 | print lFilesString[:-1]
6148 | "`);
6149 | }
6150 | else if (tkinter3Present())
6151 | {
6152 | if (lQuery)
6153 | {
6154 | response("python3-tkinter");
6155 | return cast(const(char)*)1;
6156 | }
6157 | strcpy(str, gPython3Name.ptr);
6158 | strcat(str,
6159 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");
6160 | strcat(str, "lFiles=filedialog.askopenfilename(");
6161 | if (aAllowMultipleSelects)
6162 | {
6163 | strcat(str, "multiple=1,");
6164 | }
6165 | if (some(aTitle))
6166 | {
6167 | strcat(str, "title='");
6168 | strcat(str, aTitle);
6169 | strcat(str, "',");
6170 | }
6171 | if (some(aDefaultPathAndFile))
6172 | {
6173 | getPathWithoutFinalSlash(lString, aDefaultPathAndFile);
6174 | if (some(lString))
6175 | {
6176 | strcat(str, "initialdir='");
6177 | strcat(str, lString);
6178 | strcat(str, "',");
6179 | }
6180 | getLastName(lString, aDefaultPathAndFile);
6181 | if (some(lString))
6182 | {
6183 | strcat(str, "initialfile='");
6184 | strcat(str, lString);
6185 | strcat(str, "',");
6186 | }
6187 | }
6188 |
6189 | tkinterAppendFilters(aFilters, str);
6190 |
6191 | strcat(str, `);
6192 | if not isinstance(lFiles, tuple):
6193 | print(lFiles)
6194 | else:
6195 | lFilesString = ''
6196 | for lFile in lFiles:
6197 | lFilesString += str(lFile) + '|'
6198 | print(lFilesString[:-1])
6199 | "`);
6200 | }
6201 | else if (xdialogPresent() || dialogName())
6202 | {
6203 | if (xdialogPresent())
6204 | {
6205 | if (lQuery)
6206 | {
6207 | response("xdialog");
6208 | return cast(const(char)*)1;
6209 | }
6210 | lWasGraphicDialog = true;
6211 | strcpy(str, "(Xdialog ");
6212 | }
6213 | else if (isTerminalRunning())
6214 | {
6215 | if (lQuery)
6216 | {
6217 | response("dialog");
6218 | return cast(const(char)*)0;
6219 | }
6220 | strcpy(str, "(dialog ");
6221 | }
6222 | else
6223 | {
6224 | if (lQuery)
6225 | {
6226 | response("dialog");
6227 | return cast(const(char)*)0;
6228 | }
6229 | lWasXterm = true;
6230 | strcpy(str, terminalName());
6231 | strcat(str, "'(");
6232 | strcat(str, dialogName());
6233 | strcat(str, " ");
6234 | }
6235 |
6236 | if (some(aTitle))
6237 | {
6238 | strcat(str, "--title \"");
6239 | strcat(str, aTitle);
6240 | strcat(str, "\" ");
6241 | }
6242 |
6243 | if (!xdialogPresent() && !gdialogPresent())
6244 | {
6245 | strcat(str, "--backtitle \"");
6246 | strcat(str,
6247 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
6248 | strcat(str, "\" ");
6249 | }
6250 |
6251 | strcat(str, "--fselect \"");
6252 | if (some(aDefaultPathAndFile))
6253 | {
6254 | if (!strchr(aDefaultPathAndFile, '/'))
6255 | {
6256 | strcat(str, "./");
6257 | }
6258 | strcat(str, aDefaultPathAndFile);
6259 | }
6260 | else if (!isTerminalRunning() && !lWasGraphicDialog)
6261 | {
6262 | strcat(str, getenv("HOME"));
6263 | strcat(str, "/");
6264 | }
6265 | else
6266 | {
6267 | strcat(str, "./");
6268 | }
6269 |
6270 | if (lWasGraphicDialog)
6271 | {
6272 | strcat(str, "\" 0 60 ) 2>&1 ");
6273 | }
6274 | else
6275 | {
6276 | strcat(str, "\" 0 60 >/dev/tty) ");
6277 | if (lWasXterm)
6278 | {
6279 | strcat(str,
6280 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
6281 | }
6282 | else
6283 | {
6284 | strcat(str, "2>&1 ; clear >/dev/tty");
6285 | }
6286 | }
6287 | }
6288 | else
6289 | {
6290 | if (lQuery)
6291 | {
6292 | return _inputBox(aTitle, null, null);
6293 | }
6294 | p2 = _inputBox(aTitle, "Open file", "");
6295 | if (!fileExists(p2))
6296 | return null;
6297 | return p2;
6298 | }
6299 |
6300 | if (tinyfd_verbose)
6301 | printf("str: %s\n", str);
6302 | lIn = popen(str, "r");
6303 | if (!lIn)
6304 | return null;
6305 |
6306 | lBuff[0] = '\0';
6307 | p = lBuff.ptr;
6308 | while (fgets(p, lBuff.sizeof, lIn) !is null)
6309 | {
6310 | p += strlen(p);
6311 | }
6312 | pclose(lIn);
6313 | removeLastNL(lBuff.ptr);
6314 | /* printf( "lBuff: %s\n" , lBuff ) ; */
6315 | if (lWasKdialog && aAllowMultipleSelects)
6316 | {
6317 | p = lBuff.ptr;
6318 | while (p)
6319 | {
6320 | p = strchr(p, '\n');
6321 | *p = '|';
6322 | }
6323 | }
6324 | /* printf( "lBuff2: %s\n" , lBuff ) ; */
6325 | if (!some(lBuff.ptr))
6326 | return null;
6327 |
6328 | if (aAllowMultipleSelects && strchr(lBuff.ptr, '|'))
6329 | {
6330 | p2 = ensureFilesExist(lBuff.ptr, lBuff.ptr);
6331 | }
6332 | else if (fileExists(lBuff.ptr))
6333 | {
6334 | p2 = lBuff.ptr;
6335 | }
6336 | else
6337 | return null;
6338 | /* printf( "lBuff3: %s\n" , p2 ) ; */
6339 |
6340 | return p2;
6341 | }
6342 |
6343 | const(char*) _selectFolderDialog(const char* aTitle, const char* aDefaultPath)
6344 | {
6345 | static char[MAX_PATH_OR_CMD] lBuff = '\0';
6346 | const bool lQuery = eq(aTitle, "tinyfd_query");
6347 | char[MAX_PATH_OR_CMD] str_buf = '\0';
6348 | char* str = str_buf.ptr;
6349 | FILE* lIn;
6350 | const(char)* p;
6351 | bool lWasGraphicDialog;
6352 | bool lWasXterm;
6353 | lBuff[0] = '\0';
6354 |
6355 | if (osascriptPresent())
6356 | {
6357 | if (lQuery)
6358 | {
6359 | response("applescript");
6360 | return cast(const(char)*)1;
6361 | }
6362 | strcpy(str, "osascript ");
6363 | if (!osx9orBetter())
6364 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
6365 | strcat(str, " -e 'try' -e 'POSIX path of ( choose folder ");
6366 | if (some(aTitle))
6367 | {
6368 | strcat(str, "with prompt \"");
6369 | strcat(str, aTitle);
6370 | strcat(str, "\" ");
6371 | }
6372 | if (some(aDefaultPath))
6373 | {
6374 | strcat(str, "default location \"");
6375 | strcat(str, aDefaultPath);
6376 | strcat(str, "\" ");
6377 | }
6378 | strcat(str, ")' ");
6379 | strcat(str, "-e 'on error number -128' ");
6380 | strcat(str, "-e 'end try'");
6381 | if (!osx9orBetter())
6382 | strcat(str, " -e 'end tell'");
6383 | }
6384 | else if (kdialogPresent())
6385 | {
6386 | if (lQuery)
6387 | {
6388 | response("kdialog");
6389 | return cast(const(char)*)1;
6390 | }
6391 | strcpy(str, "kdialog");
6392 | if (kdialogPresent() == 2)
6393 | {
6394 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6395 | }
6396 | strcat(str, " --getexistingdirectory ");
6397 |
6398 | if (some(aDefaultPath))
6399 | {
6400 | if (aDefaultPath[0] != '/')
6401 | {
6402 | strcat(str, "$PWD/");
6403 | }
6404 | strcat(str, "\"");
6405 | strcat(str, aDefaultPath);
6406 | strcat(str, "\"");
6407 | }
6408 | else
6409 | {
6410 | strcat(str, "$PWD/");
6411 | }
6412 |
6413 | if (some(aTitle))
6414 | {
6415 | strcat(str, " --title \"");
6416 | strcat(str, aTitle);
6417 | strcat(str, "\"");
6418 | }
6419 | }
6420 | else if (zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
6421 | {
6422 | if (zenityPresent())
6423 | {
6424 | if (lQuery)
6425 | {
6426 | response("zenity");
6427 | return cast(const(char)*)1;
6428 | }
6429 | strcpy(str, "zenity");
6430 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
6431 | {
6432 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6433 | }
6434 | }
6435 | else if (matedialogPresent())
6436 | {
6437 | if (lQuery)
6438 | {
6439 | response("matedialog");
6440 | return cast(const(char)*)1;
6441 | }
6442 | strcpy(str, "matedialog");
6443 | }
6444 | else if (shellementaryPresent())
6445 | {
6446 | if (lQuery)
6447 | {
6448 | response("shellementary");
6449 | return cast(const(char)*)1;
6450 | }
6451 | strcpy(str, "shellementary");
6452 | }
6453 | else
6454 | {
6455 | if (lQuery)
6456 | {
6457 | response("qarma");
6458 | return cast(const(char)*)1;
6459 | }
6460 | strcpy(str, "qarma");
6461 | if (!getenv("SSH_TTY"))
6462 | {
6463 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6464 | }
6465 | }
6466 | strcat(str, " --file-selection --directory");
6467 |
6468 | if (some(aTitle))
6469 | {
6470 | strcat(str, " --title=\"");
6471 | strcat(str, aTitle);
6472 | strcat(str, "\"");
6473 | }
6474 | if (some(aDefaultPath))
6475 | {
6476 | strcat(str, " --filename=\"");
6477 | strcat(str, aDefaultPath);
6478 | strcat(str, "\"");
6479 | }
6480 | if (tinyfd_silent)
6481 | strcat(str, " 2>/dev/null ");
6482 | }
6483 | else if (!xdialogPresent() && tkinter2Present())
6484 | {
6485 | if (lQuery)
6486 | {
6487 | response("python2-tkinter");
6488 | return cast(const(char)*)1;
6489 | }
6490 | strcpy(str, gPython2Name.ptr);
6491 | if (!isTerminalRunning() && isDarwin())
6492 | {
6493 | strcat(str, " -i"); /* for osx without console */
6494 | }
6495 | strcat(str,
6496 | " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();");
6497 |
6498 | if (isDarwin())
6499 | {
6500 | strcat(str,
6501 | "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set " ~
6502 | "frontmost of process \\\"Python\\\" to true' ''');");
6503 | }
6504 |
6505 | strcat(str, "print tkFileDialog.askdirectory(");
6506 | if (some(aTitle))
6507 | {
6508 | strcat(str, "title='");
6509 | strcat(str, aTitle);
6510 | strcat(str, "',");
6511 | }
6512 | if (some(aDefaultPath))
6513 | {
6514 | strcat(str, "initialdir='");
6515 | strcat(str, aDefaultPath);
6516 | strcat(str, "'");
6517 | }
6518 | strcat(str, ")\"");
6519 | }
6520 | else if (!xdialogPresent() && tkinter3Present())
6521 | {
6522 | if (lQuery)
6523 | {
6524 | response("python3-tkinter");
6525 | return cast(const(char)*)1;
6526 | }
6527 | strcpy(str, gPython3Name.ptr);
6528 | strcat(str,
6529 | " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();");
6530 | strcat(str, "print( filedialog.askdirectory(");
6531 | if (some(aTitle))
6532 | {
6533 | strcat(str, "title='");
6534 | strcat(str, aTitle);
6535 | strcat(str, "',");
6536 | }
6537 | if (some(aDefaultPath))
6538 | {
6539 | strcat(str, "initialdir='");
6540 | strcat(str, aDefaultPath);
6541 | strcat(str, "'");
6542 | }
6543 | strcat(str, ") )\"");
6544 | }
6545 | else if (xdialogPresent() || dialogName())
6546 | {
6547 | if (xdialogPresent())
6548 | {
6549 | if (lQuery)
6550 | {
6551 | response("xdialog");
6552 | return cast(const(char)*)1;
6553 | }
6554 | lWasGraphicDialog = true;
6555 | strcpy(str, "(Xdialog ");
6556 | }
6557 | else if (isTerminalRunning())
6558 | {
6559 | if (lQuery)
6560 | {
6561 | response("dialog");
6562 | return cast(const(char)*)0;
6563 | }
6564 | strcpy(str, "(dialog ");
6565 | }
6566 | else
6567 | {
6568 | if (lQuery)
6569 | {
6570 | response("dialog");
6571 | return cast(const(char)*)0;
6572 | }
6573 | lWasXterm = true;
6574 | strcpy(str, terminalName());
6575 | strcat(str, "'(");
6576 | strcat(str, dialogName());
6577 | strcat(str, " ");
6578 | }
6579 |
6580 | if (some(aTitle))
6581 | {
6582 | strcat(str, "--title \"");
6583 | strcat(str, aTitle);
6584 | strcat(str, "\" ");
6585 | }
6586 |
6587 | if (!xdialogPresent() && !gdialogPresent())
6588 | {
6589 | strcat(str, "--backtitle \"");
6590 | strcat(str,
6591 | "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY");
6592 | strcat(str, "\" ");
6593 | }
6594 |
6595 | strcat(str, "--dselect \"");
6596 | if (some(aDefaultPath))
6597 | {
6598 | strcat(str, aDefaultPath);
6599 | ensureFinalSlash(str);
6600 | }
6601 | else if (!isTerminalRunning() && !lWasGraphicDialog)
6602 | {
6603 | strcat(str, getenv("HOME"));
6604 | strcat(str, "/");
6605 | }
6606 | else
6607 | {
6608 | strcat(str, "./");
6609 | }
6610 |
6611 | if (lWasGraphicDialog)
6612 | {
6613 | strcat(str, "\" 0 60 ) 2>&1 ");
6614 | }
6615 | else
6616 | {
6617 | strcat(str, "\" 0 60 >/dev/tty) ");
6618 | if (lWasXterm)
6619 | {
6620 | strcat(str,
6621 | "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt");
6622 | }
6623 | else
6624 | {
6625 | strcat(str, "2>&1 ; clear >/dev/tty");
6626 | }
6627 | }
6628 | }
6629 | else
6630 | {
6631 | if (lQuery)
6632 | {
6633 | return _inputBox(aTitle, null, null);
6634 | }
6635 | p = _inputBox(aTitle, "Select folder", "");
6636 | if (!dirExists(p))
6637 | return null;
6638 | return p;
6639 | }
6640 | if (tinyfd_verbose)
6641 | printf("str: %s\n", str);
6642 | lIn = popen(str, "r");
6643 | if (!lIn)
6644 | return null;
6645 |
6646 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
6647 | {
6648 | }
6649 | pclose(lIn);
6650 | removeLastNL(lBuff.ptr);
6651 | /* printf( "lBuff: %s\n" , lBuff ) ; */
6652 | if (!dirExists(lBuff.ptr))
6653 | return null;
6654 | return lBuff.ptr;
6655 | }
6656 |
6657 | const(char*) _colorChooser(
6658 | const char* aTitle,
6659 | const char* aDefaultHexRGB,
6660 | ref const ubyte[3] aDefaultRGB,
6661 | ref ubyte[3] aoResultRGB)
6662 | {
6663 | static char[128] lBuff = '\0';
6664 | const bool lQuery = eq(aTitle, "tinyfd_query");
6665 | char[128] tmp_buf = '\0';
6666 | char[MAX_PATH_OR_CMD] str_buf = '\0';
6667 | char* lTmp = tmp_buf.ptr;
6668 | char* str = str_buf.ptr;
6669 | char[8] lDefaultHexRGB = '\0';
6670 | char* lpDefaultHexRGB;
6671 | ubyte[3] lDefaultRGB;
6672 | const(char)* p;
6673 | FILE* lIn;
6674 | bool lWasZenity3;
6675 | bool lWasOsascript;
6676 | bool lWasXdialog;
6677 | lBuff[0] = '\0';
6678 |
6679 | if (aDefaultHexRGB)
6680 | {
6681 | Hex2RGB(aDefaultHexRGB, lDefaultRGB);
6682 | lpDefaultHexRGB = cast(char*)aDefaultHexRGB;
6683 | }
6684 | else
6685 | {
6686 | lDefaultRGB[0] = aDefaultRGB[0];
6687 | lDefaultRGB[1] = aDefaultRGB[1];
6688 | lDefaultRGB[2] = aDefaultRGB[2];
6689 | RGB2Hex(aDefaultRGB, lDefaultHexRGB.ptr);
6690 | lpDefaultHexRGB = lDefaultHexRGB.ptr;
6691 | }
6692 |
6693 | if (osascriptPresent())
6694 | {
6695 | if (lQuery)
6696 | {
6697 | response("applescript");
6698 | return cast(const(char)*)1;
6699 | }
6700 | lWasOsascript = true;
6701 | strcpy(str, "osascript");
6702 |
6703 | if (!osx9orBetter())
6704 | {
6705 | strcat(str, " -e 'tell application \"System Events\"' -e 'Activate'");
6706 | strcat(str, " -e 'try' -e 'set mycolor to choose color default color {");
6707 | }
6708 | else
6709 | {
6710 | strcat(str,
6711 | " -e 'try' -e 'tell app (path to frontmost application as Unicode text) " ~
6712 | "to set mycolor to choose color default color {");
6713 | }
6714 |
6715 | sprintf(lTmp, "%d", 256 * lDefaultRGB[0]);
6716 | strcat(str, lTmp);
6717 | strcat(str, ",");
6718 | sprintf(lTmp, "%d", 256 * lDefaultRGB[1]);
6719 | strcat(str, lTmp);
6720 | strcat(str, ",");
6721 | sprintf(lTmp, "%d", 256 * lDefaultRGB[2]);
6722 | strcat(str, lTmp);
6723 | strcat(str, "}' ");
6724 | strcat(str,
6725 | "-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' ");
6726 | strcat(str,
6727 | "-e 'repeat with i from 2 to the count of mycolor' ");
6728 | strcat(str,
6729 | "-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' ");
6730 | strcat(str, "-e 'end repeat' ");
6731 | strcat(str, "-e 'mystring' ");
6732 | strcat(str, "-e 'on error number -128' ");
6733 | strcat(str, "-e 'end try'");
6734 | if (!osx9orBetter())
6735 | strcat(str, " -e 'end tell'");
6736 | }
6737 | else if (kdialogPresent())
6738 | {
6739 | if (lQuery)
6740 | {
6741 | response("kdialog");
6742 | return cast(const(char)*)1;
6743 | }
6744 | strcpy(str, "kdialog");
6745 | if (kdialogPresent() == 2)
6746 | {
6747 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6748 | }
6749 | sprintf(str + strlen(str), " --getcolor --default '%s'", lpDefaultHexRGB);
6750 |
6751 | if (some(aTitle))
6752 | {
6753 | strcat(str, " --title \"");
6754 | strcat(str, aTitle);
6755 | strcat(str, "\"");
6756 | }
6757 | }
6758 | else if (zenity3Present() || matedialogPresent() || shellementaryPresent() || qarmaPresent())
6759 | {
6760 | lWasZenity3 = true;
6761 | if (zenity3Present())
6762 | {
6763 | if (lQuery)
6764 | {
6765 | response("zenity3");
6766 | return cast(const(char)*)1;
6767 | }
6768 | strcpy(str, "zenity");
6769 | if (zenity3Present() >= 4 && !getenv("SSH_TTY"))
6770 | {
6771 | strcat(str, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6772 | }
6773 | }
6774 | else if (matedialogPresent())
6775 | {
6776 | if (lQuery)
6777 | {
6778 | response("matedialog");
6779 | return cast(const(char)*)1;
6780 | }
6781 | strcpy(str, "matedialog");
6782 | }
6783 | else if (shellementaryPresent())
6784 | {
6785 | if (lQuery)
6786 | {
6787 | response("shellementary");
6788 | return cast(const(char)*)1;
6789 | }
6790 | strcpy(str, "shellementary");
6791 | }
6792 | else
6793 | {
6794 | if (lQuery)
6795 | {
6796 | response("qarma");
6797 | return cast(const(char)*)1;
6798 | }
6799 | strcpy(str, "qarma");
6800 | if (!getenv("SSH_TTY"))
6801 | {
6802 | strcat(str, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */
6803 | }
6804 | }
6805 | strcat(str, " --color-selection --show-palette");
6806 | sprintf(str + strlen(str), " --color=%s", lpDefaultHexRGB);
6807 |
6808 | if (some(aTitle))
6809 | {
6810 | strcat(str, " --title=\"");
6811 | strcat(str, aTitle);
6812 | strcat(str, "\"");
6813 | }
6814 | if (tinyfd_silent)
6815 | strcat(str, " 2>/dev/null ");
6816 | }
6817 | else if (xdialogPresent())
6818 | {
6819 | if (lQuery)
6820 | {
6821 | response("xdialog");
6822 | return cast(const(char)*)1;
6823 | }
6824 | lWasXdialog = true;
6825 | strcpy(str, "Xdialog --colorsel \"");
6826 | if (some(aTitle))
6827 | {
6828 | strcat(str, aTitle);
6829 | }
6830 | strcat(str, "\" 0 60 ");
6831 | sprintf(lTmp, "%hhu %hhu %hhu", lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
6832 | strcat(str, lTmp);
6833 | strcat(str, " 2>&1");
6834 | }
6835 | else if (tkinter2Present())
6836 | {
6837 | if (lQuery)
6838 | {
6839 | response("python2-tkinter");
6840 | return cast(const(char)*)1;
6841 | }
6842 | strcpy(str, gPython2Name.ptr);
6843 | if (!isTerminalRunning() && isDarwin())
6844 | {
6845 | strcat(str, " -i"); /* for osx without console */
6846 | }
6847 |
6848 | strcat(str,
6849 | " -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();");
6850 |
6851 | if (isDarwin())
6852 | {
6853 | strcat(str,
6854 | "import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set " ~
6855 | "frontmost of process \\\"Python\\\" to true' ''');");
6856 | }
6857 |
6858 | strcat(str, "res=tkColorChooser.askcolor(color='");
6859 | strcat(str, lpDefaultHexRGB);
6860 | strcat(str, "'");
6861 |
6862 | if (some(aTitle))
6863 | {
6864 | strcat(str, ",title='");
6865 | strcat(str, aTitle);
6866 | strcat(str, "'");
6867 | }
6868 | strcat(str, `);
6869 | if res[1] is not None:
6870 | print res[1]
6871 | "`);
6872 | }
6873 | else if (tkinter3Present())
6874 | {
6875 | if (lQuery)
6876 | {
6877 | response("python3-tkinter");
6878 | return cast(const(char)*)1;
6879 | }
6880 | strcpy(str, gPython3Name.ptr);
6881 | strcat(str,
6882 | " -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();");
6883 | strcat(str, "res=colorchooser.askcolor(color='");
6884 | strcat(str, lpDefaultHexRGB);
6885 | strcat(str, "'");
6886 |
6887 | if (some(aTitle))
6888 | {
6889 | strcat(str, ",title='");
6890 | strcat(str, aTitle);
6891 | strcat(str, "'");
6892 | }
6893 | strcat(str, `);
6894 | if res[1] is not None:
6895 | print(res[1])
6896 | "`);
6897 | }
6898 | else
6899 | {
6900 | if (lQuery)
6901 | {
6902 | return _inputBox(aTitle, null, null);
6903 | }
6904 | p = _inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lpDefaultHexRGB);
6905 | if (!p || strlen(p) != 7 || p[0] != '#')
6906 | return null;
6907 |
6908 | foreach (i; 1 .. 7)
6909 | {
6910 | if (!isxdigit(p[i]))
6911 | return null;
6912 | }
6913 | Hex2RGB(p, aoResultRGB);
6914 | return p;
6915 | }
6916 |
6917 | if (tinyfd_verbose)
6918 | printf("str: %s\n", str);
6919 | lIn = popen(str, "r");
6920 | if (!lIn)
6921 | return null;
6922 |
6923 | while (fgets(lBuff.ptr, lBuff.sizeof, lIn) !is null)
6924 | {
6925 | }
6926 | pclose(lIn);
6927 | if (!some(lBuff.ptr))
6928 | return null;
6929 | /* printf( "len Buff: %lu\n" , strlen(lBuff.ptr) ) ; */
6930 | /* printf( "lBuff0: %s\n" , lBuff ) ; */
6931 | removeLastNL(lBuff.ptr);
6932 |
6933 | if (lWasZenity3)
6934 | {
6935 | if (lBuff[0] == '#')
6936 | {
6937 | if (strlen(lBuff.ptr) > 7)
6938 | {
6939 | lBuff[3] = lBuff[5];
6940 | lBuff[4] = lBuff[6];
6941 | lBuff[5] = lBuff[9];
6942 | lBuff[6] = lBuff[10];
6943 | lBuff[7] = '\0';
6944 | }
6945 | Hex2RGB(lBuff.ptr, aoResultRGB);
6946 | }
6947 | else if (lBuff[3] == '(')
6948 | {
6949 | sscanf(lBuff.ptr, "rgb(%hhu,%hhu,%hhu",
6950 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]);
6951 | RGB2Hex(aoResultRGB, lBuff.ptr);
6952 | }
6953 | else if (lBuff[4] == '(')
6954 | {
6955 | sscanf(lBuff.ptr, "rgba(%hhu,%hhu,%hhu",
6956 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]);
6957 | RGB2Hex(aoResultRGB, lBuff.ptr);
6958 | }
6959 | }
6960 | else if (lWasOsascript || lWasXdialog)
6961 | {
6962 | /* printf( "lBuff: %s\n" , lBuff ) ; */
6963 | sscanf(lBuff.ptr, "%hhu %hhu %hhu",
6964 | &aoResultRGB[0], &aoResultRGB[1], &aoResultRGB[2]);
6965 | RGB2Hex(aoResultRGB, lBuff.ptr);
6966 | }
6967 | else
6968 | {
6969 | Hex2RGB(lBuff.ptr, aoResultRGB);
6970 | }
6971 | /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */
6972 | /* printf( "lBuff: %s\n" , lBuff ) ; */
6973 | return lBuff.ptr;
6974 | }
6975 |
6976 | } // windows-unix
6977 |
--------------------------------------------------------------------------------