forked from openjdk/jfx
/
FileChooser.java
439 lines (386 loc) · 16.5 KB
/
FileChooser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
/*
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.stage;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.sun.glass.ui.CommonDialogs;
import com.sun.glass.ui.CommonDialogs.FileChooserResult;
import com.sun.javafx.tk.FileChooserType;
import com.sun.javafx.tk.Toolkit;
/**
* Provides support for standard platform file dialogs. These dialogs have look
* and feel of the platform UI components which is independent of JavaFX.
* <p>
* On some platforms where file access may be restricted or not part of the user
* model (for example, on some mobile or embedded devices), opening a file
* dialog may always result in a no-op (that is, null file(s) being returned).
* </p>
* <p>
* A {@code FileChooser} can be used to invoke file open dialogs for selecting
* single file ({@code showOpenDialog}), file open dialogs for selecting
* multiple files ({@code showOpenMultipleDialog}) and file save dialogs
* ({@code showSaveDialog}). The configuration of the displayed dialog is
* controlled by the values of the {@code FileChooser} properties set before the
* corresponding {@code show*Dialog} method is called. This configuration
* includes the dialog's title, the initial directory displayed in the dialog
* and the extension filter(s) for the listed files. For configuration
* properties which values haven't been set explicitly, the displayed dialog
* uses their platform default values. A call to a show dialog method is
* blocked until the user makes a choice or cancels the dialog. The return
* value specifies the selected file(s) or equals to {@code null} if the dialog
* has been canceled.
* </p>
* <p>
* Example:
* <pre>{@code
* FileChooser fileChooser = new FileChooser();
* fileChooser.setTitle("Open Resource File");
* fileChooser.getExtensionFilters().addAll(
* new ExtensionFilter("Text Files", "*.txt"),
* new ExtensionFilter("Image Files", "*.png", "*.jpg", "*.gif"),
* new ExtensionFilter("Audio Files", "*.wav", "*.mp3", "*.aac"),
* new ExtensionFilter("All Files", "*.*"));
* File selectedFile = fileChooser.showOpenDialog(mainStage);
* if (selectedFile != null) {
* mainStage.display(selectedFile);
* }
* }</pre>
*
* @since JavaFX 2.0
*/
public final class FileChooser {
/**
* Defines an extension filter, used for filtering which files can be chosen
* in a FileDialog based on the file name extensions.
* @since JavaFX 2.0
*/
public static final class ExtensionFilter {
private final String description;
private final List<String> extensions;
/**
* Creates an {@code ExtensionFilter} with the specified description
* and the file name extensions.
* <p>
* File name extension should be specified in the {@code *.<extension>}
* format.
*
* @param description the textual description for the filter
* @param extensions the accepted file name extensions
* @throws NullPointerException if the description or the extensions
* are {@code null}
* @throws IllegalArgumentException if the description or the extensions
* are empty
*/
public ExtensionFilter(final String description,
final String... extensions) {
validateArgs(description, extensions);
this.description = description;
this.extensions = Collections.unmodifiableList(
Arrays.asList(extensions.clone()));
}
/**
* Creates an {@code ExtensionFilter} with the specified description
* and the file name extensions.
* <p>
* File name extension should be specified in the {@code *.<extension>}
* format.
*
* @param description the textual description for the filter
* @param extensions the accepted file name extensions
* @throws NullPointerException if the description or the extensions
* are {@code null}
* @throws IllegalArgumentException if the description or the extensions
* are empty
*/
public ExtensionFilter(final String description,
final List<String> extensions) {
final String[] extensionsArray =
(extensions != null) ? extensions.toArray(
new String[extensions.size()])
: null;
validateArgs(description, extensionsArray);
this.description = description;
this.extensions = Collections.unmodifiableList(
Arrays.asList(extensionsArray));
}
/**
* Gets the description for this {@code ExtensionFilter}.
*
* @return the description
*/
public String getDescription() {
return description;
}
/**
* Gets the file name extensions for this {@code ExtensionFilter}.
* <p>
* The returned list is unmodifiable and will throw
* {@code UnsupportedOperationException} on each modification attempt.
*
* @return the file name extensions
*/
public List<String> getExtensions() {
return extensions;
}
private static void validateArgs(final String description,
final String[] extensions) {
if (description == null) {
throw new NullPointerException("Description must not be null");
}
if (description.isEmpty()) {
throw new IllegalArgumentException(
"Description must not be empty");
}
if (extensions == null) {
throw new NullPointerException("Extensions must not be null");
}
if (extensions.length == 0) {
throw new IllegalArgumentException(
"At least one extension must be defined");
}
for (String extension : extensions) {
if (extension == null) {
throw new NullPointerException(
"Extension must not be null");
}
if (extension.isEmpty()) {
throw new IllegalArgumentException(
"Extension must not be empty");
}
}
}
}
/**
* The title of the displayed file dialog.
*/
private StringProperty title;
/**
* Creates a {@code FileChooser}.
*/
public FileChooser() {
}
public final void setTitle(final String value) {
titleProperty().set(value);
}
public final String getTitle() {
return (title != null) ? title.get() : null;
}
public final StringProperty titleProperty() {
if (title == null) {
title = new SimpleStringProperty(this, "title");
}
return title;
}
/**
* The initial directory for the displayed file dialog.
*/
private ObjectProperty<File> initialDirectory;
public final void setInitialDirectory(final File value) {
initialDirectoryProperty().set(value);
}
public final File getInitialDirectory() {
return (initialDirectory != null) ? initialDirectory.get() : null;
}
public final ObjectProperty<File> initialDirectoryProperty() {
if (initialDirectory == null) {
initialDirectory =
new SimpleObjectProperty<File>(this, "initialDirectory");
}
return initialDirectory;
}
/**
* The initial file name for the displayed dialog.
* <p>
* This property is used mostly in the displayed file save dialogs as the
* initial file name for the file being saved. If set for a file open
* dialog it will have any impact on the displayed dialog only if the
* corresponding platform provides support for such property in its
* file open dialogs.
* </p>
*
* @since JavaFX 2.2.40
*/
private ObjectProperty<String> initialFileName;
public final void setInitialFileName(final String value) {
initialFileNameProperty().set(value);
}
public final String getInitialFileName() {
return (initialFileName != null) ? initialFileName.get() : null;
}
public final ObjectProperty<String> initialFileNameProperty() {
if (initialFileName == null) {
initialFileName =
new SimpleObjectProperty<String>(this, "initialFileName");
}
return initialFileName;
}
/**
* Specifies the extension filters used in the displayed file dialog.
*/
private ObservableList<ExtensionFilter> extensionFilters =
FXCollections.<ExtensionFilter>observableArrayList();
/**
* Gets the extension filters used in the displayed file dialog. Only
* one extension filter from the list is active at any time in the displayed
* dialog and only files which correspond to this extension filter are
* shown. The first extension filter from the list is activated when the
* dialog is invoked. Then the user can switch the active extension filter
* to any other extension filter from the list and in this way control the
* set of displayed files.
*
* @return An observable list of the extension filters used in this dialog
*/
public ObservableList<ExtensionFilter> getExtensionFilters() {
return extensionFilters;
}
/**
* This property is used to pre-select the extension filter for the next
* displayed dialog and to read the user-selected extension filter from the
* dismissed dialog.
* <p>
* When the file dialog is shown, the selectedExtensionFilter will be checked.
* If the value of selectedExtensionFilter is null or is not contained in
* the list of extension filters, then the first extension filter in the list
* of extension filters will be selected instead. Otherwise, the specified
* selectedExtensionFilter will be activated.
* <p>
* After the dialog is dismissed the value of this property is updated to
* match the user-selected extension filter from the dialog.
*
* @since JavaFX 8.0
*/
private ObjectProperty<ExtensionFilter> selectedExtensionFilter;
public final ObjectProperty<ExtensionFilter> selectedExtensionFilterProperty() {
if (selectedExtensionFilter == null) {
selectedExtensionFilter =
new SimpleObjectProperty<ExtensionFilter>(this,
"selectedExtensionFilter");
}
return selectedExtensionFilter;
}
public final void setSelectedExtensionFilter(ExtensionFilter filter) {
selectedExtensionFilterProperty().setValue(filter);
}
public final ExtensionFilter getSelectedExtensionFilter() {
return (selectedExtensionFilter != null)
? selectedExtensionFilter.get()
: null;
}
/**
* Shows a new file open dialog. The method doesn't return until the
* displayed open dialog is dismissed. The return value specifies
* the file chosen by the user or {@code null} if no selection has been
* made. If the owner window for the file dialog is set, input to all
* windows in the dialog's owner chain is blocked while the file dialog
* is being shown.
*
* @param ownerWindow the owner window of the displayed file dialog
* @return the selected file or {@code null} if no file has been selected
*/
public File showOpenDialog(final Window ownerWindow) {
final List<File> selectedFiles =
showDialog(ownerWindow, FileChooserType.OPEN);
return ((selectedFiles != null) && (selectedFiles.size() > 0))
? selectedFiles.get(0) : null;
}
/**
* Shows a new file open dialog in which multiple files can be selected.
* The method doesn't return until the displayed open dialog is dismissed.
* The return value specifies the files chosen by the user or {@code null}
* if no selection has been made. If the owner window for the file dialog is
* set, input to all windows in the dialog's owner chain is blocked while
* the file dialog is being shown.
* <p>
* The returned list is unmodifiable and will throw
* {@code UnsupportedOperationException} on each modification attempt.
*
* @param ownerWindow the owner window of the displayed file dialog
* @return the selected files or {@code null} if no file has been selected
*/
public List<File> showOpenMultipleDialog(final Window ownerWindow) {
final List<File> selectedFiles =
showDialog(ownerWindow, FileChooserType.OPEN_MULTIPLE);
return ((selectedFiles != null) && (selectedFiles.size() > 0))
? Collections.unmodifiableList(selectedFiles)
: null;
}
/**
* Shows a new file save dialog. The method doesn't return until the
* displayed file save dialog is dismissed. The return value specifies the
* file chosen by the user or {@code null} if no selection has been made.
* If the owner window for the file dialog is set, input to all windows in
* the dialog's owner chain is blocked while the file dialog is being shown.
*
* @param ownerWindow the owner window of the displayed file dialog
* @return the selected file or {@code null} if no file has been selected
*/
public File showSaveDialog(final Window ownerWindow) {
final List<File> selectedFiles =
showDialog(ownerWindow, FileChooserType.SAVE);
return ((selectedFiles != null) && (selectedFiles.size() > 0))
? selectedFiles.get(0) : null;
}
private ExtensionFilter findSelectedFilter(CommonDialogs.ExtensionFilter filter) {
if (filter != null) {
String description = filter.getDescription();
List<String> extensions = filter.getExtensions();
for (ExtensionFilter ef : extensionFilters) {
if (description.equals(ef.getDescription())
&& extensions.equals(ef.getExtensions())) {
return ef;
}
}
}
return null;
}
private List<File> showDialog(final Window ownerWindow,
final FileChooserType fileChooserType) {
FileChooserResult result = Toolkit.getToolkit().showFileChooser(
(ownerWindow != null) ? ownerWindow.getPeer() : null,
getTitle(),
getInitialDirectory(),
getInitialFileName(),
fileChooserType,
extensionFilters,
getSelectedExtensionFilter());
if (result == null) {
return null;
}
List<File> files = result.getFiles();
if (files != null && files.size() > 0) {
selectedExtensionFilterProperty().set(
findSelectedFilter(result.getExtensionFilter()));
}
return files;
}
}