Source code: com/puppycrawl/tools/checkstyle/gui/FileDrop.java
1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2002 Oliver Burn
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.gui;
21
22 import java.awt.Color;
23 import java.awt.Component;
24 import java.awt.Container;
25 import java.awt.datatransfer.DataFlavor;
26 import java.awt.datatransfer.Transferable;
27 import java.awt.datatransfer.UnsupportedFlavorException;
28 import java.awt.dnd.DnDConstants;
29 import java.awt.dnd.DropTarget;
30 import java.awt.dnd.DropTargetDragEvent;
31 import java.awt.dnd.DropTargetDropEvent;
32 import java.awt.dnd.DropTargetEvent;
33 import java.awt.dnd.DropTargetListener;
34 import java.awt.event.HierarchyEvent;
35 import java.awt.event.HierarchyListener;
36 import java.io.File;
37 import java.io.IOException;
38 import java.util.List;
39 import java.util.TooManyListenersException;
40 import javax.swing.BorderFactory;
41 import javax.swing.JComponent;
42 import javax.swing.border.Border;
43
44 /**
45 * This class makes it easy to drag and drop files from the operating
46 * system to a Java program. Any <tt>java.awt.Component</tt> can be
47 * dropped onto, but only <tt>javax.swing.JComponent</tt>s will indicate
48 * the drop event with a changed border.
49 * <p/>
50 * To use this class, construct a new <tt>FileDrop</tt> by passing
51 * it the target component and a <tt>Listener</tt> to receive notification
52 * when file(s) have been dropped. Here is an example:
53 * <p/>
54 * <code><pre>
55 * JPanel myPanel = new JPanel();
56 * new FileDrop( myPanel, new FileDrop.Listener()
57 * { public void filesDropped( java.io.File[] files )
58 * {
59 * // handle file drop
60 * ...
61 * } // end filesDropped
62 * }); // end FileDrop.Listener
63 * </pre></code>
64 * <p/>
65 * You can specify the border that will appear when files are being dragged by
66 * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
67 * <tt>JComponent</tt>s will show any indication with a border.
68 * <p/>
69 *
70 * <p>Original author: Robert Harder, rharder@usa.net</p>
71 *
72 * @author Robert Harder
73 * @author Lars K?hne
74 */
75 class FileDrop
76 {
77 // TODO: Not sure that changing borders is a good idea.
78 // At least we should make sure that the border insets are preserved so
79 // that the panel layout does not change during the DnD operation.
80
81 private transient Border normalBorder;
82 private transient DropTargetListener dropListener;
83
84 // TODO: Blue is not a nice color in all LookAndFeels
85 /* Default border color */
86 private static final Color DEFAULT_BORDER_COLOR =
87 new Color(0f, 0f, 1f, 0.25f);
88
89 /**
90 * Constructs a {@link FileDrop} with a default light-blue border
91 * and, if <var>c</var> is a {@link java.awt.Container}, recursively
92 * sets all elements contained within as drop targets, though only
93 * the top level container will change borders.
94 *
95 * @param c Component on which files will be dropped.
96 * @param listener Listens for <tt>filesDropped</tt>.
97 * @since 1.0
98 */
99 FileDrop(
100 final Component c,
101 final Listener listener)
102 throws TooManyListenersException
103 {
104 this( c, // Drop target
105 BorderFactory.createMatteBorder(2, 2, 2, 2, DEFAULT_BORDER_COLOR), // Drag border
106 true, // Recursive
107 listener);
108 }
109
110
111 /**
112 * Full constructor with a specified border and debugging optionally turned on.
113 * With Debugging turned on, more status messages will be displayed to
114 * <tt>out</tt>. A common way to use this constructor is with
115 * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
116 * the parameter <tt>out</tt> will result in no debugging output.
117 *
118 * @param c Component on which files will be dropped.
119 * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
120 * @param recursive Recursively set children as drop targets.
121 * @param listener Listens for <tt>filesDropped</tt>.
122 * @since 1.0
123 */
124 FileDrop(
125 final Component c,
126 final Border dragBorder,
127 final boolean recursive,
128 final Listener listener)
129 throws TooManyListenersException
130 {
131 dropListener = new FileDropTargetListener(c, dragBorder, listener);
132 makeDropTarget(c, recursive);
133 }
134
135
136 private void makeDropTarget(final Component c, boolean recursive)
137 throws TooManyListenersException
138 {
139 // Make drop target
140 final DropTarget dt = new DropTarget();
141 dt.addDropTargetListener(dropListener);
142
143 // Listen for hierarchy changes and remove the
144 // drop target when the parent gets cleared out.
145 c.addHierarchyListener(new HierarchyListener()
146 {
147 public void hierarchyChanged(HierarchyEvent evt)
148 {
149 Component parent = c.getParent();
150 if (parent == null) {
151 c.setDropTarget(null);
152 }
153 else {
154 new DropTarget(c, dropListener);
155 }
156 }
157 });
158
159 if (c.getParent() != null) {
160 new DropTarget(c, dropListener);
161 }
162
163 if (recursive && (c instanceof Container)) {
164 Container cont = (Container) c;
165 Component[] comps = cont.getComponents();
166 for (int i = 0; i < comps.length; i++)
167 makeDropTarget(comps[i], recursive);
168 }
169 }
170
171
172 /** Determine if the dragged data is a file list. */
173 private boolean isDragOk(final DropTargetDragEvent evt)
174 {
175 boolean ok = false;
176 DataFlavor[] flavors = evt.getCurrentDataFlavors();
177
178 // See if any of the flavors are a file list
179 int i = 0;
180 while (!ok && i < flavors.length) { // Is the flavor a file list?
181 if (flavors[i].equals(DataFlavor.javaFileListFlavor))
182 ok = true;
183 i++;
184 }
185
186 return ok;
187 }
188
189
190 /**
191 * Removes the drag-and-drop hooks from the component and optionally
192 * from the all children. You should call this if you add and remove
193 * components after you've set up the drag-and-drop.
194 * This will recursively unregister all components contained within
195 * <var>c</var> if <var>c</var> is a {@link .Container}.
196 *
197 * @param c The component to unregister as a drop target
198 * @since 1.0
199 */
200 static void remove(Component c)
201 {
202 remove(c, true);
203 }
204
205
206 /**
207 * Removes the drag-and-drop hooks from the component and optionally
208 * from the all children. You should call this if you add and remove
209 * components after you've set up the drag-and-drop.
210 *
211 * @param c The component to unregister
212 * @param recursive Recursively unregister components within a container
213 * @since 1.0
214 */
215 static void remove(Component c, boolean recursive)
216 {
217 c.setDropTarget(null);
218 if (recursive && (c instanceof Container)) {
219 Component[] comps = ((Container) c).getComponents();
220 for (int i = 0; i < comps.length; i++) {
221 remove(comps[i], recursive);
222 }
223 }
224 }
225
226
227 /**
228 * Implement this inner interface to listen for when files are dropped. For example
229 * your class declaration may begin like this:
230 * <code><pre>
231 * public class MyClass implements FileDrop.Listener
232 * ...
233 * public void filesDropped( File[] files )
234 * {
235 * ...
236 * } // end filesDropped
237 * ...
238 * </pre></code>
239 *
240 * @since 1.0
241 */
242 public interface Listener
243 {
244 /**
245 * This method is called when files have been successfully dropped.
246 *
247 * @param files An array of <tt>File</tt>s that were dropped.
248 * @since 1.0
249 */
250 void filesDropped(File[] files);
251 }
252
253 private class FileDropTargetListener implements DropTargetListener
254 {
255 private final Component mC;
256 private final Border mDragBorder;
257 private final Listener mListener;
258
259 public void dragEnter(DropTargetDragEvent evt)
260 {
261 if (isDragOk(evt)) {
262 if (mC instanceof JComponent) {
263 JComponent jc = (JComponent) mC;
264 normalBorder = jc.getBorder();
265 jc.setBorder(mDragBorder);
266 }
267 evt.acceptDrag(DnDConstants.ACTION_COPY);
268 }
269 else {
270 evt.rejectDrag();
271 }
272 }
273
274 public void drop(DropTargetDropEvent evt)
275 {
276 try {
277 Transferable tr = evt.getTransferable();
278
279 if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
280 evt.acceptDrop(DnDConstants.ACTION_COPY);
281
282 List fileList = (List) tr.getTransferData(
283 DataFlavor.javaFileListFlavor);
284 File[] files = new File[fileList.size()];
285 fileList.toArray(files);
286
287 if (mListener != null) {
288 mListener.filesDropped(files);
289 }
290
291 evt.getDropTargetContext().dropComplete(true);
292 }
293 else {
294 evt.rejectDrop();
295 }
296 }
297 catch (IOException io) {
298 evt.rejectDrop();
299 }
300 catch (UnsupportedFlavorException ufe) {
301 evt.rejectDrop();
302 }
303 finally {
304 if (mC instanceof JComponent) {
305 JComponent jc = (JComponent) mC;
306 jc.setBorder(normalBorder);
307 }
308 }
309 }
310
311 public void dragExit(DropTargetEvent evt)
312 {
313 if (mC instanceof JComponent) {
314 JComponent jc = (JComponent) mC;
315 jc.setBorder(normalBorder);
316 }
317 }
318
319 public void dropActionChanged(DropTargetDragEvent evt)
320 {
321 if (isDragOk(evt)) {
322 evt.acceptDrag(DnDConstants.ACTION_COPY);
323 }
324 else {
325 evt.rejectDrag();
326 }
327 }
328
329 public void dragOver(DropTargetDragEvent dtde)
330 {
331 }
332
333 public FileDropTargetListener(Component aC, Border aDragBorder, Listener aListener)
334 {
335 mC = aC;
336 mDragBorder = aDragBorder;
337 mListener = aListener;
338 }
339 }
340
341 }