Jamesy82
Jamesy82

Reputation: 379

Programmatically expand sub JMenuItems

I would like to programmatically expand a particular JMenuItem in a JPopup. For example in the code below

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

public class AutoExpandSubMenusDemo extends JFrame {

    private final JPopupMenu popup = new JPopupMenu("Popup");

    public AutoExpandSubMenusDemo() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JMenu menuB = new JMenu("B");

        menuB.add(new JMenuItem("X"));
        JMenuItem menuY = menuB.add(new JMenuItem("Y"));
        menuB.add(new JMenuItem("Z"));

        popup.add(new JMenuItem("A"));
        popup.add(menuB);
        popup.add(new JMenuItem("C"));

        final JButton button = new JButton("Show Popup Menu");
        button.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                popup.show(button, 0, button.getHeight());

                // Show menuY
            }
        });

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(button);
        getContentPane().add(buttonPanel);
    }

    public static void main(String[] args) {
        AutoExpandSubMenusDemo f = new AutoExpandSubMenusDemo();
        f.setSize(500, 300);
        f.setVisible(true);
    }
}

I would like to expand the popup menu so that the items B(menuB)/Y(menuY) are expanded and selected when the button is pressed.

Sorry if this is something that's easy to do but I've searched around and can't figure it out.

I did find the

MenuSelectionManager.defaultManager().setSelectedPath(...)

however this didn't work when I tried it and the javadoc specifies that it is called from the LaF and should not be called by clients.

Any help is much appreciated.

Upvotes: 6

Views: 2081

Answers (2)

predi
predi

Reputation: 5928

While I don't recommend doing this, since the documentation itself advises against it, here's how you could do it:

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;

public class AutoExpandSubMenusDemo extends JFrame {

    private final JPopupMenu popup = new JPopupMenu("Popup");

    public AutoExpandSubMenusDemo() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        final JMenu menuB = new JMenu("B");

        menuB.add(new JMenuItem("X"));
        final JMenuItem menuY = menuB.add(new JMenuItem("Y"));
        menuB.add(new JMenuItem("Z"));

        popup.add(new JMenuItem("A"));
        popup.add(menuB);
        popup.add(new JMenuItem("C"));

        final JButton button = new JButton("Show Popup Menu");
        button.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                popup.show(button, 0, button.getHeight());

                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        menuB.setPopupMenuVisible(true);
                        MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{popup, menuB, menuY});
                    }
                });

            }
        });

        JPanel buttonPanel = new JPanel();
        buttonPanel.add(button);
        getContentPane().add(buttonPanel);
    }

    public static void main(String[] args) {
        AutoExpandSubMenusDemo f = new AutoExpandSubMenusDemo();
        f.setSize(500, 300);
        f.setVisible(true);
    }
}

Most of this code is yours. I only added:

SwingUtilities.invokeLater(new Runnable() {

    public void run() {
        menuB.setPopupMenuVisible(true);
        MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{popup, menuB, menuY});
    }
});

which seems to work.

You could avoid abusing MenuSelectionManager via 'MenuItem.setArmed(boolean)'.

SwingUtilities.invokeLater(new Runnable() {

    public void run() {
        menuB.setPopupMenuVisible(true);
        menuB.setArmed(true);
        menuY.setArmed(true);
    }
});

The popup staying visible after selecting another menu item or dismissing the JPopupMenu still needs to be addressed though.

Another way is to fake a mouse event... :D

SwingUtilities.invokeLater(new Runnable() {

    public void run() {                        
        MouseEvent event = new MouseEvent(
                menuB, MouseEvent.MOUSE_ENTERED, 0, 0, 0, 0, 0, false);
        menuB.dispatchEvent(event);
        menuY.setArmed(true);
    }
});

This way it is as if the user actually used the mouse.

Upvotes: 8

aterai
aterai

Reputation: 9808

Another example:

MenuSelectionManager.defaultManager().setSelectedPath(
    new MenuElement[] {popup, menuB, menuB.getPopupMenu()});

enter image description here

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

public class AutoExpandSubMenusDemo2 extends JFrame {
  private final JPopupMenu popup = new JPopupMenu("Popup");

  public AutoExpandSubMenusDemo2() {
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    final JMenu menuB = new JMenu("B");

    menuB.add(new JMenuItem("X"));
    final JMenuItem menuY = menuB.add(new JMenuItem("Y"));
    menuB.add(new JMenuItem("Z"));

    popup.add(new JMenuItem("A"));
    popup.add(menuB);
    popup.add(new JMenuItem("C"));

    JPanel buttonPanel = new JPanel();
    buttonPanel.add(new JButton(new AbstractAction("Show menuB Popup") {
      @Override public void actionPerformed(ActionEvent e) {
        JButton button = (JButton) e.getSource();
        popup.show(button, 0, button.getHeight());
        //[Bug ID: JDK-6949414 JMenu.buildMenuElementArray() endless loop]
        //( http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6949414 )
        //menuB.doClick();
        MenuSelectionManager.defaultManager().setSelectedPath(
          new MenuElement[] {popup, menuB, menuB.getPopupMenu()});
      }
    }));
    buttonPanel.add(new JButton(new AbstractAction("Select menuY") {
      @Override public void actionPerformed(ActionEvent e) {
        JButton button = (JButton) e.getSource();
        popup.show(button, 0, button.getHeight());
        MenuSelectionManager.defaultManager().setSelectedPath(
          new MenuElement[] {popup, menuB, menuB.getPopupMenu(), menuY});
      }
    }));
    getContentPane().add(buttonPanel);
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new AutoExpandSubMenusDemo2();
    f.setSize(500, 300);
    f.setVisible(true);
  }
}

Upvotes: 2

Related Questions