Jtable with Jpopupmenu

JTable with JPopupMenu

It's an interesting question, because it highlights missing api on JComponent :-)

As we all know, the recommended way to register popupMenus is to use the componentPopupMenu property. Related api is

 void setComponentPopupMenu(JPopupMenu);
JPopupMenu getComponentPopupMenu();
Point getPopupLocation(MouseEvent);

what is missing (and actually needed for this requirement) is

JPopupMenu getComponentPopupMenu(MouseEvent);

this lack is all the more annoying, as the getPopupLocation is called (by AWTEventHelper deep in the LAF) after getComponentPopup(). So there's no leeway for a hack like storing the last mouse event which might have triggered the popup and then decide which/if to return popup. And returning null for the location will only result in showing it at the mouse location

The only (dirty) hack (around my utter reluctance to get my hands dirty with a MouseListener ;-) is to override getComponentPopup and decide there whether or not to return it based on current mouse position

    table = new JTable(model) {

/**
* @inherited <p>
*/
@Override
public JPopupMenu getComponentPopupMenu() {
Point p = getMousePosition();
// mouse over table and valid row
if (p != null && rowAtPoint(p) >= 0) {
// condition for showing popup triggered by mouse
if (isRowSelected(rowAtPoint(p))) {
return super.getComponentPopupMenu();
} else {
return null;
}
}
return super.getComponentPopupMenu();
}

};

the side-effect is that popup showing isn't triggered by keyboard as long as the mouse is anywhere above the table, which might or not be a problem.

JTable with dynamic JPopupMenu

Whenever you add/remove components from a visible container you need to tell the container that you are finished adding components so the layout manager can be invoked.

Your logic should be:

private void generateTablePopupMenu(int rowAtPoint)
{
popupMenu.removeAll();

if ( (rowAtPoint & 1) == 0 )
{
JMenuItem item = new JMenuItem("Even Row");
popupMenu.add(item);}
else
{
JMenuItem item = new JMenuItem("Odd Row");
popupMenu.add(item);
}

popupMenu.revalidate();
}

Row Selection in JTable with JPopupMenu via Right Mouse Click

I solve the problem. Firstly thanks to @clamp in this question.

---SOLVED---

I have removed below lines from code;

popupForTable.addPopupMenuListener(new PopupMenuListener() {

@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Point mousePoint = new Point();
mousePoint = MouseInfo.getPointerInfo().getLocation();

int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupForTable, new Point(0, 0), table));

if (rowAtPoint > -1) {
table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
}

}
});
}

@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// TODO Auto-generated method stub

}

@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// TODO Auto-generated method stub

}

});

table.setComponentPopupMenu(popupForTable);

And added this MouseListener to table;

table.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
int r = table.rowAtPoint(e.getPoint());
if (r >= 0 && r < table.getRowCount()) {
table.setRowSelectionInterval(r, r);
} else {
table.clearSelection();
}

int rowindex = table.getSelectedRow();
if (rowindex < 0)
return;
if (e.isPopupTrigger() && e.getComponent() instanceof JTable ) {
JPopupMenu popup = popupForTable;
popup.show(e.getComponent(), e.getX(), e.getY());
table.setRowSelectionInterval(r, r);
}
}
});

Thanks all who interested with this question. Maybe this solution help someones in the future.

Force JTable row selection if clicking outside visible JPopupMenu

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

JPopupMenu on JTable - Get the cell the menu was created on

@MadProgrammer's suggestion of getPopupLocation looked promising, but I couldn't work out how to get the information across between the table and the actionEvent...

I got around this by making sure that the row was selected when you rightclicked on it -> since the popup menu prevents the selection of the row, you can add in a mouse listener that makes sure the row gets selected no matter what click (left or right) is pressed.

aTable.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int r = aTable.rowAtPoint(e.getPoint());
if (r >= 0 && r < clt.getRowCount()) {
aTable.setRowSelectionInterval(r, r);
} else {
aTable.clearSelection();
}
}
});

