4

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.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Stefan
  • 2,603
  • 2
  • 33
  • 62
  • Did you get any errors? Or did the value just fail to get copied to the clipboard? – MadProgrammer Sep 19 '12 at 09:31
  • the value just failed to copy most of the time.. – Stefan Sep 19 '12 at 09:32
  • Does the `ClipboardOwner.lostOwnership` get fired?? Highly unlikely, but something might have replaced the contents. Also, the `setContents` method throws a `IllegalStateException`. It might be worth trying to catch this and see if this is possibly causing you any problems – MadProgrammer Sep 19 '12 at 09:35
  • Even the printed value to console does not give the correct value. If it fails, it's always the last try that was copied correctly. – Stefan Sep 19 '12 at 09:37
  • What is wrong with the default copy action of the default transfer handler of the table ? – Robin Sep 19 '12 at 10:07
  • What do you mean with default transfer handler? I want the user to appear a menu at right-click on the cell to choose "copy" (and later more) and it should copy the cell value to clipboard. I did not find any default action that does this? – Stefan Sep 19 '12 at 10:14

1 Answers1

5

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)

zellus
  • 9,617
  • 5
  • 39
  • 56
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • please DYM to use `ListSelectionListener` rather that `flying Point` from `mouse_hover_over` or ... – mKorbel Sep 19 '12 at 11:33
  • @mKorbel don't understand - which flying point and where does the mouseOver come into play? Anyway, no selectionListener needed, you can statically query for the lead selection (both for column and row) – kleopatra Sep 19 '12 at 11:42
  • You should be checking `MouseEvent.isPopupTrigger` as the trigger point is platform dependent. This does mean you need to monitor `mousePressed`, `mouseReleased` and `mouseClicked` though :P – MadProgrammer Sep 19 '12 at 20:41
  • @MadProgrammer don't think so: the getPopupLocation is called only if the event _is_ a popupTrigger (at least that's what I expect, didn't check - but could be the implementation - in rootPaneUI or BasicLookAndFeel, forgot - be that bad .. nooo, we have some faith, don't we :-) – kleopatra Sep 20 '12 at 07:56
  • @kleopatra Yes, you're right, wasn't paying attention to the use of the method in the `JTable`. Still a good idea if you're just using a mouse listener – MadProgrammer Sep 20 '12 at 09:11
  • @MadProgrammer true - but the whole point of using the high-level componentPopup property is to hide away those dirty details of the low-level triggers :-) – kleopatra Sep 20 '12 at 09:16