Stefan
Stefan

Reputation: 2613

Copy cell value of JTable with right click

I am showing some results in a JTable that consists of 2 columns.

File - Result

I implemented a JPopupMenu which displays a copy entry, and I try to copy the value of the cell, where I right-clicked.

filelistTable.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
         if(SwingUtilities.isRightMouseButton(e))
         {
             TablePopupMenu popup = new TablePopupMenu(filelistTable, e.getPoint());
             filelistTable.setComponentPopupMenu(popup);
         }
    }
});

--

    public TablePopupMenu(JTable table, Point p) {

        this.table = table;
        this.p = p;

        JMenuItem mntmKopieren = new JMenuItem("Kopieren");
        mntmKopieren.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                copyCellToClipboard();
            }
        });
        add(mntmKopieren);
    }

    public void copyCellToClipboard()
    {
        int r = table.rowAtPoint(p);
        int c = table.columnAtPoint(p);
        System.out.println(table.getValueAt(table.convertRowIndexToView(r), 
                table.convertRowIndexToView(c)));
        StringSelection entry = new StringSelection(table.getValueAt(table.convertRowIndexToView(r), 
                table.convertRowIndexToView(c)).toString());
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents( entry, this );

    }

Anyhow, this only works for a small number of tests. Did I do something wrong or something missing? It looks to me, as if the cell will not even get choosen correctly.

Upvotes: 4

Views: 3360

Answers (1)

kleopatra
kleopatra

Reputation: 51524

Two thingies are slightly off:

  • setting the componentPopup in the clicked is too late in the sequence of mouseEvents (popups are typically triggered on pressed or released which happen before the click)
  • the value is taken from the incorrect cell: all coordinates in a JTable are in view coordinate system, converting them to view coordinates will be completely off

That said: getting cell-coordinate related context is poorly supported. Often, the best bet is to (code snippet below)

  • override getPopupLocation(MouseEvent) and store the location somewhere
  • implement a popup/action to access the location

Fails if (as should be done in a well-behaved application), the popup could be triggered by keyboard: if that's the case, you'll need to provide some other marker (f.i. the focused cell) to act on.

final String popupLocation = "table.popupLocation";
final JTable table = new JXTable(new AncientSwingTeam()) {

    @Override
    public Point getPopupLocation(MouseEvent event) {
        // event may be null if triggered by keyboard, f.i.
        // thanks to @Mad for the heads up!
        ((JComponent) event.getComponent()).putClientProperty(
                popupLocation, event != null ? event.getPoint() : null);
        return super.getPopupLocation(event);
    }

};
JPopupMenu popup = new JPopupMenu();
Action printLocation = new AbstractAction("print cell") {

    @Override
    public void actionPerformed(ActionEvent e) {
       Point p = (Point) table.getClientProperty(popupLocation);
       if (p != null) { // popup triggered by mouse
           int row = table.rowAtPoint(p);
           int column = table.columnAtPoint(p);
           LOG.info("" + table.getValueAt(row, column)); 
       } else { // popup triggered otherwise
           // could choose f.i. by leadRow/ColumnSelection
           ...
       }
    }

};
popup.add(printLocation);
table.setComponentPopupMenu(popup);

Edit (triggered by Mad's comment):

You should be checking MouseEvent.isPopupTrigger as the trigger point is platform dependent. This does mean you need to monitor mousePressed, mouseReleased and mouseClicked

No, that's not needed (just checked :-): the mechanism that shows the componentPopup in response to a mouseEvent - happens in BasicLookAndFeel.AWTEventHelper - only does so if it is a popupTrigger.

By reading the api doc (should have done yesterday ;-) again, it turns out that the method is called always before showing the componentPopup, that is also if triggered by other means, f.i. keyboard. In that case the event param is null - and the original code would blow. On the bright side, with that guarantee, all the logic of finding the target cell/s could be moved into that method. Didn't try though, so it might not be feasable (f.i. if then the location should be based on the leadRow/ColumnSelection that might not yet be fully handled at that time)

Upvotes: 5

Related Questions