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