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();
}
});
}
}
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();
}
});
*/
}
}