Column of JTable out of place after DragGestureEvent's startDrag call

174 views Asked by At

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):

enter image description here

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.

1

There are 1 answers

0
Enwired On BEST ANSWER

Inside your implementation of dragGestureRecognized you can call tableHeader.setDraggedDistance(0);

I tested that and it worked for me.