/*
* ShellProgressMonitor.java
*
* Created on March 1, 2005, 1:40 PM
*/
package kiyut.swing.shell.util;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Dialog;
import java.awt.event.*;
import java.awt.Frame;
import java.awt.Window;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.EventListenerList;
import javax.swing.filechooser.FileSystemView;
import kiyut.swing.shell.event.ShellEvent;
import kiyut.swing.shell.event.ShellListener;
/** ShellProgressMonitor is a Shell progress utility for Move,Copy,or Delete files.
* It will open up a dialog showing the progress of status.
* User can stop the progress by clicking on the cancel button,
* however, files which already moved/copied/deleted is not cancelable.
* On Exception, do not forget to call cancel
* <br>
* TODO: add i18n support
*
* @author Kiyut
*/
public class ShellProgressMonitor {
/** COPY */
public static int COPY = 0;
/** MOVE */
public static int MOVE = 1;
/** DELETE */
public static int DELETE = 2;
/** the <code>FileSystemView</code> for this component */
private FileSystemView fsv;
/** a boolean value indicated canceled action */
private boolean canceled = false;
/** the parent component for this component, use to instantiate Dialog */
private Component parentComponent;
/** Progress Dialog for this component */
private ShellProgressDialog progressDialog;
/** an EventListenerList object */
private EventListenerList listenerList;
/** Creates a new instance of ShellProgressMonitor */
public ShellProgressMonitor(Component parentComponent) {
this(parentComponent,FileSystemView.getFileSystemView());
}
/** Constructs a <code>ShellProgressMonitor</code>
* @param parentComponent determines the Frame in which the dialog is displayed
* @param fsv FileSystemView
*/
public ShellProgressMonitor(Component parentComponent, FileSystemView fsv) {
this.parentComponent = parentComponent;
this.fsv = fsv;
listenerList = new EventListenerList();
}
/** Sets the file system view that this model uses for accessing and creating file system resources.
* @param fsv the new FileSystemView
*/
public void setFileSystemView(FileSystemView fsv) {
this.fsv = fsv;
}
/** Return the progress status is canceled or not
* @return true or false
*/
public boolean isCanceled() {
return canceled;
}
/** Adds a listener for Shell events.
* @param l the ShellListener that will be notified on the ShellEvent
*/
public void addShellListener(ShellListener l) {
listenerList.add(ShellListener.class, l);
}
/** Removes ShellListener
* @param l ShellListener to remove
*/
public void removeShellListener(ShellListener l) {
listenerList.remove(ShellListener.class, l);
}
/** Notifies all listeners that have registered interest for notification on this event type.
* @param file a file object which has been deleted.
* @param type ShellEvent Type
*/
private void fireShellEvent(File file, int type) {
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
ShellEvent shellEvent = new ShellEvent(this,type,file,null);
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ShellListener.class) {
if (type == ShellEvent.SHELL_DELETED) {
((ShellListener)listeners[i+1]).shellDeleted(shellEvent);
} else if (type == ShellEvent.SHELL_COPIED) {
((ShellListener)listeners[i+1]).shellCopied(shellEvent);
} else if (type == ShellEvent.SHELL_MOVED) {
((ShellListener)listeners[i+1]).shellMoved(shellEvent);
}
}
}
}
/** Cancel the progress */
public void cancel() {
canceled = true;
closeDialog();
}
/** close the progress dialog
*/
private void closeDialog() {
if (progressDialog != null) {
progressDialog.setVisible(false);
progressDialog.dispose();
progressDialog = null;
}
}
/** Recursively add file into the List
*/
private void addRecursive(List<File> fileList, File file) {
if (canceled == true) { return; }
if (file.isDirectory()) {
File[] files = fsv.getFiles(file,false);
for (int i=0; i<files.length; i++) {
addRecursive(fileList,files[i]);
}
}
fileList.add(file);
}
/** Start the Progress. It include the sub dir processing.
* <strong>Important:</strong> the source files must from the base/parent directory
* <strong>on exception do not forget to call cancel</strong>
*
* @param action MOVE, COPY, or DELETE
* @param sourceList The list of files
* @param dest The destination directory if action is MOVE or COPY otherwise set to null
* @throws IOException if an I/O error occurs.
* @see #cancel
*/
public void start(int action, List<File> sourceList, File dest) throws IOException {
if (action != COPY && action != MOVE && action != DELETE) {
throw new IllegalArgumentException("invalid action");
}
Window window = (Window)SwingUtilities.getRoot(parentComponent);
if (window instanceof Frame) {
progressDialog = new ShellProgressDialog((Frame)window,false);
} else {
progressDialog = new ShellProgressDialog((Dialog)window,false);
}
if (action == COPY) {
copy(sourceList,dest);
} else if (action == MOVE) {
move(sourceList,dest);
} else if (action == DELETE) {
delete(sourceList);
}
closeDialog();
}
/** Move implementation
* @param sourceList list of files to be deleted
* @param dest The destination directory
* @throws IOException if an I/O error occurs.
*/
protected void move(List<File> sourceList, File dest) throws IOException {
progressDialog.setTitle("Move...");
progressDialog.setMinimum(0);
progressDialog.setValue(0);
progressDialog.setFileText("Preparing...");
progressDialog.setFromText("");
progressDialog.setVisible(true);
progressDialog.paintImmediately();
canceled = false;
progressDialog.setMaximum(sourceList.size());
boolean b;
for (int i=0; i<sourceList.size(); i++) {
if (canceled == true) {
break;
}
File sourceFile = sourceList.get(i);
File parent = fsv.getParentDirectory(sourceFile);
progressDialog.setFileText(fsv.getSystemDisplayName(sourceFile));
if (parent != null) {
progressDialog.setFromText("From " + fsv.getSystemDisplayName(parent));
} else {
progressDialog.setFromText("");
}
progressDialog.setValue(i);
progressDialog.paintImmediately();
File destFile = new File(dest,sourceFile.getName());
if(destFile.exists() && destFile.isDirectory() == false && sourceFile.isDirectory() == false) {
// open dialog to ask to overwrite
int choice = FileReplacePane.showDialog(progressDialog,fsv,destFile,sourceFile);
if (choice == JOptionPane.CANCEL_OPTION) {
canceled = true;
break;
} else if (choice == JOptionPane.NO_OPTION) {
continue;
}
}
moveFile(sourceFile,destFile);
fireShellEvent(sourceFile,ShellEvent.SHELL_MOVED);
}
}
/** Copy implementation
* @param sourceList list of files to be deleted
* @param dest The destination directory
* @throws IOException if an I/O error occurs.
*/
protected void copy(List<File> sourceList, File dest) throws IOException {
progressDialog.setTitle("Copying...");
progressDialog.setMinimum(0);
progressDialog.setValue(0);
progressDialog.setFileText("Preparing...");
progressDialog.setFromText("");
progressDialog.setVisible(true);
progressDialog.paintImmediately();
canceled = false;
List<File> baseList = new ArrayList<File>();
List<File> copyList = new ArrayList<File>();
for (int i=0; i<sourceList.size(); i++) {
File file = sourceList.get(i);
File parent = file.getParentFile();
if (parent != null) {
if (baseList.contains(parent) == false) {
baseList.add(parent);
}
}
addRecursive(copyList,file);
}
progressDialog.setMaximum(copyList.size());
boolean b;
for (int i=0; i<copyList.size(); i++) {
if (canceled == true) {
break;
}
File sourceFile = copyList.get(i);
File parent = fsv.getParentDirectory(sourceFile);
progressDialog.setFileText(fsv.getSystemDisplayName(sourceFile));
if (parent != null) {
progressDialog.setFromText("From " + fsv.getSystemDisplayName(parent));
} else {
progressDialog.setFromText("");
}
progressDialog.setValue(i);
progressDialog.paintImmediately();
File destFile = resolveDestFile(dest,sourceFile,baseList);
if(destFile.exists() && destFile.isDirectory()==false) {
// open dialog to ask to overwrite
int choice = FileReplacePane.showDialog(progressDialog,fsv,destFile,sourceFile);
if (choice == JOptionPane.CANCEL_OPTION) {
canceled = true;
break;
} else if (choice == JOptionPane.NO_OPTION) {
continue;
}
} else {
File parentDir = destFile.getParentFile();
if (parentDir != null) {
if (!parentDir.exists()) {
parentDir.mkdirs();
}
}
if (!sourceFile.isDirectory()) {
destFile.createNewFile();
}
}
if (!sourceFile.isDirectory()) {
copyFile(sourceFile,destFile);
}
if (sourceList.contains(sourceFile)) {
fireShellEvent(sourceFile,ShellEvent.SHELL_COPIED);
}
}
}
/** Delete implementation
* @param sourceList list of files to be deleted
* @throws IOException if an I/O error occurs.
*/
protected void delete(List<File> sourceList) throws IOException {
progressDialog.setTitle("Deleting...");
progressDialog.setMinimum(0);
progressDialog.setValue(0);
progressDialog.setFileText("Preparing...");
progressDialog.setFromText("");
progressDialog.setVisible(true);
progressDialog.paintImmediately();
canceled = false;
List<File> deleteList = new ArrayList<File>();
for (int i=0; i<sourceList.size(); i++) {
addRecursive(deleteList,sourceList.get(i));
}
progressDialog.setMaximum(deleteList.size());
boolean b;
for (int i=0; i<deleteList.size(); i++) {
if (canceled == true) {
break;
}
File file = deleteList.get(i);
File parent = fsv.getParentDirectory(file);
progressDialog.setFileText(fsv.getSystemDisplayName(file));
if (parent != null) {
progressDialog.setFromText("From " + fsv.getSystemDisplayName(parent));
} else {
progressDialog.setFromText("");
}
progressDialog.setValue(i);
progressDialog.paintImmediately();
b = file.delete();
if (b == false) {
throw new IOException("Unable to delete " + file);
}
if (sourceList.contains(file)) {
fireShellEvent(file,ShellEvent.SHELL_DELETED);
}
}
}
private File resolveDestFile(File destFile, File sourceFile, List<File> baseList) {
File file = null;
String child = sourceFile.getName();
String path = sourceFile.getAbsolutePath();
for (int i=0; i < baseList.size(); i++) {
File baseFile = baseList.get(i);
if (path.startsWith(baseFile.getAbsolutePath())) {
child = path.substring(baseFile.getAbsolutePath().length());
break;
}
}
file = new File(destFile,child);
return file;
}
private void copyFile(File sourceFile, File destFile) throws IOException {
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {
if(source != null) { source.close(); }
if(destination != null) { destination.close(); }
}
}
private void moveFile(File sourceFile, File destFile) throws IOException {
boolean b = sourceFile.renameTo(destFile);
if (b == false) {
throw new IOException("Unable to move " + sourceFile);
}
}
/** DeleteProgressDialog with progress bar and cancel button
*/
public class ShellProgressDialog extends JDialog {
private JLabel fileLabel;
private JLabel fromLabel;
private JButton cancelButton;
private JProgressBar progressBar;
private JPanel panel;
/** Constructs a <code>ShellProgressDialog</code>
* @param parent determines the Dialog in which the dialog is displayed
* @param modal true for a modal dialog, false for one that allows other windows to be active at the same time
*/
public ShellProgressDialog(Dialog parent, boolean modal) {
super(parent, modal);
initComponents();
}
/** Constructs a <code>ShellProgressDialog</code>
* @param parent determines the Frame in which the dialog is displayed
* @param modal true for a modal dialog, false for one that allows other windows to be active at the same time
*/
public ShellProgressDialog(Frame parent, boolean modal) {
super(parent, modal);
initComponents();
}
/** initialize the component */
private void initComponents() {
setTitle("Progress...");
setResizable(false);
panel = new JPanel();
panel.setLayout(null);
panel.setPreferredSize(new Dimension(380,140));
getContentPane().add(panel,BorderLayout.CENTER);
fileLabel = new JLabel("");
fileLabel.setBounds(15, 60, 350, 15);
panel.add(fileLabel);
fromLabel = new JLabel("");
fromLabel.setBounds(15, 80, 350, 15);
panel.add(fromLabel);
progressBar = new JProgressBar();
progressBar.setBounds(15, 100, 250, 14);
panel.add(progressBar);
JButton cancelButton = new JButton(UIManager.getString("OptionPane.cancelButtonText"));
cancelButton.setBounds(280, 100, 75, 25);
cancelButton.setActionCommand("cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cancel();
}
});
cancelButton.requestFocus();
panel.add(cancelButton);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog();
}
});
pack();
}
/** Paint immediately */
public void paintImmediately() {
Dimension dim = getPreferredSize();
panel.paintImmediately(0,0,(int)dim.getWidth(),(int)dim.getHeight());
}
/** Sets the progress bar's minimum value
* @param min the new minimum
*/
public void setMinimum(int min) {
progressBar.setMinimum(min);
}
/** Sets the progress bar's maximum value
* @param max the new maximum
*/
public void setMaximum(int max) {
progressBar.setMaximum(max);
}
/** Sets the progress bar's current value
* @param n the new value
*/
public void setValue(int n) {
progressBar.setValue(n);
}
/** Sets the from text
* @param txt the new text
*/
public void setFromText(String txt) {
fromLabel.setText(txt);
}
/** Sets the file text
* @param txt the file text
*/
public void setFileText(String txt) {
fileLabel.setText(txt);
}
}
}