Clint
Clint

Reputation: 347

JMenuBar does not enable after being disabled

I'm disabling a JMenuBar before displaying a FileDialog (as the menu items are still active when the FileDialog is visible) using getJMenuBar().setEnabled(false) and then calling getJMenuBar().setEnabled(true) after the FileDialog closes, but the menu items do not become active after being enabled - they will if I change to another application and back to mine. I've tried calling getJMenuBar().revalidate() and/or getJMenuBar().repaint() to no avail.

Of note, I'm using a screen menu bar as I'm on OS X. Sample code that shows the problem:

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener;

public class MenuTest extends JFrame implements ActionListener {

    private JMenuItem menuItemNew = new JMenuItem("New");
    private JMenuItem menuItemOpen = new JMenuItem("Open");
    private JMenuItem menuItemSave = new JMenuItem("Save");
    private JMenu menuFile = new JMenu("File");
    private JMenuBar menuBar = new JMenuBar();

    public MenuTest() {
        super("JMenu Test");
        setSize(300, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        menuItemOpen.addActionListener(this);

        menuFile.add(menuItemNew);
        menuFile.add(menuItemOpen);
        menuFile.add(menuItemSave);
        menuBar.add(menuFile);
        setJMenuBar(menuBar);

        setVisible(true);
    }

    public void openFile() {
        getJMenuBar().setEnabled(false);
        FileDialog fd = new FileDialog(this, "Choose a file", FileDialog.LOAD);
        fd.setVisible(true);
        getJMenuBar().setEnabled(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == menuItemOpen) {
            openFile();
        }
    }

    public static void main(String[] arguements) {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        new MenuTest();
    }

}

Thanks in advance for comments / suggestions!

Upvotes: 5

Views: 718

Answers (2)

matt
matt

Reputation: 12347

For me, I could resolve the issue by enabling/disabling each JMenuItem and not the menu bar or menu itself.

I also did everything on the EDT, just in case that was the problem, but it didn't help.

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener;

public class MenuTest implements ActionListener {

    private JMenuItem menuItemNew = new JMenuItem("New");
    private JMenuItem menuItemOpen = new JMenuItem("Open");
    private JMenuItem menuItemSave = new JMenuItem("Save");
    private JMenu menuFile = new JMenu("File");
    private JMenuBar menuBar = new JMenuBar();
    JFrame frame;

    public MenuTest() {
    }
    
    public void buildGui(){
        frame = new JFrame("JMenu Test");
        frame.setSize(300, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        menuItemOpen.addActionListener(this);

        menuFile.add(menuItemNew);
        menuFile.add(menuItemOpen);
        menuFile.add(menuItemSave);
        
        menuBar.add(menuFile);
        frame.setJMenuBar(menuBar);

        frame.setVisible(true);
    }

    public void openFile() {
        menuItemNew.setEnabled(false);
        menuItemOpen.setEnabled(false);
        menuItemSave.setEnabled(false);
        
        FileDialog fd = new FileDialog(frame, "Choose a file", FileDialog.LOAD);
        fd.setVisible(true);
        
        menuItemNew.setEnabled(true);
        menuItemOpen.setEnabled(true);
        menuItemSave.setEnabled(true);
        
    }

    @Override
    public void actionPerformed(ActionEvent e) {
            openFile();
        }

    public static void main(String[] arguments) {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        EventQueue.invokeLater(()->{
            new MenuTest().buildGui();
                });
    }
}

Upvotes: 2

Jeff Holt
Jeff Holt

Reputation: 3190

I can confirm Matt's answer worked for me on macOS. But there are some things he didn't mention that I will, in the hope that it saves someone some time.

Thing 1

I tried disabling only the JMenu components in the JMenuBar so that I would have to write only one for loop. I got the same behavior that I got when I tried to temporarily disable the JMenuBar.

        JMenuBar b = getJMenuBar();
        for (int i=0; i<b.getMenuCount(); i++)
            b.getMenu(i).setEnabled(false); // then true later

Thing 2

By only temporarily disabling the JMenuBar, if I do one of the following, the menu items would work again:

  1. focus on another app and then focus on this app
  2. make this app interact on the Event Dispatch Thread in any way

Thing 3

You do not want to blindly disable and later enable a JMenuItem afterward if it was already disabled beforehand.

private ArrayList<JMenuItem> disabledMenuItems = new ArrayList<JMenuItem>();

private void disableAppMenuItems () {
    JMenuBar b = getJMenuBar();
    for (int i=0; i<b.getMenuCount(); i++) {
        JMenu m = b.getMenu(i);
        for (int j=0; j<m.getItemCount(); j++) {
            JMenuItem mi = m.getItem(j);
            if (mi != null && mi.isEnabled()) {
                mi.setEnabled(false);
                disabledMenuItems.add(mi);
            }
        }
    }
}

private void undoDisableAppMenuItems () {
    for (JMenuItem i : disabledMenuItems)
        i.setEnabled(true);
    disabledMenuItems.clear();
}

Thing 4

Not every JMenuItem in a JMenu can be disabled or enabled (e.g., JSeparator components). That's why the above code covers the case where a JMenuItem is null.

Upvotes: 1

Related Questions