/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.framework.uitools;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import org.eclipse.persistence.tools.workbench.framework.resources.DefaultResourceRepository;
import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepository;
import org.eclipse.persistence.tools.workbench.utility.io.FileTools;
/**
* This subclass of <code>JFileChooser</code> helps the user specify a relative path
* to a file. If a root file is specified and the user wants to make the selection relative to that
* file, then {@link JFileChooser#getSelectedFile()} and {@link JFileChooser#getSelectedFiles()}
* will return files specified relative to the root.
*/
public class FileChooser extends JFileChooser {
/** Determines whether the selected file(s) should be relative. */
boolean convertToRelativePath;
/** The directory to which the selected files are relative. */
private File rootFile;
/**
* If the dialog is NOT visible #get/setSelectedFile() and
* #get/setSelectedFiles() will return/take a relative file specification.
*/
private boolean dialogIsVisible;
// ********** constructors **********
/**
* Construct a file chooser at the user's "home" directory.
*/
public FileChooser() {
this(null, null);
}
/**
* Construct a file chooser at the specified directory.
*/
public FileChooser(File currentDirectory) {
this(currentDirectory, null);
}
/**
* Construct a file chooser at the specified directory with the
* specified "root" directory to which the user can choose a file
* relative to. If a "root" directory is not specified here, the
* check box will not be displayed.
*/
public FileChooser(File currentDirectory, File rootFile) {
super(currentDirectory);
this.rootFile = rootFile;
// if a root file is specified, default to true
this.convertToRelativePath = (this.rootFile != null);
}
// ********** "make relative" check box **********
/**
* override to add the "make relative" check box if appropriate
*/
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
// we only add the check box if a root file is specified
if (this.rootFile != null) {
this.addRelativeCheckBoxTo(dialog.getContentPane());
}
return dialog;
}
private void addRelativeCheckBoxTo(Container contentPane) {
ResourceRepository rr = new DefaultResourceRepository(UIToolsResourceBundle.class);
JCheckBox relativeCheckBox = new JCheckBox(rr.getString("FILECHOOSER_MAKE_RELATIVE_CHECKBOX", this.rootFile.getPath()));
relativeCheckBox.setSelected(this.convertToRelativePath);
relativeCheckBox.setMnemonic(rr.getMnemonic("FILECHOOSER_MAKE_RELATIVE_CHECKBOX"));
relativeCheckBox.setDisplayedMnemonicIndex(rr.getMnemonicIndex("FILECHOOSER_MAKE_RELATIVE_CHECKBOX"));
relativeCheckBox.addActionListener(this.buildActionListener());
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
panel.add(relativeCheckBox, BorderLayout.LINE_START);
contentPane.add(panel, BorderLayout.PAGE_END);
}
private ActionListener buildActionListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
FileChooser.this.convertToRelativePath = ((JCheckBox) e.getSource()).isSelected();
}
};
}
// ********** public API **********
/**
* override to set internal flag indicating whether the dialog is visible
*/
public int showDialog(Component parent, String approveButtonText) throws HeadlessException {
this.dialogIsVisible = true;
int result = super.showDialog(parent, approveButtonText);
this.dialogIsVisible = false;
return result;
}
/**
* override to make the selected file relative if appropriate
*/
public File getSelectedFile() {
File selectedFile = super.getSelectedFile();
selectedFile = checkForDuplicateEntry(selectedFile);
return this.convertToRelativeFile(selectedFile);
}
/**
* override to make the selected files relative if appropriate
*/
public File[] getSelectedFiles() {
File[] files = super.getSelectedFiles();
if (files.length == 1) {
files[0] = checkForDuplicateEntry(files[0]);
}
return this.convertToRelativeFiles(files);
}
/**
* TODO: Is this patching a JDK bug ??? Basically double clicking on a
* directory shows that directory in the File Name field and then
* clicking Open will append that directory to the end of the current
* directory, also, writing something in the File Name that does not
* exist gets appended
*/
private File checkForDuplicateEntry(File file) {
if (getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES ||
getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY)
{
File currentDirectory = getCurrentDirectory();
if ((file != null) && file.isAbsolute() && ! file.exists()) {
file = currentDirectory;
}
}
return file;
}
/**
* override to make the new selected file absolute if appropriate
*/
public void setSelectedFile(File file) {
super.setSelectedFile(this.convertToAbsoluteFile(file));
}
/**
* override to make the new selected files absolute if appropriate
*/
public void setSelectedFiles(File[] files) {
super.setSelectedFiles(this.convertToAbsoluteFiles(files));
}
// ********** conversion methods **********
/**
* convert the specified files to be relative to the root directory if appropriate;
* NB: we munge the array passed in
*/
private File[] convertToRelativeFiles(File[] files) {
for (int i = files.length; i-- > 0; ) {
files[i] = this.convertToRelativeFile(files[i]);
}
return files;
}
/**
* convert the specified file to be relative to the root directory if appropriate
*/
private File convertToRelativeFile(File file) {
// conversions only take place when the dialog is not visible
if (this.dialogIsVisible) {
return file;
}
// no need to convert null
if (file == null) {
return file;
}
// the check box is checked
if (this.convertToRelativePath) {
// workaround for Windows: depending on how the user navigates to a
// file, the file returned might be a Win32ShellFolder and the "parent"
// hierarchy causes problems when trying to convert the file to a file
// that is relative to the "root" file; e.g. the file C:\foo will return the
// following parents:
// C:\foo => C:\ => My Computer => C:\Documents and Settings\<user>\Desktop => null
// so we force the file to be a simple File instead of a Win32ShellFolder ~bjv
file = new File(file.getPath());
// rootFile cannot be null if convertToRelativePath is true
return FileTools.convertToRelativeFile(file, this.rootFile);
}
return file;
}
/**
* convert the specified files to be absolute from the root directory;
* NB: we munge the array passed in
*/
private File[] convertToAbsoluteFiles(File[] files) {
// no need to convert null
if (files == null) {
return files;
}
for (int i = files.length; i-- > 0; ) {
files[i] = this.convertToAbsoluteFile(files[i]);
}
return files;
}
/**
* convert the specified file to be absolute from the root directory
*/
private File convertToAbsoluteFile(File file) {
// conversions only take place when the dialog is not visible
if (this.dialogIsVisible) {
return file;
}
// no need to convert null
if (file == null) {
return file;
}
// the check box is checked
if (this.convertToRelativePath) {
// rootFile cannot be null if convertToRelativePath is true
return FileTools.convertToAbsoluteFile(file, this.rootFile);
}
return file;
}
}