JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_FileChooser_linux.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29static bool exeIsAvailable (String executable)
30{
31 ChildProcess child;
32
33 if (child.start ("which " + executable))
34 {
35 child.waitForProcessToFinish (60 * 1000);
36 return (child.getExitCode() == 0);
37 }
38
39 return false;
40}
41
42static bool isSet (int flags, int toCheck)
43{
44 return (flags & toCheck) != 0;
45}
46
47class FileChooser::Native final : public FileChooser::Pimpl,
48 private Timer
49{
50public:
51 Native (FileChooser& fileChooser, int flags)
52 : owner (fileChooser),
53 // kdialog/zenity only support opening either files or directories.
54 // Files should take precedence, if requested.
55 isDirectory (isSet (flags, FileBrowserComponent::canSelectDirectories) && ! isSet (flags, FileBrowserComponent::canSelectFiles)),
56 isSave (isSet (flags, FileBrowserComponent::saveMode)),
57 selectMultipleFiles (isSet (flags, FileBrowserComponent::canSelectMultipleItems)),
58 warnAboutOverwrite (isSet (flags, FileBrowserComponent::warnAboutOverwriting))
59 {
61
62 // use kdialog for KDE sessions or if zenity is missing
63 if (exeIsAvailable ("kdialog") && (isKdeFullSession() || ! exeIsAvailable ("zenity")))
64 addKDialogArgs();
65 else
66 addZenityArgs();
67 }
68
69 ~Native() override
70 {
71 finish (true);
72 }
73
74 void runModally() override
75 {
76 #if JUCE_MODAL_LOOPS_PERMITTED
77 child.start (args, ChildProcess::wantStdOut);
78
79 while (child.isRunning())
81 break;
82
83 finish (false);
84 #else
86 #endif
87 }
88
89 void launch() override
90 {
91 child.start (args, ChildProcess::wantStdOut);
92 startTimer (100);
93 }
94
95private:
96 FileChooser& owner;
97 bool isDirectory, isSave, selectMultipleFiles, warnAboutOverwrite;
98
99 ChildProcess child;
100 StringArray args;
101 String separator;
102
103 void timerCallback() override
104 {
105 if (! child.isRunning())
106 {
107 stopTimer();
108 finish (false);
109 }
110 }
111
112 void finish (bool shouldKill)
113 {
114 String result;
115 Array<URL> selection;
116
117 if (shouldKill)
118 child.kill();
119 else
120 result = child.readAllProcessOutput().trim();
121
122 if (result.isNotEmpty())
123 {
124 StringArray tokens;
125
126 if (selectMultipleFiles)
127 tokens.addTokens (result, separator, "\"");
128 else
129 tokens.add (result);
130
131 for (auto& token : tokens)
132 selection.add (URL (File::getCurrentWorkingDirectory().getChildFile (token)));
133 }
134
135 if (! shouldKill)
136 {
137 child.waitForProcessToFinish (60 * 1000);
138 owner.finished (selection);
139 }
140 }
141
142 static uint64 getTopWindowID() noexcept
143 {
145 return (uint64) (pointer_sized_uint) top->getWindowHandle();
146
147 return 0;
148 }
149
150 static bool isKdeFullSession()
151 {
152 return SystemStats::getEnvironmentVariable ("KDE_FULL_SESSION", String())
153 .equalsIgnoreCase ("true");
154 }
155
156 void addKDialogArgs()
157 {
158 args.add ("kdialog");
159
160 if (owner.title.isNotEmpty())
161 args.add ("--title=" + owner.title);
162
163 if (uint64 topWindowID = getTopWindowID())
164 {
165 args.add ("--attach");
166 args.add (String (topWindowID));
167 }
168
169 if (selectMultipleFiles)
170 {
171 separator = "\n";
172 args.add ("--multiple");
173 args.add ("--separate-output");
174 args.add ("--getopenfilename");
175 }
176 else
177 {
178 if (isSave) args.add ("--getsavefilename");
179 else if (isDirectory) args.add ("--getexistingdirectory");
180 else args.add ("--getopenfilename");
181 }
182
184
185 if (owner.startingFile.exists())
186 {
187 startPath = owner.startingFile;
188 }
189 else if (owner.startingFile.getParentDirectory().exists())
190 {
191 startPath = owner.startingFile.getParentDirectory();
192 }
193 else
194 {
196
197 if (isSave)
198 startPath = startPath.getChildFile (owner.startingFile.getFileName());
199 }
200
201 args.add (startPath.getFullPathName());
202 args.add ("(" + owner.filters.replaceCharacter (';', ' ') + ")");
203 }
204
205 void addZenityArgs()
206 {
207 args.add ("zenity");
208 args.add ("--file-selection");
209
210 const auto getUnderstandsConfirmOverwrite = []
211 {
212 // --confirm-overwrite is deprecated in zenity 3.91 and higher
213 ChildProcess process;
214 process.start ("zenity --version");
215 process.waitForProcessToFinish (1000);
216 const auto versionString = process.readAllProcessOutput();
217 const auto version = StringArray::fromTokens (versionString.trim(), ".", "");
218 return version.size() >= 2
219 && (version[0].getIntValue() < 3
220 || (version[0].getIntValue() == 3 && version[1].getIntValue() < 91));
221 };
222
223 if (warnAboutOverwrite && getUnderstandsConfirmOverwrite())
224 args.add ("--confirm-overwrite");
225
226 if (owner.title.isNotEmpty())
227 args.add ("--title=" + owner.title);
228
229 if (selectMultipleFiles)
230 {
231 separator = ":";
232 args.add ("--multiple");
233 args.add ("--separator=" + separator);
234 }
235 else
236 {
237 if (isSave)
238 args.add ("--save");
239 }
240
241 if (isDirectory)
242 args.add ("--directory");
243
244 if (owner.filters.isNotEmpty() && owner.filters != "*" && owner.filters != "*.*")
245 {
246 StringArray tokens;
247 tokens.addTokens (owner.filters, ";,|", "\"");
248
249 args.add ("--file-filter=" + tokens.joinIntoString (" "));
250 }
251
252 if (owner.startingFile.isDirectory())
253 owner.startingFile.setAsCurrentWorkingDirectory();
254 else if (owner.startingFile.getParentDirectory().exists())
256 else
258
259 auto filename = owner.startingFile.getFileName();
260
261 if (! filename.isEmpty())
262 args.add ("--filename=" + filename);
263
264 // supplying the window ID of the topmost window makes sure that Zenity pops up..
265 if (uint64 topWindowID = getTopWindowID())
266 setenv ("WINDOWID", String (topWindowID).toRawUTF8(), true);
267 }
268
270};
271
273{
274 #if JUCE_DISABLE_NATIVE_FILECHOOSERS
275 return false;
276 #else
277 static bool canUseNativeBox = exeIsAvailable ("zenity") || exeIsAvailable ("kdialog");
278 return canUseNativeBox;
279 #endif
280}
281
282std::shared_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*)
283{
284 return std::make_shared<Native> (owner, flags);
285}
286
287} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:418
Launches and monitors a child process.
String readAllProcessOutput()
Blocks until the process has finished, and then returns its complete output as a string.
bool isRunning() const
Returns true if the child process is alive.
bool waitForProcessToFinish(int timeoutMs) const
Blocks until the process is no longer running.
bool kill()
Attempts to kill the child process.
bool start(const String &command, int streamFlags=wantStdOut|wantStdErr)
Attempts to launch a child process command.
@ saveMode
specifies that the component should allow the user to specify the name of a file that will be used to...
@ canSelectMultipleItems
specifies that the user can select multiple items.
@ warnAboutOverwriting
specifies that the dialog should warn about overwriting existing files (if possible).
@ canSelectFiles
specifies that the user can select files (can be used in conjunction with canSelectDirectories).
@ canSelectDirectories
specifies that the user can select directories (can be used in conjunction with canSelectFiles).
Creates a dialog box to choose a file or directory to load or save.
static bool isPlatformDialogAvailable()
Returns if a native filechooser is currently available on this platform.
Base class for components that live inside a file chooser dialog box and show previews of the files t...
Represents a local file or directory.
Definition juce_File.h:45
bool isDirectory() const
Checks whether the file is a directory that exists.
bool setAsCurrentWorkingDirectory() const
Sets the current working directory to be this file.
String getFileName() const
Returns the last section of the pathname.
static File getCurrentWorkingDirectory()
Returns the current working directory.
@ userHomeDirectory
The user's home folder.
Definition juce_File.h:869
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
Finds the location of a special type of file or directory, such as a home folder or documents folder.
File getParentDirectory() const
Returns the directory that contains this file or directory.
bool exists() const
Checks whether the file actually exists.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
A special array for holding a list of strings.
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
Joins the strings in the array together into one string.
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Returns an array containing the tokens in a given string.
void add(String stringToAdd)
Appends a string at the end of the array.
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Breaks up a string into tokens and adds them to this array.
The JUCE String class!
Definition juce_String.h:53
bool equalsIgnoreCase(const String &other) const noexcept
Case-insensitive comparison with another string.
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end.
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
Returns a string with all occurrences of a character replaced with a different one.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
static String getEnvironmentVariable(const String &name, const String &defaultValue)
Returns an environment variable.
Makes repeated callbacks to a virtual method at a specified time interval.
Definition juce_Timer.h:52
void stopTimer() noexcept
Stops the timer.
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
A base class for top-level windows.
static TopLevelWindow * getActiveTopLevelWindow() noexcept
Returns the currently-active top level window.
Represents a URL and has a bunch of useful functions to manipulate it.
Definition juce_URL.h:38
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
JUCE Namespace.
unsigned int pointer_sized_uint
An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it...
unsigned long long uint64
A platform-independent 64-bit unsigned integer type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
setenv