I wish to programmatically cause a particular item on a menu to be selected and to display as such so that if Enter is pressed, the corresponding action will be performed. Ubnfortunately I find that neither JMenuItem.setSelected(), nor JPopupMenu.setSelectedItem() does what I want. Basically I want to happen what happens when either the arrow key is pressed or the mouse moves into the space of a particular item - the background color alters, indicating the selection. I did not program that, it just happens. Why don't these APIs do the same thing? This is driving me nuts. It should not be this hard. Is there some API that does what I want?
This kinda worked:
JMenuItem#setArmed(boolean);
although you don't see it unless you traverse the JMenus to get there. Perhaps if you call that on each menu above it?
EDIT: Perhaps you want an accelerator for your menu item? See: How To Use Menus: Enabling Keyboard Operation
JCheckBoxMenuItem
so that it is in sync with the GUI. I can trigger the action, but I cannot get the menu items to select or deselect. –
Cormac java.awt.Robot
can do Trick ;)
Consider the code Given below:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.Robot;
public class JMenuFrame extends JFrame implements ActionListener
{
JMenuBar bar;
JMenu menu ;
String[] items;
JMenuItem[] menuItems;
JButton start;
Robot robot;
public void prepareAndShowGUI()
{
try
{
robot = new Robot();
}
catch (Exception ex){}
bar = new JMenuBar();
menu = new JMenu("File");
items = new String[]{"Open","Save","Save As","Quit"};
menuItems = new JMenuItem[items.length];
start = new JButton("Click me");
for (int i = 0 ; i < items.length ; i++)
{
menuItems[i] = new JMenuItem(items[i]);
menuItems[i].addActionListener(this);
menu.add(menuItems[i]);
}
bar.add(menu);
setJMenuBar(bar);
start.addActionListener(this);
getContentPane().add(start,BorderLayout.SOUTH);
setPreferredSize(new Dimension(300,400));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent evt)
{
if ("Click me".equals(evt.getActionCommand()))
{
menu.doClick();
if (robot!=null)
{
for (int i = 0 ; i<=2 ; i++) //Suppose you want to select 3rd MenuItem
{
if (!menuItems[i].isEnabled())
{
continue;
}
robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_UP);
}
}
}
else
{
JOptionPane.showMessageDialog(this,evt.getActionCommand()+" is pressed","Information",JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
JMenuFrame mf = new JMenuFrame();
mf.prepareAndShowGUI();
}
});
}
}
It's ugly as sin, but this, in connection with splungebob's setArmed() answer above is the full solution: First, make the menu a MenuKeyListener and add it a a MenuKeyListener to itself. Then:
public void menuKeyReleased(MenuKeyEvent e) {
if (e.getModifiers() == 0) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER:
case KeyEvent.VK_SPACE:
for (MenuElement elem : this.getSubElements()) {
if (elem instanceof JMenuItem) {
JMenuItem item = (JMenuItem) elem;
if (item.isArmed()) {
Action action = item.getAction();
if (action != null) {
action.actionPerformed(new ActionEvent(this, 0, null));
e.consume();
setVisible(false);
}
}
}
}
}
}
}
I can't believe that this was this difficult. Swing has definite limitations when it comes to building keyboard-centric interfaces.
Although I'm not sure I agree with the requirements (see my comment in the OP), I still wanted to give it a crack. The first half of the code is just setting up the GUI so that the condition can be creted by the user.
- A menu is created with 3 items, and 3 separate checkboxes are created to control the state of the menu items.
- If at any time only one menu item is enabled, auto-expand the menu and "pre-select" the item. The code to auto-expand the menu was ripped from JMenu.
- Add a MenuKeyListener to the menu to capture the user hitting the space bar while the menu tree is expanded.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
public class JMenuDemo implements Runnable
{
private final String[] ACTIONS = new String[]{"Open", "Print", "Close"};
private JMenu fileMenu;
private JMenuItem[] menuItems;
private JCheckBox[] checkBoxes;
public static void main(String args[])
{
SwingUtilities.invokeLater(new JMenuDemo());
}
public void run()
{
JPanel panel = new JPanel(new GridLayout(0,1));
panel.setBorder(BorderFactory.createTitledBorder("Enabled"));
menuItems = new JMenuItem[ACTIONS.length];
checkBoxes = new JCheckBox[ACTIONS.length];
for (int i = 0; i < ACTIONS.length; i++)
{
final int index = i;
final String action = ACTIONS[i];
menuItems[i] = new JMenuItem(action);
menuItems[i].setAccelerator(KeyStroke.getKeyStroke(action.charAt(0),
ActionEvent.ALT_MASK));
menuItems[i].setMnemonic(action.charAt(0));
menuItems[i].addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println(action);
}
});
checkBoxes[i] = new JCheckBox(action);
checkBoxes[i].setSelected(true);
checkBoxes[i].addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent event)
{
checkBoxChanged(index);
}
});
panel.add(checkBoxes[i]);
}
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(createJMenuBar());
frame.add(panel, BorderLayout.SOUTH);
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void checkBoxChanged(int index)
{
menuItems[index].setEnabled(checkBoxes[index].isSelected());
evaluate();
}
private JMenuBar createJMenuBar()
{
fileMenu = new JMenu("File");
fileMenu.setMnemonic('F');
fileMenu.addMenuKeyListener(new MenuKeyListener()
{
@Override
public void menuKeyTyped(MenuKeyEvent event)
{
autoClick(event);
}
@Override
public void menuKeyPressed(MenuKeyEvent event) {}
@Override
public void menuKeyReleased(MenuKeyEvent event) {}
});
for (int i = 0; i < menuItems.length; i++)
{
fileMenu.add(menuItems[i]);
}
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
return menuBar;
}
private void autoClick(MenuKeyEvent event)
{
if (event.getModifiers() == 0 && event.getKeyChar() == KeyEvent.VK_SPACE)
{
for (JMenuItem menuItem : menuItems)
{
if (menuItem.isArmed())
{
menuItem.doClick();
MenuSelectionManager.defaultManager().setSelectedPath(null);
}
}
}
}
private void evaluate()
{
JMenuItem onlyOne = null;
for (JMenuItem menuItem : menuItems)
{
menuItem.setArmed(false);
if (menuItem.isEnabled())
{
if (onlyOne == null)
{
onlyOne = menuItem;
}
else
{
onlyOne = null;
break;
}
}
}
// Show the path if only one is enabled
if (onlyOne != null)
{
onlyOne.setArmed(true);
MenuElement me[] = buildMenuElementArray(fileMenu);
MenuSelectionManager.defaultManager().setSelectedPath(me);
}
}
/*
* Copied from JMenu
*/
private MenuElement[] buildMenuElementArray(JMenu leaf)
{
Vector<JComponent> elements = new Vector<JComponent>();
Component current = leaf.getPopupMenu();
while (true)
{
if (current instanceof JPopupMenu)
{
JPopupMenu pop = (JPopupMenu) current;
elements.insertElementAt(pop, 0);
current = pop.getInvoker();
}
else if (current instanceof JMenu)
{
JMenu menu = (JMenu) current;
elements.insertElementAt(menu, 0);
current = menu.getParent();
}
else if (current instanceof JMenuBar)
{
JMenuBar bar = (JMenuBar) current;
elements.insertElementAt(bar, 0);
MenuElement me[] = new MenuElement[elements.size()];
elements.copyInto(me);
return me;
}
}
}
}
I found this setSelectedPath()
works well in unison of mouse hover, unlike using menuItem.setArmed()
MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{popupMenu, menuItem});
© 2022 - 2024 — McMap. All rights reserved.
setSelected()
method controls whether aJCheckBoxMenuItem
orJRadioButtonMenuItem
has a check next to it. It does not relate to whether the item is highlighted. – Braze