Question

I created my JPopupMenu. It appears on my JTable when I right click on a cell. However, I cannot copy the data in the cell unless I first double click and then highlight the data, and then right click anywhere but this current cell to show my popup menu and copy option.

I would like to copy the data in a cell without having to double click on a cell and enter into cell edit mode where I then need to select the data.

How can I do this?

popup = new JPopupMenu();
popup.setName("popupMenu");
menuItemCopy = new JMenuItem(new DefaultEditorKit.CopyAction());
menuItemCopy.setText("Copy");
menuItemCopy.setName("copy");       
popup.add(menuItemCopy);
popup.addSeparator();
menuItemPaste = new JMenuItem(new DefaultEditorKit.PasteAction());
menuItemPaste.setText("Paste");
menuItemPaste.setName("paste");
popup.add(menuItemPaste);

Here's the code that I have in my MouseListener for my JTable, in mouseReleased() and mousePressed().

if(e.isPopupTrigger())
{
    JTable source = (JTable)e.getSource();
    int row = source.rowAtPoint( e.getPoint() );
    int column = source.columnAtPoint( e.getPoint() );

    gridView.popup.show(e.getComponent(), e.getX(), e.getY());              
}
Was it helpful?

Solution

Two things...

  1. I'm not sure how you expect a DefaultEditorKit.CopyAction and DefaultEditorKit.PasteAction to work with a JTable, these are suppose to be used with JTextComponents...
  2. The JTable will only highlight the row on a left button press (or key board navigation change), a right mouse button click doesn't do this by default.

Now, I wanted to use the JTable's component popup support, but this seems to consume all mouse events once it detects a popup trigger, which makes it (near to) impossible to highlight the row/column on a right mouse click.

Instead, I ended up adding a highlight method into my MouseListener which highlights the row/column in question and then triggers the popup.

The reason I did it this way, is the Action's associated with copying and pasting have no concept of anything other than the table, so they don't know what row/column was clicked on.

This allows these actions to focus on worrying about the selection alone.

Content is copied directly to the system clipboard via a custom transferable, which maintains the cell's original type, which means you don't need to reconstruct the object when it's pasted.

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorEvent;
import java.awt.datatransfer.FlavorListener;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import static javax.swing.Action.NAME;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

public class TestTable100 {

    public static void main(String[] args) {
        new TestTable100();
    }

    public TestTable100() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                DefaultTableModel model = new DefaultTableModel();
                model.addColumn("Type");
                model.addColumn("Column");
                for (File file : new File(System.getProperty("user.home")).listFiles()) {
                    model.addRow(new Object[]{file, file});
                }

                JTable table = new JTable(model);
                table.getColumnModel().getColumn(0).setCellRenderer(new FirstCellRenderer());

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                final JPopupMenu pm = new JPopupMenu();
                pm.add(new CopyAction(table));
                pm.add(new PasteAction(table));

                table.addMouseListener(new MouseAdapter() {

                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (e.isPopupTrigger()) {
                            highlightRow(e);
                            doPopup(e);
                        }
                    }

                    @Override
                    public void mouseReleased(MouseEvent e) {
                        if (e.isPopupTrigger()) {
                            highlightRow(e);
                            doPopup(e);
                        }
                    }

                    protected void doPopup(MouseEvent e) {
                        pm.show(e.getComponent(), e.getX(), e.getY());
                    }

                    protected void highlightRow(MouseEvent e) {
                        JTable table = (JTable) e.getSource();
                        Point point = e.getPoint();
                        int row = table.rowAtPoint(point);
                        int col = table.columnAtPoint(point);

                        table.setRowSelectionInterval(row, row);
                        table.setColumnSelectionInterval(col, col);
                    }

                });
            }
        });
    }

    public class FirstCellRenderer extends DefaultTableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {

            File f = (File) value;

            super.getTableCellRendererComponent(table,
                            value, isSelected, hasFocus, row, column);
            String prefix = f.isDirectory() ? "DIR" : "FILE";
            setText(prefix);

            return this;
        }
    }

    public class CopyAction extends AbstractAction {

        private JTable table;

        public CopyAction(JTable table) {
            this.table = table;
            putValue(NAME, "Copy");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int row = table.getSelectedRow();
            int col = table.getSelectedColumn();

            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            cb.setContents(new CellTransferable(table.getValueAt(row, col)), null);
        }

    }

    public class PasteAction extends AbstractAction {

        private JTable table;

        public PasteAction(JTable table) {
            this.table = table;
            putValue(NAME, "Paste");
            final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            cb.addFlavorListener(new FlavorListener() {
                @Override
                public void flavorsChanged(FlavorEvent e) {
                    setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR));
                }
            });
            setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR));
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int row = table.getSelectedRow();
            int col = table.getSelectedColumn();

            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            if (cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR)) {
                try {
                    Object value = cb.getData(CellTransferable.CELL_DATA_FLAVOR);
                    table.setValueAt(value, row, col);
                } catch (UnsupportedFlavorException | IOException ex) {
                    ex.printStackTrace();
                }
            }
        }

    }

    public static class CellTransferable implements Transferable {

        public static final DataFlavor CELL_DATA_FLAVOR = new DataFlavor(Object.class, "application/x-cell-value");

        private Object cellValue;

        public CellTransferable(Object cellValue) {
            this.cellValue = cellValue;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{CELL_DATA_FLAVOR};
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return CELL_DATA_FLAVOR.equals(flavor);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            if (!isDataFlavorSupported(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return cellValue;
        }

    }
}

OTHER TIPS

Here is code by using Clipboard

Steps to follow:

  • Declare some variable to store the current selected row and column index

    private static int rowIndex;
    private static int columnIndex;
    
  • Add ActionListener on MenuItem

    menuItemCopy.addActionListener(new ActionListener() {
    
        @Override
        public void actionPerformed(ActionEvent e) {
            StringSelection stringSelection = new StringSelection(String.valueOf(table1
                    .getModel().getValueAt(rowIndex, columnIndex)));
            Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
            clpbrd.setContents(stringSelection, null);
        }
    });
    
    menuItemPaste.addActionListener(new ActionListener() {
    
        @Override
        public void actionPerformed(ActionEvent e) {
            Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
            try {
                table1.getModel().setValueAt(clpbrd.getData(DataFlavor.stringFlavor), rowIndex,
                        columnIndex);
            } catch (UnsupportedFlavorException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    });
    
  • Add MouseListener on JTable to update the value of rowIndex and columnIndex and finally show the JPopupMenu

    table1.addMouseListener(new MouseAdapter() {
    
        @Override
        public void mousePressed(MouseEvent e) {
            if (e.getButton() == 3) {
                rowIndex = table1.rowAtPoint(e.getPoint());
                columnIndex = table1.columnAtPoint(e.getPoint());
    
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
        }
    
    });
    
int row = source.rowAtPoint(e.getPoint());
int column = source.columnAtPoint(e.getPoint());
Object valueInCell = table.getValueAt(row, column);

Simple!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top