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