Why is canImport() not called anymore after setDropTarget()?

drag-and-drop, java

Solution

`setTransferHandler` installs a new `DropTarget` if none exists and will setup it to use the specified `TransferHandler`. Replacing this `DropTarget` with another one afterwards will destroy this setup. Combining a custom `DropTarget` together with a `TransferHandler` that works the Swing way is rather tricky. The following will do the job:

jComponent.setTransferHandler(new MyTransferHandler());
DropTarget original = jComponent.getDropTarget();// the Swing DropTarget
MyDropTarget myDropTarget = new MyDropTarget();
myDropTarget.addDropTargetListener(original);// delegate for original behavior
jComponent.setDropTarget(myDropTarget);

Problem

I am experimenting with drag&drop features of JList in Java SE 7. The problem is, that the canImport() method in my TransferHandler implementation stops being called as soon as I set my DropTarget for the JList. Here's the simplified code to demonstrate the issue: main-class: ``` import javax.swing.ListSelectionModel; public class TestDragDrop extends javax.swing.JFrame { public TestDragDrop() { initComponents(); lstTest.setDragEnabled(true); lstTest.setTransferHandler(new MyTransferHandler()); // try to comment and uncomment the following line: lstTest.setDropTarget(new MyDropTarget()); // - commented => canImport() is called during drag gesture // - uncommented => canImport() is not called during drag gesture lstTest.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() { scroll1 = new javax.swing.JScrollPane(); lstTest = new javax.swing.JList(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); lstTest.setModel(new javax.swing.AbstractListModel() { String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" }; public int getSize() { return strings.length; } public Object getElementAt(int i) { return strings[i]; } }); scroll1.setViewportView(lstTest); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(scroll1, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(scroll1, javax.swing.GroupLayout.DEFAULT_SIZE, 174, Short.MAX_VALUE) .addContainerGap()) ); pack(); }// </editor-fold> public static void main(String args[]) { new TestDragDrop().setVisible(true); } // Variables declaration - do not modify private javax.swing.JList lstTest; private javax.swing.JScrollPane scroll1; // End of variables declaration } ``` my transfer-handler implementation: ``` import java.awt.datatransfer.Transferable; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.TransferHandler; public class MyTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent component) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent component) { if(component instanceof JList) { return new MyTransferable(new String("TestObject")); } return null; } @Override public boolean canImport(TransferHandler.TransferSupport support) { System.out.println("canImport()"); return true; } @Override public boolean importData(TransferHandler.TransferSupport support) { System.out.println("importData()"); return true; } } ``` my transferable implementation: ``` import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; public class MyTransferable implements Transferable { private final Object object; public MyTransferable(Object object) { this.object = object; } @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.javaFileListFlavor }; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { return flavor.equals(DataFlavor.javaFileListFlavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if(!isDataFlavorSupported(flavor)) { throw new UnsupportedFlavorException(flavor); } return object; } } ``` my drop-target implementation: ``` import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDropEvent; public class MyDropTarget extends DropTarget { @Override public synchronized void drop(DropTargetDropEvent evt) { evt.acceptDrop(evt.getDropAction()); if(evt.isLocalTransfer()) { System.out.println("local transfer"); } else { System.out.println("extern transfer"); } evt.dropComplete(true); } } ``` If the line lstTest.setDropTarget(new MyDropTarget()); in the main-class is switched-off/commented, the canImport() method in the MyTransferHandler is called during a drag-gesture as expected. But as soon as i uncomment this line, the canImport() method is not being called during dragging... Does someone has any idea why canImport() is not called anymore? Any help highly appreciated! Many thanks in advance.

Original source