1 package org.xnap.commons.gui.dnd; 2 3 import java.awt.Component; 4 import java.awt.datatransfer.Transferable; 5 import java.io.File; 6 import java.util.ArrayList; 7 import java.util.List; 8 import javax.swing.JComponent; 9 import javax.swing.JTree; 10 import javax.swing.tree.TreePath; 11 import org.xnap.commons.gui.Dialogs; 12 import org.xnap.commons.i18n.I18n; 13 import org.xnap.commons.i18n.I18nFactory; 14 import org.xnap.commons.io.Job; 15 import org.xnap.commons.io.JobExecutor; 16 import org.xnap.commons.io.ProgressMonitor; 17 import org.xnap.commons.io.SubTaskProgressMonitor; 18 import org.xnap.commons.settings.BooleanSetting; 19 import org.xnap.commons.settings.PropertyResource; 20 import org.xnap.commons.util.FileHelper; 21 22 /*** 23 * Provides a default file transfer handler for {@link JTree}s. 24 * <p> 25 * When a drag from a tree is initiated all selected paths are collected and 26 * their {@link TreePath#getLastPathComponent() last path components}, if 27 * they are files, are added to the {@link FileTransferable} that is created. 28 * <p> 29 * To support dragging of files you have to enable it for the tree, see 30 * {@link JTree#setDragEnabled(boolean)}. 31 * <p> 32 * When a transferable with files is dropped on the tree, the handler tries to 33 * copy the dropped files to the first directory that is selected in the tree. A 34 * confirmation dialog is shown before copying if the boolean setting 35 * {@link #getShowCopyDialogSetting()} is true. 36 * 37 * @author Felix Berger 38 */ 39 public class DefaultTreeFileTransferHandler extends AbstractFileTransferHandler 40 { 41 42 private final static I18n i18n = 43 I18nFactory.getI18n(DefaultTreeFileTransferHandler.class); 44 45 private BooleanSetting showCopyDialog; 46 47 /*** 48 * Constructs a file transfer handler for {@link JTree}s. 49 * @param showCopyDialog the boolean setting which decides if a 50 * confirmation dialog is shown before dropped files are copied to the 51 * selected directory in the tree. 52 * 53 * @throws NullPointerException if <code>showCopyDialog</code> is null. 54 */ 55 public DefaultTreeFileTransferHandler(BooleanSetting showCopyDialog) 56 { 57 if (showCopyDialog == null) { 58 throw new NullPointerException("showCopyDialog must not be null"); 59 } 60 this.showCopyDialog = showCopyDialog; 61 } 62 63 /*** 64 * Convenience constructor which creates a dummy boolean setting that 65 * says the copy confirmation dialog should not be shown. 66 * <p> 67 * See {@link #DefaultTreeFileTransferHandler(BooleanSetting)} and 68 * {@link Dialogs#showCopyDialog(Component, File[], File, BooleanSetting)}. 69 */ 70 public DefaultTreeFileTransferHandler() 71 { 72 this(new BooleanSetting(new PropertyResource(), "dummy", 73 Boolean.FALSE)); 74 } 75 76 /*** 77 * Sets the boolean setting which decides if a confirmation dialog is 78 * shown before any files are copied. 79 * <p> 80 * See {@link Dialogs#showCopyDialog(Component, File[], File, BooleanSetting)}. 81 * 82 * @throws NullPointerException if <code>setting</code> is null. 83 */ 84 public void setShowCopyDialogSetting(BooleanSetting setting) 85 { 86 if (setting == null) { 87 throw new NullPointerException("setting must not be null"); 88 } 89 showCopyDialog = setting; 90 } 91 92 /*** 93 * Returns the boolean setting which decides if a confirmation dialog 94 * is shown before any files are copied. 95 */ 96 public BooleanSetting getShowCopyDialogSetting() 97 { 98 return showCopyDialog; 99 } 100 101 /*** 102 * 103 * 104 */ 105 @Override 106 protected Transferable createTransferable(JComponent c) 107 { 108 JTree tree = (JTree)c; 109 TreePath[] selectedPaths = tree.getSelectionPaths(); 110 List<File> files = new ArrayList<File>(selectedPaths.length); 111 for (TreePath path : selectedPaths) { 112 if (path.getLastPathComponent() instanceof File) { 113 files.add((File)path.getLastPathComponent()); 114 } 115 } 116 if (!files.isEmpty()) { 117 return new FileTransferable(files); 118 } 119 return null; 120 } 121 122 /*** 123 * Returns <code>false</code> if no directory is selected in the tree 124 * where the files could be dropped into or the selected file is not an 125 * existent directory. 126 * <p> 127 * Otherwise <code>files</code> are copied to the specified directory 128 * while a progress dialog is displayed. 129 */ 130 @Override 131 public boolean importFiles(JComponent comp, List<File> files) 132 { 133 final JTree tree = (JTree)comp; 134 TreePath selectedPath = tree.getSelectionPath(); 135 if (selectedPath == null) { 136 return false; 137 } 138 final File dir = (File)selectedPath.getLastPathComponent(); 139 if (!dir.isDirectory()) { 140 return false; 141 } 142 final File[] filesToCopy = Dialogs.showCopyDialog 143 (tree, files.toArray(new File[0]), dir, 144 new BooleanSetting(new PropertyResource(), "dummy", 145 Boolean.TRUE)); 146 147 try { 148 JobExecutor.run(i18n.tr("Copying Files"), new CopyRunner(filesToCopy, dir)); 149 } 150 catch (Exception e) { 151 Dialogs.showError(tree, i18n.tr("Error copying files"), e); 152 } 153 return false; 154 } 155 156 private class CopyRunner implements Job<Object> 157 { 158 private File[] files; 159 private File targetPath; 160 161 public CopyRunner(File[] files, File targetPath) 162 { 163 this.files = files; 164 this.targetPath = targetPath; 165 } 166 167 public Object run(ProgressMonitor monitor) throws Exception 168 { 169 monitor.setTotalSteps(files.length * 100); 170 for (File file : files) { 171 File dest = FileHelper.createUnique(targetPath, file.getName()); 172 monitor.setText(i18n.tr("Copying<br>{0}<br>to<br>{1}...", file, dest)); 173 FileHelper.copy(file, dest, new SubTaskProgressMonitor(monitor, 174 100, file.length())); 175 if (monitor.isCancelled()) { 176 return null; 177 } 178 } 179 return null; 180 } 181 } 182 }