An easily forgotten fact is that a JMenu is-a AbstractButton, as such you can set an Action to it. While that Action's actionPerformed is never called, its properties are used to keep the menu's corresponding properties in sync.
So assuming all your menus are driven by (groups of) Actions, you can define a wrapper Action that syncs its own enabled state to such a group and then set that wrapper to the menu. Advantages of this approach are that you can
- re-use such a wrapper f.i. if the same grouping applies in a mainMenu and a popup
- build trees of groups
The wrapper could be something like:
/**
* Empty Action with enabled state that's the OR'ed enabled of all contained actions.
*/
public static class OrEnabledEmptyAction extends AbstractAction {
private List<Action> actions;
public OrEnabledEmptyAction(Collection<Action> actions, String name) {
super(name);
this.actions = new ArrayList<>(actions);
installEnabledListener();
updateEnabled();
}
/**
* Updates this Action's enabled state dependent on enabled of
* contained actions.
*/
private void updateEnabled() {
boolean enabled = false;
for (Action action : actions) {
enabled |= action.isEnabled();
}
setEnabled(enabled);
}
/**
* Installs a PropertyChangeListener which updates this Action's
* enabled state on notification of enabled of contained actions.
*/
private void installEnabledListener() {
PropertyChangeListener l = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("enabled".equals(evt.getPropertyName()))
updateEnabled();
}
};
for (Action action : actions) {
action.addPropertyChangeListener(l);
}
}
@Override
public void actionPerformed(ActionEvent e) {
// does nothing, never called for a JMenu anyway
}
}
Its usage (your example with an additional mainMenu to highlight the re-use):
JPopupMenu popup = new JPopupMenu();
final Action actionBeep = new DefaultEditorKit.BeepAction();
final Action actionPaste = new DefaultEditorKit.PasteAction();
final List<Action> actions = new ArrayList<>();
actions.add(actionBeep);
actions.add(actionPaste);
JMenu menu = new JMenu();
// add actions to menu in popup
for (Action action : actions) {
menu.add(action);
}
// sets the menu's action to the OR-Enabled
menu.setAction(new OrEnabledEmptyAction(actions, "Add"));
popup.add(menu);
JMenuBar bar = new JMenuBar();
JMenu mainMenu = new JMenu();
// add actions to menu in menuBar
for (Action action : actions) {
mainMenu.add(action);
}
// re-use or-action
mainMenu.setAction(menu.getAction());
bar.add(mainMenu);
frame.setJMenuBar(bar);
JTable table = new JTable(3, 3);
table.setComponentPopupMenu(popup);
// for seeing the effect, change enabled state of only one action
// per released
table.addMouseListener(new MouseAdapter() {
int index;
@Override
public void mouseReleased(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e))
return;
actions.get(index).setEnabled(!actions.get(index).isEnabled());
index = (index +1) % actions.size();
}
});