I have implemented drag and drop for JTableHeader so when the user clicks the header of a table having Ctrl pressed instead of reordering the columns a custom object is transferred.
It looks to work properly, but when the DragGestureEvent's startDrag method is called the column that was dragged gets out of place (see gap between column4 and column5):
If after that a click on the column or drag it without the Ctrl key it returns to the correct position. Does anybody know a way to have the column to keep its place?
The source code:
public class Tree {
private final String name;
public Tree (final String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Tree[" + name + "]";
};
}
And this:
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
public class DnDTest extends JPanel {
private static final long serialVersionUID = 8341231933303004329L;
private static final DataFlavor FLAVOR = new DataFlavor(Tree.class,
Tree.class.getSimpleName());
private final TableTransferHandler<Integer> customHandler = new TableTransferHandler<Integer>();
/* ************** CONSTRUCTORS *************** */
public DnDTest() {
setLayout(new GridLayout(1, 2));
final JTable table = createTable();
table.setTableHeader(new CustomTableHeader(table.getColumnModel()));
final JPanel right = new JPanel(new BorderLayout());
final JLabel dropLabel = new JLabel();
dropLabel.setTransferHandler(customHandler);
right.add(dropLabel);
this.add(right);
new DragSource().createDefaultDragGestureRecognizer(
table.getTableHeader(), DnDConstants.ACTION_COPY,
new CustomDragGesture());
}
/* ************* PRIVATE METHODS ************* */
private JTable createTable() {
final DefaultTableModel model = createTableModel();
final JTable table = new JTable(model);
this.add(new JScrollPane(table));
return table;
}
private DefaultTableModel createTableModel() {
final String[] columnNames = { "Column1", //$NON-NLS-1$
"Column2", //$NON-NLS-1$
"Column3", //$NON-NLS-1$
"Column4", //$NON-NLS-1$
"Column5" //$NON-NLS-1$
};
final Integer[][] data = new Integer[1][columnNames.length];
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = (int) (Math.random() * 30);
}
}
final DefaultTableModel model = new DefaultTableModel(data, columnNames);
return model;
}
/* ***************** CLASSES ***************** */
private static final class CustomTableHeader extends JTableHeader {
private static final long serialVersionUID = -781646896813653894L;
public CustomTableHeader(final TableColumnModel columnModel) {
super(columnModel);
}
public Tree getTree() {
return new Tree("Test tree");
}
}
private static final class CustomDragGesture implements DragGestureListener {
@Override
public void dragGestureRecognized(DragGestureEvent event) {
if (!(event.getTriggerEvent() instanceof MouseEvent && SwingUtilities
.isLeftMouseButton((MouseEvent) event.getTriggerEvent()))
|| (event.getTriggerEvent().getModifiers() & ActionEvent.CTRL_MASK) != ActionEvent.CTRL_MASK)
return;
final CustomTableHeader tableHeader = (CustomTableHeader) event
.getComponent();
Cursor cursor = null;
if (event.getDragAction() == DnDConstants.ACTION_COPY) {
cursor = DragSource.DefaultCopyDrop;
}
final Tree tree = tableHeader.getTree();
event.startDrag(cursor, new TransferableTree(tree));
}
}
private static final class TransferableTree extends Tree implements
Transferable {
private final Tree tree;
public TransferableTree(final Tree tree) {
super(tree.getName());
this.tree = tree;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { FLAVOR };
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return FLAVOR.equals(flavor);
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (FLAVOR.equals(flavor)) {
return tree;
}
throw new UnsupportedFlavorException(flavor);
}
}
private static final class TableTransferHandler<T extends Number & Comparable<T>>
extends TransferHandler {
private static final long serialVersionUID = -1794178812912767711L;
@Override
public boolean importData(JComponent comp, Transferable t) {
if (hasTreeFlavor(t.getTransferDataFlavors())) {
try {
final Tree tree = (Tree) t.getTransferData(FLAVOR);
if (comp instanceof JLabel) {
final JLabel label = (JLabel) comp;
label.setText(label.getText() + tree.toString() + ", ");
}
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
return hasTreeFlavor(transferFlavors);
}
private boolean hasTreeFlavor(DataFlavor[] transferDataFlavors) {
for (final DataFlavor flavor : transferDataFlavors) {
if (FLAVOR.equals(flavor)) {
return true;
}
}
return false;
}
}
public static void main(String[] args) {
final JFrame frame = new JFrame("Drag And Drop Test");
frame.add(new DnDTest());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(1000, 800));
frame.pack();
frame.setVisible(true);
}
}
Any idea would be appreciated.
Inside your implementation of
dragGestureRecognized
you can calltableHeader.setDraggedDistance(0);
I tested that and it worked for me.