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 }