aress31
aress31

Reputation: 350

JTable + JPopupMenu + ActionListener = Nightmare

My main class is a window containing graphical components including a JTable.

I have created a public class ContextMenu which is a custom implementation of a JPopupMenu and containing multiple JMenuItem.

I have registered a mouseListener on my JTable to show an instance of ContextMenu when a right-click is detected.

The problem is the following: "How to pass the selected rows to different function according to the chosen JMenuItem?"

The obvious answer would be to set ActionListener on my JMenuItem but remember that JTable is in a different class/object than the JPopupMenu.

Some code snipets are worth a thousand words.

public class Tab implements ITab {
private ContextMenu contextMenu;
private JTable table;
private List<SomeObject> toProcess;
--- code --
private JScrollPane drawScrollTable() {
        Object columns[] = {
            "something",
            "somethingElse"
        };
        Object rows[][] = {};
        table = new JTable(new DefaultTableModel(rows, columns));
        JScrollPane scrollPane = new JScrollPane(table);

        table.setSelectionForeground(Color.BLACK);
        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){
                contextMenu.show(e.getComponent(), e.getX(), e.getY());
            }
        });

        return scrollPane;
    }
-- code --
}

public class ContextMenu extends JPopupMenu {
    JMenuItem item;

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

        this.item(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {     
                 // Do action involving the selected row, even better if possible action involving the value hold in the column 0 of the selected row and the toProcess private field 
            }
        });

        add(item);
    }
}

I do not know if what I ask is possible.

Upvotes: 1

Views: 228

Answers (2)

Adrian Colomitchi
Adrian Colomitchi

Reputation: 3992

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)
      );

    }
}

Upvotes: 1

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Dirty way: pass references.
Cleaner way: structure your program via M-C-V

My Minimal, Complete, and Verifiable example of the dirty way:

import java.awt.BorderLayout;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class TableTest extends JPanel {
    private TableClass tableClass = new TableClass();

    public TableTest() {
        setLayout(new BorderLayout());
        add(tableClass);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("TableTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new TableTest());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

class ContextMenu extends JPopupMenu {
    private JMenuItem item;
    private TableClass tableClass;  // dirty direct reference *****

    public ContextMenu(TableClass tableClass){
        this.tableClass = tableClass;
        this.item= new JMenuItem("item");

        this.item.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {     
                int row = tableClass.getSelectedRow();
                JTable table = tableClass.getTable();
                System.out.println("row: " + row);
                StringBuilder sb = new StringBuilder("Data: ");
                for (int i = 0; i < table.getColumnCount(); i++) {
                    sb.append(table.getValueAt(row, i));
                    if (i != table.getColumnCount() - 1) {
                        sb.append(", ");
                    }
                }
                System.out.println(sb);
            }
        });

        add(item);
    }
}

class TableClass extends JPanel {
    // ***** passing **this** into the ContextMenu class
    private ContextMenu contextMenu = new ContextMenu(this); 
    private static final Integer[][] DATA = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    private static final String[] COLUMN_NAMES = {"A", "B", "C"};
    private DefaultTableModel model = new DefaultTableModel(DATA, COLUMN_NAMES);
    private JTable table = new JTable(model);

    public TableClass() {
        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) {
                    showPopUp(e);
                }
            }

            private void showPopUp(MouseEvent e) {
                contextMenu.show(e.getComponent(), e.getX(), e.getY());                
            }
        });

        setLayout(new BorderLayout());
        add(new JScrollPane(table));
    }

    public int getSelectedRow() {
        return table.getSelectedRow();
    }

    public int getSelectedColumn() {
        return table.getSelectedColumn();
    }

    public JTable getTable() {
        return table;
    }
}

Upvotes: 1

Related Questions