Question

I noticed that once my JPopupMenu becomes visible for the first time, its MenuItems do not reflect changes made to them for the next visibility. Here is the code;

private static void addListener(final JPopupMenu popup, final String someLetter)
{
    popup.addPopupMenuListener(new PopupMenuListener(){            
        @Override
        public void popupMenuWillBecomeVisible(PopupMenuEvent evt)
        {
            JMenuItem menuItem3 = (JMenuItem)popup.getComponent(3); 
            String[] letters = {"A", "B", "C", "D"};
            ArrayList<String> lettersList = new ArrayList<String>();
            lettersList.addAll(Arrays.asList(letters));
            if(lettersList.contains(someLetter)){
                menuItem3.setEnabled(true);
            }
            else{
                menuItem3.setEnabled(false);
            }
        }
    });
}

Normally I would expect that the lines under popupMenuWillBecomeVisible will always execute whenever the popupMenu is getting popped-up. But to my surprise it only works on the first time and subsequently does not test for the condition specified to to either enable or disable menuItem3. Please let somebody kindly help a brother out!

Was it helpful?

Solution

This seems to work quite well for me. Check out the example below which displays 5 menu items and toggles their "enability" everytime you perform a right-click:

import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class TestPopupMenu {

    private void initUI() {
        final JFrame frame = new JFrame(TestPopupMenu.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel();
        final JPopupMenu popup = new JPopupMenu();
        final List<JMenuItem> items = new ArrayList<JMenuItem>();
        for (int i = 0; i < 5; i++) {
            JMenuItem item = new JMenuItem(new AbstractAction("Action " + (i + 1)) {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JOptionPane.showMessageDialog(popup, getValue(NAME) + " has been clicked");
                }
            });
            item.setEnabled(i % 2 == 0);
            items.add(item);
            popup.add(item);
        }
        popup.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                for (JMenuItem item : items) {
                    item.setEnabled(!item.isEnabled());
                }
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {

            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {

            }
        });
        panel.setComponentPopupMenu(popup);
        frame.add(panel);
        frame.setSize(300, 200);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {

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

OTHER TIPS

Thanks to @mKorbel for pushing me to try and write an SSCCE cos in the process, I found a solution. Here's my SSCCE anyways, It may just be useful for someone else. who knows? Just copy and Run to see how it works.

(Note: The Code is not really short though because of the Secondary question I intend to pose after this)

import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class CustomPopup extends JPopupMenu
{
    private JMenuItem menuItem1 = new JMenuItem("One");
    private JMenuItem menuItem2 = new JMenuItem("Two");
    private JMenuItem menuItem3 = new JMenuItem("Three");
    private JMenuItem menuItem4 = new JMenuItem("Four");
    private JMenuItem menuItem5 = new JMenuItem("Five");

    public CustomPopup()
    {
        this.add(menuItem1);
        this.add(menuItem2);
        this.add(menuItem3);
        this.add(menuItem4);
        this.add(menuItem5);
        addListeners(this);
    }

    private  void addListeners(final JPopupMenu popup /*, final String someLetter*/)
    {
        popup.addPopupMenuListener(new PopupMenuListener(){            
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent evt)
            {
                JMenuItem menuItem = (JMenuItem)popup.getComponent(3); 
                String[] letters = {"A", "B", "C", "D"};
                ArrayList<String> lettersList = new ArrayList<String>();
                lettersList.addAll(Arrays.asList(letters));
                String someLetter = getRandomAlphabet();
                if(lettersList.contains(someLetter)){
                    menuItem.setEnabled(true);
                }
                else{
                    menuItem.setEnabled(false);
                }
            }
            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent evt){
                // No Override
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent evt){
                // No Override
            }
        });
    }

    private  String getRandomAlphabet()
    {
        String alpha = "";
        String Alphas[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};
        double bigNum = Math.random() * 1000000;
        String str = String.valueOf(bigNum);
        int idx = Integer.valueOf(String.valueOf(str.charAt(str.length() - 1)));
        if(idx < 6)
        {
            alpha = Alphas[idx];
        }
        else
        {
            alpha = Alphas[idx - 5];
        }
        return alpha;
    }

    public static void main(String[] args)
    {
        /*try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/

        JPanel panel = new JPanel();
        JPopupMenu popupMenu = new CustomPopup();
        panel.setComponentPopupMenu(popupMenu);
        panel.setLayout(new GridBagLayout());

        /*JButton button = new JButton("Action");
        button.setSize(new Dimension(60, 20));
        button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e)
            {
                JOptionPane.showMessageDialog(null, "Button was clicked");
            }
        });
        panel.add(button);*/

        JFrame frame = new JFrame("popupTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(200, 200));
        frame.add(panel);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

THE QUESTION: Now that this does what I want it to do, (i.e enable and disable menuItem4 dynamically based on random results from some specified computation getRandomAlphabet()), I looked at @Guillaume Polet's Lovely implementation and tested it. Very nice... So i just decided to add a button and test for one of the most annoying issues I have ever had with popups when using a UIManager that's not the Native Java Default... and the same thing happens!

What do I mean? When the LnF is not the default Metal, clicking outside the popupMenu on any other Actionable component after the popupMenu is displayed does not work on the first click (this only sets the popup invisible). Then you have to click on the component again (a second time) before its Action can be fired... Very annoying aint it?

To have a feel of this scenario, just uncomment the UIManager section of the code above ( I took that from @Guillaume Polet's beautiful solution) and also uncomment the Button section that I added, then run the program and see what happens when you try to click the button immediately after the popupMenu is displayed. The funny thing is this issue doesnt occur when using Java's default Metal LnF

I have battled this issue using System default Look and Feel on Windows 7 to no avail, so I decided to let sleeping dogs lie... But honestly, I think there's a work-around for this thing... I know there must be someone who's got a hack for this thing... Code-blocks, Directions, Links, anything, anywhere that solves this issue will be to my utmost delight. Thanks good people!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top