blob: f6b3e7f57978c6c779a811e658f6696e42c17703 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.reference.ui.tree;
import static java.awt.datatransfer.DataFlavor.javaFileListFlavor;
import static java.awt.dnd.DnDConstants.ACTION_COPY;
import static java.awt.dnd.DnDConstants.ACTION_MOVE;
import static java.awt.dnd.DragSource.DefaultMoveDrop;
import static net.sf.taverna.t2.reference.ui.tree.PreRegistrationTreeDnDHandler.InternalNodeDragTransferable.INTERNAL_NODE_FLAVOR;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
import org.apache.log4j.Logger;
/**
* An abstract handler class for drags within a {@link JTree} backed by a
* {@link DefaultTreeModel}. Implement the (@link
* {@link #handleNodeMove(MutableTreeNode, MutableTreeNode)} method to determine
* the behaviour when a node is dragged to another node in the tree. Additional
* methods are called when drags corresponding to URL, List<File> and
* String are made. Node drags have precedence, only one of the abstract methods
* is called for any given drop event.
*
* @author Tom Oinn
*/
public abstract class PreRegistrationTreeDnDHandler implements
DropTargetListener, DragGestureListener, DragSourceListener {
private static Logger logger = Logger
.getLogger(PreRegistrationTreeDnDHandler.class);
private JTree tree;
private MutableTreeNode draggedNode = null;
private DragSource source = new DragSource();
public PreRegistrationTreeDnDHandler(JTree tree) {
this.tree = tree;
new DropTarget(tree, this);
source.createDefaultDragGestureRecognizer(tree, ACTION_MOVE, this);
}
/**
* Called when a node has been dragged onto another node (nodes could be the
* same, client code should handle this correctly).
*
* @param source
* the node the drag was initiated from
* @param target
* the node the drag ended on
*/
public abstract void handleNodeMove(MutableTreeNode source,
MutableTreeNode target);
/**
* Called when a file is dragged from the native OS to the tree.
*
* @param target
* the node the drag ended on
* @param fileList
* a list of File objects corresponding to the selection dragged.
* Files may be directories, remember to check!
*/
public abstract void handleFileDrop(MutableTreeNode target,
List<File> fileList);
/**
* Called when a URL is dropped, on my system at least Firefox presents this
* type if a link or similar is dragged to the tree.
*
* @param target
* the node the drag ended on
* @param url
* a java URL object
*/
public abstract void handleUrlDrop(MutableTreeNode target, URL url);
/**
* Called when a textual type is dragged to the tree
*
* @param target
* the node the drag ended on
* @param string
* a java String object containing the dragged text
*/
public abstract void handleStringDrop(MutableTreeNode target, String string);
@Override
public void dragEnter(DropTargetDragEvent dtde) {
dtde.acceptDrag(ACTION_MOVE);
}
@Override
public void dragExit(DropTargetEvent dte) {
}
@Override
public void dragOver(DropTargetDragEvent dtde) {
dtde.acceptDrag(ACTION_MOVE);
}
@Override
public void drop(DropTargetDropEvent dtde) {
Point pt = dtde.getLocation();
DropTargetContext dtc = dtde.getDropTargetContext();
JTree tree = (JTree) dtc.getComponent();
TreePath targetPath = tree.getClosestPathForLocation(pt.x, pt.y);
MutableTreeNode target = null;
if (targetPath != null)
target = (MutableTreeNode) targetPath.getLastPathComponent();
Transferable tr = dtde.getTransferable();
if (tr.isDataFlavorSupported(INTERNAL_NODE_FLAVOR)) {
dtde.acceptDrop(ACTION_MOVE);
handleNodeMove(draggedNode, target);
draggedNode = null;
dtde.dropComplete(true);
return;
} else if (tr.isDataFlavorSupported(javaFileListFlavor)) {
dtde.acceptDrop(ACTION_COPY);
try {
@SuppressWarnings("unchecked")
List<File> fileList = (List<File>) tr
.getTransferData(javaFileListFlavor);
handleFileDrop(target, fileList);
dtde.dropComplete(true);
} catch (Exception ex) {
dtde.dropComplete(false);
}
return;
}
for (DataFlavor flavor : tr.getTransferDataFlavors()) {
String mimeType = flavor.getMimeType();
if (mimeType.startsWith("application/x-java-url")) {
dtde.acceptDrop(ACTION_COPY);
URL url;
try {
url = (URL) tr.getTransferData(flavor);
handleUrlDrop(target, url);
dtde.dropComplete(true);
} catch (UnsupportedFlavorException|IOException e) {
dtde.dropComplete(false);
logger.error("Cannot import data", e);
}
return;
} else if (mimeType.contains("class=java.lang.String;")) {
dtde.acceptDrop(ACTION_COPY);
String string;
try {
string = (String) tr.getTransferData(flavor);
handleStringDrop(target, string);
dtde.dropComplete(true);
} catch (UnsupportedFlavorException | IOException e) {
dtde.dropComplete(false);
logger.error("Cannot import data", e);
}
return;
}
}
dtde.rejectDrop();
}
@Override
public void dropActionChanged(DropTargetDragEvent dtde) {
}
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
TreePath path = tree.getSelectionPath();
if (path == null || path.getPathCount() <= 1)
// We can't move the root node or an empty selection
return;
draggedNode = (MutableTreeNode) path.getLastPathComponent();
source.startDrag(dge, DefaultMoveDrop,
new InternalNodeDragTransferable(), this);
}
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
}
@Override
public void dragEnter(DragSourceDragEvent dsde) {
}
@Override
public void dragExit(DragSourceEvent dse) {
}
@Override
public void dragOver(DragSourceDragEvent dsde) {
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {
}
/**
* Dummy data transfer flavor used to check that the drag and drop are
* managing the same events.
*
* @author Tom Oinn
*/
static class InternalNodeDragTransferable implements Transferable {
static DataFlavor INTERNAL_NODE_FLAVOR = new DataFlavor(
InternalNodeDragTransferable.class, "Internal node move");
private DataFlavor flavors[] = { INTERNAL_NODE_FLAVOR };
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
// There is no data for this, so return null
return null;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.getRepresentationClass() == InternalNodeDragTransferable.class;
}
}
}