/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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.collections.
annotations.
ReturnsUnmodifiableCollection;
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);
* }
* </code></pre>
* </p>
* @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
*/
@
ReturnsUnmodifiableCollection
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;
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
*/
@
ReturnsUnmodifiableCollection
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.
impl_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;
}
}