This means that in the rightClickMenuItem's action listener, you can grab the table's selected cell / row

rightClickMenuItem.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
aTable.get details about the selected one....
}
});

Too easy! Thanks everyone for the help.

JTable + JPopupMenu + ActionListener = Nightmare

The MouseListener should collect all the relevant information for your ContextMenu, pack it in a transport (inner private?) class and pass this info before actually showing the context menu.

    table.addMouseListener(new MouseAdapter() {         
@Override
public void mouseReleased(MouseEvent e) {
int selectedRow = table.rowAtPoint(e.getPoint());

if (selectedRow >= 0 && selectedRow < table.getRowCount()) {
if (!table.getSelectionModel().isSelectedIndex(selectedRow)) {
table.setRowSelectionInterval(selectedRow, selectedRow);
}
}

if (e.isPopupTrigger() && e.getComponent() instanceof JTable) {
this.show(e);
}
}

private void show(MouseEvent e){
int clickedRow=table.rowAtPoint(e.getPoint());
int clickedCol=table.columnAtPoint(e.getPoint());
Object data=table.getValueAt(row, i);

DataClickedOnTable transportMeThere=new DataClickedOnTable(
table, data, clickedRow, clickedColumn
);
contextMenu.setDataFromTable(transportMeThere);
contextMenu.show(e.getComponent(), e.getX(), e.getY());
}
});
///....
///...

// Just an example of structure transporting the data
// Add whatever data members are relevant
private /* inner */ class DataClickedOnTable {
public TestTable source;
public Object data;
public int row;
public int column;

public DataClickedOnTable(
TestTable source, Object data, int row, int col
) {
this.source=source;
this.data=data;
this.col=col;
this.row=row;
}
}
public class ContextMenu extends JPopupMenu {
JMenuItem item1;
JMenuItem item2;

Object dataFromTable; // make it an Integer

public ContextMenu(IBurpExtenderCallbacks callbacks){
this.item1 = new JMenuItem("item");
this.item2 = new JMenuItem("item");

this.item1(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// You already have the relevant data in the dataFromTable
// Do want you need in this context
}
});
this.item2(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// You already have the relevant data in the dataFromTable
// Do want you need to do in this context
}
});

add(item1);
add(item2);
}

void setDataFromTable(DataClickedOnTable data) {
this.dataFromTable=data;
// filter possible actions based on the received data - some
// actions are possible, some won't.
// Example:
this.item1.setEnabled(null!=data && (data.row % 2)==0);
this.item2.setEnabled(
null!=data
&& ((data.row % 2)==1 || data.data instanceof Number)
);

}
}

Dynamic (real) context menu for jtable row

JPopupMenu#show(int, int) (Java Platform SE 8)

public void show(Component invoker, int x, int y)

Displays the popup menu at the position x,y in the coordinate space of the component invoker.

Parameters:

  • invoker - the component in whose space the popup menu is to appear
  • x - the x coordinate in invoker's coordinate space at which the popup menu is to be displayed
  • y - the y coordinate in invoker's coordinate space at which the popup menu is to be displayed

Therefore, it is not necessary to convert the coordinates using the SwingUtilities.convertPoint(...) method.

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

public class JTablePopupMenuTest {
public JComponent makeUI() {
JTable table = new JTable(new DefaultTableModel(5, 3));
table.setFillsViewportHeight(true);
JPopupMenu popupMenu = new JPopupMenu() {
@Override
public void show(Component invoker, int x, int y) {
//int rowAtPoint = table.rowAtPoint(
// SwingUtilities.convertPoint(this, new Point(x, y), table));
//FilesManager.this.generateTablePopupMenu(rowAtPoint);
int rowAtPoint = table.rowAtPoint(new Point(x, y));
System.out.println(rowAtPoint);
super.show(invoker, x, y);
}
};
table.setComponentPopupMenu(popupMenu);

JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new JTablePopupMenuTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}


Related Topics



Leave a reply



Submit