Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_linux_FileChooser.cpp 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. bool FileChooser::isPlatformDialogAvailable()
  22. {
  23. #if JUCE_DISABLE_NATIVE_FILECHOOSERS
  24. return false;
  25. #else
  26. static bool canUseNativeBox = exeIsAvailable ("zenity") || exeIsAvailable ("kdialog");
  27. return canUseNativeBox;
  28. #endif
  29. }
  30. #if ! JUCE_DISABLE_NATIVE_FILECHOOSERS
  31. static bool exeIsAvailable (const char* const executable)
  32. {
  33. ChildProcess child;
  34. const bool ok = child.start ("which " + String (executable))
  35. && child.readAllProcessOutput().trim().isNotEmpty();
  36. child.waitForProcessToFinish (60 * 1000);
  37. return ok;
  38. }
  39. static uint64 getTopWindowID() noexcept
  40. {
  41. if (TopLevelWindow* top = TopLevelWindow::getActiveTopLevelWindow())
  42. return (uint64) (pointer_sized_uint) top->getWindowHandle();
  43. return 0;
  44. }
  45. static bool isKdeFullSession()
  46. {
  47. return SystemStats::getEnvironmentVariable ("KDE_FULL_SESSION", String())
  48. .equalsIgnoreCase ("true");
  49. }
  50. static void addKDialogArgs (StringArray& args, String& separator,
  51. const String& title, const File& file, const String& filters,
  52. bool isDirectory, bool isSave, bool selectMultipleFiles)
  53. {
  54. args.add ("kdialog");
  55. if (title.isNotEmpty())
  56. args.add ("--title=" + title);
  57. if (uint64 topWindowID = getTopWindowID())
  58. {
  59. args.add ("--attach");
  60. args.add (String (topWindowID));
  61. }
  62. if (selectMultipleFiles)
  63. {
  64. separator = "\n";
  65. args.add ("--multiple");
  66. args.add ("--separate-output");
  67. args.add ("--getopenfilename");
  68. }
  69. else
  70. {
  71. if (isSave) args.add ("--getsavefilename");
  72. else if (isDirectory) args.add ("--getexistingdirectory");
  73. else args.add ("--getopenfilename");
  74. }
  75. File startPath;
  76. if (file.exists())
  77. {
  78. startPath = file;
  79. }
  80. else if (file.getParentDirectory().exists())
  81. {
  82. startPath = file.getParentDirectory();
  83. }
  84. else
  85. {
  86. startPath = File::getSpecialLocation (File::userHomeDirectory);
  87. if (isSave)
  88. startPath = startPath.getChildFile (file.getFileName());
  89. }
  90. args.add (startPath.getFullPathName());
  91. args.add (filters.replaceCharacter (';', ' '));
  92. }
  93. static void addZenityArgs (StringArray& args, String& separator,
  94. const String& title, const File& file, const String& filters,
  95. bool isDirectory, bool isSave, bool selectMultipleFiles)
  96. {
  97. args.add ("zenity");
  98. args.add ("--file-selection");
  99. if (title.isNotEmpty())
  100. args.add ("--title=" + title);
  101. if (selectMultipleFiles)
  102. {
  103. separator = ":";
  104. args.add ("--multiple");
  105. args.add ("--separator=" + separator);
  106. }
  107. else
  108. {
  109. if (isDirectory) args.add ("--directory");
  110. if (isSave) args.add ("--save");
  111. }
  112. if (filters.isNotEmpty() && filters != "*" && filters != "*.*")
  113. {
  114. StringArray tokens;
  115. tokens.addTokens (filters, ";,|", "\"");
  116. for (int i = 0; i < tokens.size(); ++i)
  117. args.add ("--file-filter=" + tokens[i]);
  118. }
  119. if (file.isDirectory())
  120. file.setAsCurrentWorkingDirectory();
  121. else if (file.getParentDirectory().exists())
  122. file.getParentDirectory().setAsCurrentWorkingDirectory();
  123. else
  124. File::getSpecialLocation (File::userHomeDirectory).setAsCurrentWorkingDirectory();
  125. if (! file.getFileName().isEmpty())
  126. args.add ("--filename=" + file.getFileName());
  127. // supplying the window ID of the topmost window makes sure that Zenity pops up..
  128. if (uint64 topWindowID = getTopWindowID())
  129. setenv ("WINDOWID", String (topWindowID).toRawUTF8(), true);
  130. }
  131. void FileChooser::showPlatformDialog (Array<File>& results,
  132. const String& title, const File& file, const String& filters,
  133. bool isDirectory, bool /* selectsFiles */,
  134. bool isSave, bool /* warnAboutOverwritingExistingFiles */,
  135. bool /*treatFilePackagesAsDirs*/,
  136. bool selectMultipleFiles, FilePreviewComponent*)
  137. {
  138. const File previousWorkingDirectory (File::getCurrentWorkingDirectory());
  139. StringArray args;
  140. String separator;
  141. // use kdialog for KDE sessions or if zenity is missing
  142. if (exeIsAvailable ("kdialog") && (isKdeFullSession() || ! exeIsAvailable ("zenity")))
  143. addKDialogArgs (args, separator, title, file, filters, isDirectory, isSave, selectMultipleFiles);
  144. else
  145. addZenityArgs (args, separator, title, file, filters, isDirectory, isSave, selectMultipleFiles);
  146. ChildProcess child;
  147. if (child.start (args, ChildProcess::wantStdOut))
  148. {
  149. if (MessageManager::getInstance()->isThisTheMessageThread())
  150. {
  151. while (child.isRunning())
  152. {
  153. if (! MessageManager::getInstance()->runDispatchLoopUntil(20))
  154. break;
  155. }
  156. }
  157. const String result (child.readAllProcessOutput().trim());
  158. if (result.isNotEmpty())
  159. {
  160. StringArray tokens;
  161. if (selectMultipleFiles)
  162. tokens.addTokens (result, separator, "\"");
  163. else
  164. tokens.add (result);
  165. for (int i = 0; i < tokens.size(); ++i)
  166. results.add (File::getCurrentWorkingDirectory().getChildFile (tokens[i]));
  167. }
  168. child.waitForProcessToFinish (60 * 1000);
  169. }
  170. previousWorkingDirectory.setAsCurrentWorkingDirectory();
  171. }
  172. #endif
  173. } // namespace juce