Code
class TreeTransferHandler extends TransferHandler {
private final DataFlavor nodesFlavor = new DataFlavor(
List.class, "List of TreeNode");
@Override public int getSourceActions(JComponent c) {
return c instanceof JTree
&& TreeUtils.canStartDrag((JTree) c) ? COPY_OR_MOVE : NONE;
}
@Override protected Transferable createTransferable(JComponent c) {
Transferable transferable = null;
if (c instanceof JTree && ((JTree) c).getSelectionPaths() != null) {
List<MutableTreeNode> copies = new ArrayList<>();
Arrays.stream(((JTree) c).getSelectionPaths()).forEach(path -> {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) path.getLastPathComponent();
DefaultMutableTreeNode clone =
new DefaultMutableTreeNode(node.getUserObject());
copies.add(TreeUtils.deepCopy(node, clone));
});
transferable = new Transferable() {
@Override public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {nodesFlavor};
}
@Override public boolean isDataFlavorSupported(
DataFlavor flavor) {
return Objects.equals(nodesFlavor, flavor);
}
@Override public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException {
if (isDataFlavorSupported(flavor)) {
return copies;
} else {
throw new UnsupportedFlavorException(flavor);
}
}
};
}
return transferable;
}
@Override public boolean canImport(TransferSupport support) {
DropLocation dl = support.getDropLocation();
Component c = support.getComponent();
return support.isDrop()
&& support.isDataFlavorSupported(nodesFlavor)
&& c instanceof JTree
&& dl instanceof JTree.DropLocation
&& TreeUtils.canImportDropLocation(
(JTree) c, (JTree.DropLocation) dl);
}
@Override public boolean importData(TransferSupport support) {
Component c = support.getComponent();
DropLocation dl = support.getDropLocation();
Transferable transferable = support.getTransferable();
return canImport(support)
&& c instanceof JTree
&& dl instanceof JTree.DropLocation
&& insertNode((JTree) c, (JTree.DropLocation) dl, transferable);
}
private boolean insertNode(
JTree tree, JTree.DropLocation dl, Transferable transferable) {
TreePath path = dl.getPath();
Object p = path.getLastPathComponent();
TreeModel m = tree.getModel();
List<?> nodes = getTransferData(transferable);
if (p instanceof MutableTreeNode && m instanceof DefaultTreeModel) {
MutableTreeNode parent = (MutableTreeNode) p;
DefaultTreeModel model = (DefaultTreeModel) m;
int childIndex = dl.getChildIndex();
AtomicInteger index = new AtomicInteger(
getDropIndex(parent, childIndex));
nodes.stream()
.filter(MutableTreeNode.class::isInstance)
.map(MutableTreeNode.class::cast)
.forEach(n -> model.insertNodeInto(
n, parent, index.getAndIncrement()));
}
return !nodes.isEmpty();
}
private static int getDropIndex(
MutableTreeNode parent, int childIndex) {
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if (childIndex == -1) { // DropMode.ON
index = parent.getChildCount();
}
return index;
}
private List<?> getTransferData(Transferable t) {
List<?> nodes;
try {
nodes = (List<?>) t.getTransferData(nodesFlavor);
} catch (UnsupportedFlavorException | IOException ex) {
nodes = Collections.emptyList();
}
return nodes;
}
@Override protected void exportDone(
JComponent src, Transferable data, int action) {
if (src instanceof JTree && (action & MOVE) == MOVE) {
cleanup((JTree) src);
}
}
private void cleanup(JTree tree) {
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
TreePath[] selectionPaths = tree.getSelectionPaths();
if (selectionPaths != null) {
for (TreePath path : selectionPaths) {
model.removeNodeFromParent(
(MutableTreeNode) path.getLastPathComponent());
}
}
}
}
References