// This file is part of PleoCommand:
// Interactively control Pleo with psychobiological parameters
//
// Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Boston, USA.
package pleocmd.cfg;
import java.awt.Container;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JTextField;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import pleocmd.Log;
import pleocmd.RunnableWithArgument;
import pleocmd.exc.ConfigurationException;
import pleocmd.exc.InternalException;
import pleocmd.itfc.gui.Layouter;
import pleocmd.itfc.gui.Layouter.Button;
public final class ConfigPath extends ConfigValue {
public enum PathType {
FileForReading, FileForWriting, Directory
}
private File content;
private final PathType type;
private RunnableWithArgument modifyFile;
private JTextField tf;
private boolean acceptAllFileFilter = true;
private final List<FileFilter> filters = new ArrayList<FileFilter>();
private boolean internalMod;
public ConfigPath(final String label, final PathType type) {
super(label);
this.type = type;
clearContent();
}
public ConfigPath(final String label, final File content,
final PathType type) {
this(label, type);
try {
setContent(content);
} catch (final ConfigurationException e) {
throw new IllegalArgumentException(
"Cannot initialize default content", e);
}
}
@Override
public File getContent() {
return content;
}
public void setContent(final File content) throws ConfigurationException {
if (content == null) throw new NullPointerException("content");
if (type != PathType.FileForWriting && !content.exists())
throw new ConfigurationException("'%s' does not exist", content);
if (type != PathType.Directory && content.isDirectory())
throw new ConfigurationException("'%s' is a directory", content);
switch (type) {
case FileForReading:
if (!content.canRead())
throw new ConfigurationException("Cannot read from file '%s'",
content);
break;
case FileForWriting:
if (content.exists() ? !content.canWrite() : content
.getParentFile() != null
&& !content.getParentFile().canWrite())
throw new ConfigurationException("Cannot write to file '%s'",
content);
break;
case Directory:
if (!content.isDirectory())
throw new ConfigurationException("'%s' is not a directory",
content);
break;
default:
throw new InternalException(type);
}
checkValidString(content.getPath(), false);
this.content = content;
if (tf != null) tf.setText(content.getPath());
}
public void clearContent() {
content = new File("");
if (tf != null) tf.setText("");
}
public File getContentGUI() {
return tf == null ? content : new File(tf.getText());
}
public void setContentGUI(final File file) {
internalMod = true;
try {
if (tf != null) tf.setText(file.getPath());
} finally {
internalMod = false;
}
}
public void clearContentGUI() {
internalMod = true;
try {
if (tf != null) tf.setText("");
} finally {
internalMod = false;
}
}
public PathType getType() {
return type;
}
@Override
public String asString() {
return content.getPath();
}
@Override
public void setFromString(final String string)
throws ConfigurationException {
setContent(new File(string));
}
@Override
List<String> asStrings() {
throw new UnsupportedOperationException();
}
@Override
void setFromStrings(final List<String> strings) {
throw new UnsupportedOperationException();
}
@Override
public String getIdentifier() {
switch (type) {
case FileForReading:
return "read";
case FileForWriting:
return "write";
case Directory:
return "dir";
default:
throw new InternalException(type);
}
}
@Override
boolean isSingleLined() {
return true;
}
@Override
public boolean insertGUIComponents(final Layouter lay) {
tf = new JTextField(content.getPath(), 50);
tf.getDocument().addUndoableEditListener(new UndoableEditListener() {
@Override
public void undoableEditHappened(final UndoableEditEvent e) {
if (!isInternalMod()) invokeChangingContent(getTf().getText());
}
});
lay.add(tf, true);
lay.addButton(Button.Browse, new Runnable() {
@Override
public void run() {
selectPath(lay.getContainer().getParent());
}
});
if (modifyFile != null) {
final JButton btnModify = lay.addButton(Button.Modify,
"Edit the selected file", new Runnable() {
@Override
@SuppressWarnings("synthetic-access")
public void run() {
if (getModifyFile() != null)
getModifyFile().run(tf.getText());
}
});
btnModify.setEnabled(!tf.getText().isEmpty());
tf.addCaretListener(new CaretListener() {
@Override
@SuppressWarnings("synthetic-access")
public void caretUpdate(final CaretEvent e) {
btnModify.setEnabled(!tf.getText().isEmpty());
}
});
}
invokeChangingContent(tf.getText());
return false;
}
protected void selectPath(final Container parent) {
final JFileChooser fc = new JFileChooser(getContent());
fc.setSelectedFile(new File(tf.getText()));
fc.setAcceptAllFileFilterUsed(acceptAllFileFilter);
for (final FileFilter filter : filters)
fc.addChoosableFileFilter(filter);
switch (type) {
case FileForReading:
if (fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION)
tf.setText(fc.getSelectedFile().getPath());
break;
case FileForWriting:
if (fc.showSaveDialog(parent) == JFileChooser.APPROVE_OPTION) {
final FileFilter ff = fc.getFileFilter();
String path = fc.getSelectedFile().getPath();
if (ff instanceof FileNameExtensionFilter
&& !fc.getSelectedFile().getName().contains("."))
path = path + "."
+ ((FileNameExtensionFilter) ff).getExtensions()[0];
tf.setText(path);
}
break;
case Directory:
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION)
tf.setText(fc.getSelectedFile().getPath());
break;
default:
throw new InternalException(type);
}
}
@Override
public void setFromGUIComponents() {
try {
setContent(new File(tf.getText()));
} catch (final ConfigurationException e) {
Log.error(e, "Cannot set value '%s'", getLabel());
}
}
@Override
public void setGUIEnabled(final boolean enabled) {
if (tf != null) tf.setEnabled(enabled);
}
/**
* Sets the method which will be invoked if the user clicks on "Modify".<br>
* If <b>null</b> the "Modify" button will not be available.
*
* @param modifyFile
* {@link Runnable} or <b>null</b>
*/
public void setModifyFile(final RunnableWithArgument modifyFile) {
this.modifyFile = modifyFile;
}
/**
* @return {@link Runnable} invoked after the user clicked on "Modify" or
* <b>null</b>
*/
public RunnableWithArgument getModifyFile() {
return modifyFile;
}
/**
* Sets the list of {@link FileFilter}s available in the file selection
* dialog via {@link JFileChooser}.
*
* @param filters
* {@link List} of {@link FileFilter}
*/
public void setFileFilter(final List<FileFilter> filters) {
this.filters.clear();
this.filters.addAll(filters);
}
/**
* Determines whether the "Accept All" {@link FileFilter} is used as an
* available choice in the file selection dialog via {@link JFileChooser}.
*
* @param acceptAllFileFilter
* whether to accept all files
*/
public void setAcceptAllFileFilter(final boolean acceptAllFileFilter) {
this.acceptAllFileFilter = acceptAllFileFilter;
}
/**
* @return true if "Accept All" {@link FileFilter} will be available
*/
public boolean isAcceptAllFileFilter() {
return acceptAllFileFilter;
}
protected JTextField getTf() {
return tf;
}
protected boolean isInternalMod() {
return internalMod;
}
}