1

I have added a JPopupMenu to a JTable with setComponentPopupMenu. The problem is that while the JPopupMenu is open/visible, when I left-click on a row outside the popup menu, the menu closes but the row is not selected so I have to click again on it to highlight it. Is there any way to fix that?

EDIT

I've added sample code.

By the way this behavior occurs only in Windows LaF. I've just tested it and it appears that the default Java LaF allows for left-click row selection while the JPopupMenu is open.

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class TableSTACK {

    private static void createAndShowGUI() {

        String[] headers = {"Column 1", "Column 2"};

        Object[][] data = { {"Row",   "1"}, {"Row",   "2"},
                {"Row",   "3"}, {"Row",   "4"}, {"Row",   "5"},
                {"Row",   "6"}, {"Row",   "7"}, {"Row",   "8"}, };

        JTable table = new JTable(data, headers);
        table.setFillsViewportHeight(true);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());        

        final JMenuItem item1 = new JMenuItem();
        item1.setText("Menu Item 1");       
        final JMenuItem item2 = new JMenuItem();
        item2.setText("Menu Item 2");
        final JMenuItem item3 = new JMenuItem();
        item3.setText("Menu Item 3");

        final JPopupMenu popupMenu = new JPopupMenu();
        popupMenu.add(item1);
        popupMenu.addSeparator();
        popupMenu.add(item2);
        popupMenu.add(item3);
        table.setComponentPopupMenu(popupMenu);

        popupMenu.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        // force selection of row upon right-click (it works)
                        int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
                        if (rowAtPoint > -1) {
                            table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
                        }
                    }
                });
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        // force row selection upon exiting popup menu
                        // does not work; rowAtPoint always returns -1
                        int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(null, new Point(0, 0), table));
                        //int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
                        if (rowAtPoint > -1) {
                            table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
                        }
                    }
                });
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                // TODO
            }
        });


        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.gridx = 0;

        JScrollPane scrollPane = new JScrollPane(table);

        JPanel contentPane = new JPanel();
        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        contentPane.setLayout(new GridBagLayout());
        contentPane.add(scrollPane, gbc);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setMinimumSize(new Dimension(500, 400));
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (UnsupportedLookAndFeelException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                createAndShowGUI();
            }
        });
    }
}
AndroidX
  • 558
  • 4
  • 15
  • Use a cell editor like [`TablePopupEditor`](http://stackoverflow.com/a/3591230/230513). If this is not a duplicate, please edit your question to include a [mcve] that shows your revised approach. – trashgod Oct 27 '16 at 00:23
  • This was originally closed as a duplicate of: http://stackoverflow.com/questions/3590897/jtable-design-to-synchronize-with-back-end-data-structure. I don't think this question is about a popup editor. The question is about using a "JPopupMenu" and clicking on the table when the menu is open. I reopened the question. – camickr Oct 27 '16 at 01:07
  • I've edited my question. – AndroidX Oct 27 '16 at 01:09

1 Answers1

2

This a LAF issue.

It works for me when I use the default LAF but doesn't work when I use the platform LAF, which for me is Windows.

A potential solution on Windows is to use a MouseListener to select the line. Note the code is added to the mouseReleased event. For some reason the table does not receive the mousePressed event even though according to the AWTEventListener the table is the source of the mousePressed event.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TablePopupListener extends JPanel
{
    public TablePopupListener()
    {
        JTable table = new JTable(10, 5);
        add( new JScrollPane( table ) );

        JPopupMenu popup = new JPopupMenu();
        popup.add( new JMenuItem("Do Something1") );
        popup.add( new JMenuItem("Do Something2") );

        table.setComponentPopupMenu( popup );

        table.addMouseListener( new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                System.out.println("Pressed JTable");
            }

            public void mouseReleased(MouseEvent e)
            {
                System.out.println("Released JTable");

                int row = table.rowAtPoint( e.getPoint() );

                if (row != -1
                && !table.isRowSelected(row))
                {
                   table.setRowSelectionInterval(row, row);
                }
            }
        });
    }

    private static void createAndShowGUI()
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception ex) { System.out.println(ex); }

        JFrame frame = new JFrame("TablePopupListener");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TablePopupListener());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );

        Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
        {
            public void eventDispatched(AWTEvent e)
            {
                String event = null;

                switch (e.getID())
                {
                    case MouseEvent.MOUSE_PRESSED:  event = "Pressed: " ; break;
                    case MouseEvent.MOUSE_RELEASED:  event = "Released: " ; break;
                    case MouseEvent.MOUSE_ENTERED:  event = "Entered: " ; break;
                    case MouseEvent.MOUSE_EXITED:  event = "Exited: " ; break;
                    default: event = null; break;
                }

                if (event != null)
                {
                    System.out.println();
                    System.out.println(event + e.getSource().getClass());
                }
            }
        },  AWTEvent.MOUSE_EVENT_MASK);
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • So I guess I'll keep the `PopupMenuListener` for forcing table row selection upon right-click and I'll add a `MouseListener` to force row selection upon left-click while the `JPopupMenu` is visible. Too bad it has to be that complex... Thanks – AndroidX Oct 27 '16 at 01:21