/*
Copyright 2009 Hauke Rehfeld
This file is part of QuakeInjector.
QuakeInjector 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 3 of the License, or
(at your option) any later version.
QuakeInjector 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 QuakeInjector. If not, see <http://www.gnu.org/licenses/>.
*/
package de.haukerehfeld.quakeinjector.gui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import de.haukerehfeld.quakeinjector.ChangeListenerList;
import de.haukerehfeld.quakeinjector.RelativePath;
import de.haukerehfeld.quakeinjector.Utils;
/**
* A Panel to input paths
*/
public class JPathPanel extends JPanel {
private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
private final ChangeListenerList changeListeners = new ChangeListenerList();
private final static int inputLength = 32;
private File basePath;
private final JTextField path;
private final JLabel errorLabel;
private final JButton fileChooserButton;
private final JFileChooser chooser;
private final Verifier check;
/**
* If the saved path is absolute although we have a basepath - happens on windows when the basepath is
* on a different drive
*/
private boolean absolute = false;
/**
* @param filesAndOrDirectories what kind of files can be selected with the filechooser:
* one of JFileChooser.DIRECTORIES_ONLY,
* JFileChooser.FILES_AND_DIRECTORIES,
* JFileChooser.FILES_ONLY
*/
public JPathPanel(Verifier check, File defaultPath, int filesAndOrDirectories) {
this(check, defaultPath, null, filesAndOrDirectories);
}
/**
* @param filesAndOrDirectories what kind of files can be selected with the filechooser:
* one of JFileChooser.DIRECTORIES_ONLY,
* JFileChooser.FILES_AND_DIRECTORIES,
* JFileChooser.FILES_ONLY
*/
public JPathPanel(Verifier check,
File defaultPath,
File basePath,
int filesAndOrDirectories) {
this.check = check;
this.basePath = basePath;
if (defaultPath == null) {
defaultPath = new File("");
}
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
absolute = defaultPath.isAbsolute();
this.path = new JTextField(defaultPath.toString(), inputLength);
PathVerifier verifier = new PathVerifier();
path.setInputVerifier(verifier);
path.getDocument().addDocumentListener(verifier);
add(path);
this.errorLabel = new JLabel();
add(errorLabel);
this.chooser = new JFileChooser(getPath());
chooser.setFileSelectionMode(filesAndOrDirectories);
this.fileChooserButton = new JButton("Select");
fileChooserButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int returnVal = chooser.showOpenDialog(JPathPanel.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
setPath(file);
}
}
});
add(fileChooserButton);
}
/**
* Just checks if the current path is valid without notifying listeners
*/
public boolean verifies() {
return check();
}
/**
* Check if current path is valid and notify listeners
*/
public boolean verify() {
if (!check()) {
notifyErrorListeners();
return false;
}
else {
chooser.setCurrentDirectory(getPath());
notifyChangeListeners();
return true;
}
}
private boolean check() {
File f = getPath();
//System.out.println(f);
errorLabel.setText(check.errorMessage(f));
return this.check.verify(f);
}
public void setBasePath(File basePath) {
File oldFile = getPath();
boolean verifies = verifies();
this.basePath = basePath;
//System.out.println("Changing basePath: " + oldFile + " to " + basePath);
if (verifies) {
setPath(oldFile);
}
//put this above the if (verifies) to change chooser to the new basedir
this.chooser.setCurrentDirectory(getPath());
verify();
}
public void setPath(String path) {
setPath(new File(path));
}
public void setPath(File path) {
String pathString;
if (basePath != null) {
File relative = RelativePath.getRelativePath(basePath, path);
//may return absolute path if no relative path possible
absolute = relative.isAbsolute();
pathString = relative.toString();
}
else {
pathString = path.getAbsolutePath();
}
this.path.setText(pathString);
}
/**
* get a file representing what this pathpanel is pointing to
*/
public File getPath() {
/*
* Build a file object from - if set - the basepath and the textfield content
*/
String path = this.path.getText();
if (path == null) {
path = "";
}
File file;
if (basePath != null && !absolute) {
file = new File(basePath.getAbsolutePath() + File.separator + path);
}
else {
file = new File(path);
}
return file;
}
public void addErrorListener(ErrorListener e) {
errorListeners.add(e);
}
private void notifyErrorListeners() {
ErrorEvent e = new SimpleErrorEvent(this);
for (ErrorListener l: errorListeners) {
l.errorOccured(e);
}
}
public void addChangeListener(ChangeListener l) {
changeListeners.addChangeListener(l);
}
private void notifyChangeListeners() {
changeListeners.notifyChangeListeners(this);
}
/**
* Hack: Because i can't call verify() from the inner class that has @Override verify(Stuff s);
*/
private boolean verify_() {
return verify();
}
public interface Verifier {
public boolean verify(File file);
public String errorMessage(File file);
}
public static class WritableDirectoryVerifier implements Verifier {
public boolean verify(File f) {
return (f.exists()
&& f.isDirectory()
&& f.canRead()
&& Utils.canWriteToDirectory(f));
}
public String errorMessage(File f) {
if (!f.exists()) {
return "Doesn't exist!";
}
else if (!f.isDirectory()) {
return "Is not a directory!";
}
else if (!Utils.canWriteToDirectory(f)) {
return "Cannot be written to!";
}
return null;
}
}
private class PathVerifier extends InputVerifier implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
verify_();
}
@Override
public void removeUpdate(DocumentEvent e) {
verify_();
}
@Override
public void changedUpdate(DocumentEvent e) {
verify_();
}
@Override
public boolean verify(JComponent input) {
return verify_();
}
@Override
public boolean shouldYieldFocus(JComponent input) {
verify_();
return true;
}
}
}