Search code examples
javaswingoverridingjlistjpopupmenu

How to override the JPopupMenu method show?


my goal is to highlight a jlist item after a rightclick then show a jpopupmenu.. I read advice that overriding the method show is the way.. in my case i declare my jpopupmenu at the beginning of my class

     JPopupMenu popupMenu = new JPopupMenu();
     JMenuItem masterlist,settings;

then i have a method to set up my menuItems

    public void setPopupMenu(int depth//ignore this var){
    //popupMenu = new JPopupMenu();
    popupMenu.removeAll();

        masterlist = new JMenuItem("Masterlist");
        settings = new JMenuItem("Settings");

        //action for marsterlist
        masterlist.addActionListener(new ActionListener(){
            //stuff here
            }
        });
        //action for settings
        settings.addActionListener(new ActionListener(){
            //stuff here
            }
        });

        popupMenu.add(masterlist);
        popupMenu.add(settings);
 }

and my list is in a different method

list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
list.setComponentPopupMenu(popupMenu);

I tried putting this on a mouseAdapter of my list but the popupmenu fires first and ignores highlighting...

   if ( SwingUtilities.isRightMouseButton(mouseEvent) )//highlight the right clicked item
                 {

                    int row = list.locationToIndex(mouseEvent.getPoint());
                    list.setSelectedIndex(row); 
                    String val = (String)list.getSelectedValue();
                    System.out.println(val);
                 }

i know that overriding is something like this

  popupmenu = new JPopupMenu(){
  @Override
  public void show(){}
  }

but i cant do that because i am manipulating the menuitems on a method... or is there any other approach that anyone can suggest...


Solution

  • Rather then trying to modify the state of the JPopupMenu, why not simply modify the state of the menu item when you detect a right click...

    JList with right mouse selection and popup

    So, basically, I make use of the Actions API to define a menu item for a JPopupMenu, which allows it to register a ListSelectionListener to the underlying JList...

    public class ShowItemAction extends AbstractAction {
    
        private JList list;
    
        public ShowItemAction(JList list) {
            this.list = new JList();
            putValue(NAME, "Showing ...");
    
            list.addListSelectionListener(new ListSelectionListener() {
                @Override
                public void valueChanged(ListSelectionEvent e) {
                    if (!e.getValueIsAdjusting()) {
                        int index = list.getSelectedIndex();
                        String value = list.getSelectedValue().toString();
                        putValue(NAME, "Showing " + value + " @ " + index);
                    }
                }
            });
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            // The actual action to be performed when selected
        }
    
    }
    

    All this does is, when the selection is changed, is change the text (NAME) of the action, which changes the text of the menu item, based on the currently selected row.

    When I create the JList, I assign it a JPopupMenu via the setComponentPopupMenu method, which means I no longer need to care about it and it will be displayed appropriately based on the current look and feel requirements

    JList list = new JList(model);
    JPopupMenu popupMenu = new JPopupMenu();
    popupMenu.add(new ShowItemAction(list));
    list.setComponentPopupMenu(popupMenu);
    

    I then add a MouseListener to the JList which I use to change the row selection when you right click on the JList...

    list.addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            JList list = (JList)e.getComponent();
            if (SwingUtilities.isRightMouseButton(e)) {
                int row = list.locationToIndex(e.getPoint());
                list.setSelectedIndex(row);
            }
        }
    });
    

    This not entirely fool proof, as if you right click the JList while the popup is visible, the MouseListener doesn't appear to get notified :P

    Runnable example...

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.AbstractAction;
    import static javax.swing.Action.NAME;
    import javax.swing.DefaultListModel;
    import javax.swing.JFrame;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    import javax.swing.JScrollPane;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.event.ListSelectionEvent;
    import javax.swing.event.ListSelectionListener;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    DefaultListModel model = new DefaultListModel();
                    model.addElement("One");
                    model.addElement("Two");
                    model.addElement("Three");
                    model.addElement("Four");
                    JList list = new JList(model);
                    JPopupMenu popupMenu = new JPopupMenu();
                    popupMenu.add(new ShowItemAction(list));
                    list.setComponentPopupMenu(popupMenu);
    
                    list.addMouseListener(new MouseAdapter() {
    
                        @Override
                        public void mousePressed(MouseEvent e) {
                            if (SwingUtilities.isRightMouseButton(e)) {
                                int row = list.locationToIndex(e.getPoint());
                                list.setSelectedIndex(row);
                            }
                        }
    
                    });
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new JScrollPane(list));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class ShowItemAction extends AbstractAction {
    
            private JList list;
    
            public ShowItemAction(JList list) {
                this.list = new JList();
                putValue(NAME, "Showing ...");
    
                list.addListSelectionListener(new ListSelectionListener() {
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        if (!e.getValueIsAdjusting()) {
                            int index = list.getSelectedIndex();
                            String value = list.getSelectedValue().toString();
                            putValue(NAME, "Showing " + value + " @ " + index);
                        }
                    }
                });
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
            }
    
        }
    
    }