001: ////////////////////////////////////////////////////////////////////////////////
002: // checkstyle: Checks Java source code for adherence to a set of rules.
003: // Copyright (C) 2001-2002 Oliver Burn
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: ////////////////////////////////////////////////////////////////////////////////
019:
020: package com.puppycrawl.tools.checkstyle.gui;
021:
022: import java.awt.Color;
023: import java.awt.Component;
024: import java.awt.Container;
025: import java.awt.datatransfer.DataFlavor;
026: import java.awt.datatransfer.Transferable;
027: import java.awt.datatransfer.UnsupportedFlavorException;
028: import java.awt.dnd.DnDConstants;
029: import java.awt.dnd.DropTarget;
030: import java.awt.dnd.DropTargetDragEvent;
031: import java.awt.dnd.DropTargetDropEvent;
032: import java.awt.dnd.DropTargetEvent;
033: import java.awt.dnd.DropTargetListener;
034: import java.awt.event.HierarchyEvent;
035: import java.awt.event.HierarchyListener;
036: import java.io.File;
037: import java.io.IOException;
038: import java.util.List;
039: import java.util.TooManyListenersException;
040: import javax.swing.BorderFactory;
041: import javax.swing.JComponent;
042: import javax.swing.border.Border;
043:
044: /**
045: * This class makes it easy to drag and drop files from the operating
046: * system to a Java program. Any <tt>java.awt.Component</tt> can be
047: * dropped onto, but only <tt>javax.swing.JComponent</tt>s will indicate
048: * the drop event with a changed border.
049: * <p/>
050: * To use this class, construct a new <tt>FileDrop</tt> by passing
051: * it the target component and a <tt>Listener</tt> to receive notification
052: * when file(s) have been dropped. Here is an example:
053: * <p/>
054: * <code><pre>
055: * JPanel myPanel = new JPanel();
056: * new FileDrop( myPanel, new FileDrop.Listener()
057: * { public void filesDropped( java.io.File[] files )
058: * {
059: * // handle file drop
060: * ...
061: * } // end filesDropped
062: * }); // end FileDrop.Listener
063: * </pre></code>
064: * <p/>
065: * You can specify the border that will appear when files are being dragged by
066: * calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
067: * <tt>JComponent</tt>s will show any indication with a border.
068: * <p/>
069: *
070: * <p>Original author: Robert Harder, rharder@usa.net</p>
071: *
072: * @author Robert Harder
073: * @author Lars K?hne
074: */
075: class FileDrop {
076: // TODO: Not sure that changing borders is a good idea.
077: // At least we should make sure that the border insets are preserved so
078: // that the panel layout does not change during the DnD operation.
079:
080: private transient Border normalBorder;
081: private transient DropTargetListener dropListener;
082:
083: // TODO: Blue is not a nice color in all LookAndFeels
084: /* Default border color */
085: private static final Color DEFAULT_BORDER_COLOR = new Color(0f, 0f,
086: 1f, 0.25f);
087:
088: /**
089: * Constructs a {@link FileDrop} with a default light-blue border
090: * and, if <var>c</var> is a {@link java.awt.Container}, recursively
091: * sets all elements contained within as drop targets, though only
092: * the top level container will change borders.
093: *
094: * @param c Component on which files will be dropped.
095: * @param listener Listens for <tt>filesDropped</tt>.
096: * @since 1.0
097: */
098: FileDrop(final Component c, final Listener listener)
099: throws TooManyListenersException {
100: this (c, // Drop target
101: BorderFactory.createMatteBorder(2, 2, 2, 2,
102: DEFAULT_BORDER_COLOR), // Drag border
103: true, // Recursive
104: listener);
105: }
106:
107: /**
108: * Full constructor with a specified border and debugging optionally turned on.
109: * With Debugging turned on, more status messages will be displayed to
110: * <tt>out</tt>. A common way to use this constructor is with
111: * <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for
112: * the parameter <tt>out</tt> will result in no debugging output.
113: *
114: * @param c Component on which files will be dropped.
115: * @param dragBorder Border to use on <tt>JComponent</tt> when dragging occurs.
116: * @param recursive Recursively set children as drop targets.
117: * @param listener Listens for <tt>filesDropped</tt>.
118: * @since 1.0
119: */
120: FileDrop(final Component c, final Border dragBorder,
121: final boolean recursive, final Listener listener)
122: throws TooManyListenersException {
123: dropListener = new FileDropTargetListener(c, dragBorder,
124: listener);
125: makeDropTarget(c, recursive);
126: }
127:
128: private void makeDropTarget(final Component c, boolean recursive)
129: throws TooManyListenersException {
130: // Make drop target
131: final DropTarget dt = new DropTarget();
132: dt.addDropTargetListener(dropListener);
133:
134: // Listen for hierarchy changes and remove the
135: // drop target when the parent gets cleared out.
136: c.addHierarchyListener(new HierarchyListener() {
137: public void hierarchyChanged(HierarchyEvent evt) {
138: final Component parent = c.getParent();
139: if (parent == null) {
140: c.setDropTarget(null);
141: } else {
142: new DropTarget(c, dropListener);
143: }
144: }
145: });
146:
147: if (c.getParent() != null) {
148: new DropTarget(c, dropListener);
149: }
150:
151: if (recursive && (c instanceof Container)) {
152: final Container cont = (Container) c;
153: final Component[] comps = cont.getComponents();
154: for (int i = 0; i < comps.length; i++)
155: makeDropTarget(comps[i], recursive);
156: }
157: }
158:
159: /** Determine if the dragged data is a file list. */
160: private boolean isDragOk(final DropTargetDragEvent evt) {
161: boolean ok = false;
162: final DataFlavor[] flavors = evt.getCurrentDataFlavors();
163:
164: // See if any of the flavors are a file list
165: int i = 0;
166: while (!ok && (i < flavors.length)) { // Is the flavor a file list?
167: if (flavors[i].equals(DataFlavor.javaFileListFlavor))
168: ok = true;
169: i++;
170: }
171:
172: return ok;
173: }
174:
175: /**
176: * Removes the drag-and-drop hooks from the component and optionally
177: * from the all children. You should call this if you add and remove
178: * components after you've set up the drag-and-drop.
179: * This will recursively unregister all components contained within
180: * <var>c</var> if <var>c</var> is a {@link Container}.
181: *
182: * @param c The component to unregister as a drop target
183: * @since 1.0
184: */
185: static void remove(Component c) {
186: remove(c, true);
187: }
188:
189: /**
190: * Removes the drag-and-drop hooks from the component and optionally
191: * from the all children. You should call this if you add and remove
192: * components after you've set up the drag-and-drop.
193: *
194: * @param c The component to unregister
195: * @param recursive Recursively unregister components within a container
196: * @since 1.0
197: */
198: static void remove(Component c, boolean recursive) {
199: c.setDropTarget(null);
200: if (recursive && (c instanceof Container)) {
201: final Component[] comps = ((Container) c).getComponents();
202: for (int i = 0; i < comps.length; i++) {
203: remove(comps[i], recursive);
204: }
205: }
206: }
207:
208: /**
209: * Implement this inner interface to listen for when files are dropped. For example
210: * your class declaration may begin like this:
211: * <code><pre>
212: * public class MyClass implements FileDrop.Listener
213: * ...
214: * public void filesDropped( File[] files )
215: * {
216: * ...
217: * } // end filesDropped
218: * ...
219: * </pre></code>
220: *
221: * @since 1.0
222: */
223: public interface Listener {
224: /**
225: * This method is called when files have been successfully dropped.
226: *
227: * @param files An array of <tt>File</tt>s that were dropped.
228: * @since 1.0
229: */
230: void filesDropped(File[] files);
231: }
232:
233: private class FileDropTargetListener implements DropTargetListener {
234: private final Component mC;
235: private final Border mDragBorder;
236: private final Listener mListener;
237:
238: public void dragEnter(DropTargetDragEvent evt) {
239: if (isDragOk(evt)) {
240: if (mC instanceof JComponent) {
241: final JComponent jc = (JComponent) mC;
242: normalBorder = jc.getBorder();
243: jc.setBorder(mDragBorder);
244: }
245: evt.acceptDrag(DnDConstants.ACTION_COPY);
246: } else {
247: evt.rejectDrag();
248: }
249: }
250:
251: public void drop(DropTargetDropEvent evt) {
252: try {
253: final Transferable tr = evt.getTransferable();
254:
255: if (tr
256: .isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
257: evt.acceptDrop(DnDConstants.ACTION_COPY);
258:
259: final List fileList = (List) tr
260: .getTransferData(DataFlavor.javaFileListFlavor);
261: final File[] files = new File[fileList.size()];
262: fileList.toArray(files);
263:
264: if (mListener != null) {
265: mListener.filesDropped(files);
266: }
267:
268: evt.getDropTargetContext().dropComplete(true);
269: } else {
270: evt.rejectDrop();
271: }
272: } catch (final IOException io) {
273: evt.rejectDrop();
274: } catch (final UnsupportedFlavorException ufe) {
275: evt.rejectDrop();
276: } finally {
277: if (mC instanceof JComponent) {
278: final JComponent jc = (JComponent) mC;
279: jc.setBorder(normalBorder);
280: }
281: }
282: }
283:
284: public void dragExit(DropTargetEvent evt) {
285: if (mC instanceof JComponent) {
286: final JComponent jc = (JComponent) mC;
287: jc.setBorder(normalBorder);
288: }
289: }
290:
291: public void dropActionChanged(DropTargetDragEvent evt) {
292: if (isDragOk(evt)) {
293: evt.acceptDrag(DnDConstants.ACTION_COPY);
294: } else {
295: evt.rejectDrag();
296: }
297: }
298:
299: public void dragOver(DropTargetDragEvent dtde) {
300: }
301:
302: public FileDropTargetListener(Component aC, Border aDragBorder,
303: Listener aListener) {
304: mC = aC;
305: mDragBorder = aDragBorder;
306: mListener = aListener;
307: }
308: }
309:
310: }
|