jTable right-click popup menu
Asked Answered
V

4

27

I have a SQL database and I am working on a program that will allow me to add/delete/modify records. I already managed to add records I am working on editing/deleting them.

I want to display the existing records in a table so I am using jTable. I found some code online and modified it to pull the records and display them in a jtable but i dont know how to code the rightclick and display a popup menu.

In that popup menu I want to display options such as delete record and modify record.

This is the code I am using the make the jTable and display the data:

 private void menuDeleteAuthorActionPerformed(java.awt.event.ActionEvent evt) {                                                 
    TableFromDatabase deleteAuthor = new TableFromDatabase();
    deleteAuthor.pack();
    deleteAuthor.setVisible(true);

    Vector columnNames = new Vector();
    Vector data = new Vector();

    try
    {

        Connection connection = DriverManager.getConnection( url, user, password );

        //  Read data from a table

        String sql = "SELECT * FROM Authors";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery( sql );
        ResultSetMetaData md = rs.getMetaData();
        int columns = md.getColumnCount();

        //  Get column names

        for (int i = 1; i <= columns; i++)
        {
            columnNames.addElement( md.getColumnName(i) );
        }

        //  Get row data

        while (rs.next())
        {
            Vector row = new Vector(columns);

            for (int i = 1; i <= columns; i++)
            {
                row.addElement( rs.getObject(i) );
            }

            data.addElement( row );
        }

        rs.close();
        stmt.close();
        connection.close();
    }
    catch(Exception e)
    {
        System.out.println( e );
    }

    //  Create table with database data

    JTable table = new JTable(data, columnNames)
    {
        public Class getColumnClass(int column)
        {
            for (int row = 0; row < getRowCount(); row++)
            {
                Object o = getValueAt(row, column);

                if (o != null)
                {
                    return o.getClass();
                }
            }

            return Object.class;
        }
    };

    JScrollPane scrollPane = new JScrollPane( table );
    getContentPane().add( scrollPane );

    JPanel buttonPanel = new JPanel();
    getContentPane().add( buttonPanel, BorderLayout.SOUTH );
}

I am new to Java so please be kind in your responses. Thank you all in advance for any assistance!

Varhol answered 24/5, 2013 at 20:47 Comment(2)
Take a look at JComponent#setComponentPopupPhalanstery
This solution (similar question that isn't linked here yet) worked best for me: https://mcmap.net/q/188946/-jpopupmenu-on-jtable-gt-get-the-cell-the-menu-was-created-onWorkday
N
49

Here is an example on how to do that. The easiest way to achieve this, is to set a JPopupMenu on the JTable directly.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;

public class TestTableRightClick {

    protected void initUI() {
        final JFrame frame = new JFrame(TestTableRightClick.class.getSimpleName());
        Vector<String> columns = new Vector<String>(Arrays.asList("Name", "Age"));
        Vector<Vector<String>> data = new Vector<Vector<String>>();
        for (int i = 0; i < 50; i++) {
            Vector<String> row = new Vector<String>();
            for (int j = 0; j < columns.size(); j++) {
                row.add("Cell " + (i + 1) + "," + (j + 1));
            }
            data.add(row);
        }
        final JTable table = new JTable(data, columns);
        final JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem deleteItem = new JMenuItem("Delete");
        deleteItem.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "Right-click performed on table and choose DELETE");
            }
        });
        popupMenu.add(deleteItem);
        table.setComponentPopupMenu(popupMenu);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestTableRightClick().initUI();
            }
        });
    }
}

In case you want to automatically select the row where the right-click was made, add the following snippet:

popupMenu.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        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) {
                // TODO Auto-generated method stub

            }

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

            }
        });
Nappe answered 24/5, 2013 at 22:13 Comment(8)
+1, for using setComponentPopupMenu() so you don't have to worry about writing a MouseListener.Stewpan
This implementation won't solve the problem in the question, because it's lost the information about which record the user right clicked. (Which one would you delete/modify? Where in the table would you add?)Workday
@JoshuaGoldberg That's up to you to decide what you want to do with this. Usually, I would guess that the delete would happen on the selected row(s) and the insertion would probably be after the currently selected row. The fact the right-click does not necessarily select the row of the JTable is L&F dependent. If wanted, you could force that as well by adding a MouseListener that changes row-selection on right-click. This implementation will solve the problem in the question.Nappe
Vector is deprecated class.Resort
@Resort It's not deprecated: Vector javadoc, but it's a bit obsolete to some extend. Nevertheless, DefaultTableModel and JTable have bothe kept constructors that use Vector, so it's an easy way to set them up.Nappe
@GuillaumePolet I tried your suggestion to force row-selection on right-click using a MouseListener on the table. But that trick does not work, because the setComponentPopupMenu will somehow have the highest priority and prevent the MouseListener to react on any right clicks.Frampton
@Frampton See my update at the end which provides a snippet to force the selection of the row upon right-click.Nappe
I added some additional checking inside popupMenuWillBecomeVisible to see if multiples rows were already selected. I would see a scenario where a user would right-click but if they were already multi-selecting (>2 rows) you don't want to wipe out their existing selection. But in the case on 0 or 1 existing selections having the right-click reset the selection seems to be intuitive action.Exercitation
S
18

A problem with a JTable is that the right click does not change the row selection. So you if have an Action that works on a specific row you need to left click the row first before right clicking to have the popup menu displayed.

If you want the row to be selected where your right click then you can use code like the following:

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") );
        popup.add( new JMenuItem("Do Something2") );
        popup.add( new JMenuItem("Do Something3") );
        JMenuItem menuItem = new JMenuItem("ActionPerformed");
        menuItem.addActionListener( this );
        popup.add( menuItem );

        JTable table = new JTable(50, 5);
        table.addMouseListener( new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                System.out.println("pressed");
            }

            public void mouseReleased(MouseEvent e)
            {
                if (e.isPopupTrigger())
                {
                    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);

                    popup.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        getContentPane().add( new JScrollPane(table) );
    }

    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)
    {
        TableRightClick frame = new TableRightClick();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}
Stewpan answered 24/5, 2013 at 22:44 Comment(0)
H
0

Unfortunately, none of these solutions worked for me on both Macs and PC's. Not sure why. But this worked on both:

@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    int row = table.rowAtPoint(SwingUtilities.convertPoint(popup, 0, 0, table));

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

The main problem with my solution is that it doesn't show that the row is selected until after the user chooses one of the menu options.

Hello answered 18/1, 2022 at 5:25 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Adolescence
K
-2

Another issue is that context menus are dynamic, your solution does not deal with changing the menu depending on the clicked row

popupMenu.addPopupMenuListener(new PopupMenuListener() 
{
   @Override
   public void popupMenuWillBecomeVisible(PopupMenuEvent e) 
   { 
       int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
       generateTablePopupMenu(rowAtPoint); // here
       SwingUtilities.invokeLater(new Runnable() 
           ...
Kalasky answered 27/9, 2016 at 7:31 Comment(1)
You addressed the issue but you didn't provide any answer here.Apologia

© 2022 - 2024 — McMap. All rights reserved.