Reputation: 162
I am currently attempting to display a JPopupMenu
when right clicking a JTable
.
I have achieved this in different ways already, but none of them allow me to do what this question is about: executing some code after I right click on the table, but the popup menu is on screen.
When the popup menu is displayed and I right click outside of it, it opens up again on a different location. So there must be a listener inside it that tells it to do that, but I can't find it. Ideally, I would @Override
it and execute the same code I'm executing when I right click on the JTable.
So, to sum up the question, how do I trigger some code when I right click outside the menu (or on a table cell, for example) when the menu still has focus (or is displaying)?
EDIT: After looking more carefully, I identified the problem, but not the solution. the JPopupMenu
is not the issue, but the UIManager
. Here is the code of my test form that displays the issue I'm describing:
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
public class PopupTesting extends javax.swing.JFrame {
public PopupTesting() {
initComponents();
}
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null, null, null},
{null, null, null, null},
{null, null, null, null},
{null, null, null, null}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
jTable1.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mousePressed(java.awt.event.MouseEvent evt) {
jTable1MousePressed(evt);
}
});
jScrollPane1.setViewportView(jTable1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(61, 61, 61)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(83, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(69, 69, 69)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(121, Short.MAX_VALUE))
);
pack();
}
private void jTable1MousePressed(java.awt.event.MouseEvent evt) {
if (SwingUtilities.isRightMouseButton(evt)) {
jTable1.setRowSelectionInterval(jTable1.rowAtPoint(evt.getPoint()), jTable1.rowAtPoint(evt.getPoint()));
getPopup().show(jTable1, evt.getX(), evt.getY());
}
}
public static void main(String args[]) {
try {
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
new PopupTesting().setVisible(true);
});
}
private JPopupMenu getPopup() {
if (jTable1.getSelectedRow() > -1) {
JPopupMenu popup = new JPopupMenu();
MouseListener listener = (new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
System.out.println("I'm a menu Item.");
}
});
JMenuItem m = new JMenuItem("Regular Menu Item");
m.addMouseListener(listener);
popup.add(m);
return popup;
} return null;
}
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable1;
}
If you remove the line:
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
and the surrounding try
and catch
, it works flawlessly.
Upvotes: 0
Views: 1191
Reputation: 162
Well, after many hours of trying to fix the issue, I ended up making a dodgy fix. The code that does what I requested is here:
import java.awt.AWTException;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
public class PopupTesting extends javax.swing.JFrame {
public PopupTesting() {
initComponents();
jTable.setComponentPopupMenu(getPopup());
}
volatile int lastMouse = 1;
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTable = new javax.swing.JTable();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null, null, null},
{null, null, null, null},
{null, null, null, null},
{null, null, null, null}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
jTable.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mousePressed(java.awt.event.MouseEvent evt) {
jTableMousePressed(evt);
}
});
jScrollPane1.setViewportView(jTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(61, 61, 61)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(83, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(69, 69, 69)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(121, Short.MAX_VALUE))
);
pack();
}
private void jTableMousePressed(java.awt.event.MouseEvent evt) {
lastMouse = evt.getButton();
if (lastMouse == 3) lastMouse--;
lastMouse = InputEvent.getMaskForButton(lastMouse);
}
public static void main(String args[]) {
try {
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
new PopupTesting().setVisible(true);
});
}
private JPopupMenu getPopup() {
JPopupMenu popup = new JPopupMenu();
JMenuItem m = new JMenuItem("Regular Menu Item");
m.addActionListener((ActionEvent e) -> {
System.out.println("I'm a menu Item.");
});
popup.add(m);
PopupTesting frame = this;
popup.addAncestorListener( new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
Point mousePoint = MouseInfo.getPointerInfo().getLocation();
mousePoint = SwingUtilities.convertPoint(frame, parseInt(mousePoint.getX() - getX()), parseInt(mousePoint.getY() - getY()), jTable);
jTable.setRowSelectionInterval(jTable.rowAtPoint(mousePoint), jTable.rowAtPoint(mousePoint));
}
@Override public void ancestorRemoved(AncestorEvent event) {
try {
Robot bot = new Robot();
bot.mousePress(lastMouse);
bot.mouseRelease(lastMouse);
} catch (AWTException ex) {
Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override public void ancestorMoved(AncestorEvent event) {}
});
return popup;
}
private int parseInt(Double val) {
return val.intValue();
}
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable;
}
Despite setComponentPopupMenu()
displaying the popup again while it is still visible, it still wont trigger MouseListener
s.
The way I "fixed" it, was by using an AncestorListener
, that would trigger ancestorAdded
when the popup menu was displayed, and ancestorRemoved
when it exited the screen.
With the ancestorAdded
, I simply made it so that whenever the menu was displayed, it also selected the cell behind it (took a lot of time to figure out how to get the same position from MouseInfo
as I would get from MouseEvent
, but I got there).
With the ancestorRemoved
, I had to "cheat". Basically, every time I click (MousePressed
) on the table, I store which button was used to do it.
Then, once ancestorRemoved
gets triggered, it creates a Robot
that repeats the last mouse click (if it was mouse 1 it triggers mouse 1, mouse 2 triggers mouse 2, etc).
This solution works, however it does not allow me to hold down a mouse button when exiting the JPopupMenu
. If anyone believes to know the solution, please let me know, but for now, problem solved.
Upvotes: 1
Reputation: 324128
or on top of the table
The table header is not part of the table so it will not respond to the mouse event. You need to add a separate listener to the table header.
when you click somewhere else and the menu is on screen, it both closes and triggers the click event where you tried to click
Works fine for me using JDK8 on Windows 7 if you click on a cell of the table.
If you continue to have problems then post a SSCCE that demonstrates the problem.
Edit:
As MadProgrammer suggested in his comment, the setComponentPoupMenu(...)
method should be used. This is a newer API. I don't know what the difference is between the two approaches, but using that method works for me on both LAF's that I tested.
Edit2:
Once again it still works fine for me. Note you should NOT be adding a MouseListener to a menu item. You use an ActionListener to handle the clicking of the menu item:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TableRightClick extends JFrame implements ActionListener
{
JPopupMenu popup;
public TableRightClick()
{
popup = new JPopupMenu();
popup.add( new JMenuItem("Do Something1") );
JMenuItem menuItem = new JMenuItem("ActionPerformed");
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK));
menuItem.addActionListener( this );
popup.add( menuItem );
JTable table = new JTable(10, 5);
table.addMouseListener( new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
JTable source = (JTable)e.getSource();
int row = source.rowAtPoint( e.getPoint() );
int column = source.columnAtPoint( e.getPoint() );
if (! source.isRowSelected(row))
source.changeSelection(row, column, false, false);
}
});
table.setComponentPopupMenu( popup );
table.setPreferredScrollableViewportSize(table.getPreferredSize());
getContentPane().add( new JScrollPane(table) );
JMenuBar menuBar = new JMenuBar();
setJMenuBar( menuBar );
menuBar.add( popup );
}
public void actionPerformed(ActionEvent e)
{
Component c = (Component)e.getSource();
JPopupMenu popup = (JPopupMenu)c.getParent();
JTable table = (JTable)popup.getInvoker();
System.out.println(table.getSelectedRow() + " : " + table.getSelectedColumn());
}
public static void main(String[] args)
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception ex) { System.out.println(ex); }
TableRightClick frame = new TableRightClick();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
Upvotes: 1