1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - 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 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-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
26 namespace juce
27 {
28
29 //==============================================================================
30 class FileChooser::NonNative : public FileChooser::Pimpl
31 {
32 public:
NonNative(FileChooser & fileChooser,int flags,FilePreviewComponent * preview)33 NonNative (FileChooser& fileChooser, int flags, FilePreviewComponent* preview)
34 : owner (fileChooser),
35 selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0),
36 selectsFiles ((flags & FileBrowserComponent::canSelectFiles) != 0),
37 warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0),
38
39 filter (selectsFiles ? owner.filters : String(), selectsDirectories ? "*" : String(), {}),
40 browserComponent (flags, owner.startingFile, &filter, preview),
41 dialogBox (owner.title, {}, browserComponent, warnAboutOverwrite,
42 browserComponent.findColour (AlertWindow::backgroundColourId), owner.parent)
43 {}
44
~NonNative()45 ~NonNative() override
46 {
47 dialogBox.exitModalState (0);
48 }
49
launch()50 void launch() override
51 {
52 dialogBox.centreWithDefaultSize (nullptr);
53 dialogBox.enterModalState (true, ModalCallbackFunction::create ([this] (int r) { modalStateFinished (r); }), true);
54 }
55
runModally()56 void runModally() override
57 {
58 #if JUCE_MODAL_LOOPS_PERMITTED
59 modalStateFinished (dialogBox.show() ? 1 : 0);
60 #else
61 jassertfalse;
62 #endif
63 }
64
65 private:
modalStateFinished(int returnValue)66 void modalStateFinished (int returnValue)
67 {
68 Array<URL> result;
69
70 if (returnValue != 0)
71 {
72 for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
73 result.add (URL (browserComponent.getSelectedFile (i)));
74 }
75
76 owner.finished (result);
77 }
78
79 //==============================================================================
80 FileChooser& owner;
81 bool selectsDirectories, selectsFiles, warnAboutOverwrite;
82
83 WildcardFileFilter filter;
84 FileBrowserComponent browserComponent;
85 FileChooserDialogBox dialogBox;
86
87 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NonNative)
88 };
89
90 //==============================================================================
FileChooser(const String & chooserBoxTitle,const File & currentFileOrDirectory,const String & fileFilters,const bool useNativeBox,const bool treatFilePackagesAsDirectories,Component * parentComponentToUse)91 FileChooser::FileChooser (const String& chooserBoxTitle,
92 const File& currentFileOrDirectory,
93 const String& fileFilters,
94 const bool useNativeBox,
95 const bool treatFilePackagesAsDirectories,
96 Component* parentComponentToUse)
97 : title (chooserBoxTitle),
98 filters (fileFilters),
99 startingFile (currentFileOrDirectory),
100 parent (parentComponentToUse),
101 useNativeDialogBox (useNativeBox && isPlatformDialogAvailable()),
102 treatFilePackagesAsDirs (treatFilePackagesAsDirectories)
103 {
104 #ifndef JUCE_MAC
105 ignoreUnused (treatFilePackagesAsDirs);
106 #endif
107
108 if (! fileFilters.containsNonWhitespaceChars())
109 filters = "*";
110 }
111
~FileChooser()112 FileChooser::~FileChooser()
113 {
114 asyncCallback = nullptr;
115 }
116
117 #if JUCE_MODAL_LOOPS_PERMITTED
browseForFileToOpen(FilePreviewComponent * previewComp)118 bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComp)
119 {
120 return showDialog (FileBrowserComponent::openMode
121 | FileBrowserComponent::canSelectFiles,
122 previewComp);
123 }
124
browseForMultipleFilesToOpen(FilePreviewComponent * previewComp)125 bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComp)
126 {
127 return showDialog (FileBrowserComponent::openMode
128 | FileBrowserComponent::canSelectFiles
129 | FileBrowserComponent::canSelectMultipleItems,
130 previewComp);
131 }
132
browseForMultipleFilesOrDirectories(FilePreviewComponent * previewComp)133 bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComp)
134 {
135 return showDialog (FileBrowserComponent::openMode
136 | FileBrowserComponent::canSelectFiles
137 | FileBrowserComponent::canSelectDirectories
138 | FileBrowserComponent::canSelectMultipleItems,
139 previewComp);
140 }
141
browseForFileToSave(const bool warnAboutOverwrite)142 bool FileChooser::browseForFileToSave (const bool warnAboutOverwrite)
143 {
144 return showDialog (FileBrowserComponent::saveMode
145 | FileBrowserComponent::canSelectFiles
146 | (warnAboutOverwrite ? FileBrowserComponent::warnAboutOverwriting : 0),
147 nullptr);
148 }
149
browseForDirectory()150 bool FileChooser::browseForDirectory()
151 {
152 return showDialog (FileBrowserComponent::openMode
153 | FileBrowserComponent::canSelectDirectories,
154 nullptr);
155 }
156
showDialog(const int flags,FilePreviewComponent * const previewComp)157 bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previewComp)
158 {
159 FocusRestorer focusRestorer;
160
161 pimpl.reset (createPimpl (flags, previewComp));
162 pimpl->runModally();
163
164 // ensure that the finished function was invoked
165 jassert (pimpl == nullptr);
166
167 return (results.size() > 0);
168 }
169 #endif
170
launchAsync(int flags,std::function<void (const FileChooser &)> callback,FilePreviewComponent * previewComp)171 void FileChooser::launchAsync (int flags, std::function<void (const FileChooser&)> callback,
172 FilePreviewComponent* previewComp)
173 {
174 // You must specify a callback when using launchAsync
175 jassert (callback);
176
177 // you cannot run two file chooser dialog boxes at the same time
178 jassert (asyncCallback == nullptr);
179
180 asyncCallback = std::move (callback);
181
182 pimpl.reset (createPimpl (flags, previewComp));
183 pimpl->launch();
184 }
185
186
createPimpl(int flags,FilePreviewComponent * previewComp)187 FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp)
188 {
189 results.clear();
190
191 // the preview component needs to be the right size before you pass it in here..
192 jassert (previewComp == nullptr || (previewComp->getWidth() > 10
193 && previewComp->getHeight() > 10));
194
195 if (pimpl != nullptr)
196 {
197 // you cannot run two file chooser dialog boxes at the same time
198 jassertfalse;
199 pimpl.reset();
200 }
201
202 // You've set the flags for both saveMode and openMode!
203 jassert (! (((flags & FileBrowserComponent::saveMode) != 0)
204 && ((flags & FileBrowserComponent::openMode) != 0)));
205
206 #if JUCE_WINDOWS
207 const bool selectsFiles = (flags & FileBrowserComponent::canSelectFiles) != 0;
208 const bool selectsDirectories = (flags & FileBrowserComponent::canSelectDirectories) != 0;
209
210 if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
211 #else
212 if (useNativeDialogBox)
213 #endif
214 {
215 return showPlatformDialog (*this, flags, previewComp);
216 }
217 else
218 {
219 return new NonNative (*this, flags, previewComp);
220 }
221 }
222
getResults() const223 Array<File> FileChooser::getResults() const noexcept
224 {
225 Array<File> files;
226
227 for (auto url : getURLResults())
228 if (url.isLocalFile())
229 files.add (url.getLocalFile());
230
231 return files;
232 }
233
getResult() const234 File FileChooser::getResult() const
235 {
236 auto fileResults = getResults();
237
238 // if you've used a multiple-file select, you should use the getResults() method
239 // to retrieve all the files that were chosen.
240 jassert (fileResults.size() <= 1);
241
242 return fileResults.getFirst();
243 }
244
getURLResult() const245 URL FileChooser::getURLResult() const
246 {
247 // if you've used a multiple-file select, you should use the getResults() method
248 // to retrieve all the files that were chosen.
249 jassert (results.size() <= 1);
250
251 return results.getFirst();
252 }
253
finished(const Array<URL> & asyncResults)254 void FileChooser::finished (const Array<URL>& asyncResults)
255 {
256 std::function<void (const FileChooser&)> callback;
257 std::swap (callback, asyncCallback);
258
259 results = asyncResults;
260
261 pimpl.reset();
262
263 if (callback)
264 callback (*this);
265 }
266
267 //==============================================================================
FilePreviewComponent()268 FilePreviewComponent::FilePreviewComponent() {}
~FilePreviewComponent()269 FilePreviewComponent::~FilePreviewComponent() {}
270
271 } // namespace juce
272