This is the TransferHandler code I found here.
JTree is completely draggable, but the problem is that when I move one of the nodes, in the place where I put that node, two nodes are added instead of one.
For example, there are two nodes category1 and category2 in JTree, when I put category2 into category1, two categories2 are added inside category1.
public class TreeTransferHandler extends TransferHandler {
private DataFlavor nodesFlavor;
private DataFlavor[] flavors;
private DefaultMutableTreeNode[] nodesToRemove;
public TreeTransferHandler() {
try {
String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class="" + DefaultMutableTreeNode[].class.getName() + """;
nodesFlavor = new DataFlavor(mimeType);
flavors = new DataFlavor[]{nodesFlavor};
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFound: " + e.getMessage());
}
}
@Override
public boolean canImport(TransferSupport support) {
if (!support.isDrop())
return false;
support.setShowDropLocation(true);
if (!support.isDataFlavorSupported(nodesFlavor))
return false;
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
JTree tree = (JTree) support.getComponent();
int dropRow = tree.getRowForPath(dl.getPath());
int[] selRows = tree.getSelectionRows();
for (int selRow : selRows)
if (selRow == dropRow)
return false;
int action = support.getDropAction();
if (action == MOVE)
return haveCompleteNode(tree);
TreePath dest = dl.getPath();
DefaultMutableTreeNode targetNode = (DefaultMutableTreeNode) dest.getLastPathComponent();
TreePath path = tree.getPathForRow(selRows[0]);
DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode) path.getLastPathComponent();
if (firstNode.getChildCount() > 0 && targetNode.getLevel() < firstNode.getLevel())
return false;
return true;
}
private boolean haveCompleteNode(JTree tree) {
int[] selRows = tree.getSelectionRows();
TreePath path = tree.getPathForRow(selRows[0]);
DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode)path.getLastPathComponent();
int childCount = firstNode.getChildCount();
if(childCount > 0 && selRows.length == 1)
return false;
for(int i = 1; i < selRows.length; i++) {
path = tree.getPathForRow(selRows[i]);
DefaultMutableTreeNode next = (DefaultMutableTreeNode)path.getLastPathComponent();
if (firstNode.isNodeChild(next))
if (childCount > selRows.length - 1)
return false;
}
return true;
}
@Override
public boolean importData(TransferSupport support) {
if (!canImport(support))
return false;
DefaultMutableTreeNode[] nodes = null;
try {
Transferable transferable = support.getTransferable();
nodes = (DefaultMutableTreeNode[]) transferable.getTransferData(nodesFlavor);
} catch (Exception e) {
e.printStackTrace();
}
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
TreePath dest = dl.getPath();
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dest.getLastPathComponent();
JTree tree = (JTree) support.getComponent();
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
int index = dl.getChildIndex();
int childCount = parent.getChildCount();
if (index == -1)
index = childCount;
for (DefaultMutableTreeNode node: nodes) {
model.insertNodeInto(node, parent, index++);
}
return true;
}
@Override
protected Transferable createTransferable(JComponent c) {
JTree tree = (JTree) c;
TreePath[] paths = tree.getSelectionPaths();
if (paths != null) {
List<DefaultMutableTreeNode> copies = new ArrayList<>();
List<DefaultMutableTreeNode> toRemove = new ArrayList<>();
DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[0].getLastPathComponent();
DefaultMutableTreeNode copy = copy(node);
copies.add(copy);
toRemove.add(node);
for (TreePath path: paths) {
DefaultMutableTreeNode next = (DefaultMutableTreeNode) path.getLastPathComponent();
if (next.getLevel() < node.getLevel())
break;
else if (next.getLevel() > node.getLevel())
copy.add(copy(next));
else {
copies.add(copy(next));
toRemove.add(next);
}
}
DefaultMutableTreeNode[] nodes = copies.toArray(new DefaultMutableTreeNode[copies.size()]);
nodesToRemove = toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
return new NodesTransferable(nodes);
}
return null;
}
private DefaultMutableTreeNode copy(DefaultMutableTreeNode node) {
return (DefaultMutableTreeNode) node.clone();
}
@Override
protected void exportDone(JComponent source, Transferable data, int action) {
if ((action & MOVE) == MOVE) {
JTree tree = (JTree) source;
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
for (DefaultMutableTreeNode node: nodesToRemove)
model.removeNodeFromParent(node);
}
}
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
private class NodesTransferable implements Transferable {
DefaultMutableTreeNode[] nodes;
public NodesTransferable(DefaultMutableTreeNode[] nodes) {
this.nodes = nodes;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return nodesFlavor.equals(flavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (!isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
return nodes;
}
}
}