How to paint the Dropline of a RowHeader-JTable on the Main-JTable during a DragAndDrop?

drag-and-drop, java, jtable, swing, user-interface

Solution

I suggest you to do like this (using Swing's drag & drop functionality):

    RowHeaderTable rowTable = new RowHeaderTable(mainTable);
    rowTable.setAutoscrolls(true);
    rowTable.setDragEnabled(true);
    rowTable.setTransferHandler(new RowHeaderTransferHandler(mainTable));
    rowTable.setDropMode(DropMode.INSERT_ROWS);
    try {
        DropTarget dropTarget = rowTable.getDropTarget();
        dropTarget.addDropTargetListener(rowTable);
    }
    catch(TooManyListenersException e) {
        e.printStackTrace();
    }

Now you have to implement `DropTargetListener`:

public class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener, DropTargetListener {

These are 2 methods that you should be interested in:

    @Override
    public void dragEnter(DropTargetDragEvent dtde) { shouldPaint = true }

    @Override
    public void dragExit(DropTargetEvent dte) { shouldPaint = false }

Now you should manipulate your `mainTable` (to paint this "green line") using your TranserHandler:

    @Override
    public boolean canImport(TransferSupport supp) {
        DropLocation location = supp.getDropLocation();
        if(location instanceof javax.swing.JTable.DropLocation && shouldPaint) {
            javax.swing.JTable.DropLocation tableLocation = (javax.swing.JTable.DropLocation) location;
            int rowToInsert = tableLocation.getRow();
            //paint somehow the "green line"
            //remember that canImport is invoked when you move your mouse
        }
        return true;
    }

Basically you should implement your own row renderer or something similar (like in @naugler answer). You should somehow provide boolean `shouldPaint` to your TransferHandler as well. But this is not a big deal to implement.

Hope this helps.

Problem

I'm using a second JTable in the Viewport of a JScrollPane to build a RowHeader for a main table. DragAndDrop on the main table is disabled. On the rowheader table DnD is enabled. If a Drag on the rowheader is started by the user, I want to extend the painted rowheader dropline (the black line in the image) over the main table (like the green line in the image). Does anybody have an advice for me? Here's the SSCCE: ``` import java.awt.Component; import java.awt.EventQueue; import java.awt.Font; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; public class DNDLinePainterExampleMain extends JFrame { public DNDLinePainterExampleMain() { JTable mainTable = new JTable(4, 3); mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JTable rowTable = new RowHeaderTable(mainTable); rowTable.setAutoscrolls(true); rowTable.setDragEnabled(true); rowTable.setTransferHandler(new RowHeaderTransferHandler()); rowTable.setDropMode(DropMode.INSERT_ROWS); JScrollPane scrollPane = new JScrollPane(mainTable); scrollPane.setRowHeaderView(rowTable); scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, rowTable.getTableHeader()); this.add(scrollPane); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame f = new DNDLinePainterExampleMain(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); } }); } /* * Use a JTable as a renderer for row numbers of a given main table. This * table must be added to the row header of the scrollpane that contains the * main table. from: * http://tips4java.wordpress.com/2008/11/18/row-number-table/ */ public class RowHeaderTable extends JTable implements ChangeListener, PropertyChangeListener { private final JTable table; public RowHeaderTable(JTable table) { this.table = table; table.addPropertyChangeListener(this); setFocusable(false); setAutoCreateColumnsFromModel(false); updateRowHeight(); updateModel(); updateSelectionModel(); TableColumn column = new TableColumn(); column.setHeaderValue(""); addColumn(column); column.setCellRenderer(new RowNumberRenderer()); getColumnModel().getColumn(0).setPreferredWidth(50); setPreferredScrollableViewportSize(getPreferredSize()); getTableHeader().setReorderingAllowed(false); } @Override public void addNotify() { super.addNotify(); Component c = getParent(); // Keep scrolling of the row table in sync with the main table. if (c instanceof JViewport) { JViewport viewport = (JViewport) c; viewport.addChangeListener(this); } } /* * Delegate method to main table */ @Override public int getRowCount() { return table.getRowCount(); } @Override public int getRowHeight(int row) { return table.getRowHeight(row); } /* * This table does not use any data from the main TableModel, so just return * a value based on the row parameter. */ @Override public Object getValueAt(int row, int column) { return Integer.toString(row + 1); } /* * Don't edit data in the main TableModel by mistake */ @Override public boolean isCellEditable(int row, int column) { return false; } // implements ChangeListener @Override public void stateChanged(ChangeEvent e) { // Keep the scrolling of the row table in sync with main table JViewport viewport = (JViewport) e.getSource(); JScrollPane scrollPane = (JScrollPane) viewport.getParent(); scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); } // implements PropertyChangeListener @Override public void propertyChange(PropertyChangeEvent e) { // Keep the row table in sync with the main table if ("rowHeight".equals(e.getPropertyName())) updateRowHeight(); if ("selectionModel".equals(e.getPropertyName())) updateSelectionModel(); if ("model".equals(e.getPropertyName())) updateModel(); } private void updateRowHeight() { setRowHeight(table.getRowHeight()); } private void updateModel() { setModel(table.getModel()); } private void updateSelectionModel() { setSelectionModel(table.getSelectionModel()); } /* * Borrow the renderer from JDK1.4.2 table header */ private class RowNumberRenderer extends DefaultTableCellRenderer { public RowNumberRenderer() { setHorizontalAlignment(JLabel.CENTER); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (table != null) { JTableHeader header = table.getTableHeader(); if (header != null) { setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } } if (isSelected) { setFont(getFont().deriveFont(Font.BOLD)); } setText((value == null) ? "" : value.toString()); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } }//class RowNumberRenderer }//class RowHeaderTable public class RowHeaderTransferHandler extends TransferHandler { @Override public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } @Override protected Transferable createTransferable(JComponent c) { return new StringSelection(c.getName()); } @Override public boolean canImport(TransferSupport supp) { return true; } }//class RowHeaderTransferHandler }//class DNDLinePainterExampleMain ```

Original source